Skip to main content

๐Ÿชจ Commitment (Base)

The Commitment trait from frame_suite::commitment is the foundation of pallet-commitment.

Everything else:

  • ๐Ÿงบ indexes
  • ๐ŸŠ pools
  • โš– variants

builds on top of this base layer.

Before understanding grouped commitments, you must understand the atomic primitive:

Commitment = asset + reason + digest

This is the true center of the pallet.


๐Ÿง  The Three Foundation Traitsโ€‹

The base commitment system is built from three traits:

TraitResponsibility
InspectAssetinspect available funds
DigestModeldetermine whether a digest is Direct / Index / Pool
Commitmentfull commitment lifecycle

These form the minimum operational foundation of the pallet.

InspectAssetโ€‹

This trait answers:

how much can this proprietor safely commit?

It combines:

  • liquid reducible balance
  • optionally held reserve (PrepareForCommit)

into one validation layer.

MethodPurpose
available_funds(who)-> valuetotal funds available for commitment

DigestModelโ€‹

All digests use the same base type:

type Digest = AccountId

So the pallet must determine whether a digest is:

  • Direct
  • Index
  • Pool

This trait wraps raw digests into DigestVariant and removes that ambiguity.

MethodPurpose
determine_digest(digest, reason)-> modelclassify digest model

Commitmentโ€‹

This is the main lifecycle trait, where the invariant is enforced here:

(Proprietor, Reason) -> one commitment

The base Commitment trait is the real center of the pallet.

It controls:

Place -> Raise -> Mutate -> Resolve

and owns:

  • โœ… validation
  • ๐Ÿ‘ค ownership
  • ๐Ÿ”— digest binding
  • ๐Ÿงพ receipt tracking
  • โš–๏ธ final settlement
Key MethodsPurpose
place_commit(who, reason, digest, value, intent)Creates the first commitment by bonding funds, depositing into the digest, and storing the first receipt
raise_commit(who, reason, value, intent)Adds more value to an existing commitment by creating a new immutable receipt
resolve_commit(who, reason)Final settlement of the commitment resolves accumulated receipts, applies mint/reap imbalance, and returns final asset value
get_commit_value(who, reason)Returns the real-time live value of a proprietor's commitment after all digest updates

๏ธ๐Ÿท๏ธ Core Typesโ€‹

The pallet defines several important aliases and structures.

These are not cosmetic aliases. They define the actual architecture.

Proprietorโ€‹

type Proprietor = AccountId

The account that owns the commitment.

This account can:

  • place (deposit)
  • raise (increase)
  • resolve (withdraw)

commitments.

Digestโ€‹

type Digest = AccountId

This is the most important architectural detail:

Digest is represented using AccountIdโ€‹

All digest models use the same base type:

  • ๐ŸŽฏ DirectDigest (single direct commitment target)
  • ๐Ÿงบ IndexDigest (grouped weighted commitment target)
  • ๐ŸŠ Pool-Digest (manager-controlled pooled target)
  • ๐Ÿ“ฆ EntryDigest (individual digest inside an index)
  • ๐ŸŽฐ SlotDigest (pool allocation slot digest)

are aliases of the same digest type. This is exactly why DigestModel trait exists.

Without it, the pallet could not distinguish:

is this digest direct?
or an index?
or a pool?

Digest Sourceโ€‹

type DigestSource = AccountId

Digests can be optionally generated deterministically from the calling source using the account nonce.

This means:

  • the caller provides the source identity
  • the pallet uses the account nonce for deterministic generation
  • the resulting digest is still an AccountId (by type)

This makes digests behave much like smart-contract addresses.

Reasonโ€‹

type CommitReason<T>

This is usually the runtime composite freeze reason enum:

RuntimeFreezeReason
LockReason

Examples:

  • Staking
  • Governance
  • Escrow
  • LendingCollateral

Reason answers:

why is this commitment being created?

Intentโ€‹

type Intent = DispatchPolicy

This controls how commitment operations handle balance deductions and settlement:

  • Precision

    • Exact: the full requested value must be committed
    • BestEffort: commit as much as safely available if full value is not possible
  • Fortitude

    • Polite: use only prepared reserve/held funds
    • Force: allow fallback to liquid balance if reserve is insufficient

This makes commitment execution strict, predictable, and safe.

Commit Instanceโ€‹

type CommitInstance<T> = VirtualReceipt<T>

This is extremely important:

commitments are stored as receiptsโ€‹

  • Each deposit creates an immutable receipt.
  • Raising a commitment does NOT mutate the old one.
  • It creates another immutable commit instance.
  • These are later aggregated during resolution.

Lazy Balanceโ€‹

type LazyBalanceOf<T> = VirtualBalance<T>

Digest balances are not eagerly settled balances.

They are plugin-driven virtual balances resolved lazily through receipts.

Have You Noticed?โ€‹

type CommitInstance<T> = VirtualReceipt<T>

type LazyBalanceOf<T> = VirtualBalance<T>

Both associated types resolve to virtual structures.

This means neither the commitment receipt nor the balance model has a concrete structure within pallet-commitment itself.

Instead, their representation is deferred to the configured Balance plugin.

This is what makes the accounting layer fully pluggable.

Different balance implementations can define entirely different receipt and balance schemas while the Commitment engine continues to operate through the same abstract interfaces.

Limitsโ€‹

type Limits = LimitsProduct<T, I>

Every commitment operation is a financial operation and cannot be unbounded in intent.

Actions like:

  • placing
  • raising
  • minting
  • reaping

must always operate within safe economic boundaries.

That is why limits exist.

These represent:

  • minimum deposit bounds
  • maximum deposit bounds
  • minting limits
  • reaping limits

The lazy balance plugin defines these bounds if the balance model requires them, and commitment validation enforces them before execution.


