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
2 changes: 2 additions & 0 deletions rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
[toolchain]
channel = "1.81.0"


139 changes: 135 additions & 4 deletions src/balance.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::storage::{
AdminProposal, AutoConvertConfig, BrandingConfig, ChangelogEntry, ClaimWindow, DataKey,
DayBucket, DynamicFeeConfig, FeeRecipient, FlashStakeReceipt, GovernanceProposal,
AccessTier, AdminProposal, AutoConvertConfig, BrandingConfig, ChangelogEntry, ClaimWindow,
DataKey, DayBucket, DynamicFeeConfig, FeeRecipient, FlashStakeReceipt, GovernanceProposal,
InsurancePolicy, InsuranceProduct, Loan, LoanConfig, LotteryConfig, Milestone, MultisigConfig,
PendingAction, PriceCondition, PriorityBidRecord, RateHistoryEntry, ReferralStats, Season,
StakePosition, SunsetState, VestingEntry,
PendingAction, PriceCondition, PriorityBidRecord, RateHistoryEntry, ReferralStats,
RevenueShareMerkleRoot, RevenueSharingConfig, Season, StakePosition, SunsetState,
VestingEntry,
};

use soroban_sdk::{symbol_short, Address, Env, String, Symbol, Vec};
Expand Down Expand Up @@ -2048,3 +2049,133 @@ pub fn set_grace_period_end(env: &Env, ledger: u32) {
.instance()
.set(&symbol_short!("snst_gpe"), &ledger);
}

// ── Issue #281: Fee Revenue Sharing ──────────────────────────────────────────

pub fn get_revenue_sharing_config(env: &Env) -> Option<RevenueSharingConfig> {
env.storage().instance().get(&symbol_short!("rev_cfg"))
}

pub fn set_revenue_sharing_config(env: &Env, config: &RevenueSharingConfig) {
env.storage().instance().set(&symbol_short!("rev_cfg"), config);
}

pub fn get_revenue_share_pool(env: &Env) -> i128 {
env.storage()
.instance()
.get(&symbol_short!("rev_pool"))
.unwrap_or(0)
}

pub fn set_revenue_share_pool(env: &Env, amount: i128) {
env.storage()
.instance()
.set(&symbol_short!("rev_pool"), &amount);
}

pub fn get_revenue_share_epoch(env: &Env) -> u32 {
env.storage()
.instance()
.get(&symbol_short!("rev_ep"))
.unwrap_or(0)
}

pub fn set_revenue_share_epoch(env: &Env, epoch: u32) {
env.storage().instance().set(&symbol_short!("rev_ep"), &epoch);
}

pub fn get_revenue_share_merkle_root(env: &Env) -> Option<RevenueShareMerkleRoot> {
env.storage().instance().get(&symbol_short!("rev_mrk"))
}

pub fn set_revenue_share_merkle_root(env: &Env, root: &RevenueShareMerkleRoot) {
env.storage().instance().set(&symbol_short!("rev_mrk"), root);
}

pub fn is_revenue_share_claimed(env: &Env, user: &Address, epoch: u32) -> bool {
let key = (Symbol::new(env, "rev_clm"), user.clone(), epoch);
env.storage().persistent().get(&key).unwrap_or(false)
}

pub fn set_revenue_share_claimed(env: &Env, user: &Address, epoch: u32) {
let key = (Symbol::new(env, "rev_clm"), user.clone(), epoch);
env.storage().persistent().set(&key, &true);
}

// ── Issue #280: New Staker Reward Escrow ────────────────────────────────────

pub fn get_escrow_period(env: &Env) -> u32 {
env.storage()
.instance()
.get(&symbol_short!("esc_prd"))
.unwrap_or(0)
}

pub fn set_escrow_period(env: &Env, ledgers: u32) {
env.storage()
.instance()
.set(&symbol_short!("esc_prd"), &ledgers);
}

pub fn get_escrow_balance(env: &Env, user: &Address) -> i128 {
let key = (Symbol::new(env, "esc_bal"), user.clone());
env.storage().persistent().get(&key).unwrap_or(0)
}

pub fn set_escrow_balance(env: &Env, user: &Address, amount: i128) {
let key = (Symbol::new(env, "esc_bal"), user.clone());
env.storage().persistent().set(&key, &amount);
}

pub fn remove_escrow_balance(env: &Env, user: &Address) {
let key = (Symbol::new(env, "esc_bal"), user.clone());
env.storage().persistent().remove(&key);
}

pub fn get_escrow_release_ledger(env: &Env, user: &Address) -> Option<u32> {
let key = (Symbol::new(env, "esc_rel"), user.clone());
env.storage().persistent().get(&key)
}

pub fn set_escrow_release_ledger(env: &Env, user: &Address, ledger: u32) {
let key = (Symbol::new(env, "esc_rel"), user.clone());
env.storage().persistent().set(&key, &ledger);
}

