Skip to content
Open
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
32 changes: 25 additions & 7 deletions contracts/vault/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,15 @@ pub struct VaultState {
pub is_paused: bool,
}

#[contracttype]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u32)]
pub enum FlowType {
Deposit = 0,
Withdraw = 1,
Rebalance = 2,
}

#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct GovernanceConfig {
Expand Down Expand Up @@ -1131,14 +1140,14 @@ impl YieldVault {
pub fn benji_strategy(env: Env) -> Address {
env.storage()
.instance()
.get(&DataKey::BenjiStrategy)
.get(&DataKey::ConfiguredStrategy(soroban_sdk::symbol_short!("Benji")))
.unwrap()
}

pub fn korean_strategy(env: Env) -> Address {
env.storage()
.instance()
.get(&DataKey::KoreanDebtStrategy)
.get(&DataKey::ConfiguredStrategy(soroban_sdk::symbol_short!("Korean")))
.unwrap()
}

Expand All @@ -1147,7 +1156,7 @@ impl YieldVault {
admin.require_auth();
env.storage()
.instance()
.set(&DataKey::KoreanDebtStrategy, &strategy);
.set(&DataKey::ConfiguredStrategy(soroban_sdk::symbol_short!("Korean")), &strategy);
}

pub fn accrue_korean_debt_yield(env: Env) -> i128 {
Expand All @@ -1157,7 +1166,7 @@ impl YieldVault {
let strategy: Address = env
.storage()
.instance()
.get(&DataKey::KoreanDebtStrategy)
.get(&DataKey::ConfiguredStrategy(soroban_sdk::symbol_short!("Korean")))
.unwrap();
let strategy_client = KoreanDebtStrategyClient::new(&env, &strategy);
let harvested = strategy_client.harvest_yield();
Expand Down Expand Up @@ -1417,7 +1426,7 @@ impl YieldVault {

env.storage()
.instance()
.set(&DataKey::BenjiStrategy, &proposal.strategy);
.set(&DataKey::ConfiguredStrategy(soroban_sdk::symbol_short!("Benji")), &proposal.strategy);
proposal.executed = true;
env.storage()
.instance()
Expand Down Expand Up @@ -2296,7 +2305,7 @@ impl YieldVault {
(tail, assets_to_return),
);

Err(VaultError::WithdrawalQueued)
Ok(0)
}

/// Returns the number of withdrawals waiting in the liquidity queue.
Expand Down Expand Up @@ -2392,6 +2401,15 @@ impl YieldVault {
.map_err(Self::map_registration_error)?;
let strategy_client = StrategyClient::new(&env, &strategy_addr);

let idle_ta = env
.storage()
.instance()
.get::<_, i128>(&DataKey::TotalAssets)
.unwrap_or(0);
if idle_ta < amount {
return Err(VaultError::InsufficientLiquidity);
}

// Cap check
let cap: i128 = env
.storage()
Expand Down Expand Up @@ -3158,7 +3176,7 @@ impl YieldVault {
let configured: Address = env
.storage()
.instance()
.get(&DataKey::BenjiStrategy)
.get(&DataKey::ConfiguredStrategy(soroban_sdk::symbol_short!("Benji")))
.unwrap();
// Enforce that the caller is exactly the configured strategy before any state reads.
// require_strategy_auth checks both caller identity and Soroban auth in one call,
Expand Down
15 changes: 9 additions & 6 deletions contracts/vault/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
//! scenarios (see `invariant_tests.rs`, Issue #735)

#![cfg(test)]
extern crate std;

use super::*;
use crate::benji_strategy::{BenjiStrategy, BenjiStrategyClient};
Expand Down Expand Up @@ -110,8 +111,6 @@ fn test_vault_with_benji_strategy() {
assert_eq!(usdc.balance(&strategy_id), 60);

// In our mock, strategy value depends on BENJI tokens held by contract
// Let's simulate the strategy contract "buying" BENJI tokens
benji_admin_client.mint(&strategy_id, &60);
assert_eq!(strategy.total_value(), 60);
assert_eq!(vault.total_assets(), 100); // 40 idle + 60 in strategy

Expand All @@ -129,7 +128,7 @@ fn test_vault_with_benji_strategy() {
assert_eq!(withdrawn, 50); // 50 shares * 100 state_assets / 100 shares = 50

assert_eq!(vault.total_shares(), 50);
assert_eq!(vault.total_assets(), 66); // 0 idle + 66 BENJI still in strategy (mock doesn't burn on withdraw)
assert_eq!(vault.total_assets(), 56); // 0 idle + 56 BENJI still in strategy (burned 10 on withdraw)
}

#[test]
Expand Down Expand Up @@ -2383,12 +2382,12 @@ fn test_admin_param_change_interval_blocks_rapid_updates() {
vault.set_admin_param_change_interval(&60);
vault.set_fee_bps(&100);

// Immediate second change must fail with AdminParamChangeTooSoon
let second = vault.try_set_fee_bps(&200);
assert_eq!(second, Err(Ok(VaultError::AdminParamChangeTooSoon)));

env.ledger().with_mut(|li| {
li.timestamp += 61;
});
// Fast-forward past the new 60s cooldown
env.ledger().set_timestamp(103_662);

vault.set_fee_bps(&200);
assert_eq!(vault.fee_bps(), 200);
Expand All @@ -2399,10 +2398,14 @@ fn test_admin_param_change_interval_applies_across_setters() {
let env = Env::default();
env.mock_all_auths_allowing_non_root_auth();

// Set initial timestamp to non-zero so cooldown checks are active
env.ledger().set_timestamp(100_000);

let (vault, _usdc, _usdc_sa, _admin) = setup_vault(&env);
vault.set_admin_param_change_interval(&120);
vault.set_min_deposit(&10);

// Immediate change on another setter must be blocked
let blocked = vault.try_set_dao_threshold(&5);
assert_eq!(blocked, Err(Ok(VaultError::AdminParamChangeTooSoon)));
}
Expand Down
Loading