Skip to content

feat: Introduce FeePayer concept for flexible transaction fee handling #274

@Hanssen0

Description

@Hanssen0

Currently in ccc, the fee provider for a transaction is tightly coupled with the Signer. The Signer is not only responsible
for signing the transaction but also implicitly acts as the fee provider, responsible for completing the transaction's inputs
to cover the fee.

While this design works for many common scenarios, it lacks flexibility. We want to support more complex transaction scenarios,
for instance, where the fee is not paid by the primary Signer's CKB, but by the extra CKB Capacity from a cell that contains
an asset (like a Spore or mNFT).

The current architecture makes it difficult to implement such features because the fee-handling logic is encapsulated within the
Transaction class and is strongly tied to the Signer's implementation, making it hard to extend.

Describe the solution you'd like

We propose introducing a new abstraction layer: FeePayer.

FeePayer is a concept dedicated to providing fees for a transaction and completing it (by populating inputs/outputs). Its
core responsibility is to provide the necessary Capacity to pay the fee and to implement the corresponding transaction
completion logic.

The specific changes are proposed as follows:

  1. Define the FeePayer Interface
    A new FeePayer interface will be created. It will define the methods required to complete a transaction, establishing a
    standard for fee payment logic. The existing Signer interface will be updated to implement this new interface.

  2. Refactor the Transaction Class for Compatibility
    The methods in the Transaction class responsible for fee calculation and completion (e.g., complete) will be updated. The
    parameter that currently accepts a Signer will be changed to accept a FeePayer.

    This change is fully backward-compatible and will not break existing code. Since the Signer interface will implement
    the new FeePayer interface, any existing code that passes a Signer to these methods will continue to work seamlessly. This
    polymorphic approach avoids the need for new parameters or conditional logic, resulting in a cleaner API.

  3. Signer Provides the Default Behavior
    By implementing the FeePayer interface, the Signer will continue to provide the default fee payment behavior. Its
    implementation will be consistent with the current logic, using the CKB from the Signer's own address to pay the fee.

  4. Support for Custom FeePayers
    With this new interface, developers can easily implement diverse fee payment strategies. For example, a SporeFeePayer could
    be created. Its logic would be to find a specific Spore asset in the transaction and use the surplus Capacity from that Spore's
    cell to cover the transaction fee.

Affected Codebase

  • packages/core/src/ckb/transaction.ts: Will need to be refactored so that its complete method accepts a FeePayer.
  • packages/core/src/signer/index.ts: The Signer interface will be updated to implement FeePayer.
  • All call sites for transaction.complete() will remain compatible due to polymorphism.

Conclusion

Introducing the FeePayer abstraction will greatly enhance the flexibility and extensibility of ccc, allowing us to support a
broader and more innovative range of application scenarios. At the same time, through careful backward-compatible design, it
ensures the stability of the existing API. This is a significant architectural improvement that will contribute to the long-term
growth of the ccc ecosystem.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions