diff --git a/contracts/vault/src/lib.rs b/contracts/vault/src/lib.rs index f29ec083..f85bdea8 100644 --- a/contracts/vault/src/lib.rs +++ b/contracts/vault/src/lib.rs @@ -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 { @@ -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() } @@ -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 { @@ -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(); @@ -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() @@ -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. @@ -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() @@ -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, diff --git a/contracts/vault/src/test.rs b/contracts/vault/src/test.rs index eff80519..3d104158 100644 --- a/contracts/vault/src/test.rs +++ b/contracts/vault/src/test.rs @@ -24,6 +24,7 @@ //! scenarios (see `invariant_tests.rs`, Issue #735) #![cfg(test)] +extern crate std; use super::*; use crate::benji_strategy::{BenjiStrategy, BenjiStrategyClient}; @@ -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 @@ -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] @@ -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); @@ -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))); }