Crate pallet_xp

Source
Expand description

The XP pallet provides a modular and extensible system for managing Experience Points (XP) as a non-monetary, programmable primitive representing reputation, contribution, or progression.

This pallet is built on top of frame_suite::xp and relies heavily on its abstractions. It is strongly recommended to understand those traits before using this pallet.

§Overview

  • Config - Runtime configuration
  • Call - Dispatchable extrinsics
  • Pallet - Trait implementation for external modules

Unlike traditional fungible systems such as pallet_balances, XP is:

  • non-transferable as value
  • not issuance-based (no total supply tracking)
  • earned through controlled mechanisms

The only user-facing transfer is ownership transfer of an XP key via Call::handover. All XP value changes must occur through XpMutate::earn_xp (typically invoked by runtime logic or other pallets) or internal runtime mechanisms.

§Identity

XP is key-based, not account-based:

  • Each XP entry is identified by an XpId
  • Each XP key has exactly one owner
  • A single account can own multiple XP keys (XpOwners)
Account -- owns --> XpId (key)
                 |- free XP
                 |- reserved XP
                 |- locked XP

XP keys do not hold private keys and therefore require explicit ownership. Keys are deterministically generated using XpOwner::xp_key_gen.

§Lifecycle

The standard XP lifecycle is:

begin_xp -> earn_xp -> (reserve / lock) -> reap

Note: For pre-defined accounts, prefer initializing via GenesisConfig instead of BeginXp::begin_xp.

XP earning is not a simple increment. It integrates a pulse-based reputation system that:

  • prevents same-block abuse
  • enforces a minimum activity threshold (MinPulse)
  • scales rewards based on accumulated reputation
  • optionally accelerates growth when XP is locked

At a high level:

  • Initially, actions build reputation (pulse) instead of granting XP
  • Once active, XP grows approximately as: XP += points * reputation
if pulse < MinPulse:
    build reputation only
else:
    XP += points * pulse

This results in:

  • early usage -> builds reputation
  • consistent usage -> earns increasing XP
  • higher reputation -> amplifies future rewards

§Constraints: Reserve & Lock

XP supports two constraint mechanisms:

  • XpReserve - soft reservation (withdrawable, intent-based)
  • XpLock - strict locking (non-partial withdrawal, protocol-enforced)

These are accessible via XP traits directly, or through the fungible adapter for interoperability.

§Fungible Compatibility

The pallet provides partial implementations of fungible unbalanced traits to support interoperability with pallets expecting balance-like behavior, allowing the same logic to operate across both XP and fungible systems when used appropriately.

However:

  • XP is not fungible
  • total_issuance and active_issuance are undefined
  • transfers of value are disallowed

Prefer using XP-specific traits for precise-requirements.

§Origin Model

Most Substrate logic operates on account-based origins. In this system, execution still originates from an account, but the XP key acts as the primary subject of state transitions for XP-related operations.

Runtime logic should treat the XP key as the unit of interaction and authorization, rather than the account itself.

origin: AccountId
input: XpId
ensure owner(origin, XpId)
execute on XpId

This is facilitated via Call::call, where an XP key is provided and validated against its owner, enabling XP-scoped execution within the standard origin-driven model.

§Reaping & Liveness

XP does not use existential deposits. Instead, liveness is determined via activity:

  • Each XP entry tracks a timestamp updated on XP earning, indicating activity
  • MinTimeStamp (set via root) defines the minimum liveness threshold
  • If an XP’s timestamp falls below this threshold, it is considered inactive
  • XP with active locks is treated as in-use (runtime intent) and cannot be reaped
  • Inactive XP entries can be reaped via Call::dispose and are permanently invalidated

This ensures XP reflects active participation or active usage, rather than passive holding.

§Listeners & Hooks

The pallet exposes extensibility via Config::Extensions, where the current extensions are listener traits defined in frame_suite::xp.

Each XP lifecycle event (create, earn, slash, reserve, lock, reap, transfer) invokes the corresponding listener hook, independent of standard event emission.

  • Listeners are always executed regardless of Config::EmitEvents
  • Using XP traits directly is expected to provide accurate, intent-aligned hooks
  • Using fungible adapters will still function, but may not fully reflect XP-specific semantics

§Genesis Configuration

GenesisConfig sets how XP behaves from the start:

  • InitXp
    Starting XP assigned when a new XP entry is created.

  • PulseFactor Controls how reputation (pulse) grows over time.
    Repeated actions increase an internal counter, which periodically increases the pulse value.

    step += per_count
    if step >= threshold:
        pulse += 1
        step resets
  • MinPulse
    Minimum reputation required before XP is awarded.
    Below this threshold, actions only build reputation.
    Once reached, actions begin granting XP (scaled by reputation).

  • MinTimeStamp
    Minimum activity threshold (block number).
    If an XP entry is not updated for a sufficient duration, it becomes inactive and can be reaped.

    if timestamp < MinTimeStamp and no active locks:
        XP can be reaped
  • genesis_acc: XP identities initialized at genesis.

Flow:

  • Actions build pulse (reputation)

  • Once pulse reaches MinPulse, XP starts accumulating

  • Inactivity below MinTimeStamp allows XP to be reaped

  • Call::force_genesis_config
    Restricted to root origin.
    Allows updating these parameters at runtime to adjust system behavior.

All genesis parameters are stored in runtime storage and can be updated during runtime; they are not fixed constants.

§Development Feature Gate

This pallet includes a dev feature gate for development and testing.

Core functionality is exposed via public APIs for RPC and UI usage. The dev feature provides thin wrapper extrinsics and extended events for direct inspection.

This feature must be disabled in production runtimes due to additional debugging overhead.

Re-exports§

pub use pallet::*;

Modules§

fungible 🔒
Implementation of compatible fungible traits for the Pallet Type.
pallet
The pallet module in each FRAME pallet hosts the most important items needed to construct this pallet.
types
Core types and aliases for the XP system.
weights
Autogenerated weights for pallet_xp
xp 🔒
Implementations of XP traits for the Pallet Type.