Spark Program | Cellar — A Capacity Leasing Market for CKB Cells

Tags: CKB, dApp, Infrastructure, Dev Tool, Smart Contract, SDK, CLI, Capacity Leasing, Cell Model, Type Script, Lock Script

1. Project Overview

Cellar is a CKB-native capacity leasing protocol that lets idle CKB holders lease cell capacity to developers and applications for a bounded term.

CKB since is not a property of a live cell. It is a field on CellInput, meaning it constrains a specific spend transaction. Also, CKB cells are immutable, so borrower “writes” must consume the current lease cell and create a valid successor cell.

This revised proposal restructures Cellar as a covenant-style lease state machine:

  • Providers lease CKB capacity without giving up principal ownership.
  • Borrowers update leased data through controlled 1 input -> 1 output successor transitions.
  • Immutable lease terms are preserved across every successor.
  • The provider can reclaim the current live lease cell after an absolute expiry using since.
  • Transaction fees are paid from separate inputs so provider principal is not silently drained.
  • Cellar v0 is honest about its guarantees: principal safety plus unilateral provider reclaim after expiry, not a magical cell-level timer.

The funded MVP will produce CKB scripts, tests, TypeScript SDK helpers, CLI commands, and a testnet demo showing lease creation, borrower update, expiry, and provider reclaim.

2. Team Profile

Applicant / lead: Carlos

Role: Protocol design, CKB script implementation, TypeScript SDK, CLI, testnet deployment, and demo integration.

Contact: [email protected]

Relevant background: CKB application/protocol development, Rust smart contract work, TypeScript tooling, and dApp prototyping.

3. Project Background

CKB capacity is a scarce on-chain resource. Many CKB holders have idle capacity, while developers need temporary capacity for app data, metadata, indexable references, registries, or other application state.

Cellar creates a market between:

  • Capacity providers, who want yield while preserving their CKB principal.
  • Borrowers/developers, who want temporary cell capacity without permanently buying all required CKB up front.

The main technical challenge is not marketplace UX. It is correctly expressing a safe lease inside CKB’s cell model.

The revised design is based on these confirmed CKB semantics:

Confirmed fact Design consequence
Live cells are consumed as inputs and new cells are created as outputs. Borrower updates must recreate a successor lease cell.
since exists on CellInput, not CellOutput. Expiry must be stored in lease data/script args and checked when spending.
since is a lower-bound validity precondition. It enforces “not before expiry” for provider reclaim, but not “borrower can never update after expiry.”
Lock scripts authorize spending input cells. Type scripts enforce state rules over matching inputs/outputs. Cellar needs both a lock script and a type-script covenant.
A cell has one type-script slot. The leased cell cannot safely carry arbitrary borrower-supplied type scripts in v0.

4. Proposed Solution

Cellar v0 will implement a direct lease primitive first, then keep the marketplace offer layer as a follow-up once the core covenant is proven.

The active lease cell will look like this:

LeaseCell
  capacity: principal_capacity
  lock:     CellarLeaseLock(lease_id, provider_auth, borrower_auth)
  type:     CellarLeaseType(lease_id)
  data:     LeaseData

LeaseData will be versioned and Molecule-encoded:

LeaseData {
  version: uint8,
  lease_id: Byte32,
  provider_payout_lock_hash: Byte32,
  borrower_lock_hash: Byte32,
  principal_capacity: uint64,
  expiry_since: uint64,
  max_data_bytes: uint32,
  terms_hash: Byte32,
  payload: Bytes
}

The lease type script enforces the covenant. The lease lock script verifies the authorized party and mode.

Core Flows

Create lease

  • Provider capacity inputs fund one active LeaseCell.
  • Borrower pays rent to provider according to agreed terms.
  • The lease cell stores immutable terms, absolute expiry, capacity, and initial payload.

