Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 60 additions & 1 deletion packages/staking/src/contract.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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::<Address>::new(env));
}

/// Stake MOI tokens to increase governance voting power
Expand Down Expand Up @@ -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<Address> = 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 {
Expand Down Expand Up @@ -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<Address> = env
.storage()
.persistent()
.get(&DataKey::StakerList)
.unwrap_or_else(|| Vec::new(env));
let mut updated: Vec<Address> = 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)
Expand Down Expand Up @@ -254,6 +286,33 @@ pub fn get_stake(env: &Env, user: &Address) -> Option<StakePosition> {
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, StakePosition>(&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<Address> {
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<UnbondingPosition> {
env.storage().instance().get(&DataKey::Unbonding(user.clone()))
Expand Down
13 changes: 13 additions & 0 deletions packages/staking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Address> {
contract::get_all_stakers(&env)
}

pub fn pause(env: Env, admin: Address) -> Result<(), types::StakingError> {
contract::pause(&env, &admin)
}
Expand Down
3 changes: 3 additions & 0 deletions packages/staking/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down