From e1e9d500ab69074f84389d737fda84e64d1f13a9 Mon Sep 17 00:00:00 2001 From: coderipper Date: Tue, 5 Nov 2024 13:09:57 -0300 Subject: [PATCH 1/6] separated fees for defindex and vault --- apps/contracts/defindex/src/fee.rs | 40 +++++++++++-------- apps/contracts/defindex/src/interface.rs | 4 +- apps/contracts/defindex/src/lib.rs | 19 ++++----- apps/contracts/defindex/src/storage.rs | 6 +-- apps/contracts/defindex/src/utils.rs | 2 +- apps/contracts/factory/src/events.rs | 22 +++++------ apps/contracts/factory/src/lib.rs | 50 ++++++++++++------------ apps/contracts/factory/src/storage.rs | 2 +- apps/contracts/src/test.ts | 2 +- 9 files changed, 78 insertions(+), 69 deletions(-) diff --git a/apps/contracts/defindex/src/fee.rs b/apps/contracts/defindex/src/fee.rs index 3d2e765d..6fe55464 100644 --- a/apps/contracts/defindex/src/fee.rs +++ b/apps/contracts/defindex/src/fee.rs @@ -1,19 +1,20 @@ use soroban_sdk::{Address, Env, Map, Symbol, Vec}; -use crate::{access::AccessControl, constants::{MAX_BPS, SECONDS_PER_YEAR}, events, funds::fetch_total_managed_funds, storage::{get_defindex_receiver, get_factory, get_last_fee_assesment, get_vault_share, set_last_fee_assesment}, token::internal_mint, utils::calculate_dftokens_from_asset_amounts, ContractError}; +use crate::{access::AccessControl, constants::{MAX_BPS, SECONDS_PER_YEAR}, events, funds::fetch_total_managed_funds, storage::{get_defindex_receiver, get_factory, get_last_fee_assesment, get_vault_fee, set_last_fee_assesment}, token::internal_mint, utils::calculate_dftokens_from_asset_amounts, ContractError}; /// Fetches the current fee rate from the factory contract. /// The fee rate is expressed in basis points (BPS). -fn fetch_fee_rate(e: &Env) -> u32 { +fn fetch_defindex_fee(e: &Env) -> u32 { let factory_address = get_factory(e); // Interacts with the factory contract to get the fee rate. e.invoke_contract( &factory_address, - &Symbol::new(&e, "fee_rate"), + &Symbol::new(&e, "defindex_fee"), Vec::new(&e) ) } +/// Calculates the required fees in dfTokens based on the current APR fee rate. fn calculate_fees(e: &Env, time_elapsed: u64, fee_rate: u32) -> Result { let total_managed_funds = fetch_total_managed_funds(e); // Get total managed funds per asset @@ -34,27 +35,33 @@ fn calculate_fees(e: &Env, time_elapsed: u64, fee_rate: u32) -> Result Result<(), ContractError> { let current_timestamp = e.ledger().timestamp(); - let last_fee_assessment = get_last_fee_assesment(e); + let last_fee_assessment = get_last_fee_assesment(e); let time_elapsed = current_timestamp.checked_sub(last_fee_assessment).unwrap(); + // If no time has passed since the last fee assessment, no fees are collected if time_elapsed == 0 { return Ok(()); } - let fee_rate = fetch_fee_rate(e); + // Fetch the individual fees for DeFindex and Vault, then calculate the total rate + let defindex_fee = fetch_defindex_fee(e); + let vault_fee = get_vault_fee(e); + let total_fee_rate = defindex_fee.checked_add(vault_fee).unwrap(); - let total_fees = calculate_fees(e, time_elapsed, fee_rate)?; + // Calculate the total fees in dfTokens based on the combined fee rate + let total_fees = calculate_fees(e, time_elapsed, total_fee_rate)?; - // Mint the total fees as dfTokens - mint_fees(e, total_fees)?; + // Mint and distribute the fees proportionally + mint_fees(e, total_fees, defindex_fee, vault_fee)?; // Update the last fee assessment timestamp set_last_fee_assesment(e, ¤t_timestamp); @@ -62,18 +69,19 @@ pub fn collect_fees(e: &Env) -> Result<(), ContractError> { Ok(()) } -fn mint_fees(e: &Env, total_fees: i128) -> Result<(), ContractError> { +/// Mints dfTokens for fees and distributes them to the vault fee receiver and DeFindex receiver. +fn mint_fees(e: &Env, total_fees: i128, defindex_fee: u32, vault_fee: u32) -> Result<(), ContractError> { let access_control = AccessControl::new(&e); - + let vault_fee_receiver = access_control.get_fee_receiver()?; let defindex_receiver = get_defindex_receiver(e); - let vault_share_bps = get_vault_share(e); - - let vault_shares = (total_fees * vault_share_bps as i128) / MAX_BPS; - - let defindex_shares = total_fees - vault_shares; + // Calculate shares for each receiver based on their fee proportion + let total_fee_bps = defindex_fee as i128 + vault_fee as i128; + let defindex_shares = (total_fees * defindex_fee as i128) / total_fee_bps; + let vault_shares = total_fees - defindex_shares; + // Mint shares for both receivers internal_mint(e.clone(), vault_fee_receiver.clone(), vault_shares); internal_mint(e.clone(), defindex_receiver.clone(), defindex_shares); diff --git a/apps/contracts/defindex/src/interface.rs b/apps/contracts/defindex/src/interface.rs index f3873a90..65538e17 100644 --- a/apps/contracts/defindex/src/interface.rs +++ b/apps/contracts/defindex/src/interface.rs @@ -18,7 +18,7 @@ pub trait VaultTrait { /// * `manager` - The address responsible for managing the vault. /// * `emergency_manager` - The address with emergency control over the vault. /// * `fee_receiver` - The address that will receive fees from the vault. - /// * `vault_share` - The percentage of the vault's fees that will be sent to the DeFindex receiver. in BPS. + /// * `vault_fee` - The percentage of the vault's fees that will be sent to the DeFindex receiver. in BPS. /// * `defindex_receiver` - The address that will receive fees for DeFindex from the vault. /// * `factory` - The address of the factory that deployed the vault. /// @@ -30,7 +30,7 @@ pub trait VaultTrait { manager: Address, emergency_manager: Address, fee_receiver: Address, - vault_share: u32, + vault_fee: u32, defindex_receiver: Address, factory: Address, vault_name: String, diff --git a/apps/contracts/defindex/src/lib.rs b/apps/contracts/defindex/src/lib.rs index 044b1470..8f328d20 100755 --- a/apps/contracts/defindex/src/lib.rs +++ b/apps/contracts/defindex/src/lib.rs @@ -28,7 +28,7 @@ use funds::{fetch_current_idle_funds, fetch_current_invested_funds, fetch_total_ use interface::{AdminInterfaceTrait, VaultTrait, VaultManagementTrait}; use models::{ActionType, AssetAllocation, Instruction, Investment, OptionalSwapDetailsExactIn, OptionalSwapDetailsExactOut}; use storage::{ - get_assets, set_asset, set_defindex_receiver, set_factory, set_last_fee_assesment, set_total_assets, set_vault_share + get_assets, set_asset, set_defindex_receiver, set_factory, set_last_fee_assesment, set_total_assets, set_vault_fee }; use strategies::{get_asset_allocation_from_address, get_strategy_asset, get_strategy_client, get_strategy_struct, invest_in_strategy, pause_strategy, unpause_strategy, withdraw_from_strategy}; use token::{internal_mint, internal_burn, write_metadata, VaultToken}; @@ -54,7 +54,7 @@ impl VaultTrait for DeFindexVault { /// * `manager` - The address responsible for managing the vault. /// * `emergency_manager` - The address with emergency control over the vault. /// * `fee_receiver` - The address that will receive fees from the vault. - /// * `vault_share` - The percentage of the vault's fees that will be sent to the DeFindex receiver. in BPS. + /// * `vault_fee` - The percentage of the vault's fees that will be sent to the DeFindex receiver. in BPS. /// * `defindex_receiver` - The address that will receive fees for DeFindex from the vault. /// * `factory` - The address of the factory that deployed the vault. /// @@ -66,7 +66,7 @@ impl VaultTrait for DeFindexVault { manager: Address, emergency_manager: Address, fee_receiver: Address, - vault_share: u32, + vault_fee: u32, defindex_receiver: Address, factory: Address, vault_name: String, @@ -82,7 +82,7 @@ impl VaultTrait for DeFindexVault { access_control.set_role(&RolesDataKey::Manager, &manager); // Set Vault Share (in basis points) - set_vault_share(&e, &vault_share); + set_vault_fee(&e, &vault_fee); // Set Paltalabs Fee Receiver set_defindex_receiver(&e, &defindex_receiver); @@ -152,6 +152,9 @@ impl VaultTrait for DeFindexVault { set_last_fee_assesment(&e, &e.ledger().timestamp()); } + // fees assesment + collect_fees(&e)?; + // get assets let assets = get_assets(&e); // assets lenght should be equal to amounts_desired and amounts_min length @@ -204,8 +207,6 @@ impl VaultTrait for DeFindexVault { events::emit_deposit_event(&e, from, amounts, shares_to_mint); - // fees assesment - collect_fees(&e)?; // TODO return amounts and shares to mint Ok(()) } @@ -227,6 +228,9 @@ impl VaultTrait for DeFindexVault { check_initialized(&e)?; check_nonnegative_amount(df_amount)?; from.require_auth(); + + // fees assesment + collect_fees(&e)?; // Check if the user has enough dfTokens let df_user_balance = VaultToken::balance(e.clone(), from.clone()); @@ -292,9 +296,6 @@ impl VaultTrait for DeFindexVault { } events::emit_withdraw_event(&e, from, df_amount, amounts_withdrawn.clone()); - - // fees assesment - collect_fees(&e)?; Ok(amounts_withdrawn) } diff --git a/apps/contracts/defindex/src/storage.rs b/apps/contracts/defindex/src/storage.rs index 525e2b81..4f873b5b 100644 --- a/apps/contracts/defindex/src/storage.rs +++ b/apps/contracts/defindex/src/storage.rs @@ -82,13 +82,13 @@ pub fn get_last_fee_assesment(e: &Env) -> u64 { } // Vault Share -pub fn set_vault_share(e: &Env, vault_share: &u32) { +pub fn set_vault_fee(e: &Env, vault_fee: &u32) { e.storage() .instance() - .set(&DataKey::VaultShare, vault_share); + .set(&DataKey::VaultShare, vault_fee); } -pub fn get_vault_share(e: &Env) -> u32 { +pub fn get_vault_fee(e: &Env) -> u32 { e.storage() .instance() .get(&DataKey::VaultShare) diff --git a/apps/contracts/defindex/src/utils.rs b/apps/contracts/defindex/src/utils.rs index 3fab13cf..aa215ee3 100644 --- a/apps/contracts/defindex/src/utils.rs +++ b/apps/contracts/defindex/src/utils.rs @@ -79,9 +79,9 @@ pub fn calculate_asset_amounts_for_dftokens( pub fn calculate_dftokens_from_asset_amounts( env: &Env, asset_amounts: Map, // The input asset amounts + total_managed_funds: Map, // The total managed funds for each asset ) -> Result { let total_supply = VaultToken::total_supply(env.clone()); // Total dfToken supply - let total_managed_funds = fetch_total_managed_funds(&env); // Fetch all managed assets // Initialize the minimum dfTokens corresponding to each asset let mut min_df_tokens: Option = None; diff --git a/apps/contracts/factory/src/events.rs b/apps/contracts/factory/src/events.rs index 479cc43a..c0c23b03 100644 --- a/apps/contracts/factory/src/events.rs +++ b/apps/contracts/factory/src/events.rs @@ -8,14 +8,14 @@ use crate::defindex::AssetAllocation; pub struct InitializedEvent { pub admin: Address, pub defindex_receiver: Address, - pub fee_rate: u32, + pub defindex_fee: u32, } -pub(crate) fn emit_initialized(e: &Env, admin: Address, defindex_receiver: Address, fee_rate: u32) { +pub(crate) fn emit_initialized(e: &Env, admin: Address, defindex_receiver: Address, defindex_fee: u32) { let event: InitializedEvent = InitializedEvent { admin, defindex_receiver, - fee_rate, + defindex_fee, }; e.events() .publish(("DeFindexFactory", symbol_short!("init")), event); @@ -28,7 +28,7 @@ pub struct CreateDeFindexEvent { pub emergency_manager: Address, pub fee_receiver: Address, pub manager: Address, - pub vault_share: u32, + pub vault_fee: u32, pub assets: Vec } @@ -38,14 +38,14 @@ pub(crate) fn emit_create_defindex_vault( emergency_manager: Address, fee_receiver: Address, manager: Address, - vault_share: u32, + vault_fee: u32, assets: Vec, ) { let event = CreateDeFindexEvent { emergency_manager, fee_receiver, manager, - vault_share, + vault_fee, assets, }; @@ -81,16 +81,16 @@ pub(crate) fn emit_new_defindex_receiver(e: &Env, new_defindex_receiver: Address .publish(("DeFindexFactory", symbol_short!("nreceiver")), event); } -// NEW FEE RATE EVENT +// NEW DEFINDEX FEE EVENT #[contracttype] #[derive(Clone, Debug, Eq, PartialEq)] pub struct NewFeeRateEvent { - pub new_fee_rate: u32, + pub new_defindex_fee: u32, } -pub(crate) fn emit_new_fee_rate(e: &Env, new_fee_rate: u32) { - let event = NewFeeRateEvent { new_fee_rate }; +pub(crate) fn emit_new_defindex_fee(e: &Env, new_defindex_fee: u32) { + let event = NewFeeRateEvent { new_defindex_fee }; e.events() - .publish(("DeFindexFactory", symbol_short!("nfee_rate")), event); + .publish(("DeFindexFactory", symbol_short!("n_fee")), event); } \ No newline at end of file diff --git a/apps/contracts/factory/src/lib.rs b/apps/contracts/factory/src/lib.rs index f02aa883..f29d9b70 100644 --- a/apps/contracts/factory/src/lib.rs +++ b/apps/contracts/factory/src/lib.rs @@ -10,7 +10,7 @@ use soroban_sdk::{ }; use error::FactoryError; use defindex::{create_contract, AssetAllocation}; -use storage::{ add_new_defindex, extend_instance_ttl, get_admin, get_defi_wasm_hash, get_defindex_receiver, get_deployed_defindexes, get_fee_rate, has_admin, put_admin, put_defi_wasm_hash, put_defindex_receiver, put_fee_rate }; +use storage::{ add_new_defindex, extend_instance_ttl, get_admin, get_defi_wasm_hash, get_defindex_receiver, get_deployed_defindexes, get_fee_rate, has_admin, put_admin, put_defi_wasm_hash, put_defindex_receiver, put_defindex_fee }; fn check_initialized(e: &Env) -> Result<(), FactoryError> { if !has_admin(e) { @@ -26,7 +26,7 @@ pub trait FactoryTrait { /// * `e` - The environment in which the contract is running. /// * `admin` - The address of the contract administrator, who can manage settings. /// * `defindex_receiver` - The default address designated to receive a portion of fees. - /// * `fee_rate` - The initial annual fee rate (in basis points). + /// * `defindex_fee` - The initial annual fee rate (in basis points). /// * `defindex_wasm_hash` - The hash of the DeFindex Vault's WASM file for deploying new vaults. /// /// # Returns @@ -35,7 +35,7 @@ pub trait FactoryTrait { e: Env, admin: Address, defindex_receiver: Address, - fee_rate: u32, + defindex_fee: u32, defindex_wasm_hash: BytesN<32> ) -> Result<(), FactoryError>; @@ -45,7 +45,7 @@ pub trait FactoryTrait { /// * `e` - The environment in which the contract is running. /// * `emergency_manager` - The address assigned emergency control over the vault. /// * `fee_receiver` - The address designated to receive fees from the vault. - /// * `vault_share` - The percentage share of fees allocated to the vault's fee receiver. + /// * `vault_fee` - The percentage share of fees allocated to the vault's fee receiver. /// * `vault_name` - The name of the vault. /// * `vault_symbol` - The symbol of the vault. /// * `manager` - The address assigned as the vault manager. @@ -58,7 +58,7 @@ pub trait FactoryTrait { e: Env, emergency_manager: Address, fee_receiver: Address, - vault_share: u32, + vault_fee: u32, vault_name: String, vault_symbol: String, manager: Address, @@ -72,7 +72,7 @@ pub trait FactoryTrait { /// * `e` - The environment in which the contract is running. /// * `emergency_manager` - The address assigned emergency control over the vault. /// * `fee_receiver` - The address designated to receive fees from the vault. - /// * `vault_share` - The percentage share of fees allocated to the vault's fee receiver. + /// * `vault_fee` - The percentage share of fees allocated to the vault's fee receiver. /// * `vault_name` - The name of the vault. /// * `vault_symbol` - The symbol of the vault. /// * `manager` - The address assigned as the vault manager. @@ -87,7 +87,7 @@ pub trait FactoryTrait { caller: Address, emergency_manager: Address, fee_receiver: Address, - vault_share: u32, + vault_fee: u32, vault_name: String, vault_symbol: String, manager: Address, @@ -126,7 +126,7 @@ pub trait FactoryTrait { /// /// # Returns /// * `Result<(), FactoryError>` - Returns Ok(()) if successful, or an error if not authorized. - fn set_fee_rate(e: Env, new_fee_rate: u32) -> Result<(), FactoryError>; + fn set_defindex_fee(e: Env, new_fee_rate: u32) -> Result<(), FactoryError>; // --- Read Methods --- @@ -164,7 +164,7 @@ pub trait FactoryTrait { /// /// # Returns /// * `Result` - Returns the fee rate in basis points or an error if not found. - fn fee_rate(e: Env) -> Result; + fn defindex_fee(e: Env) -> Result; } #[contract] @@ -179,7 +179,7 @@ impl FactoryTrait for DeFindexFactory { /// * `e` - The environment in which the contract is running. /// * `admin` - The address of the contract administrator, who can manage settings. /// * `defindex_receiver` - The default address designated to receive a portion of fees. - /// * `fee_rate` - The initial annual fee rate (in basis points). + /// * `defindex_fee` - The initial annual fee rate (in basis points). /// * `defindex_wasm_hash` - The hash of the DeFindex Vault's WASM file for deploying new vaults. /// /// # Returns @@ -188,7 +188,7 @@ impl FactoryTrait for DeFindexFactory { e: Env, admin: Address, defindex_receiver: Address, - fee_rate: u32, + defindex_fee: u32, defi_wasm_hash: BytesN<32> ) -> Result<(), FactoryError> { if has_admin(&e) { @@ -198,9 +198,9 @@ impl FactoryTrait for DeFindexFactory { put_admin(&e, &admin); put_defindex_receiver(&e, &defindex_receiver); put_defi_wasm_hash(&e, defi_wasm_hash); - put_fee_rate(&e, &fee_rate); + put_defindex_fee(&e, &defindex_fee); - events::emit_initialized(&e, admin, defindex_receiver, fee_rate); + events::emit_initialized(&e, admin, defindex_receiver, defindex_fee); extend_instance_ttl(&e); Ok(()) } @@ -211,7 +211,7 @@ impl FactoryTrait for DeFindexFactory { /// * `e` - The environment in which the contract is running. /// * `emergency_manager` - The address assigned emergency control over the vault. /// * `fee_receiver` - The address designated to receive fees from the vault. - /// * `vault_share` - The percentage share of fees allocated to the vault's fee receiver. + /// * `vault_fee` - The percentage share of fees allocated to the vault's fee receiver. /// * `manager` - The address assigned as the vault manager. /// * `assets` - A vector of `AssetAllocation` structs that define the assets managed by the vault. /// * `salt` - A salt used for ensuring unique addresses for each deployed vault. @@ -222,7 +222,7 @@ impl FactoryTrait for DeFindexFactory { e: Env, emergency_manager: Address, fee_receiver: Address, - vault_share: u32, + vault_fee: u32, vault_name: String, vault_symbol: String, manager: Address, @@ -243,7 +243,7 @@ impl FactoryTrait for DeFindexFactory { &manager, &emergency_manager, &fee_receiver, - &vault_share, + &vault_fee, &defindex_receiver, ¤t_contract, &vault_name, @@ -251,7 +251,7 @@ impl FactoryTrait for DeFindexFactory { ); add_new_defindex(&e, defindex_address.clone()); - events::emit_create_defindex_vault(&e, emergency_manager, fee_receiver, manager, vault_share, assets); + events::emit_create_defindex_vault(&e, emergency_manager, fee_receiver, manager, vault_fee, assets); Ok(defindex_address) } @@ -261,7 +261,7 @@ impl FactoryTrait for DeFindexFactory { /// * `e` - The environment in which the contract is running. /// * `emergency_manager` - The address assigned emergency control over the vault. /// * `fee_receiver` - The address designated to receive fees from the vault. - /// * `vault_share` - The percentage share of fees allocated to the vault's fee receiver. + /// * `vault_fee` - The percentage share of fees allocated to the vault's fee receiver. /// * `vault_name` - The name of the vault. /// * `vault_symbol` - The symbol of the vault. /// * `manager` - The address assigned as the vault manager. @@ -276,7 +276,7 @@ impl FactoryTrait for DeFindexFactory { caller: Address, emergency_manager: Address, fee_receiver: Address, - vault_share: u32, + vault_fee: u32, vault_name: String, vault_symbol: String, manager: Address, @@ -305,7 +305,7 @@ impl FactoryTrait for DeFindexFactory { &manager, &emergency_manager, &fee_receiver, - &vault_share, + &vault_fee, &defindex_receiver, ¤t_contract, &vault_name, @@ -324,7 +324,7 @@ impl FactoryTrait for DeFindexFactory { ); add_new_defindex(&e, defindex_address.clone()); - events::emit_create_defindex_vault(&e, emergency_manager, fee_receiver, manager, vault_share, assets); + events::emit_create_defindex_vault(&e, emergency_manager, fee_receiver, manager, vault_fee, assets); Ok(defindex_address) } @@ -376,14 +376,14 @@ impl FactoryTrait for DeFindexFactory { /// /// # Returns /// * `Result<(), FactoryError>` - Returns Ok(()) if successful, or an error if not authorized. - fn set_fee_rate(e: Env, fee_rate: u32) -> Result<(), FactoryError> { + fn set_defindex_fee(e: Env, defindex_fee: u32) -> Result<(), FactoryError> { check_initialized(&e)?; extend_instance_ttl(&e); let admin = get_admin(&e); admin.require_auth(); - put_fee_rate(&e, &fee_rate); - events::emit_new_fee_rate(&e, fee_rate); + put_defindex_fee(&e, &defindex_fee); + events::emit_new_defindex_fee(&e, defindex_fee); Ok(()) } @@ -435,7 +435,7 @@ impl FactoryTrait for DeFindexFactory { /// /// # Returns /// * `Result` - Returns the fee rate in basis points or an error if not found. - fn fee_rate(e: Env) -> Result { + fn defindex_fee(e: Env) -> Result { check_initialized(&e)?; extend_instance_ttl(&e); Ok(get_fee_rate(&e)) diff --git a/apps/contracts/factory/src/storage.rs b/apps/contracts/factory/src/storage.rs index 92589bb5..bde91cd0 100644 --- a/apps/contracts/factory/src/storage.rs +++ b/apps/contracts/factory/src/storage.rs @@ -99,7 +99,7 @@ pub fn get_defindex_receiver(e: &Env) -> Address { } // Fee Rate BPS (MAX BPS = 10000) -pub fn put_fee_rate(e: &Env, value: &u32) { +pub fn put_defindex_fee(e: &Env, value: &u32) { e.storage().instance().set(&DataKey::FeeRate, value); } diff --git a/apps/contracts/src/test.ts b/apps/contracts/src/test.ts index bb33af73..5abe8656 100644 --- a/apps/contracts/src/test.ts +++ b/apps/contracts/src/test.ts @@ -81,7 +81,7 @@ export async function test_factory(addressBook: AddressBook) { const createDeFindexParams: xdr.ScVal[] = [ new Address(emergencyManager.publicKey()).toScVal(), new Address(feeReceiver.publicKey()).toScVal(), - nativeToScVal(100, { type: "u32" }), // Setting vault_share as 100 bps for demonstration + nativeToScVal(100, { type: "u32" }), // Setting vault_fee as 100 bps for demonstration nativeToScVal("Test Vault", { type: "string" }), nativeToScVal("DFT-Test-Vault", { type: "string" }), new Address(manager.publicKey()).toScVal(), From 5def99f8a3328e3791cd7b9e864381054e480f14 Mon Sep 17 00:00:00 2001 From: coderipper Date: Tue, 5 Nov 2024 13:10:52 -0300 Subject: [PATCH 2/6] whitepaper fees --- .../02-contracts/01-vault-contract.md | 66 +++++++++++++++++-- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/apps/docs/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract.md b/apps/docs/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract.md index 47992ad7..e40955da 100644 --- a/apps/docs/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract.md +++ b/apps/docs/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract.md @@ -105,18 +105,70 @@ The Emergency Manager has the authority to withdraw assets from the DeFindex in ### Management Every DeFindex has a manager, who is responsible for managing the DeFindex. The Manager can ebalance the Vault, and invest IDLE funds in strategies. -### Fee Collection -The revenues generated by the strategies are collected as shares of the DeFindex. These shares, or dfTokens, are minted whenever a deposit or withdrawal takes place. +### Fee Structure, Collection, and Distribution -The deployer can set a management fee, which can later be adjusted by the Fee Receiver. Additionally, **palta**labs charges a 0.5% APR on the vault shares. +1. Fee Receivers -The recommended initial setup suggests a fee of **0.5%-2% APR on these shares**. For example, if a DeFindex has 100 shares and the fee is set at 0.5% APR, the fees collected annually would be 1 share, split as 0.5 for the Fee Receiver and 0.5 for **palta**labs. The **palta**labs fee is defined in the Factory contract. +The DeFindex protocol defines two distinct fee receivers to reward both the creators of the DeFindex Protocol and the deployers of individual vaults: -These allocations are recalculated and minted whenever a user deposits, withdraws from the DeFindex, or when rebalancing occurs. +1. DeFindex Protocol Fee Receiver: Receives a fixed protocol fee of 0.5% APR. +2. Vault Fee Receiver: Receives a fee set by the vault deployer, typically recommended between 0.5% and 2% APR. -For instance, let's say a DeFindex starts with an initial value of 1 USDC per share and 100 shares (dfTokens). These 100 USDC are invested in a lending protocol offering an 8% APY. The DeFindex also has a 0.5% APR fee. After one year, the investment grows to 108 USDC. Additionally, 1 dfToken is minted as a fee. This means the DeFindex now holds 101 dfTokens backed by 108 USDC, making the price per share approximately 1.07 USDC. As a result, a user holding 100 dfTokens will have a value equivalent to around 107 USDC, while the collected fee will be backed by about 1.07 USDC. +The Total Management Fee is the sum of these two fees. Thus, each vault has a total APR fee rate $f_{\text{total}}$ such that: -It is expected that the Fee Receiver is associated with the manager, allowing the entity managing the Vault to be compensated through the Fee Receiver. In other words, the Fee Receiver could be the manager using the same address, or it could be a different entity such as a streaming contract, a DAO, or another party. +$f_{\text{total}} = f_{\text{DeFindex}} + f_{\text{Vault}}$ + + +where $f_{\text{DeFindex}} = 0.5\%$ (fixed) and $f_{\text{Vault}}$ is a variable APR, typically between 0.5% and 2%. + +2. Fee Collection Methodology + +The fee collection process mints new shares, or dfTokens, representing the management fees. These shares are calculated based on the elapsed time since the last fee assessment, ensuring fees are accrued in alignment with the actual period of asset management. The fee collection is triggered whenever there is a vault interaction, such as a deposit or withdrawal with calculations based on the time elapsed since the last fee assessment. + +Let: + +- $V_0$ be the Total Value Locked (TVL) at the last assessment, +- $s_0$ be the Total Shares (dfTokens) at the last assessment, +- $f_{\text{total}}$ be the Total Management Fee (APR). + +Over a time period $\Delta t$ , the fees due for collection are derived by the value equivalent in shares. + +3. Mathematical Derivation of New Fees + +To mint new shares for fee distribution, we calculate the required number of new shares, $s_f$, that correspond to the management fee over the elapsed period. + +After a period $\Delta t$ (expressed in seconds), the total shares $s_1$ should be: + +$s_1 = s_0 + s_f$ + +The total value $V_1$ remains $V_0$, assuming no deposits or withdrawals during this period. + +We establish the following condition to ensure the minted shares represent the accrued fee: + +$\frac{V_0}{s_1} \times s_f = V_0 \times f_{\text{total}} \times \frac{\Delta t}{\text{SECONDS\_PER\_YEAR}}$ + +Rearranging terms, we get: + +$$ +s_f = \frac{f_{\text{total}} \times s_0 \times \Delta t}{\text{SECONDS\_PER\_YEAR} - f_{\text{total}} \times \Delta t} +$$ + +This equation gives the precise share quantity $s_f$ to mint as dfTokens for the management fee over the period $\Delta t$. + +4. Distribution of Fees + +Once the total fees, $s_f$ , are calculated, the shares are split proportionally between the DeFindex Protocol Fee Receiver and the Vault Fee Receiver. This is done by calculating the ratio of each fee receiver’s APR to the total APR: + +$s_{\text{DeFindex}} = \frac{s_f \times f_{\text{DeFindex}}}{f_{\text{total}}}$ + + +$s_{\text{Vault}} = s_f - s_{\text{DeFindex}}$ + +This ensures that each fee receiver is allocated their respective share of dfTokens based on their fee contribution to $f_{\text{total}}$. The dfTokens are then minted to each receiver’s address as a direct representation of the fees collected. + +5. Fee Calculation Efficiency + +This calculation is performed only upon each vault interaction and considers only the time elapsed since the last assessment. This approach minimizes computational overhead and gas costs, ensuring that fee collection remains efficient.