Borrower update

  • Borrower spends the current lease cell.
  • Transaction creates exactly one valid successor lease cell.
  • The successor preserves lease ID, provider payout lock hash, borrower lock hash, principal capacity, expiry, maximum data size, terms hash, lock script, and type script.
  • Only payload changes.
  • Fees are paid from borrower-owned non-lease inputs.

Provider reclaim

  • Provider spends the current live lease cell after expiry.
  • The reclaim transaction input uses absolute since = expiry_since.
  • The type script requires a provider payout output with at least principal_capacity.
  • Fees are paid from separate provider/keeper inputs.

Mutual early close

  • Optional v0 path.
  • Requires both provider and borrower authorization.
  • Still requires provider principal payout.

5. Technical Approach

Contract Architecture

Cellar v0 will include two CKB scripts:

  1. cellar-lease-type

    • Enforces creation, update, and reclaim state transitions.
    • Preserves lease invariants across borrower updates.
    • Rejects changed terms, changed scripts, missing successor cells, multiple successors, principal leakage, and relative expiry values.
  2. cellar-lease-lock

    • Verifies witness mode and signatures.
    • Supports borrower update, provider reclaim, and optional mutual close modes.
    • Delegates state correctness to cellar-lease-type.

The revised design no longer treats since as a lease property of a cell. Instead:

  • expiry_since is stored as immutable lease data.
  • Provider reclaim uses since on the reclaim transaction input.
  • Borrower updates are modeled as successor-cell transitions.
  • The type script forces every successor to preserve the same expiry and reclaim path.

This means a provider cannot reclaim before expiry, and after expiry the provider can reclaim whichever successor lease cell is currently live.

Expiry Guarantee

Cellar v0 will not claim that non-interactive borrower updates are impossible after expiry. CKB has a lower-bound since field, but no native upper-bound field for “this transaction is valid only before X.”

Therefore the honest v0 guarantee is:

Cellar v0 is principal-safe and supports unilateral provider reclaim after absolute expiry. A watcher/keeper should monitor active leases and submit reclaim transactions as soon as expiry is reached.

If strict prevention of borrower updates after expiry is required, it must be implemented with an additional mechanism such as provider co-signing, a clock/oracle cell, or time-window permission tickets. That stricter design is outside the v0 MVP unless reviewers request it as a scope change.

Tooling

  • CKB scripts: Rust targeting CKB-VM.
  • Data encoding: Molecule.
  • SDK and CLI: TypeScript.
  • Testing: CKB script test suite covering valid and adversarial transitions.
  • Demo: testnet lease lifecycle with transaction hashes and CLI output.

Research Basis

The proposal relies on primary CKB docs and RFCs

A local probe also confirmed that CellInput has since, CellOutput does not, and absolute/relative since values encode differently.

6. To-Do List

Week 1: Specification and Script Skeleton

  • Finalize Molecule schema for LeaseData and witness modes.
  • Define exact lease_id derivation.
  • Implement cellar-lease-type skeleton.
  • Implement cellar-lease-lock skeleton.
  • Add baseline creation/update/reclaim tests.

Week 2: Covenant Rules and Adversarial Tests

  • Enforce borrower update invariants.
  • Enforce provider reclaim payout and absolute since.
  • Add tests for changed terms, changed scripts, missing successor, duplicate successor, principal leakage, relative expiry, and oversized payload.
  • Measure and document cycle costs.

Week 3: TypeScript SDK and CLI

  • Build SDK transaction helpers:
    • create lease;
    • update payload;
    • reclaim after expiry;
    • parse lease cells;
    • build watcher queries.
  • Build CLI commands:
    • cellar lease create;
    • cellar lease update;
    • cellar lease reclaim;
    • cellar lease watch.

Week 4: Testnet Demo and Documentation

  • Deploy scripts on CKB testnet.
  • Run sample direct lease lifecycle.
  • Publish testnet transaction hashes.
  • Create demo using leased payload data for NFT metadata or registry data.
  • Write README, usage guide, and limitation notes.

7. Funding Requested

Requested amount: $1,000

Estimated duration: 4 weeks

