diff --git a/packages/staking/src/contract.rs b/packages/staking/src/contract.rs index 5ce377d..4296a75 100644 --- a/packages/staking/src/contract.rs +++ b/packages/staking/src/contract.rs @@ -1,4 +1,4 @@ -use soroban_sdk::{token, symbol_short, Address, Env}; +use soroban_sdk::{token, symbol_short, Address, Env, Vec}; use crate::types::*; use common::pause; @@ -18,6 +18,11 @@ pub fn init(env: &Env, admin: &Address, token: &Address) { // Initialize total staked to 0 env.storage().instance().set(&DataKey::TotalStaked, &0i128); + + // Initialize empty staker registry used by get_all_stakers() + env.storage() + .persistent() + .set(&DataKey::StakerList, &Vec::
::new(env)); } /// Stake MOI tokens to increase governance voting power @@ -87,6 +92,17 @@ pub fn stake( .checked_add(amount) .ok_or(StakingError::Overflow)?; env.storage().instance().set(&DataKey::TotalStaked, &new_total); + + // Register user in the staker list (used by get_all_stakers) + let mut stakers: Vec
= env + .storage() + .persistent() + .get(&DataKey::StakerList) + .unwrap_or_else(|| Vec::new(env)); + stakers.push_back(user.clone()); + env.storage() + .persistent() + .set(&DataKey::StakerList, &stakers); // Emit event Staked { @@ -138,6 +154,22 @@ pub fn unstake(env: &Env, user: &Address) -> Result<(), StakingError> { // Remove stake position env.storage().instance().remove(&DataKey::Stake(user.clone())); + // Remove user from staker list + let stakers: Vec
= env + .storage() + .persistent() + .get(&DataKey::StakerList) + .unwrap_or_else(|| Vec::new(env)); + let mut updated: Vec
= Vec::new(env); + for s in stakers.iter() { + if s != *user { + updated.push_back(s); + } + } + env.storage() + .persistent() + .set(&DataKey::StakerList, &updated); + // Update total staked let total_staked: i128 = env.storage().instance() .get(&DataKey::TotalStaked) @@ -254,6 +286,33 @@ pub fn get_stake(env: &Env, user: &Address) -> Option { env.storage().instance().get(&DataKey::Stake(user.clone())) } +/// Get the raw staked token amount for a user. +/// +/// Returns `0` if the user has no active stake. This is a convenience query +/// for clients that only need the amount and not the full `StakePosition`. +pub fn get_stake_amount(env: &Env, user: &Address) -> i128 { + env.storage() + .instance() + .get::(&DataKey::Stake(user.clone())) + .map(|pos| pos.amount) + .unwrap_or(0) +} + +/// Return the list of all addresses that currently hold an active stake. +/// +/// The list is maintained in persistent storage: an address is added when +/// `stake()` is called and removed when `unstake()` is called. Addresses +/// in the unbonding period are therefore not included. +/// +/// Off-chain callers can use this to iterate over active stakers, e.g. for +/// governance snapshot builds or leaderboard queries. +pub fn get_all_stakers(env: &Env) -> Vec
{ + env.storage() + .persistent() + .get(&DataKey::StakerList) + .unwrap_or_else(|| Vec::new(env)) +} + /// Get user's unbonding position pub fn get_unbonding(env: &Env, user: &Address) -> Option { env.storage().instance().get(&DataKey::Unbonding(user.clone())) diff --git a/packages/staking/src/lib.rs b/packages/staking/src/lib.rs index 2c0943e..7bcd6af 100644 --- a/packages/staking/src/lib.rs +++ b/packages/staking/src/lib.rs @@ -50,6 +50,19 @@ impl Staking { contract::get_total_staked(&env) } + /// Returns only the raw staked token amount for a user (0 if none). + /// Convenience alternative to get_stake() for callers that don't need + /// the full StakePosition. + pub fn get_stake_amount(env: Env, user: Address) -> i128 { + contract::get_stake_amount(&env, &user) + } + + /// Returns the list of all addresses that currently hold an active stake. + /// Maintained automatically by stake() / unstake(). + pub fn get_all_stakers(env: Env) -> soroban_sdk::Vec
{ + contract::get_all_stakers(&env) + } + pub fn pause(env: Env, admin: Address) -> Result<(), types::StakingError> { contract::pause(&env, &admin) } diff --git a/packages/staking/src/types.rs b/packages/staking/src/types.rs index e17fd36..ca21e81 100644 --- a/packages/staking/src/types.rs +++ b/packages/staking/src/types.rs @@ -61,6 +61,9 @@ pub enum DataKey { Unbonding(Address), /// Total staked amount across all users TotalStaked, + /// Ordered list of addresses with an active stake position. + /// Updated on stake() and unstake(). Used by get_all_stakers(). + StakerList, } /// User's active staking position