From be6d89dcfa7e76381fe2082113776a4208a05a79 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Mon, 9 Dec 2024 20:05:52 -0300 Subject: [PATCH 01/17] =?UTF-8?q?=E2=9C=A8Add=20function=20to=20get=20tran?= =?UTF-8?q?saction=20CPU=20instructions=20&=20warn=20if=20it's=20too=20hig?= =?UTF-8?q?h?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/contracts/src/utils/tx.ts | 47 ++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/apps/contracts/src/utils/tx.ts b/apps/contracts/src/utils/tx.ts index 08074e61..4a658c4d 100644 --- a/apps/contracts/src/utils/tx.ts +++ b/apps/contracts/src/utils/tx.ts @@ -125,3 +125,50 @@ export const getCurrentTimePlusOneHour = () => { return oneHourLater; }; + +export function getTransactionBudget(tx: any): { instructions: number, readBytes: number, writeBytes: number } { + const resources = tx.envelopeXdr.value().tx().ext().value().resources() + const warningTolerance = 0.85 + const MAXWRITEBYTES = 132096 + const MAXREADBYTES = 200000 + const MAXINSTRUCTIONS = 100000000 + const budget= { + instructions: resources.instructions(), + readBytes: resources.readBytes(), + writeBytes: resources.writeBytes(), + } + const getPercentage = (value: number, max: number)=>{ + return (value * 100)/max + } + if(budget.instructions > MAXINSTRUCTIONS * warningTolerance){ + console.warn('Instructions budget exceeded') + console.table({ + value:{ + instructions: budget.instructions, + maxInstructions: MAXINSTRUCTIONS, + '%': getPercentage(budget.instructions, MAXINSTRUCTIONS) + }, + }) + } + if(budget.readBytes > MAXREADBYTES * warningTolerance){ + console.warn('ReadBytes budget exceeded') + console.table({ + value: { + readBytes: budget.readBytes, + maxReadBytes: MAXREADBYTES, + '%': getPercentage(budget.readBytes, MAXREADBYTES) + } + }) + } + if(budget.writeBytes > MAXWRITEBYTES * warningTolerance){ + console.warn('WriteBytes budget exceeded') + console.table({ + value:{ + writeBytes: budget.writeBytes, + maxWriteBytes: MAXWRITEBYTES, + '%': getPercentage(budget.writeBytes, MAXWRITEBYTES) + } + }) + } + return budget +} \ No newline at end of file From db3c4b85327cb74b782e997300d5e71858325245 Mon Sep 17 00:00:00 2001 From: esteblock Date: Tue, 10 Dec 2024 09:39:39 -0300 Subject: [PATCH 02/17] move investment function to investment file --- apps/contracts/vault/src/deposit.rs | 51 +------------------------ apps/contracts/vault/src/interface.rs | 4 -- apps/contracts/vault/src/investment.rs | 53 +++++++++++++++++++++++++- apps/contracts/vault/src/lib.rs | 4 +- 4 files changed, 54 insertions(+), 58 deletions(-) diff --git a/apps/contracts/vault/src/deposit.rs b/apps/contracts/vault/src/deposit.rs index 4092e450..6de2186c 100644 --- a/apps/contracts/vault/src/deposit.rs +++ b/apps/contracts/vault/src/deposit.rs @@ -3,11 +3,8 @@ use soroban_sdk::{panic_with_error, token::TokenClient, Address, Env, Vec}; use crate::{ funds::{ - fetch_invested_funds_for_asset, fetch_invested_funds_for_strategy, fetch_total_managed_funds, }, - investment::check_and_execute_investments, - models::{AssetInvestmentAllocation, StrategyAllocation}, storage::get_assets, token::{internal_mint, VaultToken}, utils::{calculate_deposit_amounts_and_shares_to_mint, check_nonnegative_amount}, @@ -106,50 +103,4 @@ fn mint_shares( internal_mint(e.clone(), from, shares_to_mint); } Ok(()) -} - -/// Generate investment allocations and execute them. -pub fn generate_and_execute_investments( - e: &Env, - amounts: &Vec, - assets: &Vec, -) -> Result<(), ContractError> { - let mut asset_investments = Vec::new(&e); - - for (i, amount) in amounts.iter().enumerate() { - let asset = assets.get(i as u32).unwrap(); - let (asset_invested_funds, _) = fetch_invested_funds_for_asset(&e, &asset); - - let mut strategy_allocations = Vec::new(&e); - let mut remaining_amount = amount; - - for (j, strategy) in asset.strategies.iter().enumerate() { - let strategy_invested_funds = fetch_invested_funds_for_strategy(&e, &strategy.address); - - let mut invest_amount = if asset_invested_funds > 0 { - (amount * strategy_invested_funds) / asset_invested_funds - } else { - 0 - }; - - if j == asset.strategies.len() as usize - 1 { - invest_amount = remaining_amount; - } - - remaining_amount -= invest_amount; - - strategy_allocations.push_back(Some(StrategyAllocation { - strategy_address: strategy.address.clone(), - amount: invest_amount, - })); - } - - asset_investments.push_back(Some(AssetInvestmentAllocation { - asset: asset.address.clone(), - strategy_allocations, - })); - } - - check_and_execute_investments(e.clone(), assets.clone(), asset_investments)?; - Ok(()) -} +} \ No newline at end of file diff --git a/apps/contracts/vault/src/interface.rs b/apps/contracts/vault/src/interface.rs index 0b980da3..852f8940 100644 --- a/apps/contracts/vault/src/interface.rs +++ b/apps/contracts/vault/src/interface.rs @@ -211,10 +211,6 @@ pub trait VaultTrait { /// * `Map` - A map containing each asset address and its corresponding proportional amount. fn get_asset_amounts_per_shares(e: Env, vault_shares: i128) -> Result, ContractError>; - // TODO: DELETE THIS, USED FOR TESTING - /// Temporary method for testing purposes. - // fn get_asset_amounts_for_dftokens(e: Env, df_token: i128) -> Map; - fn get_fees(e: Env) -> (u32, u32); /// Collects the fees from the vault and transfers them to the fee receiver addresses. diff --git a/apps/contracts/vault/src/investment.rs b/apps/contracts/vault/src/investment.rs index ea5b6bb1..a2565edd 100644 --- a/apps/contracts/vault/src/investment.rs +++ b/apps/contracts/vault/src/investment.rs @@ -1,10 +1,13 @@ use soroban_sdk::{Env, Vec, panic_with_error}; use crate::{ - models::AssetInvestmentAllocation, + models::{AssetInvestmentAllocation, StrategyAllocation}, strategies::invest_in_strategy, utils::{check_nonnegative_amount}, ContractError, + funds::{ + fetch_invested_funds_for_asset, fetch_invested_funds_for_strategy, + }, }; use common::models::AssetStrategySet; @@ -94,4 +97,50 @@ pub fn check_and_execute_investments( } } Ok(()) -} \ No newline at end of file +} + +/// Generate investment allocations and execute them. +pub fn generate_and_execute_investments( + e: &Env, + amounts: &Vec, + assets: &Vec, +) -> Result<(), ContractError> { + let mut asset_investments = Vec::new(&e); + + for (i, amount) in amounts.iter().enumerate() { + let asset = assets.get(i as u32).unwrap(); + let (asset_invested_funds, _) = fetch_invested_funds_for_asset(&e, &asset); + + let mut strategy_allocations = Vec::new(&e); + let mut remaining_amount = amount; + + for (j, strategy) in asset.strategies.iter().enumerate() { + let strategy_invested_funds = fetch_invested_funds_for_strategy(&e, &strategy.address); + + let mut invest_amount = if asset_invested_funds > 0 { + (amount * strategy_invested_funds) / asset_invested_funds + } else { + 0 + }; + + if j == asset.strategies.len() as usize - 1 { + invest_amount = remaining_amount; + } + + remaining_amount -= invest_amount; + + strategy_allocations.push_back(Some(StrategyAllocation { + strategy_address: strategy.address.clone(), + amount: invest_amount, + })); + } + + asset_investments.push_back(Some(AssetInvestmentAllocation { + asset: asset.address.clone(), + strategy_allocations, + })); + } + + check_and_execute_investments(e.clone(), assets.clone(), asset_investments)?; + Ok(()) +} diff --git a/apps/contracts/vault/src/lib.rs b/apps/contracts/vault/src/lib.rs index 68a7d932..c507a18a 100755 --- a/apps/contracts/vault/src/lib.rs +++ b/apps/contracts/vault/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -use deposit::{generate_and_execute_investments, process_deposit}; use soroban_sdk::{ contract, contractimpl, panic_with_error, token::{TokenClient}, @@ -26,10 +25,11 @@ mod utils; use access::{AccessControl, AccessControlTrait, RolesDataKey}; use aggregator::{internal_swap_exact_tokens_for_tokens, internal_swap_tokens_for_exact_tokens}; +use deposit::{process_deposit}; use fee::{collect_fees, fetch_defindex_fee}; use funds::{fetch_current_idle_funds, fetch_current_invested_funds, fetch_total_managed_funds}; use interface::{AdminInterfaceTrait, VaultManagementTrait, VaultTrait}; -use investment::check_and_execute_investments; +use investment::{check_and_execute_investments, generate_and_execute_investments}; use models::{ Instruction, OptionalSwapDetailsExactIn, OptionalSwapDetailsExactOut, CurrentAssetInvestmentAllocation, From 236ee67962a6754222eeb9bc8034b0dcccfdf58c Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Tue, 10 Dec 2024 09:44:50 -0300 Subject: [PATCH 03/17] =?UTF-8?q?=E2=9C=85Add=20test:=20deposit=20from=20A?= =?UTF-8?q?,=20withdraw=20from=20B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/contracts/strategies/hodl/src/test.rs | 7 +++- .../strategies/hodl/src/test/hodl/deposit.rs | 37 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/apps/contracts/strategies/hodl/src/test.rs b/apps/contracts/strategies/hodl/src/test.rs index 134c5e63..716d3dc2 100644 --- a/apps/contracts/strategies/hodl/src/test.rs +++ b/apps/contracts/strategies/hodl/src/test.rs @@ -24,6 +24,7 @@ pub struct HodlStrategyTest<'a> { strategy: HodlStrategyClient<'a>, token: TokenClient<'a>, user: Address, + user1: Address, } impl<'a> HodlStrategyTest<'a> { @@ -36,15 +37,19 @@ impl<'a> HodlStrategyTest<'a> { let admin = Address::generate(&env); let token = create_token_contract(&env, &admin); let user = Address::generate(&env); + let user1 = Address::generate(&env); // Mint 1,000,000,000 to user StellarAssetClient::new(&env, &token.address).mint(&user, &1_000_000_000); + // Mint 1,000,000,000 to user1 + StellarAssetClient::new(&env, &token.address).mint(&user1, &1_000_000_000); HodlStrategyTest { env, strategy, token, - user + user, + user1, } } diff --git a/apps/contracts/strategies/hodl/src/test/hodl/deposit.rs b/apps/contracts/strategies/hodl/src/test/hodl/deposit.rs index 7f075993..dcaaad4b 100644 --- a/apps/contracts/strategies/hodl/src/test/hodl/deposit.rs +++ b/apps/contracts/strategies/hodl/src/test/hodl/deposit.rs @@ -79,4 +79,41 @@ fn deposit_and_withdrawal_flow() { let result = test.strategy.try_withdraw(&amount_to_withdraw, &test.user); assert_eq!(result, Err(Ok(StrategyError::InsufficientBalance))); +} + +#[test] +fn deposit_from_a_withdrawal_from_b() { + let test = HodlStrategyTest::setup(); + + let result = test.strategy.try_deposit(&10_000_000, &test.user); + assert_eq!(result, Err(Ok(StrategyError::NotInitialized))); + + // initialize + let init_fn_args: Vec = (0,).into_val(&test.env); + test.strategy.initialize(&test.token.address, &init_fn_args); + + // Initial user token balance + let balance = test.token.balance(&test.user); + + let amount = 123456; + + // Deposit amount of token from the user to the strategy + test.strategy.deposit(&amount, &test.user); + + let balance_after_deposit = test.token.balance(&test.user); + assert_eq!(balance_after_deposit, balance - amount); + + // Reading strategy balance + let strategy_balance_after_deposit = test.token.balance(&test.strategy.address); + assert_eq!(strategy_balance_after_deposit, amount); + + // Reading user balance on strategy contract + let user_balance_on_strategy = test.strategy.balance(&test.user); + assert_eq!(user_balance_on_strategy, amount); + + + let amount_to_withdraw = 100_000; + // Withdrawing token from the strategy to user + let result = test.strategy.try_withdraw(&amount_to_withdraw, &test.user1); + assert_eq!(result, Err(Ok(StrategyError::InsufficientBalance))); } \ No newline at end of file From 9f9b4a13f8719d9f20b1db10c2a44b6c303f4ee5 Mon Sep 17 00:00:00 2001 From: esteblock Date: Tue, 10 Dec 2024 10:29:55 -0300 Subject: [PATCH 04/17] funds: better naming and comments on funds functions --- apps/contracts/vault/src/funds.rs | 111 +++++++++++++++---------- apps/contracts/vault/src/investment.rs | 17 +--- apps/contracts/vault/src/utils.rs | 2 +- 3 files changed, 71 insertions(+), 59 deletions(-) diff --git a/apps/contracts/vault/src/funds.rs b/apps/contracts/vault/src/funds.rs index ca05edc1..4889f78f 100644 --- a/apps/contracts/vault/src/funds.rs +++ b/apps/contracts/vault/src/funds.rs @@ -6,40 +6,58 @@ use crate::models::{StrategyAllocation, CurrentAssetInvestmentAllocation}; use crate::storage::get_assets; use crate::strategies::get_strategy_client; -/// Fetches the idle funds for a given asset. Idle funds refer to the balance of the asset -/// that is currently not invested in any strategies. +/// Retrieves the idle funds for a given asset. +/// +/// Idle funds represent the balance of the asset that is held by the current contract +/// but not actively allocated to any strategies. /// /// # Arguments /// * `e` - The current environment instance. -/// * `asset` - The asset for which idle funds are being fetched. +/// * `asset_address` - The address of the asset for which idle funds are being calculated. /// /// # Returns -/// * The idle balance (i128) of the asset in the current contract address. +/// The idle funds of the asset as an `i128`, representing the unallocated balance. pub fn fetch_idle_funds_for_asset(e: &Env, asset: &Address) -> i128 { TokenClient::new(e, &asset).balance(&e.current_contract_address()) } -/// Fetches the total funds that are invested for a given asset. -/// It iterates through all the strategies associated with the asset and sums their balances. +/// Retrieves the total funds invested in a specified strategy. +/// +/// Since only the strategy contract itself can accurately determine the amount invested, +/// this function performs a cross-contract call to the strategy to fetch the current balance +/// of the investment. /// /// # Arguments /// * `e` - The current environment instance. -/// * `asset` - The asset for which invested funds are being fetched. +/// * `strategy_address` - The address of the strategy whose investment balance is to be retrieved. /// /// # Returns -/// * The total invested balance (i128) of the asset across all strategies. -pub fn fetch_invested_funds_for_strategy(e: &Env, strategy_address: &Address) -> i128 { +/// The total invested funds in the strategy as an `i128`. +pub fn fetch_strategy_invested_funds(e: &Env, strategy_address: &Address) -> i128 { let strategy_client = get_strategy_client(e, strategy_address.clone()); strategy_client.balance(&e.current_contract_address()) } -// return total invested funds but also a vec of StrategyAllocation -pub fn fetch_invested_funds_for_asset(e: &Env, asset: &AssetStrategySet) -> (i128, Vec){ +/// Calculates the total funds invested in strategies for a given asset and +/// provides a detailed breakdown of allocations. +/// +/// This function aggregates the balances of all strategies linked to the specified +/// asset and returns both the total invested amount and a detailed allocation. +/// +/// # Arguments +/// * `e` - The current environment instance. +/// * `asset_strategy_set` - The asset and its associated set of strategies to evaluate. +/// +/// # Returns +/// A tuple containing: +/// * `i128`: The total funds invested across all strategies. +/// * `Vec`: A vector with the allocation details for each strategy. +pub fn fetch_invested_funds_for_asset(e: &Env, asset_strategy_set: &AssetStrategySet) -> (i128, Vec){ let mut invested_funds = 0; let mut strategy_allocations: Vec = Vec::new(e); - for strategy in asset.strategies.iter() { - let strategy_balance = fetch_invested_funds_for_strategy(e, &strategy.address); + for strategy in asset_strategy_set.strategies.iter() { + let strategy_balance = fetch_strategy_invested_funds(e, &strategy.address); invested_funds += strategy_balance; strategy_allocations.push_back(StrategyAllocation { strategy_address: strategy.address.clone(), @@ -49,6 +67,43 @@ pub fn fetch_invested_funds_for_asset(e: &Env, asset: &AssetStrategySet) -> (i12 (invested_funds, strategy_allocations) } +/// Fetches the total managed funds for all assets. This includes both idle and invested funds. +/// It returns a map where the key is the asset's address and the value is the total managed balance +/// (idle + invested). With this map we can calculate the current managed funds ratio. +/// +/// # Arguments +/// * `e` - The current environment instance. +/// +/// # Returns +/// * A map where each entry represents an asset's address and its total managed balance. +pub fn fetch_total_managed_funds(e: &Env) -> Map { + let assets = get_assets(e); + let mut map: Map = Map::new(e); + for asset in assets { + let idle_amount = fetch_idle_funds_for_asset(e, &asset.address); + let (invested_amount, strategy_allocations) = fetch_invested_funds_for_asset(e, &asset); + let total_amount = idle_amount + invested_amount; + map.set( + asset.address.clone(), + CurrentAssetInvestmentAllocation { + asset: asset.address.clone(), + total_amount, + idle_amount, + invested_amount, + strategy_allocations, + }, + ); + } + map +} + +/* + User experience functions. The following functions are not being used inernally in + the contrac, but are intender to be used by the client for a better user experience. + They create maps for better redeability + +*/ + /// Fetches the current idle funds for all assets managed by the contract. /// It returns a map where the key is the asset's address and the value is the idle balance. @@ -87,33 +142,3 @@ pub fn fetch_current_invested_funds(e: &Env) -> Map { } map } - -/// Fetches the total managed funds for all assets. This includes both idle and invested funds. -/// It returns a map where the key is the asset's address and the value is the total managed balance -/// (idle + invested). With this map we can calculate the current managed funds ratio. -/// -/// # Arguments -/// * `e` - The current environment instance. -/// -/// # Returns -/// * A map where each entry represents an asset's address and its total managed balance. -pub fn fetch_total_managed_funds(e: &Env) -> Map { - let assets = get_assets(e); - let mut map: Map = Map::new(e); - for asset in assets { - let idle_amount = fetch_idle_funds_for_asset(e, &asset.address); - let (invested_amount, strategy_allocations) = fetch_invested_funds_for_asset(e, &asset); - let total_amount = idle_amount + invested_amount; - map.set( - asset.address.clone(), - CurrentAssetInvestmentAllocation { - asset: asset.address.clone(), - total_amount, - idle_amount, - invested_amount, - strategy_allocations, - }, - ); - } - map -} diff --git a/apps/contracts/vault/src/investment.rs b/apps/contracts/vault/src/investment.rs index a2565edd..01e720af 100644 --- a/apps/contracts/vault/src/investment.rs +++ b/apps/contracts/vault/src/investment.rs @@ -6,7 +6,7 @@ use crate::{ utils::{check_nonnegative_amount}, ContractError, funds::{ - fetch_invested_funds_for_asset, fetch_invested_funds_for_strategy, + fetch_invested_funds_for_asset, fetch_strategy_invested_funds, }, }; use common::models::AssetStrategySet; @@ -61,19 +61,6 @@ pub fn check_and_execute_investments( panic_with_error!(&e, ContractError::WrongStrategiesLength); } - // NOTE: We can avoid this check as it if total idle funds exceed funds to invest, this will fail - // when trying to transfer - - // // Calculate total intended investment for this asset - // let total_asset_investment: i128 = asset_investment.investments.iter() - // .filter_map(|strategy| strategy.as_ref().map(|s| s.amount.unwrap_or(0))) - // .sum(); - - // // Verify total intended investment does not exceed idle funds for this asset - // if total_asset_investment > fetch_idle_funds_for_asset(&e, &asset_investment.asset) { - // panic_with_error!(&e, ContractError::InsufficientIdleFunds); - // } - // Process each defined strategy investment for the current asset for (j, strategy_investment_opt) in asset_investment.strategy_allocations.iter().enumerate() { if let Some(strategy_investment) = strategy_investment_opt { @@ -115,7 +102,7 @@ pub fn generate_and_execute_investments( let mut remaining_amount = amount; for (j, strategy) in asset.strategies.iter().enumerate() { - let strategy_invested_funds = fetch_invested_funds_for_strategy(&e, &strategy.address); + let strategy_invested_funds = fetch_strategy_invested_funds(&e, &strategy.address); let mut invest_amount = if asset_invested_funds > 0 { (amount * strategy_invested_funds) / asset_invested_funds diff --git a/apps/contracts/vault/src/utils.rs b/apps/contracts/vault/src/utils.rs index 6125b393..112399cc 100644 --- a/apps/contracts/vault/src/utils.rs +++ b/apps/contracts/vault/src/utils.rs @@ -55,7 +55,7 @@ pub fn check_nonnegative_amount(amount: i128) -> Result<(), ContractError> { // continue; // } -// let strategy_invested_funds = fetch_invested_funds_for_strategy(e, &strategy.address); +// let strategy_invested_funds = fetch_strategy_invested_funds(e, &strategy.address); // let strategy_share_of_withdrawal = // (amount * strategy_invested_funds) / total_invested_in_strategies; From b49722b43f710adaeedae7eb077980669ac2e110 Mon Sep 17 00:00:00 2001 From: esteblock Date: Tue, 10 Dec 2024 10:48:29 -0300 Subject: [PATCH 05/17] improve comments for check_and_execute_investments --- apps/contracts/vault/src/investment.rs | 46 +++++++++++++++----------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/apps/contracts/vault/src/investment.rs b/apps/contracts/vault/src/investment.rs index 01e720af..9d60ea7b 100644 --- a/apps/contracts/vault/src/investment.rs +++ b/apps/contracts/vault/src/investment.rs @@ -11,35 +11,43 @@ use crate::{ }; use common::models::AssetStrategySet; -/// Checks and executes the investments for each asset based on provided allocations. -/// The function iterates through the specified assets and asset investments to ensure validity -/// and executes investments accordingly. +/// Executes investment allocations for a set of assets based on the provided investment strategies. +/// +/// This function ensures that the specified assets and strategies match the contract's known configuration, +/// then validates and processes the investment allocations for each asset and its strategies. It assumes +/// that the caller is responsible for ensuring the correctness of investment ratios and does not check the +/// current state of the strategies or existing investments. /// /// # Arguments /// * `e` - The current environment reference. -/// * `assets` - A vector of `AssetStrategySet` that holds information about assets and their associated strategies. +/// * `assets` - A vector of `AssetStrategySet` representing the assets and their associated strategies +/// managed by this vault. /// * `asset_investments` - A vector of optional investment allocations for each asset. -///s +/// /// # Returns -/// * `Result<(), ContractError>` - Returns `Ok(())` if all investments are successful or an appropriate `ContractError` if any issue is encountered. +/// * `Result<(), ContractError>` - Returns `Ok(())` if all investments are successful, or an appropriate +/// `ContractError` if validation or execution fails. /// -/// # Function Flow -/// 1. **Iterate Over Asset Investments**: Loops through each asset investment allocation. +/// # Function Details +/// 1. **Iterates Over Asset Investments**: Loops through each asset's investment allocation, processing only +/// defined allocations. /// 2. **Validation**: -/// - **Asset Address Check**: Ensures that the asset's address matches the expected address in the allocation. -/// - **Strategy Length Check**: Verifies that the number of strategies matches between the asset and the corresponding allocation. -/// - **Note**: The total intended investment check has been removed as the subsequent operations inherently perform the same validation. -/// 3. **Process Strategy Investments**: -/// - For each strategy within an asset: -/// - **Non-Negative Amount Check**: Validates that the investment amount is non-negative. -/// - **Strategy Active Check**: Ensures that the strategy is not paused before proceeding with the investment. -/// - **Execute Investment**: Calls the `invest_in_strategy` fuction if all checks pass. +/// - Confirms that the asset's address matches the expected address in the allocation. +/// - Checks that the number of strategies in the asset matches the provided allocation. +/// 3. **Processes Strategy Investments**: +/// - Ensures that investment amounts are non-negative. +/// - Verifies that strategies are active before investing. +/// - Executes the investment for valid allocations by calling `invest_in_strategy`. /// /// # Errors -/// * Returns `ContractError::WrongAssetAddress` if an asset's address does not match the expected address. -/// * Returns `ContractError::WrongStrategiesLength` if the number of strategies in the asset and allocation do not match. -/// * Returns `ContractError::StrategyPaused` if an investment targets a paused strategy. +/// * `ContractError::WrongAssetAddress` - If the asset's address does not match the allocation. +/// * `ContractError::WrongStrategiesLength` - If the number of strategies in the asset and allocation do not match. +/// * `ContractError::StrategyPaused` - If an allocation targets a paused strategy. /// +/// # Notes +/// - The function relies on the assets being ordered consistently with the investment allocations. +/// - It allows the caller to update investment ratios freely, without verifying the current state of investments +/// or strategies. pub fn check_and_execute_investments( e: Env, assets: Vec, From f5aed8a70efa384bc0fd8531cf0de8cdd2493069 Mon Sep 17 00:00:00 2001 From: esteblock Date: Tue, 10 Dec 2024 11:00:16 -0300 Subject: [PATCH 06/17] refactor: read total managed funds at the beggining of deposit --- apps/contracts/vault/src/deposit.rs | 22 +++++++++++++++------- apps/contracts/vault/src/lib.rs | 10 +++++++++- apps/contracts/vault/src/utils.rs | 6 +----- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/apps/contracts/vault/src/deposit.rs b/apps/contracts/vault/src/deposit.rs index 6de2186c..7a8831a6 100644 --- a/apps/contracts/vault/src/deposit.rs +++ b/apps/contracts/vault/src/deposit.rs @@ -1,20 +1,19 @@ use common::models::AssetStrategySet; -use soroban_sdk::{panic_with_error, token::TokenClient, Address, Env, Vec}; +use soroban_sdk::{panic_with_error, token::TokenClient, Address, Env, Vec, Map}; use crate::{ - funds::{ - fetch_total_managed_funds, - }, storage::get_assets, token::{internal_mint, VaultToken}, utils::{calculate_deposit_amounts_and_shares_to_mint, check_nonnegative_amount}, ContractError, MINIMUM_LIQUIDITY, + models::{CurrentAssetInvestmentAllocation}, }; /// Common logic for processing deposits. pub fn process_deposit( e: &Env, assets: &Vec, + total_managed_funds: &Map, amounts_desired: &Vec, amounts_min: &Vec, from: &Address, @@ -32,12 +31,21 @@ pub fn process_deposit( let total_supply = VaultToken::total_supply(e.clone()); let (amounts, shares_to_mint) = if assets_length == 1 { - calculate_single_asset_shares(e, amounts_desired, total_supply)? + calculate_single_asset_shares( + e, + amounts_desired, + &total_managed_funds, + total_supply)? } else { if total_supply == 0 { (amounts_desired.clone(), amounts_desired.iter().sum()) } else { - calculate_deposit_amounts_and_shares_to_mint(&e, &assets, amounts_desired, amounts_min)? + calculate_deposit_amounts_and_shares_to_mint( + &e, + &assets, + &total_managed_funds, + amounts_desired, + amounts_min)? } }; @@ -63,12 +71,12 @@ pub fn process_deposit( fn calculate_single_asset_shares( e: &Env, amounts_desired: &Vec, + total_managed_funds: &Map, total_supply: i128, ) -> Result<(Vec, i128), ContractError> { let shares = if total_supply == 0 { amounts_desired.get(0).unwrap() } else { - let total_managed_funds = fetch_total_managed_funds(&e); VaultToken::total_supply(e.clone()) .checked_mul(amounts_desired.get(0).unwrap()) .unwrap_or_else(|| panic_with_error!(&e, ContractError::ArithmeticError)) diff --git a/apps/contracts/vault/src/lib.rs b/apps/contracts/vault/src/lib.rs index c507a18a..65dda37e 100755 --- a/apps/contracts/vault/src/lib.rs +++ b/apps/contracts/vault/src/lib.rs @@ -210,10 +210,18 @@ impl VaultTrait for DeFindexVault { // If this was not done before, last_fee_assesment will set to be current timestamp and this will return without action collect_fees(&e)?; + let total_managed_funds = fetch_total_managed_funds(&e); + let assets = get_assets(&e); let (amounts, shares_to_mint) = - process_deposit(&e, &assets, &amounts_desired, &amounts_min, &from)?; + process_deposit( + &e, + &assets, + &total_managed_funds, + &amounts_desired, + &amounts_min, + &from)?; events::emit_deposit_event(&e, from, amounts.clone(), shares_to_mint.clone()); if invest { diff --git a/apps/contracts/vault/src/utils.rs b/apps/contracts/vault/src/utils.rs index 112399cc..bcac091c 100644 --- a/apps/contracts/vault/src/utils.rs +++ b/apps/contracts/vault/src/utils.rs @@ -3,9 +3,6 @@ use soroban_sdk::{panic_with_error, Address, Env, Map, Vec}; use crate::{ models::{CurrentAssetInvestmentAllocation}, access::{AccessControl, AccessControlTrait, RolesDataKey}, - funds::{ - fetch_total_managed_funds, - }, token::VaultToken, ContractError, }; @@ -227,11 +224,10 @@ pub fn calculate_optimal_amounts_and_shares_with_enforced_asset( pub fn calculate_deposit_amounts_and_shares_to_mint( e: &Env, assets: &Vec, + total_managed_funds: &Map, amounts_desired: &Vec, amounts_min: &Vec, ) -> Result<(Vec, i128), ContractError> { - // Retrieve the total managed funds for each asset as a Map. - let total_managed_funds = fetch_total_managed_funds(e); for i in 0..assets.len() { // Calculate the optimal amounts and shares to mint for asset `i`. From 7bac9f79d4dbd55f73c2236072d19b2ec731b8a9 Mon Sep 17 00:00:00 2001 From: coderipper Date: Tue, 10 Dec 2024 12:57:52 -0300 Subject: [PATCH 07/17] soroswap setup --- .../strategies/blend/src/blend_pool.rs | 2 +- .../strategies/blend/src/test/blend/mod.rs | 3 +- .../blend/src/test/blend/soroswap_setup.rs | 60 +++++++++++++++++++ .../blend/src/test/blend/success.rs | 23 +++++-- 4 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 apps/contracts/strategies/blend/src/test/blend/soroswap_setup.rs diff --git a/apps/contracts/strategies/blend/src/blend_pool.rs b/apps/contracts/strategies/blend/src/blend_pool.rs index f99f4a6f..c8a79cec 100644 --- a/apps/contracts/strategies/blend/src/blend_pool.rs +++ b/apps/contracts/strategies/blend/src/blend_pool.rs @@ -113,7 +113,7 @@ pub fn claim(e: &Env, from: &Address, config: &Config) -> i128 { let pool_client = BlendPoolClient::new(e, &config.pool); // TODO: Check reserve_token_ids and how to get the correct one - pool_client.claim(from, &vec![&e, config.reserve_id], from) + pool_client.claim(from, &vec![&e, 0u32, 1u32, 2u32, 3u32], from) } pub fn perform_reinvest(e: &Env, config: &Config) -> Result{ diff --git a/apps/contracts/strategies/blend/src/test/blend/mod.rs b/apps/contracts/strategies/blend/src/test/blend/mod.rs index 916f9128..901248a4 100644 --- a/apps/contracts/strategies/blend/src/test/blend/mod.rs +++ b/apps/contracts/strategies/blend/src/test/blend/mod.rs @@ -1 +1,2 @@ -mod success; \ No newline at end of file +mod success; +mod soroswap_setup; \ No newline at end of file diff --git a/apps/contracts/strategies/blend/src/test/blend/soroswap_setup.rs b/apps/contracts/strategies/blend/src/test/blend/soroswap_setup.rs new file mode 100644 index 00000000..0f1535dc --- /dev/null +++ b/apps/contracts/strategies/blend/src/test/blend/soroswap_setup.rs @@ -0,0 +1,60 @@ +use soroban_sdk::{ + Env, BytesN, Address, + testutils::{Address as _} +}; + +fn pair_contract_wasm(e: &Env) -> BytesN<32> { + soroban_sdk::contractimport!( + file = "../external_wasms/soroswap/soroswap_pair.optimized.wasm" + ); + e.deployer().upload_contract_wasm(WASM) +} + +// SoroswapFactory Contract +mod factory { + soroban_sdk::contractimport!(file = "../external_wasms/soroswap/soroswap_factory.optimized.wasm"); + pub type SoroswapFactoryClient<'a> = Client<'a>; +} +use factory::SoroswapFactoryClient; + +pub fn create_soroswap_factory<'a>(e: &Env, setter: &Address) -> SoroswapFactoryClient<'a> { + let pair_hash = pair_contract_wasm(&e); + let factory_address = &e.register(factory::WASM, ()); + let factory = SoroswapFactoryClient::new(e, factory_address); + factory.initialize(&setter, &pair_hash); + factory +} + +// SoroswapRouter Contract +mod router { + soroban_sdk::contractimport!(file = "../external_wasms/soroswap/soroswap_router.optimized.wasm"); + pub type SoroswapRouterClient<'a> = Client<'a>; +} +pub use router::SoroswapRouterClient; + +// SoroswapRouter Contract +pub fn create_soroswap_router<'a>(e: &Env, factory: &Address) -> SoroswapRouterClient<'a> { + let router_address = &e.register(router::WASM, ()); + let router = SoroswapRouterClient::new(e, router_address); + router.initialize(factory); + router +} + +pub fn create_soroswap_pool<'a>(e: &Env, to: &Address, token_a: &Address, token_b: &Address, amount_a: &i128, amount_b: &i128) -> SoroswapRouterClient<'a> { + let soroswap_admin = Address::generate(&e); + let factory = create_soroswap_factory(&e, &soroswap_admin); + let router = create_soroswap_router(&e, &factory.address); + + router.add_liquidity( + token_a, + token_b, + &amount_a, + &amount_b, + &0i128, + &0i128, + &to, + &(e.ledger().timestamp() + 3600) + ); + + router +} \ No newline at end of file diff --git a/apps/contracts/strategies/blend/src/test/blend/success.rs b/apps/contracts/strategies/blend/src/test/blend/success.rs index 9831bd7f..fd1b76c9 100644 --- a/apps/contracts/strategies/blend/src/test/blend/success.rs +++ b/apps/contracts/strategies/blend/src/test/blend/success.rs @@ -2,6 +2,7 @@ use crate::blend_pool::{BlendPoolClient, Request}; use crate::constants::MIN_DUST; use crate::storage::DAY_IN_LEDGERS; +use crate::test::blend::soroswap_setup::create_soroswap_pool; use crate::test::{create_blend_pool, create_blend_strategy, BlendFixture, EnvTestUtils}; use crate::BlendStrategyClient; use defindex_strategy_core::StrategyError; @@ -25,10 +26,19 @@ fn success() { let blnd = e.register_stellar_asset_contract_v2(admin.clone()); let usdc = e.register_stellar_asset_contract_v2(admin.clone()); let xlm = e.register_stellar_asset_contract_v2(admin.clone()); - let _blnd_client = MockTokenClient::new(&e, &blnd.address()); + let blnd_client = MockTokenClient::new(&e, &blnd.address()); let usdc_client = MockTokenClient::new(&e, &usdc.address()); let xlm_client = MockTokenClient::new(&e, &xlm.address()); + // Setting up soroswap pool + let pool_admin = Address::generate(&e); + let amount_a = 100000000_0_000_000; + let amount_b = 50000000_0_000_000; + blnd_client.mint(&pool_admin, &amount_a); + usdc_client.mint(&pool_admin, &amount_b); + let soroswap_router = create_soroswap_pool(&e, &pool_admin, &blnd.address(), &usdc.address(), &amount_a, &amount_b); + // End of setting up soroswap pool + let blend_fixture = BlendFixture::deploy(&e, &admin, &blnd.address(), &usdc.address()); // usdc (0) and xlm (1) charge a fixed 10% borrow rate with 0% backstop take rate @@ -36,7 +46,7 @@ fn success() { // emits to each reserve token evently, and starts emissions let pool = create_blend_pool(&e, &blend_fixture, &admin, &usdc_client, &xlm_client); let pool_client = BlendPoolClient::new(&e, &pool); - let strategy = create_blend_strategy(&e, &usdc.address(), &pool, &0u32, &blnd.address(), &Address::generate(&e)); + let strategy = create_blend_strategy(&e, &usdc.address(), &pool, &0u32, &blnd.address(), &soroswap_router.address); let strategy_client = BlendStrategyClient::new(&e, &strategy); /* @@ -51,7 +61,7 @@ fn success() { usdc_client.mint(&user_2, &starting_balance); usdc_client.mint(&user_3, &starting_balance); - let user_3_balance = usdc_client.balance(&user_2); + let user_3_balance = usdc_client.balance(&user_3); assert_eq!(user_3_balance, starting_balance); @@ -219,8 +229,13 @@ fn success() { */ // harvest - // strategy_client.harvest(&usdc, &user_2, &expected_fees); + let blnd_strategy_balance = blnd_client.balance(&strategy); + assert_eq!(blnd_strategy_balance, 0); + + strategy_client.harvest(&user_2); + let blnd_strategy_balance = blnd_client.balance(&strategy); + assert_eq!(blnd_strategy_balance, 0); // -> verify harvest } From 97596f8448e5bc4b58078b4f6dc32b31439e8a79 Mon Sep 17 00:00:00 2001 From: esteblock Date: Tue, 10 Dec 2024 14:48:40 -0300 Subject: [PATCH 08/17] use total_managed_funds inside generate_and_execute_investments --- apps/contracts/vault/src/investment.rs | 93 ++++++++++++++++---------- apps/contracts/vault/src/lib.rs | 7 +- 2 files changed, 63 insertions(+), 37 deletions(-) diff --git a/apps/contracts/vault/src/investment.rs b/apps/contracts/vault/src/investment.rs index 9d60ea7b..51fc7bd8 100644 --- a/apps/contracts/vault/src/investment.rs +++ b/apps/contracts/vault/src/investment.rs @@ -1,13 +1,10 @@ -use soroban_sdk::{Env, Vec, panic_with_error}; +use soroban_sdk::{Env, Vec, panic_with_error, Map, Address}; use crate::{ - models::{AssetInvestmentAllocation, StrategyAllocation}, + models::{AssetInvestmentAllocation, StrategyAllocation, CurrentAssetInvestmentAllocation}, strategies::invest_in_strategy, utils::{check_nonnegative_amount}, ContractError, - funds::{ - fetch_invested_funds_for_asset, fetch_strategy_invested_funds, - }, }; use common::models::AssetStrategySet; @@ -94,46 +91,70 @@ pub fn check_and_execute_investments( Ok(()) } -/// Generate investment allocations and execute them. +/* + The next function is called after a deposit has been done, where the amouns have already been + calculaed to be in the correct proportion on how much to invest in every asset. + However, no proportion has been calculated on how to invest on each strategy for each asse. + This function handles this, given the current state of the strategies. + + +*/ pub fn generate_and_execute_investments( e: &Env, - amounts: &Vec, assets: &Vec, + total_managed_funds: &Map, + amounts: &Vec, ) -> Result<(), ContractError> { let mut asset_investments = Vec::new(&e); for (i, amount) in amounts.iter().enumerate() { let asset = assets.get(i as u32).unwrap(); - let (asset_invested_funds, _) = fetch_invested_funds_for_asset(&e, &asset); - - let mut strategy_allocations = Vec::new(&e); - let mut remaining_amount = amount; - - for (j, strategy) in asset.strategies.iter().enumerate() { - let strategy_invested_funds = fetch_strategy_invested_funds(&e, &strategy.address); - - let mut invest_amount = if asset_invested_funds > 0 { - (amount * strategy_invested_funds) / asset_invested_funds - } else { - 0 - }; - - if j == asset.strategies.len() as usize - 1 { - invest_amount = remaining_amount; + + let current_asset_allocation = total_managed_funds.get(asset.address.clone()).unwrap(); + let asset_invested_funds = current_asset_allocation.invested_amount; + // We only consider assets that have a non zero allocation + // if the amount already invested in the asset is 0, + // this means that there is no previous investment in the asset, so we can just + // invest, and we need to wait for the manager to execute a manual investment of the idle assets + // on the strategies. + if amount >0 && asset_invested_funds >0 { + // here the asset will be distributed amont the different strategies considering the current raio + // of investment in each strategy. + let mut strategy_allocations = Vec::new(&e); + let mut remaining_amount = amount; + + for (j, strategy) in asset.strategies.iter().enumerate() { + let strategy_invested_funds = current_asset_allocation + .strategy_allocations + .get(j as u32) + .unwrap() + .amount; + + let mut invest_amount = if j == asset.strategies.len() as usize - 1 { + remaining_amount + } else { + (amount * strategy_invested_funds) / asset_invested_funds + }; + + remaining_amount -= invest_amount; + + strategy_allocations.push_back(Some(StrategyAllocation { + strategy_address: strategy.address.clone(), + amount: invest_amount, + })); + } + + asset_investments.push_back(Some(AssetInvestmentAllocation { + asset: asset.address.clone(), + strategy_allocations, + })); + + } - - remaining_amount -= invest_amount; - - strategy_allocations.push_back(Some(StrategyAllocation { - strategy_address: strategy.address.clone(), - amount: invest_amount, - })); - } - - asset_investments.push_back(Some(AssetInvestmentAllocation { - asset: asset.address.clone(), - strategy_allocations, - })); + else { + asset_investments.push_back(None); // No investment for this asset + } + } check_and_execute_investments(e.clone(), assets.clone(), asset_investments)?; diff --git a/apps/contracts/vault/src/lib.rs b/apps/contracts/vault/src/lib.rs index 65dda37e..e62d9305 100755 --- a/apps/contracts/vault/src/lib.rs +++ b/apps/contracts/vault/src/lib.rs @@ -226,7 +226,12 @@ impl VaultTrait for DeFindexVault { if invest { // Generate investment allocations and execute them - generate_and_execute_investments(&e, &amounts, &assets)?; + generate_and_execute_investments( + &e, + &assets, + &total_managed_funds, + &amounts, + )?; } Ok((amounts, shares_to_mint)) From 0f8ba38f13a22ce6c9f3451ff90e86d6d29785ba Mon Sep 17 00:00:00 2001 From: esteblock Date: Tue, 10 Dec 2024 15:07:40 -0300 Subject: [PATCH 09/17] better rust strategy_allocations.push_back --- apps/contracts/vault/src/investment.rs | 85 ++++++++++++++------------ 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/apps/contracts/vault/src/investment.rs b/apps/contracts/vault/src/investment.rs index 51fc7bd8..fa5ccb8f 100644 --- a/apps/contracts/vault/src/investment.rs +++ b/apps/contracts/vault/src/investment.rs @@ -112,48 +112,57 @@ pub fn generate_and_execute_investments( let current_asset_allocation = total_managed_funds.get(asset.address.clone()).unwrap(); let asset_invested_funds = current_asset_allocation.invested_amount; - // We only consider assets that have a non zero allocation - // if the amount already invested in the asset is 0, - // this means that there is no previous investment in the asset, so we can just - // invest, and we need to wait for the manager to execute a manual investment of the idle assets - // on the strategies. - if amount >0 && asset_invested_funds >0 { - // here the asset will be distributed amont the different strategies considering the current raio - // of investment in each strategy. - let mut strategy_allocations = Vec::new(&e); - let mut remaining_amount = amount; - - for (j, strategy) in asset.strategies.iter().enumerate() { + + // We only consider assets that have a non zero allocation + // if the amount already invested in the asset is 0, + // this means that there is no previous investment in the asset, so we can just + // invest, and we need to wait for the manager to execute a manual investment of the idle assets + // on the strategies. + if amount >0 && asset_invested_funds >0 { + // here the asset will be distributed amont the different strategies considering the current raio + // of investment in each strategy. + let mut strategy_allocations = Vec::new(&e); + let mut remaining_amount = amount; + + for (j, strategy) in asset.strategies.iter().enumerate() { + + let mut invest_amount = if j == asset.strategies.len() as usize - 1 { + remaining_amount + } else { let strategy_invested_funds = current_asset_allocation - .strategy_allocations - .get(j as u32) - .unwrap() - .amount; - - let mut invest_amount = if j == asset.strategies.len() as usize - 1 { - remaining_amount - } else { - (amount * strategy_invested_funds) / asset_invested_funds - }; - - remaining_amount -= invest_amount; - - strategy_allocations.push_back(Some(StrategyAllocation { - strategy_address: strategy.address.clone(), - amount: invest_amount, - })); - } - - asset_investments.push_back(Some(AssetInvestmentAllocation { - asset: asset.address.clone(), - strategy_allocations, - })); + .strategy_allocations + .get(j as u32) + .unwrap() + .amount; + + (amount * strategy_invested_funds) / asset_invested_funds + }; + + remaining_amount -= invest_amount; + strategy_allocations.push_back( + if invest_amount > 0 { + Some(StrategyAllocation { + strategy_address: strategy.address.clone(), + amount: invest_amount, + }) + } else { + None + } + ); } - else { - asset_investments.push_back(None); // No investment for this asset - } + + asset_investments.push_back(Some(AssetInvestmentAllocation { + asset: asset.address.clone(), + strategy_allocations, + })); + + + } + else { + asset_investments.push_back(None); // No investments to be executed for this asset + } } From 08920d59a5fca9562b6dd5fac0f9cfffb951de1d Mon Sep 17 00:00:00 2001 From: esteblock Date: Tue, 10 Dec 2024 15:20:46 -0300 Subject: [PATCH 10/17] refactor: separae generate investment allocations and check and execute investment allocation --- apps/contracts/vault/src/investment.rs | 46 ++++++++++++++------------ apps/contracts/vault/src/lib.rs | 11 +++--- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/apps/contracts/vault/src/investment.rs b/apps/contracts/vault/src/investment.rs index fa5ccb8f..3ca42f71 100644 --- a/apps/contracts/vault/src/investment.rs +++ b/apps/contracts/vault/src/investment.rs @@ -46,9 +46,9 @@ use common::models::AssetStrategySet; /// - It allows the caller to update investment ratios freely, without verifying the current state of investments /// or strategies. pub fn check_and_execute_investments( - e: Env, - assets: Vec, - asset_investments: Vec> + e: &Env, + assets: &Vec, + asset_investments: &Vec> ) -> Result<(), ContractError> { // Iterate over each asset investment allocation @@ -99,12 +99,12 @@ pub fn check_and_execute_investments( */ -pub fn generate_and_execute_investments( +pub fn generate_investment_allocations( e: &Env, assets: &Vec, total_managed_funds: &Map, amounts: &Vec, -) -> Result<(), ContractError> { +) -> Result>, ContractError> { let mut asset_investments = Vec::new(&e); for (i, amount) in amounts.iter().enumerate() { @@ -123,23 +123,28 @@ pub fn generate_and_execute_investments( // of investment in each strategy. let mut strategy_allocations = Vec::new(&e); let mut remaining_amount = amount; - + for (j, strategy) in asset.strategies.iter().enumerate() { - - let mut invest_amount = if j == asset.strategies.len() as usize - 1 { + // Determine the investment amount for the strategy + let invest_amount = if j == asset.strategies.len() as usize - 1 { remaining_amount } else { let strategy_invested_funds = current_asset_allocation - .strategy_allocations - .get(j as u32) - .unwrap() - .amount; + .strategy_allocations + .get(j as u32) + .unwrap() + .amount; - (amount * strategy_invested_funds) / asset_invested_funds + amount + .checked_mul(strategy_invested_funds) + .and_then(|v| v.checked_div(asset_invested_funds)) + .unwrap() }; - + + // Update the remaining amount remaining_amount -= invest_amount; - + + // Add the strategy allocation strategy_allocations.push_back( if invest_amount > 0 { Some(StrategyAllocation { @@ -148,15 +153,16 @@ pub fn generate_and_execute_investments( }) } else { None - } + }, ); - } - + + // Add the asset investment allocation asset_investments.push_back(Some(AssetInvestmentAllocation { asset: asset.address.clone(), strategy_allocations, })); + } @@ -165,7 +171,5 @@ pub fn generate_and_execute_investments( } } - - check_and_execute_investments(e.clone(), assets.clone(), asset_investments)?; - Ok(()) + Ok(asset_investments) } diff --git a/apps/contracts/vault/src/lib.rs b/apps/contracts/vault/src/lib.rs index e62d9305..57d7ed52 100755 --- a/apps/contracts/vault/src/lib.rs +++ b/apps/contracts/vault/src/lib.rs @@ -29,7 +29,7 @@ use deposit::{process_deposit}; use fee::{collect_fees, fetch_defindex_fee}; use funds::{fetch_current_idle_funds, fetch_current_invested_funds, fetch_total_managed_funds}; use interface::{AdminInterfaceTrait, VaultManagementTrait, VaultTrait}; -use investment::{check_and_execute_investments, generate_and_execute_investments}; +use investment::{check_and_execute_investments, generate_investment_allocations}; use models::{ Instruction, OptionalSwapDetailsExactIn, OptionalSwapDetailsExactOut, CurrentAssetInvestmentAllocation, @@ -225,13 +225,13 @@ impl VaultTrait for DeFindexVault { events::emit_deposit_event(&e, from, amounts.clone(), shares_to_mint.clone()); if invest { - // Generate investment allocations and execute them - generate_and_execute_investments( + let asset_investments = generate_investment_allocations( &e, &assets, &total_managed_funds, &amounts, )?; + check_and_execute_investments(&e, &assets, &asset_investments)?; } Ok((amounts, shares_to_mint)) @@ -710,7 +710,10 @@ impl VaultManagementTrait for DeFindexVault { } // Check and execute investments for each asset allocation - check_and_execute_investments(e, assets, asset_investments)?; + check_and_execute_investments( + &e, + &assets, + &asset_investments)?; Ok(()) } From 4c66df91696f6e18b4749064c8b0ea324afc69c7 Mon Sep 17 00:00:00 2001 From: esteblock Date: Tue, 10 Dec 2024 15:29:58 -0300 Subject: [PATCH 11/17] improve generate_investment_allocations function comment --- apps/contracts/vault/src/investment.rs | 42 +++++++++++++++++++++----- apps/contracts/vault/src/lib.rs | 2 +- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/apps/contracts/vault/src/investment.rs b/apps/contracts/vault/src/investment.rs index 3ca42f71..ff7f1d8e 100644 --- a/apps/contracts/vault/src/investment.rs +++ b/apps/contracts/vault/src/investment.rs @@ -91,14 +91,40 @@ pub fn check_and_execute_investments( Ok(()) } -/* - The next function is called after a deposit has been done, where the amouns have already been - calculaed to be in the correct proportion on how much to invest in every asset. - However, no proportion has been calculated on how to invest on each strategy for each asse. - This function handles this, given the current state of the strategies. - - -*/ +/// Generates investment allocations for a set of assets and their associated strategies. +/// +/// This function calculates the distribution of funds across strategies for each asset based +/// on the current state of strategy investments. The allocations are returned as a vector, +/// where each entry corresponds to an asset's investment allocation or `None` if no allocation +/// is required. +/// +/// # Arguments +/// - `e` - Reference to the current environment. +/// - `assets` - A vector of `AssetStrategySet` objects representing the assets and their strategies. +/// - `total_managed_funds` - A map containing the current allocation of funds across all strategies for each asset. +/// - `amounts` - A vector of amounts representing the funds to be allocated for each asset. +/// +/// # Returns +/// - `Ok(Vec>)` - A vector of investment allocations where each entry +/// represents an asset's strategy allocations. If an asset does not require allocation, its entry is `None`. +/// - `Err(ContractError)` - If any errors occur during the allocation process, such as invalid data or calculations. +/// +/// # Function Flow +/// 1. **Iterate Over Assets**: For each asset in the provided list: +/// - Skip assets with zero amounts or no prior investments. +/// - Calculate the allocation of funds across strategies proportionally based on the current state. +/// 2. **Proportional Distribution**: +/// - For each strategy within an asset, determine the proportional investment based on its existing allocation. +/// - Ensure that all amounts are correctly calculated without overflows or division errors. +/// 3. **Prepare Allocation**: +/// - Append the calculated strategy allocations to the resulting vector. +/// - Include `None` for assets with no required allocations. +/// 4. **Return Results**: Return the vector containing the investment allocations. +/// +/// # Notes +/// - This function does not execute the investments; it only prepares the allocations. +/// - It assumes that the provided `total_managed_funds` contains valid and complete data. + pub fn generate_investment_allocations( e: &Env, assets: &Vec, diff --git a/apps/contracts/vault/src/lib.rs b/apps/contracts/vault/src/lib.rs index 57d7ed52..a191b117 100755 --- a/apps/contracts/vault/src/lib.rs +++ b/apps/contracts/vault/src/lib.rs @@ -233,8 +233,8 @@ impl VaultTrait for DeFindexVault { )?; check_and_execute_investments(&e, &assets, &asset_investments)?; } - Ok((amounts, shares_to_mint)) + } /// Handles the withdrawal process for a specified number of vault shares. From b78ee5553ac8f3e20013ab7912f42f5cc02c88b1 Mon Sep 17 00:00:00 2001 From: esteblock Date: Tue, 10 Dec 2024 15:48:07 -0300 Subject: [PATCH 12/17] test one_asset_previous_investment_success --- .../src/test/vault/deposit_and_invest.rs | 219 ++++++++++++++++-- 1 file changed, 201 insertions(+), 18 deletions(-) diff --git a/apps/contracts/vault/src/test/vault/deposit_and_invest.rs b/apps/contracts/vault/src/test/vault/deposit_and_invest.rs index dfbcd101..be86bf7f 100644 --- a/apps/contracts/vault/src/test/vault/deposit_and_invest.rs +++ b/apps/contracts/vault/src/test/vault/deposit_and_invest.rs @@ -1,13 +1,18 @@ -use soroban_sdk::{vec as sorobanvec, String, Vec, Map}; +use soroban_sdk::{vec as sorobanvec, String, Vec, Map, vec}; -use crate::test::defindex_vault::{AssetStrategySet, StrategyAllocation, CurrentAssetInvestmentAllocation}; +use crate::test::defindex_vault::{ + AssetStrategySet, + StrategyAllocation, + CurrentAssetInvestmentAllocation, + AssetInvestmentAllocation, +}; use crate::test::{ create_strategy_params_token0, create_strategy_params_token1, DeFindexVaultTest, }; -// test deposit one asset success +// with no previous investment, there should not be any investment #[test] -fn one_asset_success() { +fn one_asset_no_previous_investment() { let test = DeFindexVaultTest::setup(); test.env.mock_all_auths(); let strategy_params_token0 = create_strategy_params_token0(&test); @@ -52,6 +57,8 @@ fn one_asset_success() { &true, ); + // however because there was no previous investment, all the amount should be in idle funds + // check balances after deposit let df_balance = test.defindex_contract.balance(&users[0]); assert_eq!(df_balance, amount - 1000); @@ -59,45 +66,220 @@ fn one_asset_success() { let user_balance = test.token0.balance(&users[0]); assert_eq!(user_balance, 0i128); + // all in idle funds let vault_balance = test.token0.balance(&test.defindex_contract.address); - assert_eq!(vault_balance, 0); + assert_eq!(vault_balance, amount); // check total managed funds let mut total_managed_funds_expected = Map::new(&test.env); let strategy_investments_expected_token_0 = sorobanvec![&test.env, StrategyAllocation { strategy_address: test.strategy_client_token0.address.clone(), - amount: amount, // everything has been invested + amount: 0, // all in idle funds }]; total_managed_funds_expected.set(test.token0.address.clone(), CurrentAssetInvestmentAllocation { asset: test.token0.address.clone(), total_amount: amount, - idle_amount: 0, - invested_amount: amount, + idle_amount: amount, + invested_amount: 0, + strategy_allocations: strategy_investments_expected_token_0, + }); + + let total_managed_funds = test.defindex_contract.fetch_total_managed_funds(); + assert_eq!(total_managed_funds, total_managed_funds_expected); + + + // check current idle funds, + let mut expected_idle_map = Map::new(&test.env); + expected_idle_map.set(test.token0.address.clone(), amount); + let current_idle_funds = test.defindex_contract.fetch_current_idle_funds(); + assert_eq!(current_idle_funds, expected_idle_map); + + let mut expected_invested_map = Map::new(&test.env); + expected_invested_map.set(test.token0.address.clone(), 0); + let current_invested_funds = test.defindex_contract.fetch_current_invested_funds(); + assert_eq!(current_invested_funds, expected_invested_map); + + // Now user deposits for the second time + let amount2 = 987654321i128; + test.token0_admin_client.mint(&users[0], &amount2); + let user_balance = test.token0.balance(&users[0]); + assert_eq!(user_balance, amount2); + + // deposit AND INVEST + test.defindex_contract.deposit( + &sorobanvec![&test.env, amount2], + &sorobanvec![&test.env, amount2], + &users[0], + &true, + ); + + + // check balances after deposit + let df_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(df_balance, amount + amount2 - 1000); + + let user_balance = test.token0.balance(&users[0]); + assert_eq!(user_balance, 0i128); + + let vault_balance = test.token0.balance(&test.defindex_contract.address); + assert_eq!(vault_balance, amount + amount2); + + // check that fetch_total_managed_funds returns correct amount + let mut total_managed_funds_expected = Map::new(&test.env); + let strategy_investments_expected_token_0 = sorobanvec![&test.env, StrategyAllocation { + strategy_address: test.strategy_client_token0.address.clone(), + amount: 0, + }]; + + total_managed_funds_expected.set(test.token0.address.clone(), + CurrentAssetInvestmentAllocation { + asset: test.token0.address.clone(), + total_amount: amount + amount2, + idle_amount: amount + amount2, + invested_amount: 0, strategy_allocations: strategy_investments_expected_token_0, }); let total_managed_funds = test.defindex_contract.fetch_total_managed_funds(); assert_eq!(total_managed_funds, total_managed_funds_expected); + + // check current idle funds + let mut expected_idle_map = Map::new(&test.env); + expected_idle_map.set(test.token0.address.clone(), amount + amount2); + let current_idle_funds = test.defindex_contract.fetch_current_idle_funds(); + assert_eq!(current_idle_funds, expected_idle_map); + + // check that current invested funds is now 0, funds still in idle funds + let mut expected_invested_map = Map::new(&test.env); + expected_invested_map.set(test.token0.address.clone(), 0); + let current_invested_funds = test.defindex_contract.fetch_current_invested_funds(); + assert_eq!(current_invested_funds, expected_invested_map); +} + +#[test] +fn one_asset_previous_investment_success() { + let test = DeFindexVaultTest::setup(); + test.env.mock_all_auths(); + let strategy_params_token0 = create_strategy_params_token0(&test); + + // initialize with 1 assets + let assets: Vec = sorobanvec![ + &test.env, + AssetStrategySet { + address: test.token0.address.clone(), + strategies: strategy_params_token0.clone() + } + ]; + + test.defindex_contract.initialize( + &assets, + &test.manager, + &test.emergency_manager, + &test.vault_fee_receiver, + &2000u32, + &test.defindex_protocol_receiver, + &test.defindex_factory, + &String::from_str(&test.env, "dfToken"), + &String::from_str(&test.env, "DFT"), + ); + let amount = 123456789i128; + + let users = DeFindexVaultTest::generate_random_users(&test.env, 1); + + // Balances before deposit + test.token0_admin_client.mint(&users[0], &amount); + let user_balance = test.token0.balance(&users[0]); + assert_eq!(user_balance, amount); + + let df_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(df_balance, 0i128); + + // deposit + test.defindex_contract.deposit( + &sorobanvec![&test.env, amount], + &sorobanvec![&test.env, amount], + &users[0], + &false, + ); + + // all in idle funds + let vault_balance = test.token0.balance(&test.defindex_contract.address); + assert_eq!(vault_balance, amount); + + let mut total_managed_funds_expected = Map::new(&test.env); + let strategy_investments_expected_token_0 = sorobanvec![&test.env, StrategyAllocation { + strategy_address: test.strategy_client_token0.address.clone(), + amount: 0, // everything has been invested + }]; + + total_managed_funds_expected.set(test.token0.address.clone(), + CurrentAssetInvestmentAllocation { + asset: test.token0.address.clone(), + total_amount: amount, + idle_amount: amount, + invested_amount: 0, + strategy_allocations: strategy_investments_expected_token_0, + }); + + let total_managed_funds = test.defindex_contract.fetch_total_managed_funds(); + assert_eq!(total_managed_funds, total_managed_funds_expected); + + let amount_to_invest =100000000i128; + + // GENERATE INVESTMENT + let asset_investments = vec![ + &test.env, + Some(AssetInvestmentAllocation { + asset: test.token0.address.clone(), + strategy_allocations: vec![ + &test.env, + Some(StrategyAllocation { + strategy_address: test.strategy_client_token0.address.clone(), + amount: amount_to_invest, + }), + ], + })]; + + test.defindex_contract.invest( + &asset_investments, + ); + // Now we should have: + let mut total_managed_funds_expected = Map::new(&test.env); + let strategy_investments_expected_token_0 = sorobanvec![&test.env, StrategyAllocation { + strategy_address: test.strategy_client_token0.address.clone(), + amount: amount_to_invest, // everything has been invested + }]; + total_managed_funds_expected.set(test.token0.address.clone(), + CurrentAssetInvestmentAllocation { + asset: test.token0.address.clone(), + total_amount: amount, + idle_amount: amount -amount_to_invest, + invested_amount: amount_to_invest, + strategy_allocations: strategy_investments_expected_token_0, + }); + let total_managed_funds = test.defindex_contract.fetch_total_managed_funds(); + assert_eq!(total_managed_funds, total_managed_funds_expected); + // check current idle funds, let mut expected_idle_map = Map::new(&test.env); - expected_idle_map.set(test.token0.address.clone(), 0); + expected_idle_map.set(test.token0.address.clone(), amount -amount_to_invest); let current_idle_funds = test.defindex_contract.fetch_current_idle_funds(); assert_eq!(current_idle_funds, expected_idle_map); // check that current invested funds is now 0, funds still in idle funds //map shuould be map let mut expected_invested_map = Map::new(&test.env); - expected_invested_map.set(test.token0.address.clone(), amount); + expected_invested_map.set(test.token0.address.clone(), amount_to_invest); let current_invested_funds = test.defindex_contract.fetch_current_invested_funds(); assert_eq!(current_invested_funds, expected_invested_map); - // Now user deposits for the second time + // DEPOSIT AND INVEST let amount2 = 987654321i128; test.token0_admin_client.mint(&users[0], &amount2); let user_balance = test.token0.balance(&users[0]); @@ -111,6 +293,7 @@ fn one_asset_success() { &true, ); + // because there was already some strategy allocation, all of the amount2 should be invested // check balances after deposit let df_balance = test.defindex_contract.balance(&users[0]); @@ -121,21 +304,21 @@ fn one_asset_success() { // check that the assets are not in the vault let vault_balance = test.token0.balance(&test.defindex_contract.address); - assert_eq!(vault_balance, 0); + assert_eq!(vault_balance, amount - amount_to_invest); // check that fetch_total_managed_funds returns correct amount let mut total_managed_funds_expected = Map::new(&test.env); let strategy_investments_expected_token_0 = sorobanvec![&test.env, StrategyAllocation { strategy_address: test.strategy_client_token0.address.clone(), - amount: amount + amount2, // everything has been invested + amount: amount_to_invest + amount2, // everything has been invested }]; total_managed_funds_expected.set(test.token0.address.clone(), CurrentAssetInvestmentAllocation { asset: test.token0.address.clone(), total_amount: amount + amount2, - idle_amount: 0, - invested_amount: amount + amount2, + idle_amount: amount - amount_to_invest, + invested_amount: amount_to_invest + amount2, strategy_allocations: strategy_investments_expected_token_0, }); @@ -144,13 +327,13 @@ fn one_asset_success() { // check current idle funds let mut expected_idle_map = Map::new(&test.env); - expected_idle_map.set(test.token0.address.clone(), 0); + expected_idle_map.set(test.token0.address.clone(), amount - amount_to_invest); let current_idle_funds = test.defindex_contract.fetch_current_idle_funds(); assert_eq!(current_idle_funds, expected_idle_map); - // check that current invested funds is now 0, funds still in idle funds + // check that current invested funds let mut expected_invested_map = Map::new(&test.env); - expected_invested_map.set(test.token0.address.clone(), amount + amount2); + expected_invested_map.set(test.token0.address.clone(), amount_to_invest + amount2); let current_invested_funds = test.defindex_contract.fetch_current_invested_funds(); assert_eq!(current_invested_funds, expected_invested_map); } From 99da88c15cb2a08c2b3209392f395e3fd18b3fc2 Mon Sep 17 00:00:00 2001 From: esteblock Date: Tue, 10 Dec 2024 16:14:50 -0300 Subject: [PATCH 13/17] test: several_assets_wih_previous_investment_success --- .../src/test/vault/deposit_and_invest.rs | 282 +++++++++++++++--- 1 file changed, 235 insertions(+), 47 deletions(-) diff --git a/apps/contracts/vault/src/test/vault/deposit_and_invest.rs b/apps/contracts/vault/src/test/vault/deposit_and_invest.rs index be86bf7f..1d10bdf8 100644 --- a/apps/contracts/vault/src/test/vault/deposit_and_invest.rs +++ b/apps/contracts/vault/src/test/vault/deposit_and_invest.rs @@ -338,9 +338,8 @@ fn one_asset_previous_investment_success() { assert_eq!(current_invested_funds, expected_invested_map); } -// test deposit of several asset, considering different proportion of assets #[test] -fn several_assets_success() { +fn several_assets_no_previous_investment() { let test = DeFindexVaultTest::setup(); test.env.mock_all_auths(); let strategy_params_token0 = create_strategy_params_token0(&test); @@ -386,7 +385,7 @@ fn several_assets_success() { let df_balance = test.defindex_contract.balance(&users[0]); assert_eq!(df_balance, 0i128); - // deposit + // deposit // however wih no previous investment yet let deposit_result=test.defindex_contract.deposit( &sorobanvec![&test.env, amount0, amount1], &sorobanvec![&test.env, amount0, amount1], @@ -411,37 +410,37 @@ fn several_assets_success() { let user_balance1 = test.token1.balance(&users[0]); assert_eq!(user_balance1,0i128); - // check vault balance of asset 0 + // all in idle funds let vault_balance0 = test.token0.balance(&test.defindex_contract.address); - assert_eq!(vault_balance0, 0); + assert_eq!(vault_balance0, amount0); // check vault balance of asset 1 let vault_balance1 = test.token1.balance(&test.defindex_contract.address); - assert_eq!(vault_balance1, 0); + assert_eq!(vault_balance1, amount1); // check that fetch_total_managed_funds returns correct amount let mut total_managed_funds_expected = Map::new(&test.env); let strategy_investments_expected_token_0 = sorobanvec![&test.env, StrategyAllocation { strategy_address: test.strategy_client_token0.address.clone(), - amount: amount0, // everything has been invested + amount: 0, }]; let strategy_investments_expected_token_1 = sorobanvec![&test.env, StrategyAllocation { strategy_address: test.strategy_client_token1.address.clone(), - amount: amount1, // everything has been invested + amount: 0, }]; total_managed_funds_expected.set(test.token0.address.clone(), CurrentAssetInvestmentAllocation { asset: test.token0.address.clone(), total_amount: amount0, - idle_amount: 0, - invested_amount: amount0, + idle_amount: amount0, + invested_amount: 0, strategy_allocations: strategy_investments_expected_token_0, }); total_managed_funds_expected.set(test.token1.address.clone(), CurrentAssetInvestmentAllocation { asset: test.token1.address.clone(), total_amount: amount1, - idle_amount: 0, - invested_amount: amount1, + idle_amount: amount1, + invested_amount: 0, strategy_allocations: strategy_investments_expected_token_1, }); @@ -450,15 +449,15 @@ fn several_assets_success() { // check current idle funds let mut expected_idle_map = Map::new(&test.env); - expected_idle_map.set(test.token0.address.clone(), 0); - expected_idle_map.set(test.token1.address.clone(), 0); + expected_idle_map.set(test.token0.address.clone(), amount0); + expected_idle_map.set(test.token1.address.clone(), amount1); let current_idle_funds = test.defindex_contract.fetch_current_idle_funds(); assert_eq!(current_idle_funds, expected_idle_map); - // check that current invested funds is now correct, funds should be invested + // check that current invested funds is now correct, let mut expected_invested_map = Map::new(&test.env); - expected_invested_map.set(test.token0.address.clone(), amount0); - expected_invested_map.set(test.token1.address.clone(), amount1); + expected_invested_map.set(test.token0.address.clone(), 0); + expected_invested_map.set(test.token1.address.clone(), 0); let current_invested_funds = test.defindex_contract.fetch_current_invested_funds(); assert_eq!(current_invested_funds, expected_invested_map); @@ -486,8 +485,7 @@ fn several_assets_success() { &true, ); - // check deposit result. Ok((amounts, shares_to_mint)) - // Vec, i128 + assert_eq!(deposit_result, (sorobanvec![&test.env, amount0*2, amount1*2], amount0*2 + amount1*2)); @@ -502,71 +500,172 @@ fn several_assets_success() { let user_balance1 = test.token1.balance(&users[1]); assert_eq!(user_balance1, amount1_new - 2*amount1); - // check vault balance of asset 0 + // check vault balance of asset 0, all in idle funds let vault_balance0 = test.token0.balance(&test.defindex_contract.address); - assert_eq!(vault_balance0, 0); + assert_eq!(vault_balance0, amount0*3); // check vault balance of asset 1 let vault_balance1 = test.token1.balance(&test.defindex_contract.address); - assert_eq!(vault_balance1, 0); + assert_eq!(vault_balance1, amount1*3); // check that fetch_total_managed_funds returns correct amount let mut total_managed_funds_expected = Map::new(&test.env); let strategy_investments_expected_token_0 = sorobanvec![&test.env, StrategyAllocation { strategy_address: test.strategy_client_token0.address.clone(), - amount: amount0*3, // everything has been invested + amount: 0, // everything has been invested }]; let strategy_investments_expected_token_1 = sorobanvec![&test.env, StrategyAllocation { strategy_address: test.strategy_client_token1.address.clone(), - amount: amount1*3, // everything has been invested + amount: 0, // everything has been invested }]; total_managed_funds_expected.set(test.token0.address.clone(), CurrentAssetInvestmentAllocation { asset: test.token0.address.clone(), total_amount: amount0*3, - idle_amount: 0, - invested_amount: amount0*3, + idle_amount: amount0*3, + invested_amount: 0, strategy_allocations: strategy_investments_expected_token_0, }); total_managed_funds_expected.set(test.token1.address.clone(), CurrentAssetInvestmentAllocation { asset: test.token1.address.clone(), total_amount: amount1*3, - idle_amount: 0, - invested_amount: amount1*3, + idle_amount: amount1*3, + invested_amount: 0, strategy_allocations: strategy_investments_expected_token_1, }); let total_managed_funds = test.defindex_contract.fetch_total_managed_funds(); assert_eq!(total_managed_funds, total_managed_funds_expected); - - // check current idle funds - let mut expected_idle_map = Map::new(&test.env); - expected_idle_map.set(test.token0.address.clone(), 0); - expected_idle_map.set(test.token1.address.clone(), 0); - let current_idle_funds = test.defindex_contract.fetch_current_idle_funds(); - assert_eq!(current_idle_funds, expected_idle_map); - - // check that current invested funds is now 0, funds still in idle funds - let mut expected_invested_map = Map::new(&test.env); - expected_invested_map.set(test.token0.address.clone(), 3*amount0); - expected_invested_map.set(test.token1.address.clone(), 3*amount1); - let current_invested_funds = test.defindex_contract.fetch_current_invested_funds(); - assert_eq!(current_invested_funds, expected_invested_map); +} + +#[test] +fn several_assets_wih_previous_investment_success() { + let test = DeFindexVaultTest::setup(); + test.env.mock_all_auths(); + let strategy_params_token0 = create_strategy_params_token0(&test); + let strategy_params_token1 = create_strategy_params_token1(&test); + + // initialize with 2 assets + let assets: Vec = sorobanvec![ + &test.env, + AssetStrategySet { + address: test.token0.address.clone(), + strategies: strategy_params_token0.clone() + }, + AssetStrategySet { + address: test.token1.address.clone(), + strategies: strategy_params_token1.clone() + } + ]; + + test.defindex_contract.initialize( + &assets, + &test.manager, + &test.emergency_manager, + &test.vault_fee_receiver, + &2000u32, + &test.defindex_protocol_receiver, + &test.defindex_factory, + &String::from_str(&test.env, "dfToken"), + &String::from_str(&test.env, "DFT"), + ); + let amount0 = 123456789i128; + let amount1 = 987654321i128; + + let users = DeFindexVaultTest::generate_random_users(&test.env, 2); + + // Balances before deposit + test.token0_admin_client.mint(&users[0], &amount0); + test.token1_admin_client.mint(&users[0], &amount1); + + // deposit with no previous investment + let deposit_result=test.defindex_contract.deposit( + &sorobanvec![&test.env, amount0, amount1], + &sorobanvec![&test.env, amount0, amount1], + &users[0], + &true, + ); + + // GENERATE INVESTMENT + let amount_to_invest_0 =100000000i128; + let amount_to_invest_1 =200000000i128; + + let asset_investments = sorobanvec![ + &test.env, + Some(AssetInvestmentAllocation { + asset: test.token0.address.clone(), + strategy_allocations: vec![ + &test.env, + Some(StrategyAllocation { + strategy_address: test.strategy_client_token0.address.clone(), + amount: amount_to_invest_0, + }), + ], + }), + Some(AssetInvestmentAllocation { + asset: test.token1.address.clone(), + strategy_allocations: vec![ + &test.env, + Some(StrategyAllocation { + strategy_address: test.strategy_client_token1.address.clone(), + amount: amount_to_invest_1, + }), + ], + })]; + + test.defindex_contract.invest( + &asset_investments, + ); - // we will repeat one more time, now enforcing the first asset - let amount0_new = amount0*2; - let amount1_new = amount1*2+100; + // total managed funds + let mut total_managed_funds_expected = Map::new(&test.env); + let strategy_investments_expected_token_0 = sorobanvec![&test.env, StrategyAllocation { + strategy_address: test.strategy_client_token0.address.clone(), + amount: amount_to_invest_0, // everything has been invested + }]; + let strategy_investments_expected_token_1 = sorobanvec![&test.env, StrategyAllocation { + strategy_address: test.strategy_client_token1.address.clone(), + amount: amount_to_invest_1, // everything has been invested + }]; + total_managed_funds_expected.set(test.token0.address.clone(), + CurrentAssetInvestmentAllocation { + asset: test.token0.address.clone(), + total_amount: amount0, + idle_amount: amount0 - amount_to_invest_0, + invested_amount: amount_to_invest_0, + strategy_allocations: strategy_investments_expected_token_0, + }); + total_managed_funds_expected.set(test.token1.address.clone(), + CurrentAssetInvestmentAllocation { + asset: test.token1.address.clone(), + total_amount: amount1, + idle_amount: amount1 - amount_to_invest_1, + invested_amount: amount_to_invest_1, + strategy_allocations: strategy_investments_expected_token_1, + }); + + let total_managed_funds = test.defindex_contract.fetch_total_managed_funds(); + assert_eq!(total_managed_funds, total_managed_funds_expected); + + + // Now that we have previous invesment, we will do deposit and invest and this deposit should be invested directly + + // new user wants to do a deposit with more assets 0 than the proportion, but with minium amount 0 + // multiply amount0 by 2 + let amount0_new = amount0*2 +100 ; + let amount1_new = amount1*2; // mint this to user 1 test.token0_admin_client.mint(&users[1], &amount0_new); test.token1_admin_client.mint(&users[1], &amount1_new); - + // check user balances let user_balance0 = test.token0.balance(&users[1]); - assert_eq!(user_balance0, 100 + amount0_new); // we still have 100 from before + assert_eq!(user_balance0, amount0_new); let user_balance1 = test.token1.balance(&users[1]); assert_eq!(user_balance1, amount1_new); + // user 1 deposits let deposit_result=test.defindex_contract.deposit( &sorobanvec![&test.env, amount0_new, amount1_new], @@ -577,8 +676,97 @@ fn several_assets_success() { // check deposit result. Ok((amounts, shares_to_mint)) // Vec, i128 + assert_eq!(deposit_result, (sorobanvec![&test.env, amount0*2, amount1*2], amount0*2 + amount1*2)); + + // check balances after deposit + let df_balance = test.defindex_contract.balance(&users[1]); + assert_eq!(df_balance, 2*(amount0 + amount1)); + + let user_balance0 = test.token0.balance(&users[1]); + assert_eq!(user_balance0, amount0_new - 2*amount0); + + let user_balance1 = test.token1.balance(&users[1]); + assert_eq!(user_balance1, amount1_new - 2*amount1); + + // check vault balance of asset 0 + let vault_balance0 = test.token0.balance(&test.defindex_contract.address); + assert_eq!(vault_balance0, amount0 - amount_to_invest_0); + // check vault balance of asset 1 + let vault_balance1 = test.token1.balance(&test.defindex_contract.address); + assert_eq!(vault_balance1, amount1 - amount_to_invest_1); + + + // check that fetch_total_managed_funds returns correct amount + let mut total_managed_funds_expected = Map::new(&test.env); + let strategy_investments_expected_token_0 = sorobanvec![&test.env, StrategyAllocation { + strategy_address: test.strategy_client_token0.address.clone(), + amount: amount0*2 + amount_to_invest_0, // only new deposit and invest + }]; + let strategy_investments_expected_token_1 = sorobanvec![&test.env, StrategyAllocation { + strategy_address: test.strategy_client_token1.address.clone(), + amount: amount1*2 + amount_to_invest_1, // only new deposit and invest + }]; + total_managed_funds_expected.set(test.token0.address.clone(), + CurrentAssetInvestmentAllocation { + asset: test.token0.address.clone(), + total_amount: amount0*3, + idle_amount: amount0 - amount_to_invest_0, + invested_amount: amount0*2 + amount_to_invest_0, + strategy_allocations: strategy_investments_expected_token_0, + }); + total_managed_funds_expected.set(test.token1.address.clone(), + CurrentAssetInvestmentAllocation { + asset: test.token1.address.clone(), + total_amount: amount1*3, + idle_amount: amount1 - amount_to_invest_1, + invested_amount: amount1*2 + amount_to_invest_1, + strategy_allocations: strategy_investments_expected_token_1, + }); + let total_managed_funds = test.defindex_contract.fetch_total_managed_funds(); + assert_eq!(total_managed_funds, total_managed_funds_expected); + + // // check current idle funds + // let mut expected_idle_map = Map::new(&test.env); + // expected_idle_map.set(test.token0.address.clone(), 0); + // expected_idle_map.set(test.token1.address.clone(), 0); + // let current_idle_funds = test.defindex_contract.fetch_current_idle_funds(); + // assert_eq!(current_idle_funds, expected_idle_map); + + // // check that current invested funds is now 0, funds still in idle funds + // let mut expected_invested_map = Map::new(&test.env); + // expected_invested_map.set(test.token0.address.clone(), 3*amount0); + // expected_invested_map.set(test.token1.address.clone(), 3*amount1); + // let current_invested_funds = test.defindex_contract.fetch_current_invested_funds(); + // assert_eq!(current_invested_funds, expected_invested_map); + + // // we will repeat one more time, now enforcing the first asset + // let amount0_new = amount0*2; + // let amount1_new = amount1*2+100; + + // // mint this to user 1 + // test.token0_admin_client.mint(&users[1], &amount0_new); + // test.token1_admin_client.mint(&users[1], &amount1_new); + + // // check user balances + // let user_balance0 = test.token0.balance(&users[1]); + // assert_eq!(user_balance0, 100 + amount0_new); // we still have 100 from before + // let user_balance1 = test.token1.balance(&users[1]); + // assert_eq!(user_balance1, amount1_new); + + // // user 1 deposits + // let deposit_result=test.defindex_contract.deposit( + // &sorobanvec![&test.env, amount0_new, amount1_new], + // &sorobanvec![&test.env, 0i128, 0i128], + // &users[1], + // &true, + // ); + + // // check deposit result. Ok((amounts, shares_to_mint)) + // // Vec, i128 + // assert_eq!(deposit_result, (sorobanvec![&test.env, amount0*2, amount1*2], amount0*2 + amount1*2)); + } #[test] From 93d4511cef8a085f2dc65ba9d5b3db0f54aa35fc Mon Sep 17 00:00:00 2001 From: esteblock Date: Tue, 10 Dec 2024 16:27:59 -0300 Subject: [PATCH 14/17] complete docs --- apps/contracts/vault/src/lib.rs | 18 +- apps/contracts/vault/src/test/vault/invest.rs | 8 +- apps/rust_docs/defindex_vault/index.html | 2 +- .../defindex_vault/struct.DeFindexVault.html | 127 +++++--- .../struct.DeFindexVaultClient.html | 254 +++++++++++----- .../defindex_vault/defindex_vault-desc-0-.js | 2 +- .../src/defindex_vault/deposit.rs.html | 121 ++------ .../src/defindex_vault/funds.rs.html | 138 ++++++--- .../src/defindex_vault/interface.rs.html | 10 +- .../src/defindex_vault/investment.rs.html | 287 +++++++++++++++--- apps/rust_docs/src/defindex_vault/lib.rs.html | 266 +++++++++++----- .../src/defindex_vault/strategies.rs.html | 2 +- .../src/defindex_vault/token/contract.rs.html | 4 +- .../src/defindex_vault/utils.rs.html | 12 +- 14 files changed, 837 insertions(+), 414 deletions(-) diff --git a/apps/contracts/vault/src/lib.rs b/apps/contracts/vault/src/lib.rs index a191b117..8a995ca1 100755 --- a/apps/contracts/vault/src/lib.rs +++ b/apps/contracts/vault/src/lib.rs @@ -165,18 +165,21 @@ impl VaultTrait for DeFindexVault { Ok(()) } - /// Handles user deposits into the DeFindex Vault. + /// Handles user deposits into the DeFindex Vault and optionally allocates investments automatically. /// /// This function processes a deposit by transferring each specified asset amount from the user's address to /// the vault, allocating assets according to the vault's defined strategy ratios, and minting vault shares that - /// represent the user's proportional share in the vault. The `amounts_desired` and `amounts_min` vectors should - /// align with the vault's asset order to ensure correct allocation. + /// represent the user's proportional share in the vault. Additionally, if the `invest` parameter is set to `true`, + /// the function will immediately generate and execute investment allocations based on the vault's strategy configuration. /// /// # Parameters /// * `e` - The current environment reference (`Env`), for access to the contract state and utilities. /// * `amounts_desired` - A vector specifying the user's intended deposit amounts for each asset. /// * `amounts_min` - A vector of minimum deposit amounts required for the transaction to proceed. /// * `from` - The address of the user making the deposit. + /// * `invest` - A boolean flag indicating whether to immediately invest the deposited funds into the vault's strategies: + /// - `true`: Generate and execute investments after the deposit. + /// - `false`: Leave the deposited funds as idle assets in the vault. /// /// # Returns /// * `Result<(Vec, i128), ContractError>` - Returns the actual deposited `amounts` and `shares_to_mint` if successful, @@ -187,14 +190,21 @@ impl VaultTrait for DeFindexVault { /// 2. **Validation**: Checks that the lengths of `amounts_desired` and `amounts_min` match the vault's assets. /// 3. **Share Calculation**: Calculates `shares_to_mint` based on the vault's total managed funds and the deposit amount. /// 4. **Asset Transfer**: Transfers each specified amount from the user’s address to the vault as idle funds. - /// 5. **Vault shares Minting**: Mints vault shares for the user to represent their ownership in the vault. + /// 5. **Vault Shares Minting**: Mints vault shares for the user to represent their ownership in the vault. + /// 6. **Investment Execution**: If `invest` is `true`, generates and executes the investment allocations for the deposited funds. + /// - Allocates funds across strategies proportionally to their current state. + /// - Executes the investment to transition idle funds into the vault's strategies. /// /// # Notes /// - For the first deposit, if the vault has only one asset, shares are calculated directly based on the deposit amount. /// - For multiple assets, the function delegates to `calculate_deposit_amounts_and_shares_to_mint` /// for precise share computation. /// - An event is emitted to log the deposit, including the actual deposited amounts and minted shares. + /// - If `invest` is `false`, deposited funds remain idle, allowing for manual investment at a later time. /// + /// # Errors + /// - Returns a `ContractError` if any validation or execution step fails. + fn deposit( e: Env, amounts_desired: Vec, diff --git a/apps/contracts/vault/src/test/vault/invest.rs b/apps/contracts/vault/src/test/vault/invest.rs index dddf4a95..41d6068c 100644 --- a/apps/contracts/vault/src/test/vault/invest.rs +++ b/apps/contracts/vault/src/test/vault/invest.rs @@ -888,4 +888,10 @@ fn without_mock_all_auths() { assert_eq!(current_invested_funds, expected_map); -} \ No newline at end of file +} + +#[test] +fn one_asset_several_strategies() { + +} + diff --git a/apps/rust_docs/defindex_vault/index.html b/apps/rust_docs/defindex_vault/index.html index 0aab010a..f2145249 100644 --- a/apps/rust_docs/defindex_vault/index.html +++ b/apps/rust_docs/defindex_vault/index.html @@ -1 +1 @@ -defindex_vault - Rust

Crate defindex_vault

source ·

Structs§

Enums§

\ No newline at end of file +defindex_vault - Rust

Crate defindex_vault

source ·

Structs§

Enums§

\ No newline at end of file diff --git a/apps/rust_docs/defindex_vault/struct.DeFindexVault.html b/apps/rust_docs/defindex_vault/struct.DeFindexVault.html index 4b6ec15e..7cfa080f 100644 --- a/apps/rust_docs/defindex_vault/struct.DeFindexVault.html +++ b/apps/rust_docs/defindex_vault/struct.DeFindexVault.html @@ -23,17 +23,23 @@
§Errors
  • ContractError::AlreadyInitialized: If the vault has already been initialized.
  • ContractError::StrategyDoesNotSupportAsset: If a strategy within an asset does not support the asset’s contract.
  • -
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_deposit() -> [u8; 1196]

    Handles user deposits into the DeFindex Vault.

    +
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_deposit() -> [u8; 1196]

    Handles user deposits into the DeFindex Vault and optionally allocates investments automatically.

    This function processes a deposit by transferring each specified asset amount from the user’s address to the vault, allocating assets according to the vault’s defined strategy ratios, and minting vault shares that -represent the user’s proportional share in the vault. The amounts_desired and amounts_min vectors should -align with the vault’s asset order to ensure correct allocation.

    +represent the user’s proportional share in the vault. Additionally, if the invest parameter is set to true, +the function will immediately generate and execute investment allocations based on the vault’s strategy configuration.

    §Parameters
    • e - The current environment reference (Env), for access to the contract state and utilities.
    • amounts_desired - A vector specifying the user’s intended deposit amounts for each asset.
    • amounts_min - A vector of minimum deposit amounts required for the transaction to proceed.
    • from - The address of the user making the deposit.
    • +
    • invest - A boolean flag indicating whether to immediately invest the deposited funds into the vault’s strategies: +
        +
      • true: Generate and execute investments after the deposit.
      • +
      • false: Leave the deposited funds as idle assets in the vault.
      • +
      +
    §Returns
      @@ -46,7 +52,13 @@
      §Functio
    • Validation: Checks that the lengths of amounts_desired and amounts_min match the vault’s assets.
    • Share Calculation: Calculates shares_to_mint based on the vault’s total managed funds and the deposit amount.
    • Asset Transfer: Transfers each specified amount from the user’s address to the vault as idle funds.
    • -
    • Vault shares Minting: Mints vault shares for the user to represent their ownership in the vault.
    • +
    • Vault Shares Minting: Mints vault shares for the user to represent their ownership in the vault.
    • +
    • Investment Execution: If invest is true, generates and executes the investment allocations for the deposited funds. +
        +
      • Allocates funds across strategies proportionally to their current state.
      • +
      • Executes the investment to transition idle funds into the vault’s strategies.
      • +
      +
    • §Notes
        @@ -54,26 +66,63 @@
        §Notes
      • For multiple assets, the function delegates to calculate_deposit_amounts_and_shares_to_mint for precise share computation.
      • An event is emitted to log the deposit, including the actual deposited amounts and minted shares.
      • +
      • If invest is false, deposited funds remain idle, allowing for manual investment at a later time.
      -
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_withdraw() -> [u8; 684]

    Withdraws assets from the DeFindex Vault by burning Vault Share tokens.

    -

    This function calculates the amount of assets to withdraw based on the number of Vault Share tokens being burned, -then transfers the appropriate assets back to the user, pulling from both idle funds and strategies -as needed.

    -
    §Arguments:
    +
    §Errors
      -
    • e - The environment.
    • -
    • shares_amount - The amount of Vault Share tokens to burn for the withdrawal.
    • -
    • from - The address of the user requesting the withdrawal.
    • +
    • Returns a ContractError if any validation or execution step fails.
    -
    §Returns:
    +
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_withdraw() -> [u8; 1132]

    Handles the withdrawal process for a specified number of vault shares.

    +

    This function performs the following steps:

    +
      +
    1. Validates the environment and the inputs:
        -
      • Result<(), ContractError> - Ok if successful, otherwise returns a ContractError.
      • +
      • Ensures the contract is initialized.
      • +
      • Checks that the withdrawal amount (withdraw_shares) is non-negative.
      • +
      • Verifies the authorization of the from address.
      • +
      +
    2. +
    3. Collects applicable fees.
    4. +
    5. Calculates the proportionate withdrawal amounts for each asset based on the number of shares.
    6. +
    7. Burns the specified shares from the user’s account.
    8. +
    9. Processes the withdrawal for each asset: +
        +
      • First attempts to cover the withdrawal amount using idle funds.
      • +
      • If idle funds are insufficient, unwinds investments from the associated strategies +to cover the remaining amount, accounting for rounding errors in the last strategy.
      • +
      +
    10. +
    11. Transfers the withdrawn funds to the user’s address (from).
    12. +
    13. Emits an event to record the withdrawal details.
    14. +
    +
    §Parameters:
    +
      +
    • e: The contract environment (Env).
    • +
    • withdraw_shares: The number of vault shares to withdraw.
    • +
    • from: The address initiating the withdrawal.
    • +
    +
    §Returns:
    +
      +
    • A Result containing a vector of withdrawn amounts for each asset (Vec<i128>), +or a ContractError if the withdrawal fails.
    • +
    +
    §Errors:
    +
      +
    • ContractError::AmountOverTotalSupply: If the specified shares exceed the total supply.
    • +
    • ContractError::ArithmeticError: If any arithmetic operation fails during calculations.
    • +
    • ContractError::WrongAmountsLength: If there is a mismatch in asset allocation data.
    • +
    +
    §TODOs:
    +
      +
    • Implement minimum amounts for withdrawals to ensure compliance with potential restrictions.
    • +
    • Replace the returned vector with the original asset_withdrawal_amounts map for better structure.
    • +
    • avoid the usage of a Map, choose between using map or vector
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_emergency_withdraw() -> [u8; 712]

    Executes an emergency withdrawal from a specific strategy.

    This function allows the emergency manager or manager to withdraw all assets from a particular strategy and store them as idle funds within the vault. It also pauses the strategy to prevent further use until unpaused.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    • strategy_address - The address of the strategy to withdraw from.
    • @@ -86,7 +135,7 @@
      §Returns:
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_pause_strategy() -> [u8; 604]

    Pauses a strategy to prevent it from being used in the vault.

    This function pauses a strategy by setting its paused field to true. Only the manager or emergency manager can pause a strategy.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    • strategy_address - The address of the strategy to pause.
    • @@ -99,7 +148,7 @@
      §Returns:
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_unpause_strategy() -> [u8; 572]

    Unpauses a previously paused strategy.

    This function unpauses a strategy by setting its paused field to false, allowing it to be used again in the vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    • strategy_address - The address of the strategy to unpause.
    • @@ -110,7 +159,7 @@
      §Returns:
    • Result<(), ContractError> - Ok if successful, otherwise returns a ContractError.
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_get_assets() -> [u8; 284]

    Retrieves the list of assets managed by the DeFindex Vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    @@ -121,7 +170,7 @@
    §Returns:
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_fetch_total_managed_funds() -> [u8; 456]

    Returns the total managed funds of the vault, including both invested and idle funds.

    This function provides a map where the key is the asset address and the value is the total amount of that asset being managed by the vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    @@ -132,7 +181,7 @@
    §Returns:
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_fetch_current_invested_funds() -> [u8; 440]

    Returns the current invested funds, representing the total assets allocated to strategies.

    This function provides a map where the key is the asset address and the value is the total amount of that asset currently invested in various strategies.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    @@ -143,7 +192,7 @@
    §Returns:
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_fetch_current_idle_funds() -> [u8; 440]

    Returns the current idle funds, representing the total assets held directly by the vault (not invested).

    This function provides a map where the key is the asset address and the value is the total amount of that asset held as idle funds within the vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    @@ -154,7 +203,7 @@
    §Returns:
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_get_asset_amounts_per_shares() -> [u8; 644]

    This function extends the contract’s time-to-live and calculates how much of each asset corresponds per the provided number of vault shares (vault_shares). It provides proportional allocations for each asset in the vault relative to the specified shares.

    -
    §Arguments
    +
    §Arguments
    • e - The current environment reference.
    • vault_shares - The number of vault shares for which the corresponding asset amounts are calculated.
    • @@ -163,9 +212,9 @@
      §Returns
      • Map<Address, i128> - A map containing each asset address and its corresponding proportional amount.
      -
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_get_fees() -> [u8; 44]

    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_collect_fees() -> [u8; 68]

    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_set_fee_receiver() -> [u8; 448]

    Sets the fee receiver for the vault.

    +
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_get_fees() -> [u8; 44]

    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_collect_fees() -> [u8; 68]

    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_set_fee_receiver() -> [u8; 448]

    Sets the fee receiver for the vault.

    This function allows the manager or emergency manager to set a new fee receiver address for the vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    • caller - The address initiating the change (must be the manager or emergency manager).
    • @@ -175,8 +224,8 @@
      §Returns:
      • () - No return value.
      -
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_get_fee_receiver() -> [u8; 288]

    Retrieves the current fee receiver address for the vault.

    -
    §Arguments:
    +
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_get_fee_receiver() -> [u8; 288]

    Retrieves the current fee receiver address for the vault.

    +
    §Arguments:
    • e - The environment.
    @@ -184,9 +233,9 @@
    §Returns:
    • Result<Address, ContractError> - The fee receiver address if successful, otherwise returns a ContractError.
    -
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_set_manager() -> [u8; 300]

    Sets the manager for the vault.

    +
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_set_manager() -> [u8; 300]

    Sets the manager for the vault.

    This function allows the current manager or emergency manager to set a new manager for the vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    • manager - The new manager address.
    • @@ -195,8 +244,8 @@
      §Returns:
      • () - No return value.
      -
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_get_manager() -> [u8; 276]

    Retrieves the current manager address for the vault.

    -
    §Arguments:
    +
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_get_manager() -> [u8; 276]

    Retrieves the current manager address for the vault.

    +
    §Arguments:
    • e - The environment.
    @@ -204,9 +253,9 @@
    §Returns:
    • Result<Address, ContractError> - The manager address if successful, otherwise returns a ContractError.
    -
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_set_emergency_manager() -> [u8; 364]

    Sets the emergency manager for the vault.

    +
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_set_emergency_manager() -> [u8; 364]

    Sets the emergency manager for the vault.

    This function allows the current manager or emergency manager to set a new emergency manager for the vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    • emergency_manager - The new emergency manager address.
    • @@ -215,8 +264,8 @@
      §Returns:
      • () - No return value.
      -
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_get_emergency_manager() -> [u8; 308]

    Retrieves the current emergency manager address for the vault.

    -
    §Arguments:
    +
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_get_emergency_manager() -> [u8; 308]

    Retrieves the current emergency manager address for the vault.

    +
    §Arguments:
    • e - The environment.
    @@ -224,10 +273,10 @@
    §Returns:
    • Result<Address, ContractError> - The emergency manager address if successful, otherwise returns a ContractError.
    -
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_invest() -> [u8; 1160]

    Executes the investment of the vault’s idle funds based on the specified asset allocations. +

    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_invest() -> [u8; 1160]

    Executes the investment of the vault’s idle funds based on the specified asset allocations. This function allows partial investments by providing an optional allocation for each asset, and it ensures proper authorization and validation checks before proceeding with investments.

    -
    §Arguments
    +
    §Arguments
    • e - The current environment reference.
    • asset_investments - A vector of optional AssetInvestmentAllocation structures, where each element @@ -248,7 +297,7 @@
      §Fun
    • Investment Execution: Calls the check_and_execute_investments function to perform the investment after validating the inputs and ensuring correct execution flows for each asset allocation.
    • -
      §Errors
      +
      §Errors
      • Returns ContractError::WrongInvestmentLength if the length of asset_investments does not match the vault assets.
      • Returns ContractError if access control validation fails or if investment execution encounters an issue.
      • @@ -257,8 +306,8 @@
        §Security
        • Only addresses with the Manager role can call this function, ensuring restricted access to managing investments.
        -
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_rebalance() -> [u8; 428]

    Rebalances the vault by executing a series of instructions.

    -
    §Arguments:
    +
    source§

    impl DeFindexVault

    source

    pub const fn spec_xdr_rebalance() -> [u8; 428]

    Rebalances the vault by executing a series of instructions.

    +
    §Arguments:
    • e - The environment.
    • instructions - A vector of Instruction structs representing actions (withdraw, invest, swap, zapper) to be taken.
    • diff --git a/apps/rust_docs/defindex_vault/struct.DeFindexVaultClient.html b/apps/rust_docs/defindex_vault/struct.DeFindexVaultClient.html index bdb7187f..2c84c94f 100644 --- a/apps/rust_docs/defindex_vault/struct.DeFindexVaultClient.html +++ b/apps/rust_docs/defindex_vault/struct.DeFindexVaultClient.html @@ -81,17 +81,23 @@
      §Errors
      amounts_min: &Vec<i128>, from: &Address, invest: &bool, -) -> (Vec<i128>, i128)

      Handles user deposits into the DeFindex Vault.

      +) -> (Vec<i128>, i128)

      Handles user deposits into the DeFindex Vault and optionally allocates investments automatically.

      This function processes a deposit by transferring each specified asset amount from the user’s address to the vault, allocating assets according to the vault’s defined strategy ratios, and minting vault shares that -represent the user’s proportional share in the vault. The amounts_desired and amounts_min vectors should -align with the vault’s asset order to ensure correct allocation.

      +represent the user’s proportional share in the vault. Additionally, if the invest parameter is set to true, +the function will immediately generate and execute investment allocations based on the vault’s strategy configuration.

      §Parameters
      • e - The current environment reference (Env), for access to the contract state and utilities.
      • amounts_desired - A vector specifying the user’s intended deposit amounts for each asset.
      • amounts_min - A vector of minimum deposit amounts required for the transaction to proceed.
      • from - The address of the user making the deposit.
      • +
      • invest - A boolean flag indicating whether to immediately invest the deposited funds into the vault’s strategies: +
          +
        • true: Generate and execute investments after the deposit.
        • +
        • false: Leave the deposited funds as idle assets in the vault.
        • +
        +
      §Returns
        @@ -104,7 +110,13 @@
        §Functio
      • Validation: Checks that the lengths of amounts_desired and amounts_min match the vault’s assets.
      • Share Calculation: Calculates shares_to_mint based on the vault’s total managed funds and the deposit amount.
      • Asset Transfer: Transfers each specified amount from the user’s address to the vault as idle funds.
      • -
      • Vault shares Minting: Mints vault shares for the user to represent their ownership in the vault.
      • +
      • Vault Shares Minting: Mints vault shares for the user to represent their ownership in the vault.
      • +
      • Investment Execution: If invest is true, generates and executes the investment allocations for the deposited funds. +
          +
        • Allocates funds across strategies proportionally to their current state.
        • +
        • Executes the investment to transition idle funds into the vault’s strategies.
        • +
        +
      • §Notes
          @@ -112,6 +124,11 @@
          §Notes
        • For multiple assets, the function delegates to calculate_deposit_amounts_and_shares_to_mint for precise share computation.
        • An event is emitted to log the deposit, including the actual deposited amounts and minted shares.
        • +
        • If invest is false, deposited funds remain idle, allowing for manual investment at a later time.
        • +
        +
        §Errors
        +
          +
        • Returns a ContractError if any validation or execution step fails.
    source

    pub fn try_deposit( &self, @@ -119,17 +136,23 @@

    §Notes
    amounts_min: &Vec<i128>, from: &Address, invest: &bool, -) -> Result<Result<(Vec<i128>, i128), <(Vec<i128>, i128) as TryFromVal<Env, Val>>::Error>, Result<ContractError, InvokeError>>

    Handles user deposits into the DeFindex Vault.

    +) -> Result<Result<(Vec<i128>, i128), <(Vec<i128>, i128) as TryFromVal<Env, Val>>::Error>, Result<ContractError, InvokeError>>

    Handles user deposits into the DeFindex Vault and optionally allocates investments automatically.

    This function processes a deposit by transferring each specified asset amount from the user’s address to the vault, allocating assets according to the vault’s defined strategy ratios, and minting vault shares that -represent the user’s proportional share in the vault. The amounts_desired and amounts_min vectors should -align with the vault’s asset order to ensure correct allocation.

    +represent the user’s proportional share in the vault. Additionally, if the invest parameter is set to true, +the function will immediately generate and execute investment allocations based on the vault’s strategy configuration.

    §Parameters
    • e - The current environment reference (Env), for access to the contract state and utilities.
    • amounts_desired - A vector specifying the user’s intended deposit amounts for each asset.
    • amounts_min - A vector of minimum deposit amounts required for the transaction to proceed.
    • from - The address of the user making the deposit.
    • +
    • invest - A boolean flag indicating whether to immediately invest the deposited funds into the vault’s strategies: +
        +
      • true: Generate and execute investments after the deposit.
      • +
      • false: Leave the deposited funds as idle assets in the vault.
      • +
      +
    §Returns
      @@ -142,7 +165,13 @@
      §Fun
    • Validation: Checks that the lengths of amounts_desired and amounts_min match the vault’s assets.
    • Share Calculation: Calculates shares_to_mint based on the vault’s total managed funds and the deposit amount.
    • Asset Transfer: Transfers each specified amount from the user’s address to the vault as idle funds.
    • -
    • Vault shares Minting: Mints vault shares for the user to represent their ownership in the vault.
    • +
    • Vault Shares Minting: Mints vault shares for the user to represent their ownership in the vault.
    • +
    • Investment Execution: If invest is true, generates and executes the investment allocations for the deposited funds. +
        +
      • Allocates funds across strategies proportionally to their current state.
      • +
      • Executes the investment to transition idle funds into the vault’s strategies.
      • +
      +
    • §Notes
        @@ -150,44 +179,113 @@
        §Notes
      • For multiple assets, the function delegates to calculate_deposit_amounts_and_shares_to_mint for precise share computation.
      • An event is emitted to log the deposit, including the actual deposited amounts and minted shares.
      • +
      • If invest is false, deposited funds remain idle, allowing for manual investment at a later time.
      -
    source

    pub fn withdraw(&self, withdraw_shares: &i128, from: &Address) -> Vec<i128>

    Withdraws assets from the DeFindex Vault by burning Vault Share tokens.

    -

    This function calculates the amount of assets to withdraw based on the number of Vault Share tokens being burned, -then transfers the appropriate assets back to the user, pulling from both idle funds and strategies -as needed.

    -
    §Arguments:
    +
    §Errors
      -
    • e - The environment.
    • -
    • shares_amount - The amount of Vault Share tokens to burn for the withdrawal.
    • -
    • from - The address of the user requesting the withdrawal.
    • +
    • Returns a ContractError if any validation or execution step fails.
    -
    §Returns:
    +
    source

    pub fn withdraw(&self, withdraw_shares: &i128, from: &Address) -> Vec<i128>

    Handles the withdrawal process for a specified number of vault shares.

    +

    This function performs the following steps:

    +
      +
    1. Validates the environment and the inputs:
        -
      • Result<(), ContractError> - Ok if successful, otherwise returns a ContractError.
      • +
      • Ensures the contract is initialized.
      • +
      • Checks that the withdrawal amount (withdraw_shares) is non-negative.
      • +
      • Verifies the authorization of the from address.
      • +
      +
    2. +
    3. Collects applicable fees.
    4. +
    5. Calculates the proportionate withdrawal amounts for each asset based on the number of shares.
    6. +
    7. Burns the specified shares from the user’s account.
    8. +
    9. Processes the withdrawal for each asset: +
        +
      • First attempts to cover the withdrawal amount using idle funds.
      • +
      • If idle funds are insufficient, unwinds investments from the associated strategies +to cover the remaining amount, accounting for rounding errors in the last strategy.
      • +
      +
    10. +
    11. Transfers the withdrawn funds to the user’s address (from).
    12. +
    13. Emits an event to record the withdrawal details.
    14. +
    +
    §Parameters:
    +
      +
    • e: The contract environment (Env).
    • +
    • withdraw_shares: The number of vault shares to withdraw.
    • +
    • from: The address initiating the withdrawal.
    • +
    +
    §Returns:
    +
      +
    • A Result containing a vector of withdrawn amounts for each asset (Vec<i128>), +or a ContractError if the withdrawal fails.
    • +
    +
    §Errors:
    +
      +
    • ContractError::AmountOverTotalSupply: If the specified shares exceed the total supply.
    • +
    • ContractError::ArithmeticError: If any arithmetic operation fails during calculations.
    • +
    • ContractError::WrongAmountsLength: If there is a mismatch in asset allocation data.
    • +
    +
    §TODOs:
    +
      +
    • Implement minimum amounts for withdrawals to ensure compliance with potential restrictions.
    • +
    • Replace the returned vector with the original asset_withdrawal_amounts map for better structure.
    • +
    • avoid the usage of a Map, choose between using map or vector
    source

    pub fn try_withdraw( &self, withdraw_shares: &i128, from: &Address, -) -> Result<Result<Vec<i128>, <Vec<i128> as TryFromVal<Env, Val>>::Error>, Result<ContractError, InvokeError>>

    Withdraws assets from the DeFindex Vault by burning Vault Share tokens.

    -

    This function calculates the amount of assets to withdraw based on the number of Vault Share tokens being burned, -then transfers the appropriate assets back to the user, pulling from both idle funds and strategies -as needed.

    -
    §Arguments:
    +) -> Result<Result<Vec<i128>, <Vec<i128> as TryFromVal<Env, Val>>::Error>, Result<ContractError, InvokeError>>

    Handles the withdrawal process for a specified number of vault shares.

    +

    This function performs the following steps:

    +
      +
    1. Validates the environment and the inputs:
        -
      • e - The environment.
      • -
      • shares_amount - The amount of Vault Share tokens to burn for the withdrawal.
      • -
      • from - The address of the user requesting the withdrawal.
      • +
      • Ensures the contract is initialized.
      • +
      • Checks that the withdrawal amount (withdraw_shares) is non-negative.
      • +
      • Verifies the authorization of the from address.
      -
      §Returns:
      +
    2. +
    3. Collects applicable fees.
    4. +
    5. Calculates the proportionate withdrawal amounts for each asset based on the number of shares.
    6. +
    7. Burns the specified shares from the user’s account.
    8. +
    9. Processes the withdrawal for each asset:
        -
      • Result<(), ContractError> - Ok if successful, otherwise returns a ContractError.
      • +
      • First attempts to cover the withdrawal amount using idle funds.
      • +
      • If idle funds are insufficient, unwinds investments from the associated strategies +to cover the remaining amount, accounting for rounding errors in the last strategy.
      • +
      +
    10. +
    11. Transfers the withdrawn funds to the user’s address (from).
    12. +
    13. Emits an event to record the withdrawal details.
    14. +
    +
    §Parameters:
    +
      +
    • e: The contract environment (Env).
    • +
    • withdraw_shares: The number of vault shares to withdraw.
    • +
    • from: The address initiating the withdrawal.
    • +
    +
    §Returns:
    +
      +
    • A Result containing a vector of withdrawn amounts for each asset (Vec<i128>), +or a ContractError if the withdrawal fails.
    • +
    +
    §Errors:
    +
      +
    • ContractError::AmountOverTotalSupply: If the specified shares exceed the total supply.
    • +
    • ContractError::ArithmeticError: If any arithmetic operation fails during calculations.
    • +
    • ContractError::WrongAmountsLength: If there is a mismatch in asset allocation data.
    • +
    +
    §TODOs:
    +
      +
    • Implement minimum amounts for withdrawals to ensure compliance with potential restrictions.
    • +
    • Replace the returned vector with the original asset_withdrawal_amounts map for better structure.
    • +
    • avoid the usage of a Map, choose between using map or vector
    source

    pub fn emergency_withdraw(&self, strategy_address: &Address, caller: &Address)

    Executes an emergency withdrawal from a specific strategy.

    This function allows the emergency manager or manager to withdraw all assets from a particular strategy and store them as idle funds within the vault. It also pauses the strategy to prevent further use until unpaused.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    • strategy_address - The address of the strategy to withdraw from.
    • @@ -205,7 +303,7 @@
      §Returns:

      This function allows the emergency manager or manager to withdraw all assets from a particular strategy and store them as idle funds within the vault. It also pauses the strategy to prevent further use until unpaused.

      -
      §Arguments:
      +
      §Arguments:
      • e - The environment.
      • strategy_address - The address of the strategy to withdraw from.
      • @@ -218,7 +316,7 @@
        §Returns:
    source

    pub fn pause_strategy(&self, strategy_address: &Address, caller: &Address)

    Pauses a strategy to prevent it from being used in the vault.

    This function pauses a strategy by setting its paused field to true. Only the manager or emergency manager can pause a strategy.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    • strategy_address - The address of the strategy to pause.
    • @@ -235,7 +333,7 @@
      §Returns:
      ) -> Result<Result<(), <() as TryFromVal<Env, Val>>::Error>, Result<ContractError, InvokeError>>

      Pauses a strategy to prevent it from being used in the vault.

      This function pauses a strategy by setting its paused field to true. Only the manager or emergency manager can pause a strategy.

      -
      §Arguments:
      +
      §Arguments:
      • e - The environment.
      • strategy_address - The address of the strategy to pause.
      • @@ -248,7 +346,7 @@
        §Returns:
    source

    pub fn unpause_strategy(&self, strategy_address: &Address, caller: &Address)

    Unpauses a previously paused strategy.

    This function unpauses a strategy by setting its paused field to false, allowing it to be used again in the vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    • strategy_address - The address of the strategy to unpause.
    • @@ -265,7 +363,7 @@
      §Returns:
      ) -> Result<Result<(), <() as TryFromVal<Env, Val>>::Error>, Result<ContractError, InvokeError>>

      Unpauses a previously paused strategy.

      This function unpauses a strategy by setting its paused field to false, allowing it to be used again in the vault.

      -
      §Arguments:
      +
      §Arguments:
      • e - The environment.
      • strategy_address - The address of the strategy to unpause.
      • @@ -276,7 +374,7 @@
        §Returns:
      • Result<(), ContractError> - Ok if successful, otherwise returns a ContractError.
    source

    pub fn get_assets(&self) -> Vec<AssetStrategySet>

    Retrieves the list of assets managed by the DeFindex Vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    @@ -287,7 +385,7 @@
    §Returns:
    source

    pub fn try_get_assets( &self, ) -> Result<Result<Vec<AssetStrategySet>, <Vec<AssetStrategySet> as TryFromVal<Env, Val>>::Error>, Result<Error, InvokeError>>

    Retrieves the list of assets managed by the DeFindex Vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    @@ -300,7 +398,7 @@
    §Returns:
    ) -> Map<Address, CurrentAssetInvestmentAllocation>

    Returns the total managed funds of the vault, including both invested and idle funds.

    This function provides a map where the key is the asset address and the value is the total amount of that asset being managed by the vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    @@ -313,7 +411,7 @@
    §Returns:
    ) -> Result<Result<Map<Address, CurrentAssetInvestmentAllocation>, <Map<Address, CurrentAssetInvestmentAllocation> as TryFromVal<Env, Val>>::Error>, Result<Error, InvokeError>>

    Returns the total managed funds of the vault, including both invested and idle funds.

    This function provides a map where the key is the asset address and the value is the total amount of that asset being managed by the vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    @@ -324,7 +422,7 @@
    §Returns:
    source

    pub fn fetch_current_invested_funds(&self) -> Map<Address, i128>

    Returns the current invested funds, representing the total assets allocated to strategies.

    This function provides a map where the key is the asset address and the value is the total amount of that asset currently invested in various strategies.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    @@ -337,7 +435,7 @@
    §Returns:
    ) -> Result<Result<Map<Address, i128>, <Map<Address, i128> as TryFromVal<Env, Val>>::Error>, Result<Error, InvokeError>>

    Returns the current invested funds, representing the total assets allocated to strategies.

    This function provides a map where the key is the asset address and the value is the total amount of that asset currently invested in various strategies.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    @@ -348,7 +446,7 @@
    §Returns:
    source

    pub fn fetch_current_idle_funds(&self) -> Map<Address, i128>

    Returns the current idle funds, representing the total assets held directly by the vault (not invested).

    This function provides a map where the key is the asset address and the value is the total amount of that asset held as idle funds within the vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    @@ -361,7 +459,7 @@
    §Returns:
    ) -> Result<Result<Map<Address, i128>, <Map<Address, i128> as TryFromVal<Env, Val>>::Error>, Result<Error, InvokeError>>

    Returns the current idle funds, representing the total assets held directly by the vault (not invested).

    This function provides a map where the key is the asset address and the value is the total amount of that asset held as idle funds within the vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    @@ -375,7 +473,7 @@
    §Returns:
    ) -> Map<Address, i128>

    This function extends the contract’s time-to-live and calculates how much of each asset corresponds per the provided number of vault shares (vault_shares). It provides proportional allocations for each asset in the vault relative to the specified shares.

    -
    §Arguments
    +
    §Arguments
    • e - The current environment reference.
    • vault_shares - The number of vault shares for which the corresponding asset amounts are calculated.
    • @@ -390,7 +488,7 @@
      §Returns
      ) -> Result<Result<Map<Address, i128>, <Map<Address, i128> as TryFromVal<Env, Val>>::Error>, Result<ContractError, InvokeError>>

      This function extends the contract’s time-to-live and calculates how much of each asset corresponds per the provided number of vault shares (vault_shares). It provides proportional allocations for each asset in the vault relative to the specified shares.

      -
      §Arguments
      +
      §Arguments
    source§

    impl<'a> DeFindexVaultClient<'a>

    source

    pub fn set_fee_receiver(&self, caller: &Address, new_fee_receiver: &Address)

    Sets the fee receiver for the vault.

    +) -> Result<Result<(), <() as TryFromVal<Env, Val>>::Error>, Result<ContractError, InvokeError>>
    source§

    impl<'a> DeFindexVaultClient<'a>

    source

    pub fn set_fee_receiver(&self, caller: &Address, new_fee_receiver: &Address)

    Sets the fee receiver for the vault.

    This function allows the manager or emergency manager to set a new fee receiver address for the vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    • caller - The address initiating the change (must be the manager or emergency manager).
    • @@ -415,13 +513,13 @@
      §Returns:
      • () - No return value.
      -
    source

    pub fn try_set_fee_receiver( +

    source

    pub fn try_set_fee_receiver( &self, caller: &Address, new_fee_receiver: &Address, ) -> Result<Result<(), <() as TryFromVal<Env, Val>>::Error>, Result<Error, InvokeError>>

    Sets the fee receiver for the vault.

    This function allows the manager or emergency manager to set a new fee receiver address for the vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    • caller - The address initiating the change (must be the manager or emergency manager).
    • @@ -431,8 +529,8 @@
      §Returns:
      • () - No return value.
      -
    source

    pub fn get_fee_receiver(&self) -> Address

    Retrieves the current fee receiver address for the vault.

    -
    §Arguments:
    +
    source

    pub fn get_fee_receiver(&self) -> Address

    Retrieves the current fee receiver address for the vault.

    +
    §Arguments:
    • e - The environment.
    @@ -440,10 +538,10 @@
    §Returns:
    • Result<Address, ContractError> - The fee receiver address if successful, otherwise returns a ContractError.
    -
    source

    pub fn try_get_fee_receiver( +

    source

    pub fn try_get_fee_receiver( &self, ) -> Result<Result<Address, <Address as TryFromVal<Env, Val>>::Error>, Result<ContractError, InvokeError>>

    Retrieves the current fee receiver address for the vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    @@ -451,9 +549,9 @@
    §Returns:
    • Result<Address, ContractError> - The fee receiver address if successful, otherwise returns a ContractError.
    -
    source

    pub fn set_manager(&self, manager: &Address)

    Sets the manager for the vault.

    +
    source

    pub fn set_manager(&self, manager: &Address)

    Sets the manager for the vault.

    This function allows the current manager or emergency manager to set a new manager for the vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    • manager - The new manager address.
    • @@ -462,12 +560,12 @@
      §Returns:
      • () - No return value.
      -
    source

    pub fn try_set_manager( +

    source

    pub fn try_set_manager( &self, manager: &Address, ) -> Result<Result<(), <() as TryFromVal<Env, Val>>::Error>, Result<Error, InvokeError>>

    Sets the manager for the vault.

    This function allows the current manager or emergency manager to set a new manager for the vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    • manager - The new manager address.
    • @@ -476,8 +574,8 @@
      §Returns:
      • () - No return value.
      -
    source

    pub fn get_manager(&self) -> Address

    Retrieves the current manager address for the vault.

    -
    §Arguments:
    +
    source

    pub fn get_manager(&self) -> Address

    Retrieves the current manager address for the vault.

    +
    §Arguments:
    • e - The environment.
    @@ -485,10 +583,10 @@
    §Returns:
    • Result<Address, ContractError> - The manager address if successful, otherwise returns a ContractError.
    -
    source

    pub fn try_get_manager( +

    source

    pub fn try_get_manager( &self, ) -> Result<Result<Address, <Address as TryFromVal<Env, Val>>::Error>, Result<ContractError, InvokeError>>

    Retrieves the current manager address for the vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    @@ -496,9 +594,9 @@
    §Returns:
    • Result<Address, ContractError> - The manager address if successful, otherwise returns a ContractError.
    -
    source

    pub fn set_emergency_manager(&self, emergency_manager: &Address)

    Sets the emergency manager for the vault.

    +
    source

    pub fn set_emergency_manager(&self, emergency_manager: &Address)

    Sets the emergency manager for the vault.

    This function allows the current manager or emergency manager to set a new emergency manager for the vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    • emergency_manager - The new emergency manager address.
    • @@ -507,12 +605,12 @@
      §Returns:
      • () - No return value.
      -
    source

    pub fn try_set_emergency_manager( +

    source

    pub fn try_set_emergency_manager( &self, emergency_manager: &Address, ) -> Result<Result<(), <() as TryFromVal<Env, Val>>::Error>, Result<Error, InvokeError>>

    Sets the emergency manager for the vault.

    This function allows the current manager or emergency manager to set a new emergency manager for the vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    • emergency_manager - The new emergency manager address.
    • @@ -521,8 +619,8 @@
      §Returns:
      • () - No return value.
      -
    source

    pub fn get_emergency_manager(&self) -> Address

    Retrieves the current emergency manager address for the vault.

    -
    §Arguments:
    +
    source

    pub fn get_emergency_manager(&self) -> Address

    Retrieves the current emergency manager address for the vault.

    +
    §Arguments:
    • e - The environment.
    @@ -530,10 +628,10 @@
    §Returns:
    • Result<Address, ContractError> - The emergency manager address if successful, otherwise returns a ContractError.
    -
    source

    pub fn try_get_emergency_manager( +

    source

    pub fn try_get_emergency_manager( &self, ) -> Result<Result<Address, <Address as TryFromVal<Env, Val>>::Error>, Result<ContractError, InvokeError>>

    Retrieves the current emergency manager address for the vault.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    @@ -541,10 +639,10 @@
    §Returns:
    • Result<Address, ContractError> - The emergency manager address if successful, otherwise returns a ContractError.
    -
    source§

    impl<'a> DeFindexVaultClient<'a>

    source

    pub fn invest(&self, asset_investments: &Vec<Option<AssetInvestmentAllocation>>)

    Executes the investment of the vault’s idle funds based on the specified asset allocations. +

    source§

    impl<'a> DeFindexVaultClient<'a>

    source

    pub fn invest(&self, asset_investments: &Vec<Option<AssetInvestmentAllocation>>)

    Executes the investment of the vault’s idle funds based on the specified asset allocations. This function allows partial investments by providing an optional allocation for each asset, and it ensures proper authorization and validation checks before proceeding with investments.

    -
    §Arguments
    +
    §Arguments
    • e - The current environment reference.
    • asset_investments - A vector of optional AssetInvestmentAllocation structures, where each element @@ -565,7 +663,7 @@
      §Fun
    • Investment Execution: Calls the check_and_execute_investments function to perform the investment after validating the inputs and ensuring correct execution flows for each asset allocation.
    • -
      §Errors
      +
      §Errors
      • Returns ContractError::WrongInvestmentLength if the length of asset_investments does not match the vault assets.
      • Returns ContractError if access control validation fails or if investment execution encounters an issue.
      • @@ -574,13 +672,13 @@
        §Security
        • Only addresses with the Manager role can call this function, ensuring restricted access to managing investments.
        -
    source

    pub fn try_invest( +

    source

    pub fn try_invest( &self, asset_investments: &Vec<Option<AssetInvestmentAllocation>>, ) -> Result<Result<(), <() as TryFromVal<Env, Val>>::Error>, Result<ContractError, InvokeError>>

    Executes the investment of the vault’s idle funds based on the specified asset allocations. This function allows partial investments by providing an optional allocation for each asset, and it ensures proper authorization and validation checks before proceeding with investments.

    -
    §Arguments
    +
    §Arguments
    • e - The current environment reference.
    • asset_investments - A vector of optional AssetInvestmentAllocation structures, where each element @@ -601,7 +699,7 @@
      §Fun
    • Investment Execution: Calls the check_and_execute_investments function to perform the investment after validating the inputs and ensuring correct execution flows for each asset allocation.
    • -
      §Errors
      +
      §Errors
      • Returns ContractError::WrongInvestmentLength if the length of asset_investments does not match the vault assets.
      • Returns ContractError if access control validation fails or if investment execution encounters an issue.
      • @@ -610,8 +708,8 @@
        §Security
        • Only addresses with the Manager role can call this function, ensuring restricted access to managing investments.
        -
    source

    pub fn rebalance(&self, instructions: &Vec<Instruction>)

    Rebalances the vault by executing a series of instructions.

    -
    §Arguments:
    +
    source

    pub fn rebalance(&self, instructions: &Vec<Instruction>)

    Rebalances the vault by executing a series of instructions.

    +
    §Arguments:
    • e - The environment.
    • instructions - A vector of Instruction structs representing actions (withdraw, invest, swap, zapper) to be taken.
    • @@ -620,11 +718,11 @@
      §Returns:
      • Result<(), ContractError> - Ok if successful, otherwise returns a ContractError.
      -
    source

    pub fn try_rebalance( +

    source

    pub fn try_rebalance( &self, instructions: &Vec<Instruction>, ) -> Result<Result<(), <() as TryFromVal<Env, Val>>::Error>, Result<ContractError, InvokeError>>

    Rebalances the vault by executing a series of instructions.

    -
    §Arguments:
    +
    §Arguments:
    • e - The environment.
    • instructions - A vector of Instruction structs representing actions (withdraw, invest, swap, zapper) to be taken.
    • diff --git a/apps/rust_docs/search.desc/defindex_vault/defindex_vault-desc-0-.js b/apps/rust_docs/search.desc/defindex_vault/defindex_vault-desc-0-.js index 6f60559a..ef38bd57 100644 --- a/apps/rust_docs/search.desc/defindex_vault/defindex_vault-desc-0-.js +++ b/apps/rust_docs/search.desc/defindex_vault/defindex_vault-desc-0-.js @@ -1 +1 @@ -searchState.loadedDescShard("defindex_vault", 0, "DeFindexVaultClient is a client for calling the contract …\nHandles user deposits into the DeFindex Vault.\nExecutes an emergency withdrawal from a specific strategy.\nReturns the current idle funds, representing the total …\nReturns the current invested funds, representing the total …\nReturns the total managed funds of the vault, including …\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nThis function extends the contract’s time-to-live and …\nRetrieves the list of assets managed by the DeFindex Vault.\nRetrieves the current emergency manager address for the …\nRetrieves the current fee receiver address for the vault.\nRetrieves the current manager address for the vault.\nInitializes the DeFindex Vault contract with the required …\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nExecutes the investment of the vault’s idle funds based …\nPauses a strategy to prevent it from being used in the …\nRebalances the vault by executing a series of instructions.\nSets the emergency manager for the vault.\nSets the fee receiver for the vault.\nSets the manager for the vault.\nHandles user deposits into the DeFindex Vault.\nExecutes an emergency withdrawal from a specific strategy.\nReturns the current idle funds, representing the total …\nReturns the current invested funds, representing the total …\nReturns the total managed funds of the vault, including …\nThis function extends the contract’s time-to-live and …\nRetrieves the list of assets managed by the DeFindex Vault.\nRetrieves the current emergency manager address for the …\nRetrieves the current fee receiver address for the vault.\nRetrieves the current manager address for the vault.\nInitializes the DeFindex Vault contract with the required …\nExecutes the investment of the vault’s idle funds based …\nPauses a strategy to prevent it from being used in the …\nRebalances the vault by executing a series of instructions.\nSets the emergency manager for the vault.\nSets the fee receiver for the vault.\nSets the manager for the vault.\nUnpauses a previously paused strategy.\nWithdraws assets from the DeFindex Vault by burning Vault …\nHandles user deposits into the DeFindex Vault.\nExecutes an emergency withdrawal from a specific strategy.\nReturns the current idle funds, representing the total …\nReturns the current invested funds, representing the total …\nReturns the total managed funds of the vault, including …\nThis function extends the contract’s time-to-live and …\nRetrieves the list of assets managed by the DeFindex Vault.\nRetrieves the current emergency manager address for the …\nRetrieves the current fee receiver address for the vault.\nRetrieves the current manager address for the vault.\nInitializes the DeFindex Vault contract with the required …\nExecutes the investment of the vault’s idle funds based …\nPauses a strategy to prevent it from being used in the …\nRebalances the vault by executing a series of instructions.\nSets the emergency manager for the vault.\nSets the fee receiver for the vault.\nSets the manager for the vault.\nUnpauses a previously paused strategy.\nWithdraws assets from the DeFindex Vault by burning Vault …\nUnpauses a previously paused strategy.\nWithdraws assets from the DeFindex Vault by burning Vault …") \ No newline at end of file +searchState.loadedDescShard("defindex_vault", 0, "DeFindexVaultClient is a client for calling the contract …\nHandles user deposits into the DeFindex Vault and …\nExecutes an emergency withdrawal from a specific strategy.\nReturns the current idle funds, representing the total …\nReturns the current invested funds, representing the total …\nReturns the total managed funds of the vault, including …\nReturns the argument unchanged.\nReturns the argument unchanged.\nReturns the argument unchanged.\nThis function extends the contract’s time-to-live and …\nRetrieves the list of assets managed by the DeFindex Vault.\nRetrieves the current emergency manager address for the …\nRetrieves the current fee receiver address for the vault.\nRetrieves the current manager address for the vault.\nInitializes the DeFindex Vault contract with the required …\nCalls U::from(self).\nCalls U::from(self).\nCalls U::from(self).\nExecutes the investment of the vault’s idle funds based …\nPauses a strategy to prevent it from being used in the …\nRebalances the vault by executing a series of instructions.\nSets the emergency manager for the vault.\nSets the fee receiver for the vault.\nSets the manager for the vault.\nHandles user deposits into the DeFindex Vault and …\nExecutes an emergency withdrawal from a specific strategy.\nReturns the current idle funds, representing the total …\nReturns the current invested funds, representing the total …\nReturns the total managed funds of the vault, including …\nThis function extends the contract’s time-to-live and …\nRetrieves the list of assets managed by the DeFindex Vault.\nRetrieves the current emergency manager address for the …\nRetrieves the current fee receiver address for the vault.\nRetrieves the current manager address for the vault.\nInitializes the DeFindex Vault contract with the required …\nExecutes the investment of the vault’s idle funds based …\nPauses a strategy to prevent it from being used in the …\nRebalances the vault by executing a series of instructions.\nSets the emergency manager for the vault.\nSets the fee receiver for the vault.\nSets the manager for the vault.\nUnpauses a previously paused strategy.\nHandles the withdrawal process for a specified number of …\nHandles user deposits into the DeFindex Vault and …\nExecutes an emergency withdrawal from a specific strategy.\nReturns the current idle funds, representing the total …\nReturns the current invested funds, representing the total …\nReturns the total managed funds of the vault, including …\nThis function extends the contract’s time-to-live and …\nRetrieves the list of assets managed by the DeFindex Vault.\nRetrieves the current emergency manager address for the …\nRetrieves the current fee receiver address for the vault.\nRetrieves the current manager address for the vault.\nInitializes the DeFindex Vault contract with the required …\nExecutes the investment of the vault’s idle funds based …\nPauses a strategy to prevent it from being used in the …\nRebalances the vault by executing a series of instructions.\nSets the emergency manager for the vault.\nSets the fee receiver for the vault.\nSets the manager for the vault.\nUnpauses a previously paused strategy.\nHandles the withdrawal process for a specified number of …\nUnpauses a previously paused strategy.\nHandles the withdrawal process for a specified number of …") \ No newline at end of file diff --git a/apps/rust_docs/src/defindex_vault/deposit.rs.html b/apps/rust_docs/src/defindex_vault/deposit.rs.html index cfd84a81..aad311dc 100644 --- a/apps/rust_docs/src/defindex_vault/deposit.rs.html +++ b/apps/rust_docs/src/defindex_vault/deposit.rs.html @@ -112,67 +112,22 @@ 112 113 114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155
    use common::models::AssetStrategySet;
    -use soroban_sdk::{panic_with_error, token::TokenClient, Address, Env, Vec};
    +use soroban_sdk::{panic_with_error, token::TokenClient, Address, Env, Vec, Map};
     
     use crate::{
    -    funds::{
    -        fetch_invested_funds_for_asset, fetch_invested_funds_for_strategy,
    -        fetch_total_managed_funds,
    -    },
    -    investment::check_and_execute_investments,
    -    models::{AssetInvestmentAllocation, StrategyAllocation},
         storage::get_assets,
         token::{internal_mint, VaultToken},
         utils::{calculate_deposit_amounts_and_shares_to_mint, check_nonnegative_amount},
         ContractError, MINIMUM_LIQUIDITY,
    +    models::{CurrentAssetInvestmentAllocation},
     };
     
     /// Common logic for processing deposits.
     pub fn process_deposit(
         e: &Env,
         assets: &Vec<AssetStrategySet>,
    +    total_managed_funds: &Map<Address, CurrentAssetInvestmentAllocation>,
         amounts_desired: &Vec<i128>,
         amounts_min: &Vec<i128>,
         from: &Address,
    @@ -190,12 +145,21 @@
     
         let total_supply = VaultToken::total_supply(e.clone());
         let (amounts, shares_to_mint) = if assets_length == 1 {
    -        calculate_single_asset_shares(e, amounts_desired, total_supply)?
    +        calculate_single_asset_shares(
    +            e, 
    +            amounts_desired, 
    +            &total_managed_funds,
    +            total_supply)?
         } else {
             if total_supply == 0 {
                 (amounts_desired.clone(), amounts_desired.iter().sum())
             } else {
    -            calculate_deposit_amounts_and_shares_to_mint(&e, &assets, amounts_desired, amounts_min)?
    +            calculate_deposit_amounts_and_shares_to_mint(
    +                &e, 
    +                &assets, 
    +                &total_managed_funds,
    +                amounts_desired, 
    +                amounts_min)?
             }
         };
     
    @@ -212,7 +176,7 @@
         }
     
         // Mint shares
    -    mint_shares(e, total_supply, shares_to_mint, from.clone())?;
    +    mint_shares(e, &total_supply, shares_to_mint, from.clone())?;
     
         Ok((amounts, shares_to_mint))
     }
    @@ -221,12 +185,12 @@
     fn calculate_single_asset_shares(
         e: &Env,
         amounts_desired: &Vec<i128>,
    +    total_managed_funds: &Map<Address, CurrentAssetInvestmentAllocation>,
         total_supply: i128,
     ) -> Result<(Vec<i128>, i128), ContractError> {
         let shares = if total_supply == 0 {
             amounts_desired.get(0).unwrap()
         } else {
    -        let total_managed_funds = fetch_total_managed_funds(&e);
             VaultToken::total_supply(e.clone())
                 .checked_mul(amounts_desired.get(0).unwrap())
                 .unwrap_or_else(|| panic_with_error!(&e, ContractError::ArithmeticError))
    @@ -243,11 +207,11 @@
     /// Mint vault shares.
     fn mint_shares(
         e: &Env,
    -    total_supply: i128,
    +    total_supply: &i128,
         shares_to_mint: i128,
         from: Address,
     ) -> Result<(), ContractError> {
    -    if total_supply == 0 {
    +    if *total_supply == 0 {
             if shares_to_mint < MINIMUM_LIQUIDITY {
                 panic_with_error!(&e, ContractError::InsufficientAmount);
             }
    @@ -261,51 +225,4 @@
             internal_mint(e.clone(), from, shares_to_mint);
         }
         Ok(())
    -}
    -
    -/// Generate investment allocations and execute them.
    -pub fn generate_and_execute_investments(
    -    e: &Env,
    -    amounts: &Vec<i128>,
    -    assets: &Vec<AssetStrategySet>,
    -) -> Result<(), ContractError> {
    -    let mut asset_investments = Vec::new(&e);
    -
    -    for (i, amount) in amounts.iter().enumerate() {
    -        let asset = assets.get(i as u32).unwrap();
    -        let (asset_invested_funds, _) = fetch_invested_funds_for_asset(&e, &asset);
    -
    -        let mut strategy_allocations = Vec::new(&e);
    -        let mut remaining_amount = amount;
    -
    -        for (j, strategy) in asset.strategies.iter().enumerate() {
    -            let strategy_invested_funds = fetch_invested_funds_for_strategy(&e, &strategy.address);
    -
    -            let mut invest_amount = if asset_invested_funds > 0 {
    -                (amount * strategy_invested_funds) / asset_invested_funds
    -            } else {
    -                0
    -            };
    -
    -            if j == asset.strategies.len() as usize - 1 {
    -                invest_amount = remaining_amount;
    -            }
    -
    -            remaining_amount -= invest_amount;
    -
    -            strategy_allocations.push_back(Some(StrategyAllocation {
    -                strategy_address: strategy.address.clone(),
    -                amount: invest_amount,
    -            }));
    -        }
    -
    -        asset_investments.push_back(Some(AssetInvestmentAllocation {
    -            asset: asset.address.clone(),
    -            strategy_allocations,
    -        }));
    -    }
    -
    -    check_and_execute_investments(e.clone(), assets.clone(), asset_investments)?;
    -    Ok(())
    -}
    -
    \ No newline at end of file +} \ No newline at end of file diff --git a/apps/rust_docs/src/defindex_vault/funds.rs.html b/apps/rust_docs/src/defindex_vault/funds.rs.html index ca92d951..374bdcba 100644 --- a/apps/rust_docs/src/defindex_vault/funds.rs.html +++ b/apps/rust_docs/src/defindex_vault/funds.rs.html @@ -117,6 +117,31 @@ 117 118 119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144
    use soroban_sdk::token::TokenClient;
     use soroban_sdk::{Address, Env, Map, Vec};
     
    @@ -125,40 +150,58 @@
     use crate::storage::get_assets;
     use crate::strategies::get_strategy_client;
     
    -/// Fetches the idle funds for a given asset. Idle funds refer to the balance of the asset
    -/// that is currently not invested in any strategies.
    +/// Retrieves the idle funds for a given asset.
    +/// 
    +/// Idle funds represent the balance of the asset that is held by the current contract 
    +/// but not actively allocated to any strategies.
     ///
     /// # Arguments
     /// * `e` - The current environment instance.
    -/// * `asset` - The asset for which idle funds are being fetched.
    +/// * `asset_address` - The address of the asset for which idle funds are being calculated.
     ///
     /// # Returns
    -/// * The idle balance (i128) of the asset in the current contract address.
    +/// The idle funds of the asset as an `i128`, representing the unallocated balance.
     pub fn fetch_idle_funds_for_asset(e: &Env, asset: &Address) -> i128 {
         TokenClient::new(e, &asset).balance(&e.current_contract_address())
     }
     
    -/// Fetches the total funds that are invested for a given asset.
    -/// It iterates through all the strategies associated with the asset and sums their balances.
    +/// Retrieves the total funds invested in a specified strategy.
    +/// 
    +/// Since only the strategy contract itself can accurately determine the amount invested, 
    +/// this function performs a cross-contract call to the strategy to fetch the current balance 
    +/// of the investment.
     ///
     /// # Arguments
     /// * `e` - The current environment instance.
    -/// * `asset` - The asset for which invested funds are being fetched.
    +/// * `strategy_address` - The address of the strategy whose investment balance is to be retrieved.
     ///
     /// # Returns
    -/// * The total invested balance (i128) of the asset across all strategies.
    -pub fn fetch_invested_funds_for_strategy(e: &Env, strategy_address: &Address) -> i128 {
    +/// The total invested funds in the strategy as an `i128`.
    +pub fn fetch_strategy_invested_funds(e: &Env, strategy_address: &Address) -> i128 {
         let strategy_client = get_strategy_client(e, strategy_address.clone());
         strategy_client.balance(&e.current_contract_address())
     }
     
     
    -// return total invested funds but also a vec of StrategyAllocation
    -pub fn fetch_invested_funds_for_asset(e: &Env, asset: &AssetStrategySet) -> (i128, Vec<StrategyAllocation>){
    +/// Calculates the total funds invested in strategies for a given asset and 
    +/// provides a detailed breakdown of allocations.
    +///
    +/// This function aggregates the balances of all strategies linked to the specified 
    +/// asset and returns both the total invested amount and a detailed allocation.
    +///
    +/// # Arguments
    +/// * `e` - The current environment instance.
    +/// * `asset_strategy_set` - The asset and its associated set of strategies to evaluate.
    +///
    +/// # Returns
    +/// A tuple containing:
    +/// * `i128`: The total funds invested across all strategies.
    +/// * `Vec<StrategyAllocation>`: A vector with the allocation details for each strategy.
    +pub fn fetch_invested_funds_for_asset(e: &Env, asset_strategy_set: &AssetStrategySet) -> (i128, Vec<StrategyAllocation>){
         let mut invested_funds = 0;
         let mut strategy_allocations: Vec<StrategyAllocation> = Vec::new(e);
    -    for strategy in asset.strategies.iter() {
    -        let strategy_balance = fetch_invested_funds_for_strategy(e, &strategy.address);
    +    for strategy in asset_strategy_set.strategies.iter() {
    +        let strategy_balance = fetch_strategy_invested_funds(e, &strategy.address);
             invested_funds += strategy_balance;
             strategy_allocations.push_back(StrategyAllocation {
                 strategy_address: strategy.address.clone(),
    @@ -166,10 +209,47 @@
             });
         }
         (invested_funds, strategy_allocations)
    +} 
    +
    +/// Fetches the total managed funds for all assets. This includes both idle and invested funds.
    +/// It returns a map where the key is the asset's address and the value is the total managed balance
    +/// (idle + invested). With this map we can calculate the current managed funds ratio.
    +///
    +/// # Arguments
    +/// * `e` - The current environment instance.
    +///
    +/// # Returns
    +/// * A map where each entry represents an asset's address and its total managed balance.
    +pub fn fetch_total_managed_funds(e: &Env) -> Map<Address, CurrentAssetInvestmentAllocation> {
    +    let assets = get_assets(e);
    +    let mut map: Map<Address, CurrentAssetInvestmentAllocation> = Map::new(e);
    +    for asset in assets {
    +        let idle_amount = fetch_idle_funds_for_asset(e, &asset.address);
    +        let (invested_amount, strategy_allocations) = fetch_invested_funds_for_asset(e, &asset);
    +        let total_amount = idle_amount + invested_amount;
    +        map.set(
    +            asset.address.clone(),
    +            CurrentAssetInvestmentAllocation {
    +                asset: asset.address.clone(),
    +                total_amount,
    +                idle_amount,
    +                invested_amount,
    +                strategy_allocations,
    +            },
    +        );
    +    }
    +    map
     }
     
    +/*
    +    User experience functions. The following functions are not being used inernally in 
    +    the contrac, but are intender to be used by the client for a better user experience.
    +    They create maps for better redeability
    +
    +*/
    +
     
    -/// Fetches the current idle funds for all assets managed by the contract.
    +/// Fetches the current idle funds for all assets managed by the contract.
     /// It returns a map where the key is the asset's address and the value is the idle balance.
     ///
     /// # Arguments
    @@ -206,34 +286,4 @@
         }
         map
     }
    -
    -/// Fetches the total managed funds for all assets. This includes both idle and invested funds.
    -/// It returns a map where the key is the asset's address and the value is the total managed balance
    -/// (idle + invested). With this map we can calculate the current managed funds ratio.
    -///
    -/// # Arguments
    -/// * `e` - The current environment instance.
    -///
    -/// # Returns
    -/// * A map where each entry represents an asset's address and its total managed balance.
    -pub fn fetch_total_managed_funds(e: &Env) -> Map<Address, CurrentAssetInvestmentAllocation> {
    -    let assets = get_assets(e);
    -    let mut map: Map<Address, CurrentAssetInvestmentAllocation> = Map::new(e);
    -    for asset in assets {
    -        let idle_amount = fetch_idle_funds_for_asset(e, &asset.address);
    -        let (invested_amount, strategy_allocations) = fetch_invested_funds_for_asset(e, &asset);
    -        let total_amount = idle_amount + invested_amount;
    -        map.set(
    -            asset.address.clone(),
    -            CurrentAssetInvestmentAllocation {
    -                asset: asset.address.clone(),
    -                total_amount,
    -                idle_amount,
    -                invested_amount,
    -                strategy_allocations,
    -            },
    -        );
    -    }
    -    map
    -}
     
    \ No newline at end of file diff --git a/apps/rust_docs/src/defindex_vault/interface.rs.html b/apps/rust_docs/src/defindex_vault/interface.rs.html index 4a7d8897..bdfdaeae 100644 --- a/apps/rust_docs/src/defindex_vault/interface.rs.html +++ b/apps/rust_docs/src/defindex_vault/interface.rs.html @@ -326,10 +326,6 @@ 326 327 328 -329 -330 -331 -332
    use soroban_sdk::{Address, Env, Map, String, Vec};
     
     use crate::{
    @@ -543,11 +539,7 @@
         /// * `Map<Address, i128>` - A map containing each asset address and its corresponding proportional amount.
         fn get_asset_amounts_per_shares(e: Env, vault_shares: i128) -> Result<Map<Address, i128>, ContractError>;
         
    -    // TODO: DELETE THIS, USED FOR TESTING
    -    /// Temporary method for testing purposes.
    -    // fn get_asset_amounts_for_dftokens(e: Env, df_token: i128) -> Map<Address, i128>;
    -
    -    fn get_fees(e: Env) -> (u32, u32);
    +    fn get_fees(e: Env) -> (u32, u32);
     
         /// Collects the fees from the vault and transfers them to the fee receiver addresses. 
         fn collect_fees(e: Env) -> Result<(), ContractError>;
    diff --git a/apps/rust_docs/src/defindex_vault/investment.rs.html b/apps/rust_docs/src/defindex_vault/investment.rs.html
    index 51af785f..159e8c0e 100644
    --- a/apps/rust_docs/src/defindex_vault/investment.rs.html
    +++ b/apps/rust_docs/src/defindex_vault/investment.rs.html
    @@ -95,49 +95,161 @@
     95
     96
     97
    -
    use soroban_sdk::{Env, Vec, panic_with_error};
    +98
    +99
    +100
    +101
    +102
    +103
    +104
    +105
    +106
    +107
    +108
    +109
    +110
    +111
    +112
    +113
    +114
    +115
    +116
    +117
    +118
    +119
    +120
    +121
    +122
    +123
    +124
    +125
    +126
    +127
    +128
    +129
    +130
    +131
    +132
    +133
    +134
    +135
    +136
    +137
    +138
    +139
    +140
    +141
    +142
    +143
    +144
    +145
    +146
    +147
    +148
    +149
    +150
    +151
    +152
    +153
    +154
    +155
    +156
    +157
    +158
    +159
    +160
    +161
    +162
    +163
    +164
    +165
    +166
    +167
    +168
    +169
    +170
    +171
    +172
    +173
    +174
    +175
    +176
    +177
    +178
    +179
    +180
    +181
    +182
    +183
    +184
    +185
    +186
    +187
    +188
    +189
    +190
    +191
    +192
    +193
    +194
    +195
    +196
    +197
    +198
    +199
    +200
    +201
    +
    use soroban_sdk::{Env, Vec, panic_with_error, Map, Address};
     
     use crate::{
    -    models::AssetInvestmentAllocation,
    +    models::{AssetInvestmentAllocation, StrategyAllocation, CurrentAssetInvestmentAllocation},
         strategies::invest_in_strategy,
         utils::{check_nonnegative_amount},
         ContractError,
     };
     use common::models::AssetStrategySet;
     
    -/// Checks and executes the investments for each asset based on provided allocations.
    -/// The function iterates through the specified assets and asset investments to ensure validity 
    -/// and executes investments accordingly.
    +/// Executes investment allocations for a set of assets based on the provided investment strategies.
    +/// 
    +/// This function ensures that the specified assets and strategies match the contract's known configuration, 
    +/// then validates and processes the investment allocations for each asset and its strategies. It assumes 
    +/// that the caller is responsible for ensuring the correctness of investment ratios and does not check the 
    +/// current state of the strategies or existing investments.
     ///
     /// # Arguments
     /// * `e` - The current environment reference.
    -/// * `assets` - A vector of `AssetStrategySet` that holds information about assets and their associated strategies.
    +/// * `assets` - A vector of `AssetStrategySet` representing the assets and their associated strategies 
    +///   managed by this vault.
     /// * `asset_investments` - A vector of optional investment allocations for each asset.
    -///s
    +///
     /// # Returns
    -/// * `Result<(), ContractError>` - Returns `Ok(())` if all investments are successful or an appropriate `ContractError` if any issue is encountered.
    +/// * `Result<(), ContractError>` - Returns `Ok(())` if all investments are successful, or an appropriate 
    +///   `ContractError` if validation or execution fails.
     ///
    -/// # Function Flow
    -/// 1. **Iterate Over Asset Investments**: Loops through each asset investment allocation.
    +/// # Function Details
    +/// 1. **Iterates Over Asset Investments**: Loops through each asset's investment allocation, processing only 
    +///    defined allocations.
     /// 2. **Validation**:
    -///    - **Asset Address Check**: Ensures that the asset's address matches the expected address in the allocation.
    -///    - **Strategy Length Check**: Verifies that the number of strategies matches between the asset and the corresponding allocation.
    -///    - **Note**: The total intended investment check has been removed as the subsequent operations inherently perform the same validation.
    -/// 3. **Process Strategy Investments**:
    -///    - For each strategy within an asset:
    -///      - **Non-Negative Amount Check**: Validates that the investment amount is non-negative.
    -///      - **Strategy Active Check**: Ensures that the strategy is not paused before proceeding with the investment.
    -///      - **Execute Investment**: Calls the `invest_in_strategy` fuction if all checks pass.
    +///    - Confirms that the asset's address matches the expected address in the allocation.
    +///    - Checks that the number of strategies in the asset matches the provided allocation.
    +/// 3. **Processes Strategy Investments**:
    +///    - Ensures that investment amounts are non-negative.
    +///    - Verifies that strategies are active before investing.
    +///    - Executes the investment for valid allocations by calling `invest_in_strategy`.
     ///
     /// # Errors
    -/// * Returns `ContractError::WrongAssetAddress` if an asset's address does not match the expected address.
    -/// * Returns `ContractError::WrongStrategiesLength` if the number of strategies in the asset and allocation do not match.
    -/// * Returns `ContractError::StrategyPaused` if an investment targets a paused strategy.
    +/// * `ContractError::WrongAssetAddress` - If the asset's address does not match the allocation.
    +/// * `ContractError::WrongStrategiesLength` - If the number of strategies in the asset and allocation do not match.
    +/// * `ContractError::StrategyPaused` - If an allocation targets a paused strategy.
     ///
    +/// # Notes
    +/// - The function relies on the assets being ordered consistently with the investment allocations.
    +/// - It allows the caller to update investment ratios freely, without verifying the current state of investments 
    +///   or strategies.
     pub fn check_and_execute_investments(
    -    e: Env, 
    -    assets: Vec<AssetStrategySet>,
    -    asset_investments: Vec<Option<AssetInvestmentAllocation>>
    +    e: &Env, 
    +    assets: &Vec<AssetStrategySet>,
    +    asset_investments: &Vec<Option<AssetInvestmentAllocation>>
     ) -> Result<(), ContractError> {
     
         // Iterate over each asset investment allocation
    @@ -155,20 +267,7 @@
                     panic_with_error!(&e, ContractError::WrongStrategiesLength);
                 }
     
    -            // NOTE: We can avoid this check as it if total idle funds exceed funds to invest, this will fail
    -            // when trying to transfer
    -
    -            // // Calculate total intended investment for this asset
    -            // let total_asset_investment: i128 = asset_investment.investments.iter()
    -            //     .filter_map(|strategy| strategy.as_ref().map(|s| s.amount.unwrap_or(0)))
    -            //     .sum();
    -
    -            // // Verify total intended investment does not exceed idle funds for this asset
    -            // if total_asset_investment > fetch_idle_funds_for_asset(&e, &asset_investment.asset) {
    -            //     panic_with_error!(&e, ContractError::InsufficientIdleFunds);
    -            // }
    -
    -            // Process each defined strategy investment for the current asset
    +            // Process each defined strategy investment for the current asset
                 for (j, strategy_investment_opt) in asset_investment.strategy_allocations.iter().enumerate() {
                     if let Some(strategy_investment) = strategy_investment_opt {
                         // Validate amount is non-negative
    @@ -191,4 +290,114 @@
             }
         }
         Ok(())
    -}
    \ No newline at end of file +} + +/// Generates investment allocations for a set of assets and their associated strategies. +/// +/// This function calculates the distribution of funds across strategies for each asset based +/// on the current state of strategy investments. The allocations are returned as a vector, +/// where each entry corresponds to an asset's investment allocation or `None` if no allocation +/// is required. +/// +/// # Arguments +/// - `e` - Reference to the current environment. +/// - `assets` - A vector of `AssetStrategySet` objects representing the assets and their strategies. +/// - `total_managed_funds` - A map containing the current allocation of funds across all strategies for each asset. +/// - `amounts` - A vector of amounts representing the funds to be allocated for each asset. +/// +/// # Returns +/// - `Ok(Vec<Option<AssetInvestmentAllocation>>)` - A vector of investment allocations where each entry +/// represents an asset's strategy allocations. If an asset does not require allocation, its entry is `None`. +/// - `Err(ContractError)` - If any errors occur during the allocation process, such as invalid data or calculations. +/// +/// # Function Flow +/// 1. **Iterate Over Assets**: For each asset in the provided list: +/// - Skip assets with zero amounts or no prior investments. +/// - Calculate the allocation of funds across strategies proportionally based on the current state. +/// 2. **Proportional Distribution**: +/// - For each strategy within an asset, determine the proportional investment based on its existing allocation. +/// - Ensure that all amounts are correctly calculated without overflows or division errors. +/// 3. **Prepare Allocation**: +/// - Append the calculated strategy allocations to the resulting vector. +/// - Include `None` for assets with no required allocations. +/// 4. **Return Results**: Return the vector containing the investment allocations. +/// +/// # Notes +/// - This function does not execute the investments; it only prepares the allocations. +/// - It assumes that the provided `total_managed_funds` contains valid and complete data. + +pub fn generate_investment_allocations( + e: &Env, + assets: &Vec<AssetStrategySet>, + total_managed_funds: &Map<Address, CurrentAssetInvestmentAllocation>, + amounts: &Vec<i128>, +) -> Result<Vec<Option<AssetInvestmentAllocation>>, ContractError> { + let mut asset_investments = Vec::new(&e); + + for (i, amount) in amounts.iter().enumerate() { + let asset = assets.get(i as u32).unwrap(); + + let current_asset_allocation = total_managed_funds.get(asset.address.clone()).unwrap(); + let asset_invested_funds = current_asset_allocation.invested_amount; + + // We only consider assets that have a non zero allocation + // if the amount already invested in the asset is 0, + // this means that there is no previous investment in the asset, so we can just + // invest, and we need to wait for the manager to execute a manual investment of the idle assets + // on the strategies. + if amount >0 && asset_invested_funds >0 { + // here the asset will be distributed amont the different strategies considering the current raio + // of investment in each strategy. + let mut strategy_allocations = Vec::new(&e); + let mut remaining_amount = amount; + + for (j, strategy) in asset.strategies.iter().enumerate() { + // Determine the investment amount for the strategy + let invest_amount = if j == asset.strategies.len() as usize - 1 { + remaining_amount + } else { + let strategy_invested_funds = current_asset_allocation + .strategy_allocations + .get(j as u32) + .unwrap() + .amount; + + amount + .checked_mul(strategy_invested_funds) + .and_then(|v| v.checked_div(asset_invested_funds)) + .unwrap() + }; + + // Update the remaining amount + remaining_amount -= invest_amount; + + // Add the strategy allocation + strategy_allocations.push_back( + if invest_amount > 0 { + Some(StrategyAllocation { + strategy_address: strategy.address.clone(), + amount: invest_amount, + }) + } else { + None + }, + ); + } + + // Add the asset investment allocation + asset_investments.push_back(Some(AssetInvestmentAllocation { + asset: asset.address.clone(), + strategy_allocations, + })); + + + + } + else { + asset_investments.push_back(None); // No investments to be executed for this asset + } + + } + Ok(asset_investments) +} + \ No newline at end of file diff --git a/apps/rust_docs/src/defindex_vault/lib.rs.html b/apps/rust_docs/src/defindex_vault/lib.rs.html index 201be0d2..ecdf9d65 100644 --- a/apps/rust_docs/src/defindex_vault/lib.rs.html +++ b/apps/rust_docs/src/defindex_vault/lib.rs.html @@ -748,11 +748,61 @@ 748 749 750 +751 +752 +753 +754 +755 +756 +757 +758 +759 +760 +761 +762 +763 +764 +765 +766 +767 +768 +769 +770 +771 +772 +773 +774 +775 +776 +777 +778 +779 +780 +781 +782 +783 +784 +785 +786 +787 +788 +789 +790 +791 +792 +793 +794 +795 +796 +797 +798 +799 +800 +801
    #![no_std]
    -use deposit::{generate_and_execute_investments, process_deposit};
    -use soroban_sdk::{
    +use soroban_sdk::{
         contract, contractimpl, panic_with_error,
    -    token::{TokenClient, TokenInterface},
    +    token::{TokenClient},
         Address, Env, Map, String, Vec,
     };
     use soroban_token_sdk::metadata::TokenMetadata;
    @@ -776,10 +826,11 @@
     
     use access::{AccessControl, AccessControlTrait, RolesDataKey};
     use aggregator::{internal_swap_exact_tokens_for_tokens, internal_swap_tokens_for_exact_tokens};
    +use deposit::{process_deposit};
     use fee::{collect_fees, fetch_defindex_fee};
     use funds::{fetch_current_idle_funds, fetch_current_invested_funds, fetch_total_managed_funds}; 
     use interface::{AdminInterfaceTrait, VaultManagementTrait, VaultTrait};
    -use investment::check_and_execute_investments;
    +use investment::{check_and_execute_investments, generate_investment_allocations};
     use models::{
         Instruction, OptionalSwapDetailsExactIn,
         OptionalSwapDetailsExactOut, CurrentAssetInvestmentAllocation,
    @@ -792,9 +843,9 @@
     use strategies::{
         get_strategy_asset, get_strategy_client,
         get_strategy_struct, invest_in_strategy, pause_strategy, unpause_strategy,
    -    withdraw_from_strategy,
    +    unwind_from_strategy,
     };
    -use token::{internal_burn, write_metadata, VaultToken};
    +use token::{internal_burn, write_metadata};
     use utils::{
         calculate_asset_amounts_per_vault_shares,
         check_initialized,
    @@ -915,18 +966,21 @@
             Ok(())
         }
     
    -    /// Handles user deposits into the DeFindex Vault.
    +    /// Handles user deposits into the DeFindex Vault and optionally allocates investments automatically.
         ///
         /// This function processes a deposit by transferring each specified asset amount from the user's address to
         /// the vault, allocating assets according to the vault's defined strategy ratios, and minting vault shares that
    -    /// represent the user's proportional share in the vault. The `amounts_desired` and `amounts_min` vectors should
    -    /// align with the vault's asset order to ensure correct allocation.
    +    /// represent the user's proportional share in the vault. Additionally, if the `invest` parameter is set to `true`,
    +    /// the function will immediately generate and execute investment allocations based on the vault's strategy configuration.
         ///
         /// # Parameters
         /// * `e` - The current environment reference (`Env`), for access to the contract state and utilities.
         /// * `amounts_desired` - A vector specifying the user's intended deposit amounts for each asset.
         /// * `amounts_min` - A vector of minimum deposit amounts required for the transaction to proceed.
         /// * `from` - The address of the user making the deposit.
    +    /// * `invest` - A boolean flag indicating whether to immediately invest the deposited funds into the vault's strategies:
    +    ///     - `true`: Generate and execute investments after the deposit.
    +    ///     - `false`: Leave the deposited funds as idle assets in the vault.
         ///
         /// # Returns
         /// * `Result<(Vec<i128>, i128), ContractError>` - Returns the actual deposited `amounts` and `shares_to_mint` if successful,
    @@ -937,14 +991,21 @@
         /// 2. **Validation**: Checks that the lengths of `amounts_desired` and `amounts_min` match the vault's assets.
         /// 3. **Share Calculation**: Calculates `shares_to_mint` based on the vault's total managed funds and the deposit amount.
         /// 4. **Asset Transfer**: Transfers each specified amount from the user’s address to the vault as idle funds.
    -    /// 5. **Vault shares Minting**: Mints vault shares for the user to represent their ownership in the vault.
    +    /// 5. **Vault Shares Minting**: Mints vault shares for the user to represent their ownership in the vault.
    +    /// 6. **Investment Execution**: If `invest` is `true`, generates and executes the investment allocations for the deposited funds.
    +    ///     - Allocates funds across strategies proportionally to their current state.
    +    ///     - Executes the investment to transition idle funds into the vault's strategies.
         ///
         /// # Notes
         /// - For the first deposit, if the vault has only one asset, shares are calculated directly based on the deposit amount.
         /// - For multiple assets, the function delegates to `calculate_deposit_amounts_and_shares_to_mint`
         ///   for precise share computation.
         /// - An event is emitted to log the deposit, including the actual deposited amounts and minted shares.
    +    /// - If `invest` is `false`, deposited funds remain idle, allowing for manual investment at a later time.
         ///
    +    /// # Errors
    +    /// - Returns a `ContractError` if any validation or execution step fails.
    +
         fn deposit(
             e: Env,
             amounts_desired: Vec<i128>,
    @@ -960,33 +1021,68 @@
             // If this was not done before, last_fee_assesment will set to be current timestamp and this will return without action
             collect_fees(&e)?;
     
    +        let total_managed_funds = fetch_total_managed_funds(&e);
    +
             let assets = get_assets(&e);
     
             let (amounts, shares_to_mint) =
    -            process_deposit(&e, &assets, &amounts_desired, &amounts_min, &from)?;
    +            process_deposit(
    +                &e, 
    +                &assets, 
    +                &total_managed_funds,
    +                &amounts_desired, 
    +                &amounts_min, 
    +                &from)?;
             events::emit_deposit_event(&e, from, amounts.clone(), shares_to_mint.clone());
     
             if invest {
    -            // Generate investment allocations and execute them
    -            generate_and_execute_investments(&e, &amounts, &assets)?;
    +            let asset_investments = generate_investment_allocations(
    +                &e,
    +                &assets,
    +                &total_managed_funds,
    +                &amounts,
    +            )?;
    +            check_and_execute_investments(&e, &assets, &asset_investments)?;
             }
    -
             Ok((amounts, shares_to_mint))
    +
         }
     
    -    /// Withdraws assets from the DeFindex Vault by burning Vault Share tokens.
    +    /// Handles the withdrawal process for a specified number of vault shares.
         ///
    -    /// This function calculates the amount of assets to withdraw based on the number of Vault Share tokens being burned,
    -    /// then transfers the appropriate assets back to the user, pulling from both idle funds and strategies
    -    /// as needed.
    +    /// This function performs the following steps:
    +    /// 1. Validates the environment and the inputs:
    +    ///    - Ensures the contract is initialized.
    +    ///    - Checks that the withdrawal amount (`withdraw_shares`) is non-negative.
    +    ///    - Verifies the authorization of the `from` address.
    +    /// 2. Collects applicable fees.
    +    /// 3. Calculates the proportionate withdrawal amounts for each asset based on the number of shares.
    +    /// 4. Burns the specified shares from the user's account.
    +    /// 5. Processes the withdrawal for each asset:
    +    ///    - First attempts to cover the withdrawal amount using idle funds.
    +    ///    - If idle funds are insufficient, unwinds investments from the associated strategies
    +    ///      to cover the remaining amount, accounting for rounding errors in the last strategy.
    +    /// 6. Transfers the withdrawn funds to the user's address (`from`).
    +    /// 7. Emits an event to record the withdrawal details.
         ///
    -    /// # Arguments:
    -    /// * `e` - The environment.
    -    /// * `shares_amount` - The amount of Vault Share tokens to burn for the withdrawal.
    -    /// * `from` - The address of the user requesting the withdrawal.
    +    /// ## Parameters:
    +    /// - `e`: The contract environment (`Env`).
    +    /// - `withdraw_shares`: The number of vault shares to withdraw.
    +    /// - `from`: The address initiating the withdrawal.
         ///
    -    /// # Returns:
    -    /// * `Result<(), ContractError>` - Ok if successful, otherwise returns a ContractError.
    +    /// ## Returns:
    +    /// - A `Result` containing a vector of withdrawn amounts for each asset (`Vec<i128>`),
    +    ///   or a `ContractError` if the withdrawal fails.
    +    ///
    +    /// ## Errors:
    +    /// - `ContractError::AmountOverTotalSupply`: If the specified shares exceed the total supply.
    +    /// - `ContractError::ArithmeticError`: If any arithmetic operation fails during calculations.
    +    /// - `ContractError::WrongAmountsLength`: If there is a mismatch in asset allocation data.
    +    ///
    +    /// ## TODOs:
    +    /// - Implement minimum amounts for withdrawals to ensure compliance with potential restrictions.
    +    /// - Replace the returned vector with the original `asset_withdrawal_amounts` map for better structure.
    +    /// - avoid the usage of a Map, choose between using map or vector
         fn withdraw(
             e: Env,
             withdraw_shares: i128,
    @@ -1010,69 +1106,72 @@
             )?;
         
             // Burn the shares after calculating the withdrawal amounts
    -        // this will panic with error if the user does not have enough balance
    +        // This will panic with error if the user does not have enough balance
             internal_burn(e.clone(), from.clone(), withdraw_shares);
         
    -        // Loop through each asset to handle the withdrawal
    +        let assets = get_assets(&e); // Use assets for iteration order
    +        // Loop through each asset to handle the withdrawal
             let mut withdrawn_amounts: Vec<i128> = Vec::new(&e);
    -        for (asset_address, requested_withdrawal_amount) in asset_withdrawal_amounts.iter() {
    -
    -            let asset_allocation = total_managed_funds
    -            .get(asset_address.clone())
    -            .unwrap_or_else(|| panic_with_error!(&e, ContractError::WrongAmountsLength));
     
    -            // Check idle funds for this asset
    -            let idle_funds = asset_allocation.idle_amount;
    -        
    -            // Withdraw from idle funds first
    -            if idle_funds >= requested_withdrawal_amount {
    -                // Idle funds cover the full amount
    -                TokenClient::new(&e, &asset_address).transfer(
    -                    &e.current_contract_address(),
    -                    &from,
    -                    &requested_withdrawal_amount,
    -                );
    -                withdrawn_amounts.push_back(requested_withdrawal_amount);
    -                continue;
    -            } else {
    -                let mut total_withdrawn_for_asset = 0;
    -                // Partial withdrawal from idle funds
    -                total_withdrawn_for_asset += idle_funds;
    -                let remaining_withdrawal_amount = requested_withdrawal_amount - idle_funds;
    -                
    -                // Withdraw the remaining amount from strategies
    -                let total_invested_amount = asset_allocation.invested_amount;
    -                
    -                for strategy_allocation in asset_allocation.strategy_allocations.iter() {
    -                    // TODO: If strategy is paused, should we skip it? Otherwise, the calculation will go wrong.
    -                    // if strategy.paused {
    -                    //     continue;
    -                    // }
    -                    
    -                    // Amount to unwind from strategy
    -                    let strategy_withdrawal_share =
    -                    (remaining_withdrawal_amount * strategy_allocation.amount) / total_invested_amount;
    -                    
    -                    if strategy_withdrawal_share > 0 {
    -                        withdraw_from_strategy(&e, &strategy_allocation.strategy_address, &strategy_withdrawal_share)?;
    -                        TokenClient::new(&e, &asset_address).transfer(
    -                            &e.current_contract_address(),
    -                            &from,
    -                            &strategy_withdrawal_share,
    -                        );
    -                        total_withdrawn_for_asset += strategy_withdrawal_share;
    +        for asset in assets.iter() { // Use assets instead of asset_withdrawal_amounts
    +            let asset_address = &asset.address;
    +
    +            if let Some(requested_withdrawal_amount) = asset_withdrawal_amounts.get(asset_address.clone()) {
    +                let asset_allocation = total_managed_funds
    +                    .get(asset_address.clone())
    +                    .unwrap_or_else(|| panic_with_error!(&e, ContractError::WrongAmountsLength));
    +
    +                let idle_funds = asset_allocation.idle_amount;
    +
    +                if idle_funds >= requested_withdrawal_amount {
    +                    TokenClient::new(&e, asset_address).transfer(
    +                        &e.current_contract_address(),
    +                        &from,
    +                        &requested_withdrawal_amount,
    +                    );
    +                    withdrawn_amounts.push_back(requested_withdrawal_amount);
    +                } else {
    +                    let mut cumulative_amount_for_asset = idle_funds;
    +                    let remaining_amount_to_unwind = requested_withdrawal_amount
    +                        .checked_sub(idle_funds)
    +                        .unwrap();
    +
    +                    let total_invested_amount = asset_allocation.invested_amount;
    +
    +                    for (i, strategy_allocation) in asset_allocation.strategy_allocations.iter().enumerate() {
    +                        let strategy_amount_to_unwind: i128 = if i == (asset_allocation.strategy_allocations.len() as usize) - 1 {
    +                            requested_withdrawal_amount
    +                                .checked_sub(cumulative_amount_for_asset)
    +                                .unwrap()
    +                        } else {
    +                            remaining_amount_to_unwind
    +                                .checked_mul(strategy_allocation.amount)
    +                                .and_then(|result| result.checked_div(total_invested_amount))
    +                                .unwrap_or(0)
    +                        };
    +
    +                        if strategy_amount_to_unwind > 0 {
    +                            unwind_from_strategy(&e, &strategy_allocation.strategy_address, &strategy_amount_to_unwind)?;
    +                            cumulative_amount_for_asset += strategy_amount_to_unwind;
    +                        }
                         }
    +
    +                    TokenClient::new(&e, asset_address).transfer(
    +                        &e.current_contract_address(),
    +                        &from,
    +                        &cumulative_amount_for_asset,
    +                    );
    +                    withdrawn_amounts.push_back(cumulative_amount_for_asset);
                     }
    -                TokenClient::new(&e, &asset_address).transfer(
    -                    &e.current_contract_address(),
    -                    &from,
    -                    &total_withdrawn_for_asset,
    -                );
    -                withdrawn_amounts.push_back(total_withdrawn_for_asset);
    -            }
    +            } else {
    +                withdrawn_amounts.push_back(0); // No withdrawal for this asset
    +            }
             }
    -    
    -        events::emit_withdraw_event(&e, from, withdraw_shares, withdrawn_amounts.clone());
    +
    +        
    +        // TODO: Add minimuim amounts for withdrawn_amounts
    +        // TODO: Return the asset_withdrawal_amounts Map instead of a vec
    +        events::emit_withdraw_event(&e, from, withdraw_shares, withdrawn_amounts.clone());
         
             Ok(withdrawn_amounts)
         }
    @@ -1422,7 +1521,10 @@
             }
     
             // Check and execute investments for each asset allocation
    -        check_and_execute_investments(e, assets, asset_investments)?;
    +        check_and_execute_investments(
    +            &e, 
    +            &assets, 
    +            &asset_investments)?;
     
             Ok(())
         }
    @@ -1450,7 +1552,7 @@
                 match instruction.action {
                     ActionType::Withdraw => match (&instruction.strategy, &instruction.amount) {
                         (Some(strategy_address), Some(amount)) => {
    -                        withdraw_from_strategy(&e, strategy_address, amount)?;
    +                        unwind_from_strategy(&e, strategy_address, amount)?;
                         }
                         _ => return Err(ContractError::MissingInstructionData),
                     },
    diff --git a/apps/rust_docs/src/defindex_vault/strategies.rs.html b/apps/rust_docs/src/defindex_vault/strategies.rs.html
    index 54c1712d..7616e28f 100644
    --- a/apps/rust_docs/src/defindex_vault/strategies.rs.html
    +++ b/apps/rust_docs/src/defindex_vault/strategies.rs.html
    @@ -295,7 +295,7 @@
         Err(ContractError::StrategyNotFound)
     }
     
    -pub fn withdraw_from_strategy(
    +pub fn unwind_from_strategy(
         e: &Env,
         strategy_address: &Address,
         amount: &i128,
    diff --git a/apps/rust_docs/src/defindex_vault/token/contract.rs.html b/apps/rust_docs/src/defindex_vault/token/contract.rs.html
    index a59ef0b6..38bb210e 100644
    --- a/apps/rust_docs/src/defindex_vault/token/contract.rs.html
    +++ b/apps/rust_docs/src/defindex_vault/token/contract.rs.html
    @@ -149,7 +149,6 @@
     149
     150
     151
    -152
     
    //! This contract demonstrates a sample implementation of the Soroban token
     //! interface.
     use crate::token::allowance::{read_allowance, spend_allowance, write_allowance};
    @@ -158,12 +157,11 @@
     use crate::token::total_supply::{decrease_total_supply, increase_total_supply, read_total_supply};
     
     #[cfg(test)]
    -use crate::token::storage_types::{AllowanceDataKey, AllowanceValue, DataKey};
    +use crate::token::storage_types::{AllowanceDataKey};
     use crate::token::storage_types::{INSTANCE_BUMP_AMOUNT, INSTANCE_LIFETIME_THRESHOLD};
     use soroban_sdk::token::{self, Interface as _};
     use soroban_sdk::{contract, contractimpl, Address, Env, String};
     use soroban_token_sdk::TokenUtils;
    -use crate::ContractError;
     
     fn check_nonnegative_amount(amount: i128) {
         if amount < 0 {
    diff --git a/apps/rust_docs/src/defindex_vault/utils.rs.html b/apps/rust_docs/src/defindex_vault/utils.rs.html
    index 32207785..c100e290 100644
    --- a/apps/rust_docs/src/defindex_vault/utils.rs.html
    +++ b/apps/rust_docs/src/defindex_vault/utils.rs.html
    @@ -271,18 +271,11 @@
     271
     272
     273
    -274
    -275
    -276
    -277
     
    use soroban_sdk::{panic_with_error, Address, Env, Map, Vec};
     
     use crate::{
         models::{CurrentAssetInvestmentAllocation},
         access::{AccessControl, AccessControlTrait, RolesDataKey},
    -    funds::{
    -        fetch_total_managed_funds,
    -    },
         token::VaultToken,
         ContractError,
     };
    @@ -332,7 +325,7 @@
     //             continue;
     //         }
     
    -//         let strategy_invested_funds = fetch_invested_funds_for_strategy(e, &strategy.address);
    +//         let strategy_invested_funds = fetch_strategy_invested_funds(e, &strategy.address);
     
     //         let strategy_share_of_withdrawal =
     //             (amount * strategy_invested_funds) / total_invested_in_strategies;
    @@ -504,11 +497,10 @@
     pub fn calculate_deposit_amounts_and_shares_to_mint(
         e: &Env,
         assets: &Vec<AssetStrategySet>,
    +    total_managed_funds: &Map<Address, CurrentAssetInvestmentAllocation>,
         amounts_desired: &Vec<i128>,
         amounts_min: &Vec<i128>,
     ) -> Result<(Vec<i128>, i128), ContractError> {
    -    // Retrieve the total managed funds for each asset as a Map<Address, i128>.
    -    let total_managed_funds = fetch_total_managed_funds(e);
     
         for i in 0..assets.len() {
             // Calculate the optimal amounts and shares to mint for asset `i`.
    
    From 4d5b0e79181fd7598e8c711b972298327faf0bd4 Mon Sep 17 00:00:00 2001
    From: chopan123 
    Date: Tue, 10 Dec 2024 17:04:54 -0300
    Subject: [PATCH 15/17] rebalancer and fees changed
    
    ---
     .../02-contracts/01-vault-contract.md         | 35 ++++++++++++-------
     1 file changed, 23 insertions(+), 12 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 84007931..269b9c1a 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
    @@ -1,6 +1,6 @@
     
     # DeFindex Vault Contract
    -This contract serves as the core of the DeFindex platform, responsible for managing assets, executing strategies, and ensuring proper asset rebalancing. It operates with four primary roles: **Deployer**, **Fee Receiver**, **Manager**, and **Emergency Manager**. Additionally, the contract functions as a token referred to as the *dfToken* that represents the shares of the vault.
    +This contract serves as the core of the DeFindex platform, responsible for managing assets, executing strategies, and ensuring proper asset rebalancing. It operates with four primary roles: **Deployer**, **Fee Receiver**, **Manager**, **rebalancer** and **Emergency Manager**. Additionally, the contract functions as a token referred to as the *dfToken* that represents the shares of the vault.
     
     While anyone can invest in a DeFindex, only the Manager and Emergency Manager have the authority to move funds between strategies or even outside strategies and into the Vault itself (see idle assets and emergency withdrawal).
     
    @@ -14,7 +14,7 @@ Because Strategies are the only one that know exactly the current balance of the
     Or if the Vault executes Strategies at its own name (auth), it should execute a speficic `get_assets_balance` function in the strategy contract to know exactely how many assets it has at a specific moment.
     
     ## Initialization
    -The DeFindex Vault contract is structured with specific roles and strategies for managing assets effectively. The key roles include the **Fee Receiver**, **Manager**, and **Emergency Manager**, each responsible for different tasks in managing the Vault. Additionally, a predefined set of strategies determines how assets will be allocated within the Vault. A management fee is also established at the time of initialization, which can later be adjusted by the Fee Receiver. Further details on fee handling are explained later in the document.
    +The DeFindex Vault contract is structured with specific roles and strategies for managing assets effectively. The key roles include the **Fee Receiver**, **Manager**, **rebalancer** and **Emergency Manager**, each responsible for different tasks in managing the Vault. Additionally, a predefined set of strategies determines how assets will be allocated within the Vault. A performance fee is also established at the time of initialization, which can later be adjusted by the Fee Receiver or the Manager. Further details on fee handling are explained later in the document.
     
     The allocation ratios for these strategies are not set during the deployment but are defined during the first deposit made into the Vault. For example, imagine a scenario where the Vault is set up to allocate 20% of its assets to a USDC lending pool (like Blend), 30% to another USDC lending pool (such as YieldBlox), and 50% to a USDC-XLM liquidity pool on an Automated Market Maker (AMM) platform (like Soroswap). 
     
    @@ -22,6 +22,8 @@ To establish this allocation, the deployer must make a first deposit into the Va
     
     Once the contract is initialized and the first deposit is made, the **Manager** has the authority to adjust the allocation ratios over time. For example, if market conditions change or certain strategies perform better, the Manager can rebalance the allocations between the strategies to optimize performance. However, the Manager is limited to reallocating funds only between the existing strategies. They cannot introduce new strategies, which ensures the safety of user funds by minimizing potential security risks.
     
    +The **rebalancer** can only move funds between strategies and nothing else. This allow to have a keeper or bot constantly checking for the best yields and less risk. One does not want a bot to have the authority to change other roles.
    +
     This restriction on adding new strategies is a deliberate security feature. Allowing new strategies could increase the attack surface, potentially exposing the Vault to more vulnerabilities. By keeping the strategies fixed, the contract provides a stable and secure environment for users’ assets while still allowing flexibility in reallocating funds between existing strategies.
     
     In summary:
    @@ -155,14 +157,14 @@ Fees are charged on a per-strategy basis, meaning each strategy independently ca
     #### Detailed Workflow
     
     1. **Fee Structure Example**:
    -   - Protocol Fee Receiver: 5%
    -   - Vault Fee Receiver: 15%
    +   - Protocol Fee Receiver: 25%
    +   - Vault Fee Receiver: 20%
     
     2. **Execution Example**:
        - A user deposits 100 USDC into a vault with one strategy.
        - The strategy earns 10 USDC in gains.
        - The vault collects 20% of the gains as fees (2 USDC).
    -   - Fees are distributed between the protocol (0.5 USDC) and the manager (1.5 USDC).
    +   - From the fees collected, 25% is going to the Protocol (0.5 USDC), and the rest is going to the Vault Fee Receiver.
        - The total assets of the vault become \(100 + 10 - 2 = 108\) USDC.
     
     #### Strategy Gains Tracking
    @@ -196,13 +198,11 @@ Once gains are tracked, fees can be inspected and/or locked for future distribut
     The locking process is done by the manager calling the `lock_fees()` function.
     
     ```rust
    -fn lock_fees() {
    +fn lock_fees(new_fee_bps: Option) {
         for strategy in strategies {
             if gains_or_losses > 0 {
    -            let protocol_fee = gains_or_losses * protocol_fee_receiver / MAX_BPS;
    -            let vault_fee = gains_or_losses * vault_fee_receiver / MAX_BPS;
    -            lock_fee(strategy.asset, protocol_fee_receiver, protocol_fee);
    -            lock_fee(strategy.asset, vault_fee_receiver, vault_fee);
    +            let total_fee = gains_or_losses * new_fee_bps.unwrap_or(vault_fee_bps) / MAX_BPS;
    +            lock_fee(strategy.asset, total_fee);
                 reset_gains_or_losses(strategy);
             }
         }
    @@ -210,6 +210,17 @@ fn lock_fees() {
     ```
     
     When locking the fees, it is applied the current ratio to all the gains, and then they are reset to 0. If there is not gains, there is no fee to lock, and gains_or_losses can't be reset to 0.
    +Also, this is run everytime a `withdraw` call occurs.
    +
    +If, for some reason the yield generated by a strategy is too little, we can call the function `release_fee()` to make some of the fees go to the gain_or_losses.
    +**Pseudocode for release_fees**
    +```rust
    +fn release_fees(strategy: Address, amount: i128) {
    +    release_fee(strategy.asset, amount)
    +    let previous_gains_or_losses = get_gains_or_losses(strategy);
    +    store_gains_or_losses(strategy, current_gains_or_losses + amount);
    +}
    +```
     
     Then, the fees are distributed to the protocol and manager, whenever a person calls the `distribute_fees()` function.
     
    @@ -219,8 +230,8 @@ fn distribute_fees() {
         for strategy in strategies {
             let locked_fees = get_locked_fees(strategy);
             if locked_fees > 0 {
    -            transfer_from_strategy(strategy.asset, protocol_fee_receiver, locked_fees * protocol_fee_receiver / MAX_BPS);
    -            transfer_from_strategy(strategy.asset, vault_fee_receiver, locked_fees * vault_fee_receiver / MAX_BPS);
    +            transfer_from_strategy(strategy.asset, protocol_fee_receiver, locked_fees * protocol_fee_bps / MAX_BPS);
    +            transfer_from_strategy(strategy.asset, vault_fee_receiver, locked_fees * (MAX_BPS - protocol_fee_bps) / MAX_BPS);
                 reset_locked_fees(strategy);
             }
         }
    
    From d38aab631323aff9149831d0042a11a1b8b0c59a Mon Sep 17 00:00:00 2001
    From: coderipper 
    Date: Tue, 10 Dec 2024 21:12:38 -0300
    Subject: [PATCH 16/17] blend and soroswap tests passing
    
    ---
     .../strategies/blend/src/blend_pool.rs        |  2 +-
     .../strategies/blend/src/constants.rs         |  2 +-
     .../strategies/blend/src/soroswap.rs          | 24 ++++++++++++++++++-
     .../blend/src/test/blend/success.rs           | 16 ++++++-------
     4 files changed, 33 insertions(+), 11 deletions(-)
    
    diff --git a/apps/contracts/strategies/blend/src/blend_pool.rs b/apps/contracts/strategies/blend/src/blend_pool.rs
    index c8a79cec..e3cf662a 100644
    --- a/apps/contracts/strategies/blend/src/blend_pool.rs
    +++ b/apps/contracts/strategies/blend/src/blend_pool.rs
    @@ -112,7 +112,7 @@ pub fn withdraw(e: &Env, from: &Address, amount: &i128, config: &Config) -> (i12
     pub fn claim(e: &Env, from: &Address, config: &Config) -> i128 {
         let pool_client = BlendPoolClient::new(e, &config.pool);
     
    -    // TODO: Check reserve_token_ids and how to get the correct one
    +    // TODO: Hardcoded reserve_token_ids for now
         pool_client.claim(from, &vec![&e, 0u32, 1u32, 2u32, 3u32], from)
     }
     
    diff --git a/apps/contracts/strategies/blend/src/constants.rs b/apps/contracts/strategies/blend/src/constants.rs
    index 1a61405b..f4ef1ef4 100644
    --- a/apps/contracts/strategies/blend/src/constants.rs
    +++ b/apps/contracts/strategies/blend/src/constants.rs
    @@ -5,4 +5,4 @@ pub const SCALAR_9: i128 = 1_000_000_000;
     /// The minimum amount of tokens than can be deposited or withdrawn from the vault
     pub const MIN_DUST: i128 = 0_0010000;
     
    -pub const REWARD_THRESHOLD: i128 = 500_0000000;
    \ No newline at end of file
    +pub const REWARD_THRESHOLD: i128 = 40_0000000;
    \ No newline at end of file
    diff --git a/apps/contracts/strategies/blend/src/soroswap.rs b/apps/contracts/strategies/blend/src/soroswap.rs
    index 7a9318ee..3e8e10fa 100644
    --- a/apps/contracts/strategies/blend/src/soroswap.rs
    +++ b/apps/contracts/strategies/blend/src/soroswap.rs
    @@ -1,5 +1,5 @@
     use defindex_strategy_core::StrategyError;
    -use soroban_sdk::{vec, Address, Env, IntoVal, Symbol, Val, Vec};
    +use soroban_sdk::{auth::{ContractContext, InvokerContractAuthEntry, SubContractInvocation}, vec, Address, Env, IntoVal, Symbol, Val, Vec};
     
     use crate::storage::Config;
     
    @@ -19,6 +19,28 @@ pub fn internal_swap_exact_tokens_for_tokens(
         swap_args.push_back(to.to_val());
         swap_args.push_back(deadline.into_val(e));
     
    +    // Maybe instead of using the router directly, we should use the pair for swaps
    +    let pair_address: Address = e.invoke_contract(
    +        &config.router,
    +        &Symbol::new(&e, "router_pair_for"),
    +        vec![&e, path.get(0).unwrap().into_val(e), path.get(1).unwrap().into_val(e)]
    +    );
    +
    +    e.authorize_as_current_contract(vec![
    +        &e,
    +        InvokerContractAuthEntry::Contract(SubContractInvocation {
    +            context: ContractContext {
    +                contract: path.get(0).unwrap().clone(),
    +                fn_name: Symbol::new(&e, "transfer"),
    +                args: (
    +                    e.current_contract_address(),
    +                    pair_address,
    +                    amount_in.clone()).into_val(e),
    +            },
    +            sub_invocations: vec![&e],
    +        }),
    +    ]);
    +
         e.invoke_contract(
             &config.router,
             &Symbol::new(&e, "swap_exact_tokens_for_tokens"),
    diff --git a/apps/contracts/strategies/blend/src/test/blend/success.rs b/apps/contracts/strategies/blend/src/test/blend/success.rs
    index fd1b76c9..98c10575 100644
    --- a/apps/contracts/strategies/blend/src/test/blend/success.rs
    +++ b/apps/contracts/strategies/blend/src/test/blend/success.rs
    @@ -208,16 +208,12 @@ fn success() {
             )
         );
     
    -    strategy_client.withdraw(&withdraw_amount, &user_3);
    -
         // -> verify withdraw
         assert_eq!(usdc_client.balance(&user_2), withdraw_amount);
    -    assert_eq!(usdc_client.balance(&user_3), withdraw_amount);
         assert_eq!(strategy_client.balance(&user_2), 0);
    -    assert_eq!(strategy_client.balance(&user_3), 0);
     
         // -> verify withdraw from empty vault fails
    -    let result = strategy_client.try_withdraw(&MIN_DUST, &user_3);
    +    let result = strategy_client.try_withdraw(&MIN_DUST, &user_2);
         assert_eq!(result, Err(Ok(StrategyError::InsufficientBalance)));
     
         // TODO: Finish harvest testings, pending soroswap router setup with a blend token pair with the underlying asset
    @@ -232,10 +228,14 @@ fn success() {
         let blnd_strategy_balance = blnd_client.balance(&strategy);
         assert_eq!(blnd_strategy_balance, 0);
     
    -    strategy_client.harvest(&user_2);
    +    strategy_client.harvest(&user_3);
     
         let blnd_strategy_balance = blnd_client.balance(&strategy);
         assert_eq!(blnd_strategy_balance, 0);
    -    // -> verify harvest
    -    
    +
    +    let usdc_strategy_balance = usdc_client.balance(&strategy);
    +    assert_eq!(usdc_strategy_balance, 0);
    +
    +    let user_3_strategy_balance = strategy_client.balance(&user_3);
    +    assert_eq!(user_3_strategy_balance, 1226627059);    
     }
    
    From a67c1c5c5be092fecb2dd4a8c020e65f94154b63 Mon Sep 17 00:00:00 2001
    From: chopan123 
    Date: Wed, 11 Dec 2024 11:55:16 -0300
    Subject: [PATCH 17/17] fix
    
    ---
     .../strategies/hodl/src/test/hodl/deposit.rs   | 18 +++++-------------
     1 file changed, 5 insertions(+), 13 deletions(-)
    
    diff --git a/apps/contracts/strategies/hodl/src/test/hodl/deposit.rs b/apps/contracts/strategies/hodl/src/test/hodl/deposit.rs
    index 6e52c8d3..d3f5765a 100644
    --- a/apps/contracts/strategies/hodl/src/test/hodl/deposit.rs
    +++ b/apps/contracts/strategies/hodl/src/test/hodl/deposit.rs
    @@ -1,7 +1,6 @@
     use crate::test::create_hodl_strategy;
     use crate::test::HodlStrategyTest;
     use crate::test::StrategyError;
    -
     // test deposit with negative amount
     #[test]
     fn deposit_with_negative_amount() {
    @@ -27,7 +26,6 @@ fn deposit_mock_auths() {
     fn deposit_and_withdrawal_flow() {
         let test = HodlStrategyTest::setup();
     
    -    // initialize
         let strategy = create_hodl_strategy(&test.env, &test.token.address);
     
         // Initial user token balance
    @@ -76,13 +74,7 @@ fn deposit_and_withdrawal_flow() {
     #[test]
     fn deposit_from_a_withdrawal_from_b() {
         let test = HodlStrategyTest::setup();
    -
    -    let result = test.strategy.try_deposit(&10_000_000, &test.user);
    -    assert_eq!(result, Err(Ok(StrategyError::NotInitialized)));
    -
    -    // initialize
    -    let init_fn_args: Vec = (0,).into_val(&test.env);
    -    test.strategy.initialize(&test.token.address, &init_fn_args);   
    +    let strategy = create_hodl_strategy(&test.env, &test.token.address);
     
         // Initial user token balance
         let balance = test.token.balance(&test.user);
    @@ -90,22 +82,22 @@ fn deposit_from_a_withdrawal_from_b() {
         let amount = 123456;
     
         // Deposit amount of token from the user to the strategy
    -    test.strategy.deposit(&amount, &test.user);
    +    strategy.deposit(&amount, &test.user);
     
         let balance_after_deposit = test.token.balance(&test.user);
         assert_eq!(balance_after_deposit, balance - amount);
     
         // Reading strategy balance
    -    let strategy_balance_after_deposit = test.token.balance(&test.strategy.address);
    +    let strategy_balance_after_deposit = test.token.balance(&strategy.address);
         assert_eq!(strategy_balance_after_deposit, amount);
     
         // Reading user balance on strategy contract
    -    let user_balance_on_strategy = test.strategy.balance(&test.user);
    +    let user_balance_on_strategy = strategy.balance(&test.user);
         assert_eq!(user_balance_on_strategy, amount);
     
     
         let amount_to_withdraw = 100_000;
         // Withdrawing token from the strategy to user
    -    let result = test.strategy.try_withdraw(&amount_to_withdraw, &test.user1);
    +    let result = strategy.try_withdraw(&amount_to_withdraw, &test.user1);
         assert_eq!(result, Err(Ok(StrategyError::InsufficientBalance)));
     }
    \ No newline at end of file