Design of Approve Cell
背景
在 SUDT 的相关开发中,我们遇到了一个与用户体验紧密相关的问题。那就是,用户如何在没有某 SUDT 的 ACP cell 的时候接收该 SUDT?
一种解决方案是,用户需要自行创建一个该 SUDT 的 ACP cell(anyone-can-pay cell),而用户实际上无法在链上提前得知有人将要给 ta 发送一个新的 SUDT,目前只能通过链下通知的方式,告知用户创建需要 ACP cell。另外一种解决方案是,发送方需要在发送 SUDT 时赠送给对方一个该 SUDT 的 ACP cell。这两种解决方案在一定程度上会造成接收方无法便捷地接收代币,发送方需要额外付出转账成本的问题。
为了解决这一问题,我们设计了一个 Approve Cell 的方式,当发送方给一个没有该 SUDT ACP cell 的用户发送 SUDT 时,可以将 SUDT 转至一个 Approve Cell,然后等待接收方前来认领。这样就可以实现接收方在没有提前创建该 SUDT 的 ACP cell 的情况下,从链上获取到发送方的代币发送信息,进而再决定要不要创建一个 ACP cell 来接收该部分资产。
解决方案
发送方将需要转账的 SUDT 转移至一个 Approve Cell,在 Approve Cell 的 lock args 中添加发送方和接收方的信息,接收方在链上读取到相关的授权信息后,即可按照规则进行解锁。
// Structure of Approve Cell
capacity: uint128 // 8 bytes
lock script:
code_hash: <approve_cell_lock_script> // 32 bytes
hash_type: type // 1 byte
args: <receiver_lock_hash>, <sender_lock_hash> // 20 bytes + 20 bytes
type script: <sudt_type_script> // 65 bytes
data:{
sudt_amount // 16 bytes
}
// Total: 162 bytes
- Apprvoe cell 设置有
approval 周期(限制发送方无法在 approval 周期内撤销 Approve Cell,这个会写死在approve_cell_lock_script 内)
Approve Cell 的解锁逻辑
Approve Cell 的 lock script 有两种解锁方式:
- 接收方接收。接收方按照 Approve Cell 的规则解锁 Approve Cell,解锁方式根据签名规则分成两种:
- 接收方提供 lock args,并使用默认的 secp256k1_blake160 作为 code_hash,组装成 lock script,并进行签名,且组装成的 lock script 经对比与 Approve Cell 中的
receiver_lock_hash一致。 - 接收方直接提供与
receiver_lock_hash一致的 cell 作为 input cell,并进行签名。
- 接收方提供 lock args,并使用默认的 secp256k1_blake160 作为 code_hash,组装成 lock script,并进行签名,且组装成的 lock script 经对比与 Approve Cell 中的
- 发送方撤回。发送方创建的 Approve Cell 在链上存在时间超过
approve 周期未被接收方领取时,发送方可以通过提供与sender_lock_hash一致的 cell 作为 input cell,并进行签名,实现撤回。
交易结构示例
创建 Approve Cell
1. 发送方给没有该 UDT ACP Cell 的用户发送 UDT 时,将会创建一个 Approve Cell
Inputs:
SUDT Cell:
Capacity: 142 CKBytes
Lock: <sender_lock_script>
Type: <sudt_type_script>
Data:
sudt_amount: 1000 UDT
CKB Normal Cell:
Capacity: 1000 CKBytes
Lock: <sender_lock_script>
Outputs:
Approve Cell:
Capacity: 162 CKBytes
Lock:
code_hash: <approve_cell_lock_script>
hash_type: type
args: <receiver_lock_hash>, <sender_lock_hash>
Type: <sudt_type_script>
Data:
sudt_amount: 100 UDT // approve amount of UDT
SUDT Cell:
Capacity: 142 CKBytes
Lock: <sender_lock_script>
Type: <sudt_type_script>
Data:
sudt_amount: 900 UDT
CKB Normal Cell:
Capacity: (838-fee) CKBytes
Lock: <sender_lock_script>
Witnesses
<valid signature for sender_sudt_cell_lock_hash>
<valid signature for sender_ckb_normal_cell_lock_hash>
Approve Cell 解锁逻辑1: 接收方申领
接收方使用官方 secp256k1_blake160 lock 进行接收(适用于 CKB 短地址)
接收方提供 lock args,并使用默认的 secp256k1_blake160 作为 code_hash,组装成 lock script,并进行签名,且组装成的 lock script 经对比与 Approve Cell 中的 receiver_lock_hash 一致。
Inputs:
Approve Cell:
Capacity: 162 CKBytes
Lock:
code_hash: <approve_cell_lock_script>
hash_type: type
args: <receiver_lock_hash>, <sender_lock_hash>
Type: <sudt_type_script>
Data:
sudt_amount: 100 UDT
Anyone SUDT Cell:
Capacity: 142 CKBytes
Lock: <anyone_lock_script>
Type: <sudt_type_script>
Data:
sudt_amount: 200 UDT
Outputs:
Anyone SUDT Cell:
Capacity: 142 CKBytes
Lock: <anyone_lock_script>
Type: <sudt_type_script>
Data:
sudt_amount: 300 UDT
CKB Normal Cell:
Capacity: 162 CKBytes
Lock: <sender_lock_script>
Witnesses:
<valid signature for secp256k1_blake160 + receiver lock script> // need a identifier, marked as receiver
<valid signature for anyone_lock_script>
接收方使用其他 lock script 进行接收
接收方直接提供与 receiver_lock_hash 一致的 cell 作为 input cell,并进行签名。
Inputs:
Approve Cell:
Capacity: 162 CKBytes
Lock:
code_hash: <approve_cell_lock_script>
hash_type: type
args: <receiver_lock_hash>, <sender_lock_hash>
Type: <sudt_type_script>
Data:
sudt_amount: 100 UDT
SUDT Cell:
Capacity: 142 CKBytes
Lock: <receiver_lock_script> // equals to the receiver_lock_hash in the args part of Approve Cell
Type: <sudt_type_script>
Data:
sudt_amount: 200 UDT
Outputs:
SUDT Cell:
Capacity: 142 CKBytes
Lock: <receiver_lock_script>
Type: <sudt_type_script>
Data:
sudt_amount: 300 UDT
CKB Normal Cell: // send CKB back to the sender
Capacity: 162 CKBytes
Lock: <sender_lock_script>
Witnesses:
<000...000>
<valid signature for receiver_lock_hash> // need a identifier, marked as receiver
Approve Cell 解锁逻辑2: 发送方撤回
发送方创建的 Approve Cell 在链上存在时间超过 approval 周期未被接收方领取时,发送方撤回 Approve Cell。
撤回时发送方需要在 input cells 中加入一个 lock script 与 Approve Cell args 中的 sender_lock_hash 一致的 cell,根据用户持有各种 cells 的不同情况,会衍生出多种多样的交易组装方式,为了方便前端筛选合适 cell 组装交易,我们可以优先考虑以下两种情况:
- 如果发送方还有该 SUDT 的 cell (我们假设 SUDT cell 通常采用的都是 ACP lock),则将其作为 input cell,进行签名
- 如果发送方没有该 SUDT 的 cell,而有与
sender_lock_hash一致的 CKB Normal Cell,则将其作为 input cell,进行签名
发送方还有该 SUDT 的 cell
Inputs:
Approve Cell:
Capacity: 162 CKBytes
Lock:
code_hash: <approve_cell_lock_script>
hash_type: type
args: <receiver_lock_hash>, <sender_lock_hash>
Type: <sudt_type_script>
Data:
sudt_amount: 100 UDT
USDT Cell: // sender can use a SUDT Cell to unlock the Approve Cell
Capacity: 142 CKBytes
Lock: <sender_lock_script> // equals to the sender_lock_hash in the args part of Approve Cell
Type: <sudt_type_script> // equals to the type script of Apporve Cell
Data:
sudt_amount: 900 UDT
Outputs:
SUDT Cell:
Capacity: 142 CKBytes
Lock: <sender_lock_script>
Type: <sudt_type_script>
Data:
sudt_amount: 1000 UDT
CKB Normal Cell:
Capacity: 162 CKBytes
Lock: <sender_lock_script>
Witnesses:
<000...000>
<valid signature for sender_lock_hash> // need a identifier, marked as sender
发送方没有该 SUDT 的 cell,但还有 CKB Normal Cell
Inputs:
Approve Cell:
Capacity: 162 CKBytes
Lock:
code_hash: <approve_cell_lock_script>
hash_type: type
args: <receiver_lock_hash>, <sender_lock_hash>
Type: <sudt_type_script>
Data:
sudt_amount: 100 UDT
CKB Normal Cell: // sender can use a CKB Normal Cell to unlock the Approve Cell
Capacity: XX CKBytes
Lock: <sender_lock_script> // equals to the sender_lock_hash in the args part of Approve Cell
Outputs:
SUDT Cell:
Capacity: 142 CKBytes
Lock: <sender_lock_script>
Type: <sudt_type_script>
Data:
sudt_amount: 100 UDT
Sender CKB Normal Cell:
Capacity: XX+20 CKBytes
Lock: <sender_lock_script>
Witnesses:
<000...000>
<valid signature for sender_lock_hash> // need a identifier, marked as sender