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
12 changes: 12 additions & 0 deletions peerx-contracts/counter/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,18 @@ pub enum PeerXError {
NoClaimableBonuses = 604,
DistributionTooEarly = 605,

// ── Treasury ─────────────────────────────────────────────────────────────
/// Deposit amount must be positive.
TreasuryInvalidAmount = 610,
/// Withdraw amount exceeds current treasury balance.
TreasuryInsufficientFunds = 611,
/// The governance timelock for this withdrawal has not elapsed yet.
TreasuryTimelockNotElapsed = 612,
/// A withdrawal request for this ID was not found.
TreasuryWithdrawNotFound = 613,
/// The withdrawal request has already been executed.
TreasuryWithdrawAlreadyExecuted = 614,

// ── Emergency / circuit-breaker ─────────────────────────────────────────
NotEmergencyAdmin = 700,

Expand Down
63 changes: 63 additions & 0 deletions peerx-contracts/counter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ mod risk_management_tests;
// Staking Bonus System
mod staking_bonus;

// Treasury
mod treasury;
pub use treasury::{TreasuryManager, TreasuryKey, WithdrawRequest};

// Re-export fee adjustment types
#[cfg(feature = "experimental")]
pub use dynamic_fee_adjustment::{
Expand Down Expand Up @@ -1412,6 +1416,65 @@ impl CounterContract {
StakingBonusManager::execute_distribution(&env)
}

// ────────────────────────────────────────────────────────────────────────
// Treasury
// ────────────────────────────────────────────────────────────────────────

/// Credit `amount` to the treasury from `source`.
///
/// This entry point is intentionally public so governance or admin tools
/// can make manual top-ups. Penalty credits from early-unstake happen
/// automatically inside `staking_bonus::unstake_early`.
///
/// # Errors
/// Returns [`PeerXError::TreasuryInvalidAmount`] when `amount <= 0`.
pub fn treasury_deposit(env: Env, amount: i128, source: Symbol) -> Result<(), ContractError> {
TreasuryManager::deposit(&env, amount, source)
}

/// Return the current treasury balance (read-only, no auth required).
pub fn treasury_balance(env: Env) -> i128 {
TreasuryManager::balance(&env)
}

/// Governance-gated two-phase withdrawal.
///
/// * Phase 1 — `request`: creates a pending request locked by the
/// configured timelock (default 48 h). Returns the request ID.
/// * Phase 2 — `execute`: after the timelock, finalise the request.
/// Returns the executed [`WithdrawRequest`].
///
/// Both phases require admin auth.
///
/// # Arguments
/// * `phase` – Either `symbol_short!("request")` or
/// `symbol_short!("execute")`.
/// * `amount` – Amount for the `request` phase (ignored for `execute`).
/// * `destination`– Destination address for the `request` phase (ignored
/// for `execute`).
/// * `request_id` – For the `execute` phase: ID returned by the `request`
/// phase. Use `0` for the `request` phase.
pub fn treasury_withdraw(
env: Env,
admin: Address,
phase: Symbol,
amount: i128,
destination: Address,
request_id: u64,
) -> Result<u64, ContractError> {
admin.require_auth();
crate::admin::require_admin(&env, &admin)?;

if phase == symbol_short!("request") {
let id = TreasuryManager::request_withdraw(&env, amount, destination)?;
Ok(id)
} else {
// execute phase — returns the request id on success
TreasuryManager::execute_withdraw(&env, request_id)?;
Ok(request_id)
}
}

// ────────────────────────────────────────────────────────────────────────
// KYC Verification System
// ────────────────────────────────────────────────────────────────────────
Expand Down
9 changes: 9 additions & 0 deletions peerx-contracts/counter/src/staking_bonus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
/// - 365 days: 50% bonus
use soroban_sdk::{contracttype, symbol_short, Address, Env, Vec};
use crate::errors::PeerXError;
use crate::treasury::TreasuryManager;

// ────────────────────────────────────────────────────────────────────────────
// Constants
Expand Down Expand Up @@ -315,6 +316,14 @@ impl StakingBonusManager {
(stake_id, penalty),
);

// Credit the penalty to the treasury (acceptance criterion #85).
// A deposit failure must not prevent the user from unstaking; we
// silently allow it only when the amount is somehow zero, which
// cannot happen here (penalty is always ≥ 1 when amount ≥ 10).
if penalty > 0 {
let _ = TreasuryManager::deposit(env, penalty, symbol_short!("penalty"));
}

Ok((principal_returned, penalty))
}

Expand Down
Loading
Loading