use crate::*; use common_data::constants::*; use common_data::math::{mul_div, Rounding}; use near_sdk::{log, Promise}; #[near] impl Contract { // ==================== Protocol Configuration Management ==================== // Protocol account has access to configure protocol fee parameters and claim protocol fees // Protocol fees come from cuts of management/performance fees (state.protocol_fees_owed_vault) /// Set protocol management fee cut (basis points) pub fn protocol_set_management_fee_cut(&mut self, cut_bps: BasisPoints) { self.internal_validate_protocol_caller(); self.internal_validate_basis_points(cut_bps); self.protocol_config.protocol_management_fee_cut_bps = cut_bps; log!("Protocol management fee cut updated to {} bps", cut_bps); } /// Set protocol performance fee cut (basis points) pub fn protocol_set_performance_fee_cut(&mut self, cut_bps: BasisPoints) { self.internal_validate_protocol_caller(); self.internal_validate_basis_points(cut_bps); self.protocol_config.protocol_performance_fee_cut_bps = cut_bps; log!("Protocol performance fee cut updated to {} bps", cut_bps); } /// Set protocol fee recipient (for fee distribution) pub fn protocol_set_fee_recipient(&mut self, recipient: AccountId) { self.internal_validate_protocol_caller(); self.internal_validate_account_id(&recipient); self.protocol_config.protocol_fee_recipient = recipient.clone(); log!("Protocol fee recipient updated to {}", recipient); } /// Update entire protocol configuration pub fn protocol_update_config(&mut self, config: ProtocolConfig) { self.internal_validate_protocol_caller(); // Validate all basis points in config self.internal_validate_basis_points(config.protocol_management_fee_cut_bps); self.internal_validate_basis_points(config.protocol_performance_fee_cut_bps); self.internal_validate_account_id(&config.protocol_fee_recipient); self.protocol_config = config; log!("Protocol config updated"); } pub fn protocol_set_account(&mut self, protocol_account: AccountId) { self.internal_validate_protocol_caller(); require!( env::is_valid_account_id(protocol_account.as_bytes()), "Invalid protocol account ID" ); self.protocol_account = protocol_account.clone(); log!("Protocol account updated to {}", protocol_account); } // ==================== Protocol Fee Claiming ==================== /// Claim accumulated protocol fees (management + performance fee cuts) /// Transfers protocol_fees_owed_vault to protocol_config.protocol_fee_recipient pub fn protocol_claim_fees_for_asset(&mut self, asset: Asset) -> Promise { self.internal_validate_protocol_caller(); let protocol_fees_owed = self .state .protocol_fees_owed .get(&asset) .unwrap_or(&U128(0)) .clone(); require!( protocol_fees_owed.0 > 0, "No protocol fees to claim for this asset" ); // Check sufficient available assets let vault_balance = self.get_vault_balance(&asset); require!( vault_balance.available_amount.0 >= protocol_fees_owed.0, "Insufficient available assets to claim protocol fees" ); // Clear protocol fees owed self.state.protocol_fees_owed.insert(asset.clone(), U128(0)); // Decrease available asset balance self.internal_update_asset_balance(&asset, protocol_fees_owed, false); log!( "Claiming {} {} in protocol fees for recipient {}", protocol_fees_owed.0, asset, self.protocol_config.protocol_fee_recipient ); // Transfer protocol fees to protocol fee recipient asset.create_transfer_promise( self.protocol_config.protocol_fee_recipient.clone(), protocol_fees_owed, Some("Protocol Fee Claim".to_string()), ) } // ==================== Protocol Getters ==================== /// Get protocol management fee cut pub fn protocol_get_management_fee_cut(&self) -> BasisPoints { self.protocol_config.protocol_management_fee_cut_bps } /// Get protocol performance fee cut pub fn protocol_get_performance_fee_cut(&self) -> BasisPoints { self.protocol_config.protocol_performance_fee_cut_bps } /// Get protocol fee recipient pub fn protocol_get_fee_recipient(&self) -> AccountId { self.protocol_config.protocol_fee_recipient.clone() } /// Get complete protocol configuration pub fn protocol_get_config(&self) -> ProtocolConfig { self.protocol_config.clone() } /// Get protocol fee summary pub fn protocol_get_fee_summary(&self) -> serde_json::Value { serde_json::json!({ "management_fee_cut_bps": self.protocol_get_management_fee_cut(), "performance_fee_cut_bps": self.protocol_get_performance_fee_cut(), "fee_recipient": self.protocol_get_fee_recipient(), "fees_owed": self.get_all_protocol_fees_owed(), }) } /// Check if protocol has any fee configuration pub fn protocol_has_fee_configuration(&self) -> bool { self.protocol_config.protocol_management_fee_cut_bps > 0 || self.protocol_config.protocol_performance_fee_cut_bps > 0 } } impl Contract { // ==================== Fee Management ==================== /// Add fees to the vault with protocol split /// Used by accountant to track management and performance fees pub fn internal_add_fees( &mut self, asset: Asset, total_fee: U128, protocol_cut_bps: BasisPoints, ) { if protocol_cut_bps == 0 || total_fee.0 == 0 { // If no protocol cut or no fee, all goes to vault creator let current_fees = self.state.fees_owed.get(&asset).unwrap_or(&U128(0)).0; self.state.fees_owed.insert( asset.clone(), U128( current_fees .checked_add(total_fee.0) .expect("Vault fees owed overflow"), ), ); return; } let protocol_fee = U128(mul_div( total_fee.0, protocol_cut_bps as u128, BASIS_POINTS_DIVISOR, Rounding::Up, )); let vault_creator_fee = U128( total_fee .0 .checked_sub(protocol_fee.0) .expect("Protocol fee exceeds total fee"), ); // Add protocol fee to protocol fees owed let current_protocol_fees = self .state .protocol_fees_owed .get(&asset) .unwrap_or(&U128(0)) .0; self.state.protocol_fees_owed.insert( asset.clone(), U128( current_protocol_fees .checked_add(protocol_fee.0) .expect("Protocol fees owed overflow"), ), ); // Add remainder to vault creator fees let current_fees = self.state.fees_owed.get(&asset).unwrap_or(&U128(0)).0; self.state.fees_owed.insert( asset.clone(), U128( current_fees .checked_add(vault_creator_fee.0) .expect("Vault fees owed overflow"), ), ); } }