Funding use:

  • CKB script development.
  • Test suite and adversarial validation.
  • TypeScript SDK and CLI.
  • Testnet deployment.
  • Demo app and documentation.

8. Deliverables & How to Verify Them

Deliverable 1: Formal Cellar v0 Specification

Contents

  • Lease state machine.
  • Molecule schema.
  • Script args and witness formats.
  • Absolute since policy.
  • Security invariants and known limitations.

Verification

  • Reviewer can inspect the spec and confirm it does not treat since as a cell property.
  • Reviewer can confirm borrower updates are defined as successor transitions.
  • Reviewer can confirm strict post-expiry borrower freeze is not overclaimed.

Deliverable 2: CKB Scripts

Contents

  • cellar-lease-type.
  • cellar-lease-lock.

Verification

  • Build scripts from source.
  • Run local tests.
  • Confirm invalid transactions fail for:
    • relative expiry;
    • changed provider payout lock;
    • changed expiry;
    • changed principal;
    • missing successor;
    • duplicate successor;
    • changed lock/type scripts;
    • reclaim before expiry;
    • reclaim payout below principal.

Deliverable 3: Test Suite

Contents

  • Creation tests.
  • Borrower update tests.
  • Provider reclaim tests.
  • Mutual close tests if included.
  • Adversarial tests.

Verification

  • Reviewer runs test command from README.
  • Test output shows all valid cases passing and invalid cases rejected.
  • Cycle budget report is included.

Deliverable 4: TypeScript SDK

Contents

  • Transaction builders for lease creation, payload update, and reclaim.
  • Lease cell parser.
  • Watcher helper for expiring leases.

Verification

  • Example script creates a valid transaction skeleton.
  • SDK examples are used by CLI and demo.

Deliverable 5: CLI

Contents

  • cellar lease create.
  • cellar lease update.
  • cellar lease reclaim.
  • cellar lease watch.

Verification

  • CLI can create, update, and reclaim a testnet lease.
  • CLI outputs transaction hashes and parsed lease state.

Deliverable 6: Testnet Deployment and Demo

Contents

  • Deployed scripts.
  • Sample testnet lease cells.
  • Demo showing borrower update and provider reclaim.

Verification

  • Reviewer can inspect published testnet transaction hashes.
  • Demo README includes commands and expected results.
  • Early reclaim rejection is verified by local tests, because invalid transactions are not accepted on-chain.

9. Current State vs Funded Work

Current State

  • Original forum idea exists.
  • Matt’s feedback has been reviewed.
  • CKB docs and RFCs have been checked.
  • The design has been restructured around CKB’s actual cell and since semantics.
  • A local Lumos v0.23.0 probe confirmed:
    • CellInput has since;
    • CellOutput does not have since;
    • absolute and relative since values encode differently.

Funded Work

The requested funding covers the implementation and validation of the revised v0 design:

  • Production-quality script prototype.
  • Complete tests.
  • SDK and CLI.
  • Testnet deployment.
  • Demo and documentation.

The marketplace offer book is intentionally deferred until the direct lease primitive is proven.

10. CKB Alignment

Cellar is directly aligned with CKB because it builds on CKB’s unique cell model and capacity economics.

It supports CKB by:

  • Increasing the utility of idle CKB capacity.
  • Creating a new primitive for temporary on-chain storage and app data.
  • Giving developers a way to use capacity without permanently acquiring all of it up front.
  • Demonstrating safe covenant-style state transitions on CKB.
  • Producing reusable SDK and CLI tooling for future CKB applications.
  • Making the distinction between lock scripts, type scripts, cells, inputs, outputs, and since clearer for builders.
5 Likes

really glad to see your interest in this use case!

one thing I wanted to clarify is that since is a property of a transaction, not the cell itself. If you use since to enforce the timelock you are bound to a single signed transaction, the leased capacity can’t actually be used (unless the storage is just written once.. maybe that is the use case though :)).

