Idea about the composability of assets in ckb

Recently, I am working on the prototype of the generic payment channel (GPC). But I find the current model is not completely generic. For example, if the implementation of the payment channel contract in ethereum follows the ERC20 standard, any token that complies with the ERC20 standard can create channel each other. But it is impossible in current GPC implementation because one cell can only store one typescript. To achieve perfect composability, I have two ways to go.

  • Multiple gpc outputs.
  • A brand new typescript can store many typescripts.

For me, the first one is ugly, because the transaction about gpc will be swollen. If you have 100 types of assets, you need 100 outputs. So I try to explore the possibility to fuse different typescripts into one. One possible solution is creating another special typescript called collector.

The structure of the output_data of collector is shown above. In a cell, only output_data and typescript is related to type, so we only need to record them. With such a structure, we can merge different typescripts into one single output_data. Then, collector should guarantee the data keep consistent until these types are return to their typescripts correspondingly. That all it needs to do. Let me explain it more precisely. There are three cases.

  • collector is only in outputs: It checks whether the aggregate of input type is equal to the output_data of collector.
  • collector are both in inputs and outputs: It should check the output_data in inputs and outputs are consistent.
  • collector is only in the inputs: It checks all the hashes in output_data of collector appear in the output. This rule ensures no asset will burn out. The typescript about assets in outputs will take the responsibility of validating the output_data by their ways.

Nevertheless, we can easily see there are two drawbacks.

Firstly, we need to persuade all contract developers to treat collector as an exception. More specifically, they should believe the output_data from collector about their typescripts are legal. So collector is allowed to “burn” (store) and “create” (release) their tokens (output_data).

Secondly, the collector is exclusive, in outputs at least. That is, if there is a collector in outputs, the existence of any other typescript in outputs should cause failure during the validation of collector. The rationale is simple. For example, I have 100 UDT1 and 50 UDT2, and I want to fuse 50 UDT1 and 50 UDT2. So I will have a change with 50 UDT1. In this case, if we want collector to recognize the change output and calculate 50 + 50 = 100. We should update its code to teach it how to parse and calculate UDT1. However, it will cause a huge body of code because every time you create a new type, we should update the code. Also, frequent updates will undoubtedly reduce users’ trust in the security of the contract.

That is my idea about the composability of type in ckb, I hope it can become something like TYPEID in current ckb. Of course, it is still naive and may have some errors. I would appreciate it if you have any comments.


I’m afraid it’s very difficult. A better way to achieve this is to use some deposit cells, that allows multiple assets locking into a single deposit cell. And tread this single cell as an endpoint of the GPC. This method won’t break the existing logics in sUDT. It connects the UDT with the GPC by middleware, not in a direct coupling way.


You can design the collector as a typescript along with a lockscript to overcome the drawbacks.


  • inputs
    • udt cell 1
      • type: udt1
      • data: 100
      • lock: Alice
    • udt cell 2
      • type: udt2
      • data: 50
      • lock: Bob
  • outputs
    • collector cell
      • type: collector type
      • data:
        • assets: 50 udt1, 50 udt2
        • pubkeys: Alice, Bob
        • timeout: T
        • nonce
      • lock: anyone-can-unlock
    • udt cell 3
      • type: udt1
      • data: 50
      • lock: Alice
    • udt cell 4
      • type: udt1
      • data: 50
      • lock: collector lock
    • udt cell 5
      • type: udt2
      • data: 50
      • lock: collector lock

Collector lockscript ensures typescript of the first input is collector typescript. Collector typescript verify other conditions in your original design.


I think this is unnecessary. UDT developers can choose whether to honor collector all by themselves independently, depending on their own use cases.

1 Like

@zhichunlu @huwenchao good ideas! Since we are talking about aggregate UDTs owned by separate users, who should provide the ckbytes for the aggregated cell in your solution?

This reminds me of EIP-998.


Well, users can negotiate it easily. We can divide the cell into two parts, the public one and the private one.

The public part includes everything except output_data. And the private part is the type tuples belongs to you in the output_data. So the ckbytes of public part is shared by all users, and the private part is borne by the owner of the asset.

1 Like

I prefer this suggestion. A combinator can be defined on top of sUDTs using some kind of hash tree.