Skip to main content

⚙️ Balance Engine

The balance layer is the financial execution boundary of pallet-commitment.

This is where one of the most important architectural separations exists:

pallet-commitment != balance engine

The pallet does not implement:

  • 📥 deposit logic
  • 📤 withdrawal valuation
  • 📈 minting rules
  • 📉 reaping rules
  • 🧾 receipt math
  • 🕓 snapshot resolution
  • 💰 final asset computation

Instead, all of that is delegated through pluggable execution.

pallet-commitment = commitment orchestration

LazyBalance = accounting execution

This distinction is fundamental.

Responsibilitypallet-commitmentBalance System
Ownershipwho owns value-
Routingwhere value belongs-
Execution Timingwhen accounting must happen-
Value Evolution-how value changes
Receipt Resolution-how receipts resolve
Final Settlement-what final value is returned

🧠 Why This Separation Exists

A commitment system must solve two different problems:

  • ownership
  • accounting

Hardcoding both inside one pallet would make the system rigid and unsafe.

More importantly:

deposit, withdraw, mint, and reap may require completely different rules across runtimes

and those rules should not be fixed inside pallet-commitment.

The goal is:

modularity + extensibility

not one permanent financial model.

Examples

Some runtimes may want:

  • ♾ unbounded deposits
  • 📏 strict min/max deposit limits
  • ⏳ withdrawal cooldown periods
  • 💸 early withdrawal penalties
  • 📈 minting caps
  • 📉 controlled reap limits
  • 🔒 reserve-only withdrawals
  • ⚖ force withdrawal fallback rules

These are balance execution rules, not commitment ownership rules.

Why Not Extend Each Rule?

Adding separate hooks for:

deposit
withdraw
mint
reap
limits
snapshots

creates fragmented and hard-to-maintain architecture.

Instead:

plug the full balance system

not

patch individual balance rules

The only requirement is it must preserve lazy-balance resolution

As long as that holds, the accounting model can be fully customized.

Problem TypeQuestionsHandled By
Ownership Problem1. Who owns bonded value?
2. Which digest does it belong to?
3. Is it Direct, Index, or Pool?
4. Who is allowed to resolve it?
pallet-commitment
Accounting Problem1. How does value evolve?
2. How are rewards added?
3. How are penalties removed?
4. How is the final withdrawable value computed?
Plugins respecting to frame_suite::assets's LazyBalance implementation in pallet-commitment

This is balance architecture.


🧩 Plugin-Based Execution

Balance execution is not hardcoded.

It is resolved through the plugin system.

behavior = plugin contract

execution = plugin resolution

not

behavior = pallet implementation

The pallet only forwards execution into the configured balance model via Config trait.

This allows:

  • reusable commitment logic
  • runtime-controlled accounting behavior
  • different balance strategies using the same pallet

The commitment system stays stable. Only the accounting execution changes.

Responsibility Areapallet-commitmentBalance System
Lifecycle📥 place_commit()
📈 raise_commit()
📤 resolve_commit()
💰 deposit()
📤 withdraw()
📈 mint()
📉 reap()
🧹 drain()
Structures🆔 digests
🧺 indexes
🏊 pools
🧾 receipt valuation
🕓 snapshot resolution
Ownership👤 who owns commitments
📍 where receipts belong
🔗 grouped commitment identity
⚖ proportional redemption
💰 final claim value
Validation🏦 reserve preparation
🎛 polite / force funding behavior
✅ deposit feasibility
📤 withdrawal feasibility
📈 minting bounds
📉 reaping bounds
Imbalance📈 pending issue
📉 pending reap
⚖ enforces issue / reap adjustments
Limits📏 consumes limits during validation📏 defines min/max deposits and safe operational thresholds
Final Responsibility⏰ decides when accounting happens⚙ decides how value changes

🧾 The Lazy Balance Model

The accounting model used by pallet-commitment comes from LazyBalance in frame_suite::assets.

It is built on three core objects:

  • 💰 Balance
  • 🧾 Receipt
  • 🕓 SnapShot

Everything else derives from these.

💰 Balance

Balance is the canonical mutable state. It is the real economic value of a digest.

It receives all direct updates:

  • 📥 deposit
  • 📈 mint
  • 📉 reap
  • 🧹 drain
  • 🧺 grouped redistribution
Digest -> Balance

This is the source of truth.

Receipts do not store final value. Balance does.

🧾 Receipt

A receipt is created during deposit.

deposit() -> issue receipt

It represents:

  • ownership claim
  • entitlement over deposited value
  • the right to redeem later

It does not store final withdrawable value.