(I think) how this should work is covenant style logic that forces the lock script to remain unchanged, one spending path for the leasor (which doesn’t change and becomes spendable after a certain time) and another spending path for leasee.

3 Likes

Matt, thanks for catching this. You’re right: the original proposal treated since too much like a property of the leased cell, when on CKB it is actually a CellInput field that constrains a specific spend transaction.

I’ve restructured the design around that feedback. The revised Cellar v0 is no longer “lock a cell with since and let the borrower write into it.” Instead, it is a covenant/state-machine model:

  • the active lease cell carries a Cellar type script and Cellar lock script;

  • borrower writes consume the current lease cell and create exactly one valid successor lease cell;

  • the successor must preserve the same lease ID, provider payout lock, borrower lock, principal capacity, expiry, and Cellar scripts;

  • provider reclaim is a 1 input -> 0 lease output transition after expiry;

  • the reclaim transaction uses an absolute since on the lease cell input;

  • fees are paid from separate inputs so provider principal is not drained.

I also found one additional limitation worth stating clearly: since gives a lower-bound validity condition, not an upper-bound. So v0 can enforce “provider cannot reclaim before expiry” and “provider can reclaim after expiry,” but it should not claim that non-interactive borrower updates are impossible after expiry. A borrower could still try to race by creating another valid successor after expiry, though the provider can reclaim that successor immediately because the absolute expiry is preserved. That is a liveness/griefing issue, not a principal-loss issue, if the covenant invariants hold.

So the revised v0 promise is:

Cellar is principal-safe and supports unilateral provider reclaim after absolute expiry. It does not claim hard non-interactive borrower freeze after expiry unless we add provider co-signing, a clock/oracle cell, or a time-window ticket design.

I’ve updated the proposal around this model. Thanks again, this materially improved the architecture.

1 Like

Glad it was useful! Regarding the upper bound, this idea has been proposed to allow scripts to access approximate current time. I think you could require that the time oracle be referenced and then prevent future transactions once the window passed, just one more thing to consider.

2 Likes

Thanks Matt, I went back through the CKB docs and the CTO post, and I think your suggestion fits the exact missing piece.

The clean model is:

  • `since` stays on the provider reclaim path. It gives the lower bound: provider reclaim is invalid before the absolute expiry.
  • CTO goes on the borrower update path. It gives the upper bound: borrower updates are invalid once approximate current time has passed the lease expiry.

So in Cellar, every borrower update would still be a normal successor-cell transition: consume the current lease cell, create exactly one valid successor, preserve principal, provider payout lock, lease ID, and expiry. But in a CTO-aware mode, that update transaction would also need to reference valid CTO oracle cell(s), probably through `cell_deps`, so the Cellar type script can read the oracle time and compare it against `expiry_since`.

One thing I need to be careful about is freshness. Cellar should not accept an arbitrary old CTO cell, because that would recreate the same stale-time problem. The rule needs to reference a recognized CTO oracle group and follow CTO’s freshness/bounded-staleness model, possibly requiring multiple cells from the group if the lease needs a tighter cutoff.

So I think the final design should be:

Base Cellar:
principal-safe lease, absolute `since` for provider reclaim, watcher/keeper recommended.

CTO-aware Cellar:
same principal safety, but borrower successor updates must prove oracle time is still before expiry, giving a script-enforced update cutoff subject to CTO’s freshness and availability assumptions.

That makes more sense than trying to force `since` to do both sides of the time window.

Hi @Carlos_Bunny! It’s nice to see such a proposal for improving CKB’s fundamental usability here. From a high level of abstraction, lending onchain data storage capacity will definitely provide a new option to other devs.

However, as you mentioned in the original proposal, there are still some potential issues in the current design that might affect the usability of Cellar. Like:

This greatly limited the use cases.

Which means if the borrower refuses to authorize, the fund is locked permanently, so the provider suffers from asset loss.

I encourage you to specify the boundary of use cases: what devs can do with Cellar and what devs cannot. This helps the Spark committee and future devs to better understand what Cellar is suited for.

