Skip to content

Contract State

This page describes all persistent state stored by the hub contract.

Storage Layout

CONFIG

const CONFIG: Item<Config> = Item::new("config");

Single item storing the contract configuration:

Config {
    admin: Addr,
    pending_admin: Option<Addr>,
    fee_bps: u16,                // Protocol fee in basis points (display only)
    min_stake_threshold: Uint128, // Minimum pending before delegation
    min_claim_threshold: Uint128, // Minimum rewards before claiming
    paused: bool,
    treasury_address: Addr,
    compound_bps: u16,           // Basis points to compound (restake)
    treasury_bps: u16,           // Basis points to treasury
    operations_address: Option<Addr>, // Receives operations share
    operations_bps: u16,         // Basis points to operations (default: 0)
    jail_grace_period: u64,      // Seconds before jailed validator is rebalanced
    max_validators: u32,         // Maximum registered validators (default: 250)
}

VALIDATORS

const VALIDATORS: Map<&str, ValidatorState> = Map::new("validators_v2");

Keyed by cosmosvaloper1... address. Stores the registration and staking state for each validator:

ValidatorState {
    referral_id: String,         // Deterministic, derived from valoper address
    registered_at: u64,          // Block timestamp at registration
    total_staked: Uint128,       // Total ATOM delegated to this validator
    active: bool,                // Whether the validator is currently active
    deactivated_by_admin: bool,  // True if admin deactivated (restricts reactivation)
}

REFERRAL_INDEX

const REFERRAL_INDEX: Map<&str, String> = Map::new("referral_idx_v2");

Reverse index mapping referral_id to valoper_address. Used to look up which validator a referral ID belongs to.

  • Added on registration
  • Removed on deactivation (blocks new fee attribution)
  • Restored on reactivation (same referral ID)

PENDING_FEES

const PENDING_FEES: Map<&str, Uint128> = Map::new("pending_fees_v2");

Keyed by cosmosvaloper1... address. Tracks ATOM received via ReceiveFees but not yet delegated. Set to zero after StakePending delegates the amount.

On deactivation, a validator's pending fees are redistributed proportionally to active validators (by total_staked weight, equal split if all weights are zero).

GLOBAL_STATS

const GLOBAL_STATS: Item<GlobalStatsState> = Item::new("global_stats");

Protocol-wide counters:

GlobalStatsState {
    total_atom_staked: Uint128,     // Sum of all delegations
    total_validators: u32,          // Total ever registered
    active_validators: u32,         // Currently active
    total_rewards_claimed: Uint128, // Cumulative rewards claimed
}

JAIL_RECORDS

const JAIL_RECORDS: Map<&str, JailRecord> = Map::new("jail_records");

Keyed by cosmosvaloper1... address. Tracks when a validator was first detected as jailed:

JailRecord {
    first_seen_jailed: u64,  // Block timestamp when jailing was first detected
}

Used by rebalance to enforce the grace period. Cleared when the validator returns to active status.

STAKE_EVENTS

const STAKE_EVENTS: Map<u64, StakeEventState> = Map::new("stake_events");
const STAKE_EVENT_COUNT: Item<u64> = Item::new("stake_event_count");

Stores the last 100 stake events in a circular buffer. Used by the RecentStakeEvents query to display recent staking activity:

StakeEventState {
    validator: String,   // valoper address
    amount: Uint128,     // uatom delegated
    timestamp: u64,      // block timestamp
}

PENDING_REBALANCE

const PENDING_REBALANCE: Map<u64, PendingRebalanceOp> = Map::new("pending_rebalance");

Temporary state for in-flight rebalance operations. Cleaned up after the redelegation completes:

PendingRebalanceOp {
    source_valoper: String,   // Validator being redelegated from
    target_valoper: String,   // Validator being redelegated to
    amount: Uint128,          // Amount being moved
}

If the redelegation fails (e.g., existing redelegation in progress), the total_staked changes are reversed.

COMPOUND_STATE

const COMPOUND_STATE: Item<CompoundState> = Item::new("compound_state");

Tracks pagination progress during a paginated compound operation:

CompoundState {
    remaining: Uint128,      // ATOM remaining to delegate in subsequent pages
    cursor: Option<String>,  // Last processed validator address
    eligible_count: u32,     // Total eligible validators at start of compound
    processed_count: u32,    // Validators processed so far
}

Only present while a paginated compound is in progress. Cleared automatically when the final page completes. Can be cleared manually by the admin via ClearCompoundState if the sequence gets stuck.

COMPOUND_CARRYOVER

const COMPOUND_CARRYOVER: Item<Uint128> = Item::new("compound_carryover");

Tracks compound amounts that could not be delegated (e.g., when no eligible validators exist). This amount is exempt from treasury/operations cuts in the next compound cycle to prevent double taxation.

PENDING_UNBONDINGS

const PENDING_UNBONDINGS: Map<u64, UnbondingEntry> = Map::new("pending_unbond");
const UNBONDING_SEQ: Item<u64> = Item::new("unbond_seq");

UnbondingEntry {
    amount: Uint128,       // Unbonding amount in uatom
    completion_time: u64,  // Estimated completion time (seconds)
}

Tracks pending unbonding amounts so that returned principal is not taxed as staking rewards during compounding. Entries are created by Undelegate and cleaned up by CompoundRewards once the unbonding period completes.

Constants

Constant Value Description
MAX_STAKE_EVENTS 100 Number of recent stake events stored on-chain
DEFAULT_JAIL_GRACE_PERIOD 604800 (7 days) Default grace period before jailed validators are rebalanced
DEFAULT_MAX_VALIDATORS 250 Default maximum number of registered validators

Type Definitions (Shared)

These types are used in query responses:

ValidatorInfo {
    valoper: String,
    referral_id: String,
    registered_at: u64,
    total_staked: Uint128,
    active: bool,
    deactivated_by_admin: bool,
}

GlobalStats {
    total_atom_staked: Uint128,
    total_validators: u32,
}

StakeEvent {
    validator: String,
    amount: Uint128,
    timestamp: u64,
}

PendingFee {
    validator: String,
    amount: Uint128,
}

Note: GlobalStatsState (internal) has additional fields (active_validators, total_rewards_claimed) not exposed in the GlobalStats query response type.