pub fn remove_escrow_release_ledger(env: &Env, user: &Address) {
let key = (Symbol::new(env, "esc_rel"), user.clone());
env.storage().persistent().remove(&key);
}

// ── Issue #282: Stake-Gated Access ───────────────────────────────────────────

pub fn get_access_tiers(env: &Env) -> Vec<AccessTier> {
env.storage()
.instance()
.get(&symbol_short!("acc_tier"))
.unwrap_or_else(|| Vec::new(env))
}

pub fn set_access_tiers(env: &Env, tiers: &Vec<AccessTier>) {
env.storage()
.instance()
.set(&symbol_short!("acc_tier"), tiers);
}

pub fn get_user_access_tier(env: &Env, user: &Address) -> Option<u32> {
let key = (Symbol::new(env, "acc_u_tr"), user.clone());
env.storage().persistent().get(&key)
}

pub fn set_user_access_tier(env: &Env, user: &Address, tier_index: u32) {
let key = (Symbol::new(env, "acc_u_tr"), user.clone());
env.storage().persistent().set(&key, &tier_index);
}

pub fn remove_user_access_tier(env: &Env, user: &Address) {
let key = (Symbol::new(env, "acc_u_tr"), user.clone());
env.storage().persistent().remove(&key);
}



18 changes: 18 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,8 +433,26 @@ pub enum VaultFeatureError {
/// `liquidate_loan()` when the borrower's LTV is still below
/// `LIQUIDATION_LTV_BPS` (9 000).
LoanNotLiquidatable = 22,

// ── Issue #281: Fee Revenue Sharing ──────────────────────────────────────
/// `set_revenue_sharing()`: `share_bps` above 10 000.
InvalidRevenueShareConfig = 32,
/// `claim_revenue_share()`: invalid Merkle proof.
RevenueShareInvalidProof = 33,
/// `claim_revenue_share()`: user already claimed for this epoch.
RevenueShareAlreadyClaimed = 34,

// ── Issue #282: Stake-Gated Access ───────────────────────────────────────
/// `set_access_tier()`: 5 access tiers are already configured.
TooManyAccessTiers = 35,
/// `claim_access_token()`: user does not qualify for any access tier.
IneligibleForAccessTier = 36,
/// `revoke_access_token()`: user still qualifies for their current tier.
UserStillEligible = 37,
}



impl From<VaultError> for VaultExtError {
fn from(err: VaultError) -> Self {
match err {
Expand Down
53 changes: 53 additions & 0 deletions src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1084,3 +1084,56 @@ pub fn force_resolved(
env.events()
.publish(topics, (principal, accrued_reward, ledger));
}

// ── Issue #281: Fee Revenue Sharing ──────────────────────────────────────────

pub fn revenue_distributed(
env: &Env,
merkle_root: &soroban_sdk::Bytes,
total_amount: i128,
ledger: u32,
) {
let topics = (symbol_short!("rev_dist"),);
env.events()
.publish(topics, (merkle_root.clone(), total_amount, ledger));
}

pub fn revenue_share_claimed(
env: &Env,
user: &Address,
amount: i128,
epoch: u32,
ledger: u32,
) {
let topics = (symbol_short!("rev_clm"), user);
env.events().publish(topics, (amount, epoch, ledger));
}

// ── Issue #280: New Staker Reward Escrow ────────────────────────────────────

pub fn escrow_released(env: &Env, user: &Address, escrowed_amount: i128, ledger: u32) {
let topics = (symbol_short!("esc_rel"), user);
env.events()
.publish(topics, (user.clone(), escrowed_amount, ledger));
}

// ── Issue #282: Stake-Gated Access ───────────────────────────────────────────

pub fn access_token_issued(env: &Env, user: &Address, tier_index: u32, ledger: u32) {
let topics = (symbol_short!("acc_iss"), user);
env.events().publish(topics, (tier_index, ledger));
}

pub fn access_token_revoked(
env: &Env,
user: &Address,
tier_index: u32,
reason: Symbol,
ledger: u32,
) {
let topics = (symbol_short!("acc_rev"), user);
env.events().publish(topics, (tier_index, reason, ledger));
}



29 changes: 29 additions & 0 deletions src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1034,3 +1034,32 @@ pub enum SunsetState {
ForceResolutionActive,
Closed,
}

// ── Issue #281: Fee Revenue Sharing ──────────────────────────────────────────

#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub struct RevenueSharingConfig {
pub governance_token: Address,
pub share_bps: u32,
}

#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub struct RevenueShareMerkleRoot {
pub root: soroban_sdk::Bytes,
pub epoch: u32,
pub total_amount: i128,
}

// ── Issue #282: Stake-Gated Access ───────────────────────────────────────────

#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub struct AccessTier {
pub min_stake: i128,
pub min_duration_ledgers: u32,
pub access_token_contract: Address,
}


Loading
Loading