๐Ÿ—‚๏ธ Core Storageโ€‹

This is where architectural understanding becomes real.

CommitMapโ€‹

(Proprietor, Reason) -> CommitInfo

CommitInfo stores:

  • ownership
  • the bound digest
  • variant
  • immutable commit receipts

This is the proprietor's commitment record.

CommitMap is NOT the balance. It stores ownership + references.

Actual value is resolved lazily through receipts.

DigestMapโ€‹

(Reason, Digest) -> DigestInfo

DigestInfo stores:

  • shared digest balances
  • one lazy balance per semantic variant

This is where protocol-level economic state lives (shared balance / vault).

Exampleโ€‹

Validator A -> Digest XYZ

-> affirmative balance (150 DOT, 5 commitments)
-> contrary balance (45 DOT, 3 commitments)

ReasonValueโ€‹

Reason -> Total Value

Tracks total committed value across all proprietors for one reason.

Examples:

  • Total staking value
  • Total governance deposits
  • Total lending collateral

This supports fast aggregate accounting.

AssetToIssueโ€‹

Pending rewards to mint into the underlying fungible system.

Used when:

withdraw > deposit

AssetToReapโ€‹

Pending penalties to burn from the underlying fungible system.

Used when:

deposit > withdraw

๐Ÿ›ก๏ธ Core Invariantsโ€‹

The base commitment layer enforces strict rules.

These invariants are what make the pallet safe.

One Commitment Per Reasonโ€‹

(Proprietor, Reason) -> one commitment

This is the most important invariant.

A proprietor cannot create:

two commitments
for same reason

This prevents:

  • fragmented ownership
  • ambiguous resolution
  • double accounting
  • unsafe lifecycle transitions

If grouping is needed, use indexes or pools. Never multiple direct commitments.

Commitment Value Can Only Increaseโ€‹

You can:

place_commit()
raise_commit()

but not partially reduce.

Reduction happens only through a complete-exit (resolution) from the reason's commitment:

resolve_commit()

This preserves immutability guarantees. The commitment pallet keeps these imbalance maps so resolution can remain lazy.

Instead of immediately minting or burning on every digest mutation, the system records the pending imbalance and settles it during commitment resolution.

This means these values should be understood as:

pending but eventual

for the underlying fungible system.

Digest Is Immutableโ€‹

Once committed, until resolved:

(Proprietor, Reason) -> Digest

does not change.

The commitment target is permanent and only value changes. Never the binding target.

Commitments Are Receipt-Basedโ€‹

Raising a commitment does not overwrite prior state.

It creates another immutable receipt.

This guarantees:

  • historical correctness
  • deterministic settlement
  • safe lazy resolution

not mutable balance rewriting.


๐Ÿ”„ Lifecycle Methodsโ€‹

The Commitment trait implements the full lifecycle.

The real lifecycle is:

Place -> Raise -> Mutate -> Resolve

There is no separate partial withdraw step.

Resolution performs final withdrawal.

Placeโ€‹

place_commit()

Creates the first commitment.

Flow:

-> validate
-> deduct balance
-> deposit to digest
-> issue receipt
-> store CommitInfo under CommitMap

This creates ownership.

Raiseโ€‹

raise_commit()

Adds value to an existing commitment.

Important:

raise does NOT mutate old receiptโ€‹

It creates a new immutable commit instance.

This is still a deposit (place) operation internally.

Mutateโ€‹

set_digest_value()

or model-specific digest updates.

This changes:

shared digest state, not individual receipts.โ€‹

Examples:

  • validator rewards
  • validator slashing
  • governance penalties
  • lending liquidation

Receipts remain unchanged and the Digest value evolves.

This is the core of lazy resolution.

Resolveโ€‹

resolve_commit()

This is the final settlement operation.

Flow:

-> evaluates receipts
-> computes final withdrawable value
-> resolves imbalance
-> mints rewards if needed
-> burns penalties if needed
-> transfers the final asset value back to the proprietor
-> removes finalized commitment state

Resolution is withdrawal.โ€‹


โš–๏ธ Imbalance Resolutionโ€‹

One of the strongest parts of the pallet.

Handled by internal trait:

CommitBalance::resolve_imbalance()

Rules:

deposit < withdraw -> mint reward

deposit == withdraw -> balanced

deposit > withdraw -> burn penalty

This uses:

  • AssetToIssue (yet to be minted)
  • AssetToReap (yet to be burned)

plus low-level fungible mint/burn operations.

The commitment pallet keeps these imbalance maps so resolution can remain lazy.

Instead of immediately minting or burning on every digest mutation, the system records the pending imbalance and settles it during commitment resolution.

This means these values should be understood as:

pending but eventual

for the underlying fungible system.

This is how the pallet preserves financial correctness without relying on balanced fungible traits to give oppurtunity to both

  • pallet-balances (fully fungible)
  • pallet-xp (quasi-fungible)

You'll see "pending, but eventual" strategies throughout the pallet. Thus classifies pallet-commitment as a lazy execution model.


๐Ÿฆ Native Reserve Fundingโ€‹

Commitments usually consume funds from:

HoldReason::PrepareForCommit

This is the native optional reserve.

Flow:

// With
Fortitude::Polite

// consumes hold first
place_commit() / raise_commit()

only reserve funds are used.

With:

// With
Fortitude::Force

// consumes liquid/free balance
place_commit() / raise_commit()

liquid balance fallback is allowed.

This is how safe and effective native funding is enforced.


๐Ÿš€ Next Stepโ€‹

Now we move from the base commitment lifecycle into grouped commitment structures.

This is where one commitment can safely distribute value across many digests using weighted ownership.

๐Ÿ‘‰ Architecture -> Indexes