use super::basis_points::BasisPoints; use super::constants::*; use super::nano_percent::NanoPercent; use near_sdk::{json_types::U128, near, require}; /// Simplified vault configuration for the vault contract /// Focuses on essential vault operations without the full complexity of the main vault config #[near(serializers=[json, borsh])] #[derive(Clone, Debug)] pub struct VaultConfig { /// hard limit on tvl cap pub tvl_cap: Option, /// Maximum deposit amount that can be processed synchronously pub max_sync_deposit: U128, /// Maximum percentage of total vault assets that can be redeemed synchronously (basis points, 10000 = 100%) pub max_sync_redeem_ratio: u16, /// Number of decimals for share price precision (e.g., 8 for traditional finance) pub share_price_decimals: u8, /// Extra decimals for internal precision calculations pub extra_decimals: u8, /// Minimum per-deposit amount (prevents dust & excessive rounding loss) pub min_deposit: U128, /// Minimum per-withdrawal amount (prevents dust & fee inefficiencies) pub min_withdraw: U128, /// Minimum delay (nanosec) from redeem request to give users time to cancel pub redeem_delay_nanosec: U128, /// Whether this is a private vault (requires whitelist) pub is_private: bool, /// Whether blacklist is enabled pub blacklist_enabled: bool, /// Deposit flow cap (optional rate limiting) pub deposit_flow_cap: Option, /// Withdrawal flow cap (optional rate limiting) pub withdrawal_flow_cap: Option, /// Redeem confirmation (true = required, false = not needed) pub redeem_confirmation: bool, /// Whether emergency pause functionality is enabled pub emergency_pause_enabled: bool, /// Whether ownership transfer is enabled pub ownership_transfer_enabled: bool, /// Maximum share price staleness in nanoseconds /// Deposits and redeems will fail if share price is older than this pub max_share_price_staleness_ns: U128, // -------------------- Fee Configuration -------------------- /// Continuous management fee in bps annualized (e.g., 200 = 2.00%) pub management_fee_bps: BasisPoints, /// Performance/incentive fee in bps on profits above HWM (e.g., 1_500 = 15.00%) pub performance_fee_bps: BasisPoints, /// Interval (nanosec) between crystallizations; 0 = crystallize on every profit event pub crystallization_interval_nanosec: U128, /// Optional baseline APY (in basis points annualized) /// Performance fee applies only to yield above this hurdle /// Example: 700 = 7.00% APY hurdle pub performance_hurdle_bps: Option, // -------------------- Price Validation -------------------- /// Minimum price deviation from TWAP/prev observation in nano percent (100_000_000_000 = 100%) to guard against errors pub new_price_min_deviation_nanopercent: NanoPercent, /// Maximum price deviation from TWAP/prev observation in nano percent (100_000_000_000 = 100%) to guard against spikes pub new_price_max_deviation_nanopercent: NanoPercent, /// Minimum time between price updates pub new_price_min_interval_nanosec: U128, /// Maximum time between price updates pub new_price_max_interval_nanosec: U128, } /// Flow control configuration #[near(serializers=[json, borsh])] #[derive(Clone, Debug)] pub struct FlowCap { /// Maximum amount allowed in the time period (in base token units) pub amount: U128, /// Time period in nanoseconds pub period_nanosec: u64, } impl Default for VaultConfig { fn default() -> Self { Self { tvl_cap: None, max_sync_deposit: U128(DEFAULT_MAX_SYNC_DEPOSIT), // 10,000 tokens with 6 decimals (adjust based on asset) max_sync_redeem_ratio: MAX_SYNC_REDEEM_RATIO_BPS, // 100% of total assets (adjust this if needed to hard enforce redeem size) share_price_decimals: DEFAULT_SHARE_PRICE_DECIMALS, // Default to 8 decimals like traditional finance extra_decimals: DEFAULT_EXTRA_DECIMALS, // Default to 18 for high precision min_deposit: U128(0), // No minimum by default min_withdraw: U128(0), // No minimum by default redeem_delay_nanosec: U128(0), // No delay by default is_private: false, // Not private by default blacklist_enabled: false, // Blacklist disabled by default deposit_flow_cap: None, // No flow cap by default withdrawal_flow_cap: None, // No flow cap by default redeem_confirmation: true, emergency_pause_enabled: true, // Emergency pause enabled by default for safety ownership_transfer_enabled: true, // Ownership transfer enabled by default max_share_price_staleness_ns: U128(DEFAULT_MAX_SHARE_PRICE_STALENESS_NS), // 1 hour default // Fee defaults management_fee_bps: 0, // No management fee by default performance_fee_bps: 0, // No performance fee by default crystallization_interval_nanosec: U128(DEFAULT_CRYSTALLIZATION_INTERVAL_NS), // 1 week default performance_hurdle_bps: None, // No hurdle by default // Price validation defaults (0 = disabled) new_price_min_deviation_nanopercent: 0, new_price_max_deviation_nanopercent: 0, new_price_min_interval_nanosec: U128(0), new_price_max_interval_nanosec: U128(0), } } } impl VaultConfig { /// Validate the configuration parameters pub fn validate(&self) { require!( self.share_price_decimals <= MAX_SHARE_PRICE_DECIMALS, "Share price decimals cannot exceed 30" ); require!( self.extra_decimals <= MAX_EXTRA_DECIMALS, "Extra decimals cannot exceed 30" ); require!( self.share_price_decimals + self.extra_decimals <= MAX_COMBINED_DECIMALS, "Overflow protection: share price decimals + extra decimals must not exceed 38 decimals" ); // Validate sync limits require!( self.max_sync_redeem_ratio <= BASIS_POINTS_DIVISOR_U16, "Max sync redeem ratio cannot exceed 100% (10000 basis points)" ); // Validate time intervals are reasonable if self.redeem_delay_nanosec.0 > 0 { require!( self.redeem_delay_nanosec.0 >= NANOSECONDS_PER_MINUTE, "Redeem delay too short (< 1 minute)" ); require!( self.redeem_delay_nanosec.0 <= NANOSECONDS_PER_WEEK, "Redeem delay too long (> 1 week)" ); } // Validate flow caps if present if let Some(ref flow_cap) = self.deposit_flow_cap { flow_cap.validate(); } if let Some(ref flow_cap) = self.withdrawal_flow_cap { flow_cap.validate(); } // Validate share price staleness require!( self.max_share_price_staleness_ns.0 >= MIN_SHARE_PRICE_STALENESS_NS, "Share price staleness too short (< 1 minute)" ); require!( self.max_share_price_staleness_ns.0 <= MAX_SHARE_PRICE_STALENESS_NS, "Share price staleness too long (> 7 days)" ); // -------------------- Fee Validations -------------------- // Management fees should be reasonable (0-100% = 0-10,000 bps) require!( self.management_fee_bps <= BASIS_POINTS_DIVISOR_U16, "Management fee exceeds 100%" ); // Performance fees should be reasonable (0-100% = 0-10,000 bps) require!( self.performance_fee_bps <= BASIS_POINTS_DIVISOR_U16, "Performance fee exceeds 100%" ); // Performance hurdle should be reasonable if set (0-100% APY = 0-10,000 bps) if let Some(hurdle) = self.performance_hurdle_bps { require!( hurdle <= BASIS_POINTS_DIVISOR_U16, "Performance hurdle exceeds 100% APY" ); } // Crystallization interval: 1 minute to 1 year if self.crystallization_interval_nanosec.0 > 0 { require!( self.crystallization_interval_nanosec.0 >= NANOSECONDS_PER_MINUTE, "Crystallization interval too short (< 1 minute)" ); require!( self.crystallization_interval_nanosec.0 <= NANOSECONDS_PER_YEAR, "Crystallization interval too long (> 1 year)" ); } // -------------------- Price Deviation Validations -------------------- // Min deviation should be less than max deviation require!( self.new_price_min_deviation_nanopercent <= self.new_price_max_deviation_nanopercent, "Min price deviation must be <= max price deviation" ); // Max price deviation should be reasonable (0-100% = 0-100,000,000,000 nano percent) require!( self.new_price_max_deviation_nanopercent <= NANO_PERCENT_DIVISOR, "Max price deviation exceeds 100%" ); // -------------------- Time Interval Validations -------------------- // Min interval should be less than or equal to max interval require!( self.new_price_min_interval_nanosec.0 <= self.new_price_max_interval_nanosec.0, "Min price interval must be <= max price interval" ); // New price intervals: 1 minute to 1 year if self.new_price_min_interval_nanosec.0 > 0 { require!( self.new_price_min_interval_nanosec.0 >= NANOSECONDS_PER_MINUTE, "Min price interval too short (< 1 minute)" ); } if self.new_price_max_interval_nanosec.0 > 0 { require!( self.new_price_max_interval_nanosec.0 <= NANOSECONDS_PER_YEAR, "Max price interval too long (> 1 year)" ); } } pub fn validate_config_change(&self, new_config: &VaultConfig) { // Validate that config invariants are maintained require!( self.is_private == new_config.is_private, "Cannot change privacy setting of the vault" ); require!( self.blacklist_enabled == new_config.blacklist_enabled, "Cannot change blacklist setting of the vault" ); require!( self.ownership_transfer_enabled == new_config.ownership_transfer_enabled, "Cannot change ownership transfer setting of the vault" ); require!( self.share_price_decimals == new_config.share_price_decimals, "Cannot change share price decimals" ); require!( self.extra_decimals == new_config.extra_decimals, "Cannot change extra decimals" ); } /// Get the redeem delay in nanoseconds pub fn get_redeem_delay(&self) -> u128 { self.redeem_delay_nanosec.0 } /// Get effective deposit flow cap pub fn get_effective_deposit_cap(&self) -> Option<&FlowCap> { self.deposit_flow_cap.as_ref() } /// Get effective withdrawal flow cap pub fn get_effective_withdrawal_cap(&self) -> Option<&FlowCap> { self.withdrawal_flow_cap.as_ref() } } impl FlowCap { /// Validate flow cap configuration pub fn validate(&self) { require!(self.amount.0 > 0, "Flow cap amount must be positive"); const ONE_HOUR_NS: u64 = NANOSECONDS_PER_HOUR as u64; const ONE_YEAR_NS: u64 = NANOSECONDS_PER_YEAR as u64; require!( self.period_nanosec >= ONE_HOUR_NS, "Flow cap period too short (< 1 hour)" ); require!( self.period_nanosec <= ONE_YEAR_NS, "Flow cap period too long (> 1 year)" ); } }