Because final value is computed only during withdrawal as receipts are immutable.

They never update when balance changes.

🕓 SnapShot

SnapShot is the time-indexed projection of balance state.

It preserves historical balance information.

This allows:

past state -> present resolution

and enables:

  • proportional redemption
  • deferred settlement
  • deterministic final value
  • correct lazy withdrawal resolution

⚡ Correct Execution Flow

This is the actual balance flow.

Step 1 - Deposit

A user places a commitment.

Example:

Alice commits 100

The pallet:

  • validates ownership
  • selects digest
  • routes commitment

Then the balance system executes:

deposit()

This:

  • places value into digest balance
  • creates a receipt
  • returns effective committed value
Alice owns a receipt

not a fixed future balance

Step 2 - Digest Evolves

Later, protocol events happen i.e., usage pallets modify digest state.

Example:

validator earns rewards

or

validator gets slashed

The balance system executes:

mint()
// or
reap()

This updates digest balance only. No receipts are updated.

No user records change. Only shared balance state changes.

Step 3 - Resolution

When Alice resolves:

resolve_commit()

the pallet:

  • verifies ownership
  • loads stored receipt
  • routes to correct digest model and its balance

Then the balance system executes:

withdraw(receipt, mut balance)

This computes final redeemable value

using:

  • current digest balance
  • receipt proportion
  • historical reference state

Then final settlement occurs. This is lazy resolution.


🧩 Virtual Structure Architecture

This is similar to virtual functions.

virtual functions: choose function implementation at runtime

virtual structures: choose structure implementation outside the pallet

Instead of hardcoding:

struct Balance {
...
}

the system uses virtual primitives defined in frame_suite::virtuals where:

  • the structure itself is selected externally
  • fields are type-driven
  • storage layout is representation-agnostic
  • bounds and extensions are attached dynamically

This is not just a normal associated type.

It is not:

choose one fixed type

It is:

choose how the structure itself is composed

outside the pallet.

So not only behavior is pluggable, even the balance primitives themselves are virtualized.

plugins choose execution

virtuals choose structure

This is what makes LazyBalance fully extensible without redesigning the pallet.

⚖️ Imbalance Resolution

The balance model itself does not know anything about protocol rewards or penalties.

It only maintains:

deposit -> receipt

withdraw(receipt) -> final value

A receipt is created with an initial deposited value.

Later, when that receipt is resolved, the final withdrawal value may be different.

Example:

initial deposit = 100

final withdrawal = 120
initial deposit = 100

final withdrawal = 80

The balance system simply computes the final value.

It does not handle how the underlying fungible asset supply should be corrected.

That responsibility belongs to pallet-commitment.

If:

withdraw > deposit

the pallet records pending minting using AssetToIssue Storage Value.

If:

deposit > withdraw

the pallet records pending burning using AssetToReap Storage Value.


🎛 Dispatch Policy

Balance execution uses:

type DispatchPolicy = (Precision, Fortitude)

This controls how operations are allowed to execute.

Precision

Exact

  • Must execute exact requested value.
  • Failure means failure.
  • Used for strict protocol correctness.

BestEffort

  • Partial execution is allowed.
  • Used when bounded flexibility is safe.

Fortitude

Polite

  • Use only prepared reserve funding.
  • Native PrepareForCommit reserve only
  • Strict and predictable.

Force

  • If reserve is insufficient fallback to free balance is allowed.
  • Useful for runtime-controlled flexibility.
  • This is especially important for native reserve integration.

📏 Limits

Every commitment operation is a financial operation.

It cannot be unbounded.

The balance system provides safe operational bounds for:

  • minimum deposits
  • maximum deposits
  • minting limits
  • reaping limits
  • safe execution thresholds

Validation always respects these bounds.

The pallet consumes these limits. It does not define them.


🛡 Core Invariants

The balance architecture guarantees:

  • 🧾 receipts are immutable
  • ⚡ resolution is always lazy
  • 💰 balance remains canonical
  • 🕓 snapshots preserve history
  • ⚖ imbalance is eventually settled
  • 🚫 no eager receipt mutation
  • 🔒 ownership and accounting stay separated

If these break: the economic model breaks

This is the most critical boundary in the entire pallet.


📌 In One Sentence

Commitment controls ownership. Balance controls economics.

or more precisely:

pallet-commitment = economic coordinator

LazyBalance = accounting execution

🚀 Next Step

Now we move from financial execution into semantic commitment positions.

This is where the same digest can represent opposite economic meanings like:

long / short

yes / no

for / against

without changing the commitment engine itself.

👉 Architecture -> Variants