BTW, I observed some basic fact mistakes in the proposal. Aside from the since problem Matt mentioned, another example is

You can use the type script only, since it triggers validation whenever it is present in either the inputs or the outputs.

Considering that I also notice some common format errors here, I want to reiterate here that while we don’t oppose developers’ using AI in the proposal process, please be responsible for the content you publish and check the AI-generated results. Unverified AI-generated content places a burden on all readers that is many times greater than the effort required to generate it.


WHY? People keep ignoring Markdown formatting recently. At least check after copying an AI’s response.

Please show some respect.

10 Likes

I reckon from certain angle your statement is defensible as a separation-of-concerns design.

But from my pov, you probably indeed need not to deploy, say, both a bespoke CellarLeaseLock and a CellarLeaseType, as a bespoke lock plus type creates a small distributed protocol between the two scripts.

if:

lock says: mode = borrower_update
type says: this transaction looks like borrower_update

then:

they must agree on witness encoding, mode interpretation, identities and transition classification. That creates:

  • duplicated or coupled transition-classification logic;
  • cross-script invariants that neither script enforces alone;
  • two deployment artefacts and two audit surfaces;
  • potentially bespoke wallet and signing support;
  • the risk of an authority mismatch, where the lock authorises one operation whilst the type script validates the transaction as another.

With a potential type-centric design, the transaction itself carries ordinary signed authority inputs. The type script can read transaction inputs, outputs and their lock hashes through CKB syscalls, whilst standard lock scripts continue to perform ordinary signature authorisation.

7 Likes

Hi , thanks for the direct feedback. Point taken on both the technical accuracy and the formatting. That is on me, and I’ll be more careful before posting a revision.

You’re right that I overstated the need for both a custom CellarLeaseLock and CellarLeaseType. The cleaner design should be type-centric: Cellar’s lease covenant lives in the type script, while ordinary locks handle normal authorization.

I also agree that the use-case boundary needs to be clearer. Cellar v0 should not be framed as “leased capacity for any arbitrary cell.” Since the Cellar covenant uses the type slot, v0 is better suited for payload/reference-style storage: metadata, hashes, pointers, registry entries, indexable app data, or temporary records. It is not suitable for borrower cells that need their own type script, like UDT/NFT state cells or protocols with custom type rules.

On mutual early close, I should clarify that it is only an optional convenience path before expiry. If the borrower refuses, the provider should still be able to reclaim through the normal expiry path; otherwise the design is unsafe.

So the revised framing would be: Cellar v0 is a type-script covenant for leased payload/reference cells, with provider principal preserved and unilateral reclaim after expiry.

Does that sound more reasonable to you?

1 Like

My previous framing split Cellar-specific logic across a custom lock and custom type script, which creates the coordination problem you described: mode, witness format, identities, and transition classification all have to line up across two scripts.

A cleaner design is type-centric:

  • Cellar’s lease rules live in one type script.

  • Ordinary lock scripts handle ordinary signatures.

  • Borrower/provider authority is proven by including their signed input cells.

  • The Cellar type script checks those authority inputs by lock hash and validates the lease transition.

For example, a borrower update would spend the current lease cell, create one valid successor, and include a borrower-controlled input. The borrower’s normal lock proves authorization, while the Cellar type script checks that the borrower lock hash matches the lease terms and that principal, expiry, provider payout, and lease ID are preserved.

There’s a significant issue here: without the protection of the type script, all data mentioned here can be easily faked by anyone. That means Cellar can only provide data availability for borrowers.

With such a constraint, the only one use case I can imagine is to borrow capacity from Cellar for deploying scripts. That said, the usability of these scripts is still fragile since they will eventually be recycled according to the protocol.

As for other use cases, considering that data readers will (almost) always have to verify the correctness, providing data on the witness might always be easier, cheaper and more reliable.

I look forward to a more detailed description of the use cases you mentioned!

1 Like