From 7bb8b29f746d3a6bd521faafaf56921744e1aa5b Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Fri, 29 Nov 2024 08:56:20 -0300 Subject: [PATCH 01/14] =?UTF-8?q?=E2=9C=A8=20add=20multiple=20rebalance=20?= =?UTF-8?q?scenarios=20and=20error=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/contracts/vault/src/test/rebalance.rs | 279 ++++++++++++++++++++- 1 file changed, 277 insertions(+), 2 deletions(-) diff --git a/apps/contracts/vault/src/test/rebalance.rs b/apps/contracts/vault/src/test/rebalance.rs index dffb1e7a..d81888ab 100644 --- a/apps/contracts/vault/src/test/rebalance.rs +++ b/apps/contracts/vault/src/test/rebalance.rs @@ -1,4 +1,4 @@ -use soroban_sdk::{vec as sorobanvec, String, Vec}; +use soroban_sdk::{vec as sorobanvec, InvokeError, String, Vec}; use crate::test::{ create_strategy_params_token0, @@ -13,9 +13,10 @@ use crate::test::{ }, DeFindexVaultTest, }; +use crate::test::defindex_vault::ContractError; #[test] -fn rebalance() { +fn rebalance_multi_instructions() { let test = DeFindexVaultTest::setup(); test.env.mock_all_auths(); let strategy_params_token0 = create_strategy_params_token0(&test); @@ -105,3 +106,277 @@ fn rebalance() { let vault_balance = test.token0.balance(&test.defindex_contract.address); assert_eq!(vault_balance, instruction_amount_1); } + +#[test] +fn rebalance_one_instruction() { + let test = DeFindexVaultTest::setup(); + test.env.mock_all_auths(); + let strategy_params_token0 = create_strategy_params_token0(&test); + 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, "TestVault"), + &String::from_str(&test.env, "TSTV"), + ); + let amount = 987654321i128; + + let users = DeFindexVaultTest::generate_random_users(&test.env, 1); + + 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); + + test.defindex_contract.deposit( + &sorobanvec![&test.env, amount], + &sorobanvec![&test.env, amount], + &users[0], + ); + + let df_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(df_balance, amount - 1000); + + let investments = sorobanvec![ + &test.env, + Some(AssetInvestmentAllocation { + asset: test.token0.address.clone(), + strategy_investments: sorobanvec![ + &test.env, + Some(StrategyInvestment { + strategy: test.strategy_client_token0.address.clone(), + amount: amount, + }), + ], + }), + ]; + + test.defindex_contract.invest(&investments); + + let vault_balance = test.token0.balance(&test.defindex_contract.address); + assert_eq!(vault_balance, 0); + + // REBALANCE + + let instruction_amount_0 = 200i128; + + let instructions = sorobanvec![ + &test.env, + Instruction { + action: ActionType::Withdraw, + strategy: Some(test.strategy_client_token0.address.clone()), + amount: Some(instruction_amount_0), + swap_details_exact_in: OptionalSwapDetailsExactIn::None, + swap_details_exact_out: OptionalSwapDetailsExactOut::None, + }, + ]; + + test.defindex_contract.rebalance(&instructions); + + let vault_balance = test.token0.balance(&test.defindex_contract.address); + assert_eq!(vault_balance, instruction_amount_0); +} + +#[test] +fn rebalance_empty_instructions(){ + let test = DeFindexVaultTest::setup(); + test.env.mock_all_auths(); + + let strategy_params_token0 = create_strategy_params_token0(&test); + 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, + &100u32, + &test.defindex_protocol_receiver, + &test.defindex_factory, + &String::from_str(&test.env, "testVault"), + &String::from_str(&test.env, "TSTV"), + ); + let amount: i128 = 987654321; + let users = DeFindexVaultTest::generate_random_users(&test.env, 1); + test.token0_admin_client.mint(&users[0], &amount); + let vault_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(vault_balance, 0i128); + + test.defindex_contract.deposit( + &sorobanvec![&test.env, amount], + &sorobanvec![&test.env, amount], + &users[0], + ); + let df_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(df_balance, amount - 1000); + + let instructions = sorobanvec![ + &test.env, + Instruction { + action: ActionType::Withdraw, + strategy: None, + amount: None, + swap_details_exact_in: OptionalSwapDetailsExactIn::None, + swap_details_exact_out: OptionalSwapDetailsExactOut::None, + }, + ]; + let rebalance = test.defindex_contract.try_rebalance(&instructions); + assert_eq!(rebalance, Err(Ok(ContractError::MissingInstructionData))); + + let no_strategy_instructions = sorobanvec![ + &test.env, + Instruction { + action: ActionType::Withdraw, + strategy: Some(test.strategy_client_token0.address.clone()), + amount: None, + swap_details_exact_in: OptionalSwapDetailsExactIn::None, + swap_details_exact_out: OptionalSwapDetailsExactOut::None, + }, + ]; + let rebalance = test.defindex_contract.try_rebalance(&no_strategy_instructions); + assert_eq!(rebalance, Err(Ok(ContractError::MissingInstructionData))); + + let no_amount_instructions = sorobanvec![ + &test.env, + Instruction { + action: ActionType::Withdraw, + strategy: Some(test.strategy_client_token0.address.clone()), + amount: None, + swap_details_exact_in: OptionalSwapDetailsExactIn::None, + swap_details_exact_out: OptionalSwapDetailsExactOut::None, + }, + ]; + let rebalance = test.defindex_contract.try_rebalance(&no_amount_instructions); + assert_eq!(rebalance, Err(Ok(ContractError::MissingInstructionData))); +} + +#[test] +fn rebalance_no_instructions(){ + let test = DeFindexVaultTest::setup(); + test.env.mock_all_auths(); + + let strategy_params_token0 = create_strategy_params_token0(&test); + 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, + &100u32, + &test.defindex_protocol_receiver, + &test.defindex_factory, + &String::from_str(&test.env, "testVault"), + &String::from_str(&test.env, "TSTV"), + ); + let amount: i128 = 987654321; + let users = DeFindexVaultTest::generate_random_users(&test.env, 1); + test.token0_admin_client.mint(&users[0], &amount); + let vault_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(vault_balance, 0i128); + + test.defindex_contract.deposit( + &sorobanvec![&test.env, amount], + &sorobanvec![&test.env, amount], + &users[0], + ); + let df_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(df_balance, amount - 1000); + + let rebalance = test.defindex_contract.try_rebalance(&sorobanvec![&test.env]); + assert_eq!(rebalance, Err(Ok(ContractError::NoInstructions))); +} + +#[test] +fn rebalance_insufficient_balance(){ + let test = DeFindexVaultTest::setup(); + test.env.mock_all_auths(); + + let strategy_params_token0 = create_strategy_params_token0(&test); + 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, + &100u32, + &test.defindex_protocol_receiver, + &test.defindex_factory, + &String::from_str(&test.env, "testVault"), + &String::from_str(&test.env, "TSTV"), + ); + let amount: i128 = 987654321; + let users = DeFindexVaultTest::generate_random_users(&test.env, 1); + test.token0_admin_client.mint(&users[0], &amount); + let vault_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(vault_balance, 0i128); + + test.defindex_contract.deposit( + &sorobanvec![&test.env, amount], + &sorobanvec![&test.env, amount], + &users[0], + ); + let df_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(df_balance, amount - 1000); + + let withdraw_instructions = sorobanvec![ + &test.env, + Instruction { + action: ActionType::Withdraw, + strategy: Some(test.strategy_client_token0.address.clone()), + amount: Some(amount + 1), + swap_details_exact_in: OptionalSwapDetailsExactIn::None, + swap_details_exact_out: OptionalSwapDetailsExactOut::None, + }, + ]; + let rebalance = test.defindex_contract.try_rebalance(&withdraw_instructions); + assert_eq!(rebalance, Err(Ok(ContractError::StrategyWithdrawError))); + + let invest_instructions = sorobanvec!( + &test.env, + Instruction { + action: ActionType::Invest, + strategy: Some(test.strategy_client_token0.address.clone()), + amount: Some(amount + 1), + swap_details_exact_in: OptionalSwapDetailsExactIn::None, + swap_details_exact_out: OptionalSwapDetailsExactOut::None, + }, + ); + let rebalance = test.defindex_contract.try_rebalance(&invest_instructions); + if rebalance == Err(Err(InvokeError::Contract(10))) { + return; + } else { + panic!("Expected error not returned"); + } +} + From 98749e6af6e938ae7dc98d31141c455f2a56d235 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Mon, 2 Dec 2024 09:26:29 -0300 Subject: [PATCH 02/14] =?UTF-8?q?=F0=9F=A9=B9=20Fix=20blance=20reading=20s?= =?UTF-8?q?ubmitting=20tx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/contracts/src/tests/strategy.ts | 6 ++-- .../src/tests/testTwoStrategiesVault.ts | 16 +++++----- apps/contracts/src/tests/vault.ts | 30 +++++++------------ 3 files changed, 21 insertions(+), 31 deletions(-) diff --git a/apps/contracts/src/tests/strategy.ts b/apps/contracts/src/tests/strategy.ts index 18c86afa..d9164045 100644 --- a/apps/contracts/src/tests/strategy.ts +++ b/apps/contracts/src/tests/strategy.ts @@ -29,11 +29,13 @@ export async function checkUserBalance(contractAddress: string, userPublicKey: s contractAddress, methodName, [userAddress], - source ? source : Keypair.random() + source ? source : Keypair.random(), + true ); // Convert the result to a native JavaScript number - const balance = scValToNative(result.returnValue) as number; + console.log(result.result.retval) + const balance = scValToNative(result.result.retval) as number; console.log(`Balance for user ${userPublicKey}:`, balance); return balance; diff --git a/apps/contracts/src/tests/testTwoStrategiesVault.ts b/apps/contracts/src/tests/testTwoStrategiesVault.ts index d8f22be6..79fab09f 100644 --- a/apps/contracts/src/tests/testTwoStrategiesVault.ts +++ b/apps/contracts/src/tests/testTwoStrategiesVault.ts @@ -11,6 +11,7 @@ import { AddressBook } from "../utils/address_book.js"; import { airdropAccount, invokeContract, invokeCustomContract } from "../utils/contract.js"; import { config } from "../utils/env_config.js"; import { ActionType, AssetInvestmentAllocation, depositToVault, getVaultBalanceInStrategy, Instruction, investVault, rebalanceVault, fetchParsedCurrentIdleFunds, fetchCurrentInvestedFunds } from "./vault.js"; +import { checkUserBalance } from "./strategy.js"; const soroswapUSDC = new Address("CAAFIHB4I7WQMJMKC22CZVQNNX7EONWSOMT6SUXK6I3G3F6J4XFRWNDI"); @@ -138,7 +139,6 @@ const mintToken = async () => { } await mintToken(); - // Step 1: Deposit to vault and capture initial balances const { user, balanceBefore: depositBalanceBefore, result: depositResult, balanceAfter: depositBalanceAfter, status: depositStatus } = await depositToVault(deployedVault, [initialAmount], testUser); @@ -149,12 +149,11 @@ console.log(" -- ") console.log(" -- ") - const idleFundsAfterDeposit = await fetchParsedCurrentIdleFunds(deployedVault, user); const investedFundsAfterDeposit = await fetchCurrentInvestedFunds(deployedVault, user); -const hodlBalanceBeforeInvest = await getVaultBalanceInStrategy(hodl_strategy, deployedVault, user); -const fixedBalanceBeforeInvest = await getVaultBalanceInStrategy(fixed_apr_strategy, deployedVault, user); +const hodlBalanceBeforeInvest = await checkUserBalance(hodl_strategy, deployedVault, user); +const fixedBalanceBeforeInvest = await checkUserBalance(fixed_apr_strategy, deployedVault, user); // Step 2: Invest in vault idle funds const investParams: AssetInvestmentAllocation[] = [ @@ -180,8 +179,8 @@ console.log('🚀 « investResult:', investResult); const idleFundsAfterInvest = await fetchParsedCurrentIdleFunds(deployedVault, user); const investedFundsAfterInvest = await fetchCurrentInvestedFunds(deployedVault, user); -const afterInvestHodlBalance = await getVaultBalanceInStrategy(hodl_strategy, deployedVault, user); -const afterInvestFixedBalance = await getVaultBalanceInStrategy(fixed_apr_strategy, deployedVault, user); +const afterInvestHodlBalance = await checkUserBalance(hodl_strategy, deployedVault, user); +const afterInvestFixedBalance = await checkUserBalance(fixed_apr_strategy, deployedVault, user); // 10000 USDC -> Total Balance // 1500 USDC -> Hodl Strategy @@ -218,13 +217,12 @@ console.log('🚀 « rebalanceParams:', rebalanceParams); // 3500 USDC -> Idle const rebalanceResult = await rebalanceVault(deployedVault, rebalanceParams, manager); -console.log('🚀 « rebalanceResult:', rebalanceResult) const idleFundsAfterRebalance = await fetchParsedCurrentIdleFunds(deployedVault, user); const investedFundsAfterRebalance = await fetchCurrentInvestedFunds(deployedVault, user); -const afterRebalanceHodlBalance = await getVaultBalanceInStrategy(hodl_strategy, deployedVault, user); -const afterRebalanceFixedBalance = await getVaultBalanceInStrategy(fixed_apr_strategy, deployedVault, user); +const afterRebalanceHodlBalance = await checkUserBalance(hodl_strategy, deployedVault, user); +const afterRebalanceFixedBalance = await checkUserBalance(fixed_apr_strategy, deployedVault, user); console.table({ hodlStrategy: { diff --git a/apps/contracts/src/tests/vault.ts b/apps/contracts/src/tests/vault.ts index 4260782b..223385a8 100644 --- a/apps/contracts/src/tests/vault.ts +++ b/apps/contracts/src/tests/vault.ts @@ -21,9 +21,10 @@ import { airdropAccount, invokeCustomContract } from "../utils/contract.js"; const network = process.argv[2]; -export async function depositToVault(deployedVault: string, amount: number[], user?: Keypair, ) { +export async function depositToVault(deployedVault: string, amount: number[], user?: Keypair, invest?: boolean) { // Create and fund a new user account if not provided const newUser = user ? user : Keypair.random(); + const investBool = invest ? invest : false; console.log('🚀 ~ depositToVault ~ newUser.publicKey():', newUser.publicKey()); console.log('🚀 ~ depositToVault ~ newUser.secret():', newUser.secret()); @@ -41,7 +42,8 @@ export async function depositToVault(deployedVault: string, amount: number[], us const depositParams: xdr.ScVal[] = [ xdr.ScVal.scvVec(amountsDesired.map((amount) => nativeToScVal(amount, { type: "i128" }))), xdr.ScVal.scvVec(amountsMin.map((min) => nativeToScVal(min, { type: "i128" }))), - (new Address(newUser.publicKey())).toScVal() + (new Address(newUser.publicKey())).toScVal(), + xdr.ScVal.scvBool(investBool) ]; try { @@ -102,7 +104,6 @@ export async function getDfTokenBalance(deployedVault: string, userPublicKey: st source ? source : Keypair.random(), // No specific source is needed as we are just querying the balance true // Set to simulate mode if testing on an uncommitted transaction ); - const balance = scValToNative(result.result.retval) return balance; } catch (error) { @@ -184,19 +185,19 @@ export async function withdrawFromVault(deployedVault: string, withdrawAmount: n */ export async function fetchCurrentIdleFunds(deployedVault: string, user: Keypair): Promise> { try { - const result = await invokeCustomContract(deployedVault, "fetch_current_idle_funds", [], user); - return result.map(scValToNative); // Convert result to native format if needed + const result = await invokeCustomContract(deployedVault, "fetch_current_idle_funds", [], user, false); + const parsedResult = scValToNative(result.returnValue); + return parsedResult; // Convert result to native format if needed } catch (error) { console.error("❌ Failed to fetch current idle funds:", error); throw error; } } -export async function fetchParsedCurrentIdleFunds(deployedVault: string, user: Keypair) { +export async function fetchParsedCurrentIdleFunds(deployedVault: string, user: Keypair): Promise<{ address: string, amount: bigint }[]> { try { - const res = await invokeCustomContract(deployedVault, "fetch_current_idle_funds", [], user); - const funds = scValToNative(res.returnValue); - const mappedFunds = Object.entries(funds).map(([key, value]) => ({ + const res = await fetchCurrentIdleFunds(deployedVault, user); + const mappedFunds = Object.entries(res).map(([key, value]) => ({ address: key, amount: value, })); @@ -453,17 +454,6 @@ function mapSwapDetailsExactOut(details: SwapDetailsExactOut) { ]; } -export async function getVaultBalanceInStrategy(strategyAddress: string, vaultAddress: string, user: Keypair) { - const address = new Address(vaultAddress); - try { - const res = await invokeCustomContract(strategyAddress, "balance",[address.toScVal()],user) - return scValToNative(res.returnValue); - } catch (error) { - console.error('🔴 « error:', error); - return 0; - } - } - export async function fetchCurrentInvestedFunds(deployedVault:string, user:Keypair) { try { const res = await invokeCustomContract(deployedVault, "fetch_current_invested_funds", [], user); From c8d485f032b80adbac574b7e85bea982448072ee Mon Sep 17 00:00:00 2001 From: coderipper Date: Mon, 2 Dec 2024 10:40:36 -0300 Subject: [PATCH 03/14] disabled xycloans tests --- apps/contracts/strategies/xycloans/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/contracts/strategies/xycloans/src/lib.rs b/apps/contracts/strategies/xycloans/src/lib.rs index 8383087f..7e303cfa 100644 --- a/apps/contracts/strategies/xycloans/src/lib.rs +++ b/apps/contracts/strategies/xycloans/src/lib.rs @@ -167,4 +167,4 @@ impl DeFindexStrategyTrait for XycloansAdapter { } } -mod test; \ No newline at end of file +// mod test; // TODO: Uncomment when working on this vault/tests \ No newline at end of file From 8073940417a35bb035a0c0316b1bdec168e66620 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:18:35 -0300 Subject: [PATCH 04/14] =?UTF-8?q?=F0=9F=A9=B9Fix=20deposit=20function=20ca?= =?UTF-8?q?lls=20on=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test_deposit.rs | 11 +++++++---- .../test_withdraw.rs | 4 ++-- .../test_vault_one_hodl_strategy/test_deposit.rs | 11 +++++++---- .../test_vault_one_hodl_strategy/test_invest.rs | 10 +++++----- .../test_withdraw.rs | 16 ++++++++-------- apps/contracts/vault/src/test/deposit.rs | 6 ++++++ apps/contracts/vault/src/test/rebalance.rs | 6 +++++- 7 files changed, 40 insertions(+), 24 deletions(-) diff --git a/apps/contracts/integration-test/src/test/test_vault_one_fixed_strategy/test_deposit.rs b/apps/contracts/integration-test/src/test/test_vault_one_fixed_strategy/test_deposit.rs index 693ff7c8..0380d83c 100644 --- a/apps/contracts/integration-test/src/test/test_vault_one_fixed_strategy/test_deposit.rs +++ b/apps/contracts/integration-test/src/test/test_vault_one_fixed_strategy/test_deposit.rs @@ -49,7 +49,7 @@ fn test_fixed_apr_deposit_success() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); let vault_balance = enviroment.token.balance(&enviroment.vault_contract.address); assert_eq!(vault_balance, deposit_amount); @@ -113,7 +113,7 @@ fn test_fixed_apr_deposit_insufficient_balance() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); } #[test] @@ -177,7 +177,7 @@ fn test_fixed_apr_deposit_multiple_users() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user1); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user1, &false); enviroment.vault_contract .mock_auths(&[MockAuth { @@ -204,7 +204,7 @@ fn test_fixed_apr_deposit_multiple_users() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user2); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user2, &false); let vault_balance = enviroment.token.balance(&enviroment.vault_contract.address); assert_eq!(vault_balance, deposit_amount * 2); @@ -250,6 +250,7 @@ fn test_fixed_apr_deposit_zero_amount() { &svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, + &false ); assert_eq!(result, Err(Ok(VaultContractError::InsufficientAmount))); @@ -282,6 +283,7 @@ fn test_fixed_apr_deposit_negative_amount() { &svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, + &false ); assert_eq!(result, Err(Ok(VaultContractError::NegativeNotAllowed))); @@ -314,6 +316,7 @@ fn test_fixed_apr_deposit_insufficient_minimum_liquidity() { &svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, + &false ); assert_eq!(result, Err(Ok(VaultContractError::InsufficientAmount))); diff --git a/apps/contracts/integration-test/src/test/test_vault_one_fixed_strategy/test_withdraw.rs b/apps/contracts/integration-test/src/test/test_vault_one_fixed_strategy/test_withdraw.rs index 708d05f4..97a91a63 100644 --- a/apps/contracts/integration-test/src/test/test_vault_one_fixed_strategy/test_withdraw.rs +++ b/apps/contracts/integration-test/src/test/test_vault_one_fixed_strategy/test_withdraw.rs @@ -46,7 +46,7 @@ fn test_fixed_apr_no_invest_withdraw_success() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); setup.env.ledger().set_timestamp(setup.env.ledger().timestamp() + ONE_YEAR_IN_SECONDS); @@ -128,7 +128,7 @@ fn test_fixed_apr_invest_withdraw_success() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); let investments = svec![ &setup.env, diff --git a/apps/contracts/integration-test/src/test/test_vault_one_hodl_strategy/test_deposit.rs b/apps/contracts/integration-test/src/test/test_vault_one_hodl_strategy/test_deposit.rs index 3ae650b0..cd7a4eeb 100644 --- a/apps/contracts/integration-test/src/test/test_vault_one_hodl_strategy/test_deposit.rs +++ b/apps/contracts/integration-test/src/test/test_vault_one_hodl_strategy/test_deposit.rs @@ -49,7 +49,7 @@ fn test_deposit_success() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); let vault_balance = enviroment.token.balance(&enviroment.vault_contract.address); assert_eq!(vault_balance, deposit_amount); @@ -113,7 +113,7 @@ fn test_deposit_insufficient_balance() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); } #[test] @@ -177,7 +177,7 @@ fn test_deposit_multiple_users() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user1); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user1, &false); enviroment.vault_contract .mock_auths(&[MockAuth { @@ -204,7 +204,7 @@ fn test_deposit_multiple_users() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user2); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user2, &false); let vault_balance = enviroment.token.balance(&enviroment.vault_contract.address); assert_eq!(vault_balance, deposit_amount * 2); @@ -250,6 +250,7 @@ fn test_deposit_zero_amount() { &svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, + &false ); assert_eq!(result, Err(Ok(VaultContractError::InsufficientAmount))); @@ -282,6 +283,7 @@ fn test_deposit_negative_amount() { &svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, + &false ); assert_eq!(result, Err(Ok(VaultContractError::NegativeNotAllowed))); @@ -314,6 +316,7 @@ fn test_deposit_insufficient_minimum_liquidity() { &svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, + &false ); assert_eq!(result, Err(Ok(VaultContractError::InsufficientAmount))); diff --git a/apps/contracts/integration-test/src/test/test_vault_one_hodl_strategy/test_invest.rs b/apps/contracts/integration-test/src/test/test_vault_one_hodl_strategy/test_invest.rs index 035faf50..faec6852 100644 --- a/apps/contracts/integration-test/src/test/test_vault_one_hodl_strategy/test_invest.rs +++ b/apps/contracts/integration-test/src/test/test_vault_one_hodl_strategy/test_invest.rs @@ -49,7 +49,7 @@ fn test_invest_success() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); let df_balance = enviroment.vault_contract.balance(&user); assert_eq!(df_balance, deposit_amount - MINIMUM_LIQUIDITY); @@ -153,7 +153,7 @@ fn test_invest_exceeding_investing_lenght() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); let df_balance = enviroment.vault_contract.balance(&user); assert_eq!(df_balance, deposit_amount - MINIMUM_LIQUIDITY); @@ -276,7 +276,7 @@ fn test_invest_insufficient_balance() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); } #[test] @@ -340,7 +340,7 @@ fn test_invest_multiple_users() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user1); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user1, &false); enviroment.vault_contract .mock_auths(&[MockAuth { @@ -367,7 +367,7 @@ fn test_invest_multiple_users() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user2); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user2, &false); let df_balance_user1 = enviroment.vault_contract.balance(&user1); let df_balance_user2 = enviroment.vault_contract.balance(&user2); diff --git a/apps/contracts/integration-test/src/test/test_vault_one_hodl_strategy/test_withdraw.rs b/apps/contracts/integration-test/src/test/test_vault_one_hodl_strategy/test_withdraw.rs index 057524ae..e3601633 100644 --- a/apps/contracts/integration-test/src/test/test_vault_one_hodl_strategy/test_withdraw.rs +++ b/apps/contracts/integration-test/src/test/test_vault_one_hodl_strategy/test_withdraw.rs @@ -49,7 +49,7 @@ fn test_withdraw_no_invest_success() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); let df_balance = enviroment.vault_contract.balance(&user); assert_eq!(df_balance, deposit_amount - MINIMUM_LIQUIDITY); @@ -133,7 +133,7 @@ fn test_withdraw_partial_success() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); let df_balance = enviroment.vault_contract.balance(&user); assert_eq!(df_balance, deposit_amount - MINIMUM_LIQUIDITY); @@ -212,7 +212,7 @@ fn test_withdraw_insufficient_balance() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); let df_balance = enviroment.vault_contract.balance(&user); assert_eq!(df_balance, deposit_amount - MINIMUM_LIQUIDITY); @@ -292,7 +292,7 @@ fn test_withdraw_after_invest() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); let user_balance_after_deposit = enviroment.token.balance(user); assert_eq!(user_balance_after_deposit, 0); @@ -445,7 +445,7 @@ fn test_withdraw_multiple_users() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user1); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user1, &false); enviroment.vault_contract .mock_auths(&[MockAuth { @@ -472,7 +472,7 @@ fn test_withdraw_multiple_users() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user2); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user2, &false); let df_balance_user1 = enviroment.vault_contract.balance(&user1); let df_balance_user2 = enviroment.vault_contract.balance(&user2); @@ -587,7 +587,7 @@ fn test_withdraw_after_invest_multiple_users() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user1); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user1, &false); enviroment.vault_contract .mock_auths(&[MockAuth { @@ -614,7 +614,7 @@ fn test_withdraw_after_invest_multiple_users() { ] }, }]) - .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user2); + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user2, &false); let df_balance_user1 = enviroment.vault_contract.balance(&user1); let df_balance_user2 = enviroment.vault_contract.balance(&user2); diff --git a/apps/contracts/vault/src/test/deposit.rs b/apps/contracts/vault/src/test/deposit.rs index 54217e6b..1b3e1648 100644 --- a/apps/contracts/vault/src/test/deposit.rs +++ b/apps/contracts/vault/src/test/deposit.rs @@ -773,6 +773,7 @@ fn deposit_amounts_min_greater_than_amounts_desired(){ &sorobanvec![&test.env, amount0, amount1], &sorobanvec![&test.env, amount0 + 1, amount1 + 1], &users[0], + &false ); // this should fail @@ -832,6 +833,7 @@ fn deposit_transfers_tokens_from_user_to_vault(){ &sorobanvec![&test.env, amount0, amount1], &sorobanvec![&test.env, amount0, amount1], &users[0], + &false ); // check balances after deposit @@ -888,6 +890,7 @@ fn test_deposit_arithmetic_error() { &sorobanvec![&test.env, large_amount], &sorobanvec![&test.env, large_amount], &users[0], + &false ); // Try to deposit a large amount @@ -895,6 +898,7 @@ fn test_deposit_arithmetic_error() { &sorobanvec![&test.env, large_amount], &sorobanvec![&test.env, large_amount], &users[0], + &false ); // Verify that the returned error is ContractError::ArithmeticError @@ -951,6 +955,7 @@ fn deposit_amounts_desired_zero() { &sorobanvec![&test.env, 0i128], &sorobanvec![&test.env, 0i128], &users[0], + &false ); // Verify that the returned error is ContractError::InsufficientAmount @@ -1014,6 +1019,7 @@ fn deposit_insufficient_funds_with_error_message() { &sorobanvec![&test.env, amount0 + 1, amount1 + 1], &sorobanvec![&test.env, amount0 + 1, amount1 + 1], &users[0], + &false ); if deposit_result == Err(Err(InvokeError::Contract(10))) { diff --git a/apps/contracts/vault/src/test/rebalance.rs b/apps/contracts/vault/src/test/rebalance.rs index 397c5c5d..19bb9cf4 100644 --- a/apps/contracts/vault/src/test/rebalance.rs +++ b/apps/contracts/vault/src/test/rebalance.rs @@ -147,6 +147,7 @@ fn rebalance_one_instruction() { &sorobanvec![&test.env, amount], &sorobanvec![&test.env, amount], &users[0], + &false ); let df_balance = test.defindex_contract.balance(&users[0]); @@ -226,6 +227,7 @@ fn rebalance_empty_instructions(){ &sorobanvec![&test.env, amount], &sorobanvec![&test.env, amount], &users[0], + &false ); let df_balance = test.defindex_contract.balance(&users[0]); assert_eq!(df_balance, amount - 1000); @@ -304,6 +306,7 @@ fn rebalance_no_instructions(){ &sorobanvec![&test.env, amount], &sorobanvec![&test.env, amount], &users[0], + &false ); let df_balance = test.defindex_contract.balance(&users[0]); assert_eq!(df_balance, amount - 1000); @@ -346,8 +349,9 @@ fn rebalance_insufficient_balance(){ &sorobanvec![&test.env, amount], &sorobanvec![&test.env, amount], &users[0], + &false ); - let df_balance = test.defindex_contract.balance(&users[0]); + let df_balance: i128 = test.defindex_contract.balance(&users[0]); assert_eq!(df_balance, amount - 1000); let withdraw_instructions = sorobanvec![ From 7060d58fc720a281c1f753a15b78fbd531684ad0 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:00:59 -0300 Subject: [PATCH 05/14] =?UTF-8?q?=F0=9F=A9=B9Fix=20create=20&=20deposit=20?= =?UTF-8?q?method=20on=20factory?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/contracts/factory/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/contracts/factory/src/lib.rs b/apps/contracts/factory/src/lib.rs index 43175e34..90cbebd4 100644 --- a/apps/contracts/factory/src/lib.rs +++ b/apps/contracts/factory/src/lib.rs @@ -322,6 +322,7 @@ impl FactoryTrait for DeFindexFactory { deposit_args.push_back(amounts.to_val()); deposit_args.push_back(amounts_min.to_val()); deposit_args.push_back(caller.to_val()); + deposit_args.push_back(false.into_val(&e)); e.invoke_contract::(&defindex_address, &Symbol::new(&e, "deposit"), deposit_args); From e464a398ff3d320710c05784a97cd714003c0baf Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Mon, 2 Dec 2024 15:17:14 -0300 Subject: [PATCH 06/14] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Renamed=20tests=20&?= =?UTF-8?q?=20create=20test=20modules?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/contracts/factory/src/test.rs | 4 +- .../factory/src/test/{ => factory}/admin.rs | 8 +- .../src/test/{ => factory}/create_defindex.rs | 4 +- .../src/test/{ => factory}/initialize.rs | 8 +- .../contracts/factory/src/test/factory/mod.rs | 3 + apps/contracts/integration-test/src/test.rs | 4 +- .../test/test_vault_one_hodl_strategy/mod.rs | 3 - .../test/vault_one_fixed_strategy/deposit.rs | 323 ++++++ .../mod.rs | 4 +- .../test/vault_one_fixed_strategy/withdraw.rs | 210 ++++ .../test/vault_one_hodl_strategy/deposit.rs | 323 ++++++ .../test/vault_one_hodl_strategy/invest.rs | 426 +++++++ .../src/test/vault_one_hodl_strategy/mod.rs | 3 + .../test/vault_one_hodl_strategy/withdraw.rs | 728 ++++++++++++ apps/contracts/strategies/blend/src/test.rs | 5 +- .../blend/src/test/{ => blend}/deposit.rs | 0 .../blend/src/test/{ => blend}/events.rs | 0 .../blend/src/test/{ => blend}/initialize.rs | 0 .../strategies/blend/src/test/blend/mod.rs | 4 + .../blend/src/test/{ => blend}/withdraw.rs | 0 .../strategies/fixed_apr/src/test.rs | 5 +- .../src/test/{ => fixed_apr}/deposit.rs | 0 .../src/test/{ => fixed_apr}/harvest.rs | 0 .../src/test/{ => fixed_apr}/initialize.rs | 0 .../fixed_apr/src/test/fixed_apr/mod.rs | 4 + .../src/test/{ => fixed_apr}/withdraw.rs | 0 apps/contracts/strategies/hodl/src/test.rs | 5 +- .../hodl/src/test/{ => hodl}/deposit.rs | 0 .../hodl/src/test/{ => hodl}/events.rs | 0 .../hodl/src/test/{ => hodl}/initialize.rs | 0 .../strategies/hodl/src/test/hodl/mod.rs | 4 + .../hodl/src/test/{ => hodl}/withdraw.rs | 0 apps/contracts/vault/src/test.rs | 9 +- .../vault/src/test/{ => vault}/admin.rs | 10 +- .../contracts/vault/src/test/vault/deposit.rs | 1031 +++++++++++++++++ .../test/{ => vault}/deposit_and_invest.rs | 4 +- .../test/{ => vault}/emergency_withdraw.rs | 2 +- .../vault/src/test/{ => vault}/initialize.rs | 14 +- .../vault/src/test/{ => vault}/invest.rs | 18 +- apps/contracts/vault/src/test/vault/mod.rs | 8 + .../vault/src/test/vault/rebalance.rs | 387 +++++++ .../vault/src/test/{ => vault}/withdraw.rs | 4 +- 42 files changed, 3499 insertions(+), 66 deletions(-) rename apps/contracts/factory/src/test/{ => factory}/admin.rs (96%) rename apps/contracts/factory/src/test/{ => factory}/create_defindex.rs (96%) rename apps/contracts/factory/src/test/{ => factory}/initialize.rs (92%) create mode 100644 apps/contracts/factory/src/test/factory/mod.rs delete mode 100644 apps/contracts/integration-test/src/test/test_vault_one_hodl_strategy/mod.rs create mode 100644 apps/contracts/integration-test/src/test/vault_one_fixed_strategy/deposit.rs rename apps/contracts/integration-test/src/test/{test_vault_one_fixed_strategy => vault_one_fixed_strategy}/mod.rs (91%) create mode 100644 apps/contracts/integration-test/src/test/vault_one_fixed_strategy/withdraw.rs create mode 100644 apps/contracts/integration-test/src/test/vault_one_hodl_strategy/deposit.rs create mode 100644 apps/contracts/integration-test/src/test/vault_one_hodl_strategy/invest.rs create mode 100644 apps/contracts/integration-test/src/test/vault_one_hodl_strategy/mod.rs create mode 100644 apps/contracts/integration-test/src/test/vault_one_hodl_strategy/withdraw.rs rename apps/contracts/strategies/blend/src/test/{ => blend}/deposit.rs (100%) rename apps/contracts/strategies/blend/src/test/{ => blend}/events.rs (100%) rename apps/contracts/strategies/blend/src/test/{ => blend}/initialize.rs (100%) create mode 100644 apps/contracts/strategies/blend/src/test/blend/mod.rs rename apps/contracts/strategies/blend/src/test/{ => blend}/withdraw.rs (100%) rename apps/contracts/strategies/fixed_apr/src/test/{ => fixed_apr}/deposit.rs (100%) rename apps/contracts/strategies/fixed_apr/src/test/{ => fixed_apr}/harvest.rs (100%) rename apps/contracts/strategies/fixed_apr/src/test/{ => fixed_apr}/initialize.rs (100%) create mode 100644 apps/contracts/strategies/fixed_apr/src/test/fixed_apr/mod.rs rename apps/contracts/strategies/fixed_apr/src/test/{ => fixed_apr}/withdraw.rs (100%) rename apps/contracts/strategies/hodl/src/test/{ => hodl}/deposit.rs (100%) rename apps/contracts/strategies/hodl/src/test/{ => hodl}/events.rs (100%) rename apps/contracts/strategies/hodl/src/test/{ => hodl}/initialize.rs (100%) create mode 100644 apps/contracts/strategies/hodl/src/test/hodl/mod.rs rename apps/contracts/strategies/hodl/src/test/{ => hodl}/withdraw.rs (100%) rename apps/contracts/vault/src/test/{ => vault}/admin.rs (97%) create mode 100644 apps/contracts/vault/src/test/vault/deposit.rs rename apps/contracts/vault/src/test/{ => vault}/deposit_and_invest.rs (99%) rename apps/contracts/vault/src/test/{ => vault}/emergency_withdraw.rs (98%) rename apps/contracts/vault/src/test/{ => vault}/initialize.rs (95%) rename apps/contracts/vault/src/test/{ => vault}/invest.rs (98%) create mode 100644 apps/contracts/vault/src/test/vault/mod.rs create mode 100644 apps/contracts/vault/src/test/vault/rebalance.rs rename apps/contracts/vault/src/test/{ => vault}/withdraw.rs (98%) diff --git a/apps/contracts/factory/src/test.rs b/apps/contracts/factory/src/test.rs index 77d207c4..ada9cf66 100644 --- a/apps/contracts/factory/src/test.rs +++ b/apps/contracts/factory/src/test.rs @@ -155,6 +155,4 @@ impl<'a> DeFindexFactoryTest<'a> { } } -mod admin; -mod initialize; -mod create_defindex; \ No newline at end of file +mod factory; \ No newline at end of file diff --git a/apps/contracts/factory/src/test/admin.rs b/apps/contracts/factory/src/test/factory/admin.rs similarity index 96% rename from apps/contracts/factory/src/test/admin.rs rename to apps/contracts/factory/src/test/factory/admin.rs index b52e80d0..c11d44e9 100644 --- a/apps/contracts/factory/src/test/admin.rs +++ b/apps/contracts/factory/src/test/factory/admin.rs @@ -5,7 +5,7 @@ use alloc::vec; use crate::test::DeFindexFactoryTest; #[test] -fn test_set_new_admin_by_admin() { +fn set_new_admin_by_admin() { let test = DeFindexFactoryTest::setup(); @@ -46,7 +46,7 @@ fn test_set_new_admin_by_admin() { #[test] #[should_panic(expected = "HostError: Error(Auth, InvalidAction)")] // Unauthorized -fn test_set_new_admin_by_unauthorized() { +fn set_new_admin_by_unauthorized() { let test = DeFindexFactoryTest::setup(); test.factory_contract.initialize(&test.admin, &test.defindex_receiver, &100u32, &test.defindex_wasm_hash); @@ -69,7 +69,7 @@ fn test_set_new_admin_by_unauthorized() { } #[test] -fn test_set_defindex_receiver_by_admin() { +fn set_defindex_receiver_by_admin() { let test = DeFindexFactoryTest::setup(); test.factory_contract.initialize(&test.admin, &test.defindex_receiver, &100u32, &test.defindex_wasm_hash); @@ -109,7 +109,7 @@ fn test_set_defindex_receiver_by_admin() { #[test] #[should_panic(expected = "HostError: Error(Auth, InvalidAction)")] // Unauthorized -fn test_set_fee_receiver_by_unauthorized() { +fn set_fee_receiver_by_unauthorized() { let test = DeFindexFactoryTest::setup(); test.factory_contract.initialize(&test.admin, &test.defindex_receiver, &100u32, &test.defindex_wasm_hash); diff --git a/apps/contracts/factory/src/test/create_defindex.rs b/apps/contracts/factory/src/test/factory/create_defindex.rs similarity index 96% rename from apps/contracts/factory/src/test/create_defindex.rs rename to apps/contracts/factory/src/test/factory/create_defindex.rs index a47ed17d..0f3e70c1 100644 --- a/apps/contracts/factory/src/test/create_defindex.rs +++ b/apps/contracts/factory/src/test/factory/create_defindex.rs @@ -3,7 +3,7 @@ use soroban_sdk::{vec, BytesN, String, Vec}; use crate::test::{create_asset_params, DeFindexFactoryTest}; #[test] -fn test_create_defindex_success() { +fn create_success() { let test = DeFindexFactoryTest::setup(); test.factory_contract.initialize(&test.admin, &test.defindex_receiver, &100u32, &test.defindex_wasm_hash); @@ -28,7 +28,7 @@ fn test_create_defindex_success() { } #[test] -fn test_create_defindex_deposit_success() { +fn create_and_deposit_success() { let test = DeFindexFactoryTest::setup(); test.env.mock_all_auths(); diff --git a/apps/contracts/factory/src/test/initialize.rs b/apps/contracts/factory/src/test/factory/initialize.rs similarity index 92% rename from apps/contracts/factory/src/test/initialize.rs rename to apps/contracts/factory/src/test/factory/initialize.rs index 64cd8abc..4edce2a9 100644 --- a/apps/contracts/factory/src/test/initialize.rs +++ b/apps/contracts/factory/src/test/factory/initialize.rs @@ -4,7 +4,7 @@ use crate::error::FactoryError; use crate::test::{create_asset_params, DeFindexFactoryTest}; #[test] -fn test_initialize_and_get_storage() { +fn initialize_and_get_storage() { let test = DeFindexFactoryTest::setup(); test.factory_contract.initialize(&test.admin, &test.defindex_receiver, &100u32, &test.defindex_wasm_hash); @@ -17,7 +17,7 @@ fn test_initialize_and_get_storage() { } #[test] -fn test_get_storage_not_yet_initialized() { +fn get_storage_not_yet_initialized() { let test = DeFindexFactoryTest::setup(); let factory_admin = test.factory_contract.try_admin(); let factory_defindex_receiver = test.factory_contract.try_defindex_receiver(); @@ -27,7 +27,7 @@ fn test_get_storage_not_yet_initialized() { } #[test] -fn test_initialize_twice() { +fn initialize_twice() { let test = DeFindexFactoryTest::setup(); test.factory_contract.initialize(&test.admin, &test.defindex_receiver, &100u32, &test.defindex_wasm_hash); @@ -40,7 +40,7 @@ fn test_initialize_twice() { } #[test] -fn test_create_defindex_not_yet_initialized() { +fn create_defindex_not_yet_initialized() { let test = DeFindexFactoryTest::setup(); let asset_params = create_asset_params(&test); diff --git a/apps/contracts/factory/src/test/factory/mod.rs b/apps/contracts/factory/src/test/factory/mod.rs new file mode 100644 index 00000000..0f2211a8 --- /dev/null +++ b/apps/contracts/factory/src/test/factory/mod.rs @@ -0,0 +1,3 @@ +mod admin; +mod create_defindex; +mod initialize; \ No newline at end of file diff --git a/apps/contracts/integration-test/src/test.rs b/apps/contracts/integration-test/src/test.rs index e3d16982..538a90a6 100644 --- a/apps/contracts/integration-test/src/test.rs +++ b/apps/contracts/integration-test/src/test.rs @@ -54,5 +54,5 @@ impl<'a> IntegrationTest<'a> { } #[cfg(test)] -mod test_vault_one_hodl_strategy; -mod test_vault_one_fixed_strategy; \ No newline at end of file +mod vault_one_hodl_strategy; +mod vault_one_fixed_strategy; \ No newline at end of file diff --git a/apps/contracts/integration-test/src/test/test_vault_one_hodl_strategy/mod.rs b/apps/contracts/integration-test/src/test/test_vault_one_hodl_strategy/mod.rs deleted file mode 100644 index 46f7cb99..00000000 --- a/apps/contracts/integration-test/src/test/test_vault_one_hodl_strategy/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod test_deposit; -mod test_withdraw; -mod test_invest; \ No newline at end of file diff --git a/apps/contracts/integration-test/src/test/vault_one_fixed_strategy/deposit.rs b/apps/contracts/integration-test/src/test/vault_one_fixed_strategy/deposit.rs new file mode 100644 index 00000000..43f84cac --- /dev/null +++ b/apps/contracts/integration-test/src/test/vault_one_fixed_strategy/deposit.rs @@ -0,0 +1,323 @@ +use crate::{setup::create_vault_one_asset_fixed_strategy, test::IntegrationTest, vault::{VaultContractError, MINIMUM_LIQUIDITY}}; +use soroban_sdk::{testutils::{MockAuth, MockAuthInvoke}, vec as svec, IntoVal, Vec}; + +#[test] +fn fixed_apr_deposit_success() { + let enviroment = create_vault_one_asset_fixed_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 100_000_0_000_000i128; + + let users = IntegrationTest::generate_random_users(&setup.env, 1); + let user = &users[0]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user, &user_starting_balance); + let user_balance = enviroment.token.balance(user); + assert_eq!(user_balance, user_starting_balance); + + let deposit_amount = 10_000_0_000_000i128; + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); + + let vault_balance = enviroment.token.balance(&enviroment.vault_contract.address); + assert_eq!(vault_balance, deposit_amount); + + let user_balance_after_deposit = enviroment.token.balance(user); + assert_eq!(user_balance_after_deposit, user_starting_balance - deposit_amount); + + let df_balance = enviroment.vault_contract.balance(&user); + assert_eq!(df_balance, deposit_amount - MINIMUM_LIQUIDITY); + + let total_supply = enviroment.vault_contract.total_supply(); + assert_eq!(total_supply, deposit_amount); +} + +#[test] +#[should_panic(expected = "HostError: Error(Contract, #10)")] +fn fixed_apr_deposit_insufficient_balance() { + let enviroment = create_vault_one_asset_fixed_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 5_000_0_000_000i128; // Less than deposit amount + + let users = IntegrationTest::generate_random_users(&setup.env, 1); + let user = &users[0]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user, &user_starting_balance); + let user_balance = enviroment.token.balance(user); + assert_eq!(user_balance, user_starting_balance); + + let deposit_amount = 10_000_0_000_000i128; + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); +} + +#[test] +fn fixed_apr_deposit_multiple_users() { + let enviroment = create_vault_one_asset_fixed_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 100_000_0_000_000i128; + + let users = IntegrationTest::generate_random_users(&setup.env, 2); + let user1 = &users[0]; + let user2 = &users[1]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user1, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user1, &user_starting_balance); + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user2, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user2, &user_starting_balance); + + let user1_balance = enviroment.token.balance(user1); + let user2_balance = enviroment.token.balance(user2); + assert_eq!(user1_balance, user_starting_balance); + assert_eq!(user2_balance, user_starting_balance); + + let deposit_amount = 10_000_0_000_000i128; + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user1.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user1.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user1.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user1, &false); + + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user2.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user2.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user2.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user2, &false); + + let vault_balance = enviroment.token.balance(&enviroment.vault_contract.address); + assert_eq!(vault_balance, deposit_amount * 2); + + let user1_balance_after_deposit = enviroment.token.balance(user1); + let user2_balance_after_deposit = enviroment.token.balance(user2); + assert_eq!(user1_balance_after_deposit, user_starting_balance - deposit_amount); + assert_eq!(user2_balance_after_deposit, user_starting_balance - deposit_amount); + + let df_balance_user1 = enviroment.vault_contract.balance(&user1); + let df_balance_user2 = enviroment.vault_contract.balance(&user2); + assert_eq!(df_balance_user1, deposit_amount - MINIMUM_LIQUIDITY); + assert_eq!(df_balance_user2, deposit_amount); + + let total_supply = enviroment.vault_contract.total_supply(); + assert_eq!(total_supply, deposit_amount * 2); +} + +#[test] +fn fixed_apr_deposit_zero_amount() { + let enviroment = create_vault_one_asset_fixed_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 100_000_0_000_000i128; + + let users = IntegrationTest::generate_random_users(&setup.env, 1); + let user = &users[0]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user, &user_starting_balance); + let user_balance = enviroment.token.balance(user); + assert_eq!(user_balance, user_starting_balance); + + let deposit_amount = 0i128; + let result = enviroment.vault_contract.mock_all_auths().try_deposit( + &svec![&setup.env, deposit_amount], + &svec![&setup.env, deposit_amount], + &user, + &false + ); + + assert_eq!(result, Err(Ok(VaultContractError::InsufficientAmount))); +} + +#[test] +fn fixed_apr_deposit_negative_amount() { + let enviroment = create_vault_one_asset_fixed_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 100_000_0_000_000i128; + + let users = IntegrationTest::generate_random_users(&setup.env, 1); + let user = &users[0]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user, &user_starting_balance); + let user_balance = enviroment.token.balance(user); + assert_eq!(user_balance, user_starting_balance); + + let deposit_amount = -10_000_0_000_000i128; + let result = enviroment.vault_contract.mock_all_auths().try_deposit( + &svec![&setup.env, deposit_amount], + &svec![&setup.env, deposit_amount], + &user, + &false + ); + + assert_eq!(result, Err(Ok(VaultContractError::NegativeNotAllowed))); +} + +#[test] +fn fixed_apr_deposit_insufficient_minimum_liquidity() { + let enviroment = create_vault_one_asset_fixed_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 100_000_0_000_000i128; + + let users = IntegrationTest::generate_random_users(&setup.env, 1); + let user = &users[0]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user, &user_starting_balance); + let user_balance = enviroment.token.balance(user); + assert_eq!(user_balance, user_starting_balance); + + let deposit_amount = MINIMUM_LIQUIDITY - 1; + let result = enviroment.vault_contract.mock_all_auths().try_deposit( + &svec![&setup.env, deposit_amount], + &svec![&setup.env, deposit_amount], + &user, + &false + ); + + assert_eq!(result, Err(Ok(VaultContractError::InsufficientAmount))); +} \ No newline at end of file diff --git a/apps/contracts/integration-test/src/test/test_vault_one_fixed_strategy/mod.rs b/apps/contracts/integration-test/src/test/vault_one_fixed_strategy/mod.rs similarity index 91% rename from apps/contracts/integration-test/src/test/test_vault_one_fixed_strategy/mod.rs rename to apps/contracts/integration-test/src/test/vault_one_fixed_strategy/mod.rs index 33ee3550..e2b73add 100644 --- a/apps/contracts/integration-test/src/test/test_vault_one_fixed_strategy/mod.rs +++ b/apps/contracts/integration-test/src/test/vault_one_fixed_strategy/mod.rs @@ -1,7 +1,7 @@ use super::ONE_YEAR_IN_SECONDS; -mod test_deposit; -mod test_withdraw; +mod deposit; +mod withdraw; pub fn calculate_yield(user_balance: i128, apr: u32, time_elapsed: u64) -> i128 { // Calculate yield based on the APR, time elapsed, and user's balance diff --git a/apps/contracts/integration-test/src/test/vault_one_fixed_strategy/withdraw.rs b/apps/contracts/integration-test/src/test/vault_one_fixed_strategy/withdraw.rs new file mode 100644 index 00000000..ad4ff6b8 --- /dev/null +++ b/apps/contracts/integration-test/src/test/vault_one_fixed_strategy/withdraw.rs @@ -0,0 +1,210 @@ +use crate::{setup::{create_vault_one_asset_fixed_strategy, VAULT_FEE}, test::{vault_one_fixed_strategy::calculate_yield, IntegrationTest, DEFINDEX_FEE, ONE_YEAR_IN_SECONDS}, vault::{defindex_vault_contract::{AssetInvestmentAllocation, StrategyInvestment}, MINIMUM_LIQUIDITY}}; +use soroban_sdk::{testutils::{Ledger, MockAuth, MockAuthInvoke}, vec as svec, IntoVal, Vec}; + +#[test] +fn fixed_apr_no_invest_withdraw_success() { + let enviroment = create_vault_one_asset_fixed_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 10_000_0_000_000i128; + let deposit_amount = 10_000_0_000_000i128; + + let users = IntegrationTest::generate_random_users(&setup.env, 1); + let user = &users[0]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user, &user_starting_balance); + + enviroment.vault_contract.mock_auths(&[MockAuth { + address: &user.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); + + setup.env.ledger().set_timestamp(setup.env.ledger().timestamp() + ONE_YEAR_IN_SECONDS); + + // // TODO: The vault should call harvest method on the strategy contract + // enviroment.strategy_contract.mock_all_auths().harvest(&enviroment.vault_contract.address); + + let df_balance_before_withdraw = enviroment.vault_contract.balance(&user); + assert_eq!(df_balance_before_withdraw, deposit_amount - MINIMUM_LIQUIDITY); + + enviroment.vault_contract.mock_auths(&[MockAuth { + address: &user.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "withdraw", + args: ( + df_balance_before_withdraw.clone(), + user.clone() + ).into_val(&setup.env), + sub_invokes: &[] + }, + }]) + .withdraw(&df_balance_before_withdraw, &user); + + let charged_fee_user = (deposit_amount - MINIMUM_LIQUIDITY) * (DEFINDEX_FEE as i128 + VAULT_FEE as i128) / 10000; + let expected_amount_user = deposit_amount - MINIMUM_LIQUIDITY - charged_fee_user; + + let user_balance_after_withdraw = enviroment.token.balance(user); + assert_eq!(user_balance_after_withdraw, expected_amount_user); + + let vault_balance = enviroment.token.balance(&enviroment.vault_contract.address); + assert_eq!(vault_balance, charged_fee_user + MINIMUM_LIQUIDITY); + + let df_balance = enviroment.vault_contract.balance(&user); + assert_eq!(df_balance, 0); +} + +#[test] +fn fixed_apr_invest_withdraw_success() { + let enviroment = create_vault_one_asset_fixed_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 10_000_0_000_000i128; + let deposit_amount = 10_000_0_000_000i128; + + let users = IntegrationTest::generate_random_users(&setup.env, 1); + let user = &users[0]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user, &user_starting_balance); + + enviroment.vault_contract.mock_auths(&[MockAuth { + address: &user.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); + + let investments = svec![ + &setup.env, + Some(AssetInvestmentAllocation { + asset: enviroment.token.address.clone(), + strategy_investments: svec![ + &setup.env, + Some(StrategyInvestment { + strategy: enviroment.strategy_contract.address.clone(), + amount: deposit_amount, + }), + ], + }), + ]; + + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &enviroment.manager.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "invest", + args: ( + Vec::from_array(&setup.env,[ + Some( + AssetInvestmentAllocation { + asset: enviroment.token.address.clone(), + strategy_investments: + svec![&setup.env, + Some(StrategyInvestment { + strategy: enviroment.strategy_contract.address.clone(), + amount: deposit_amount, + }) + ] + } + ) + ]), + ).into_val(&setup.env), + sub_invokes: &[] + }, + }]) + .invest(&investments); + + setup.env.ledger().set_timestamp(setup.env.ledger().timestamp() + ONE_YEAR_IN_SECONDS); + + // TODO: The vault should call harvest method on the strategy contract + enviroment.strategy_contract.mock_all_auths().harvest(&enviroment.vault_contract.address); + + let df_balance_before_withdraw = enviroment.vault_contract.balance(&user); + assert_eq!(df_balance_before_withdraw, deposit_amount - MINIMUM_LIQUIDITY); + + enviroment.vault_contract.mock_auths(&[MockAuth { + address: &user.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "withdraw", + args: ( + df_balance_before_withdraw.clone(), + user.clone() + ).into_val(&setup.env), + sub_invokes: &[] + }, + }]) + .withdraw(&df_balance_before_withdraw, &user); + + let user_expected_reward = calculate_yield(deposit_amount.clone(), 1000u32, ONE_YEAR_IN_SECONDS); + + let charged_fee_user = (deposit_amount + user_expected_reward - MINIMUM_LIQUIDITY) * (DEFINDEX_FEE as i128 + VAULT_FEE as i128) / 10000; + let expected_amount_user = deposit_amount + user_expected_reward - MINIMUM_LIQUIDITY - charged_fee_user; + + let user_balance_after_withdraw = enviroment.token.balance(user); + //TODO: 98 missing? + assert_eq!(user_balance_after_withdraw, expected_amount_user - 98); + + let vault_balance = enviroment.token.balance(&enviroment.vault_contract.address); + assert_eq!(vault_balance, 0); + + let df_balance = enviroment.vault_contract.balance(&user); + assert_eq!(df_balance, 0); +} \ No newline at end of file diff --git a/apps/contracts/integration-test/src/test/vault_one_hodl_strategy/deposit.rs b/apps/contracts/integration-test/src/test/vault_one_hodl_strategy/deposit.rs new file mode 100644 index 00000000..cd7a4eeb --- /dev/null +++ b/apps/contracts/integration-test/src/test/vault_one_hodl_strategy/deposit.rs @@ -0,0 +1,323 @@ +use crate::{setup::create_vault_one_asset_hodl_strategy, test::IntegrationTest, vault::{VaultContractError, MINIMUM_LIQUIDITY}}; +use soroban_sdk::{testutils::{MockAuth, MockAuthInvoke}, vec as svec, IntoVal, Vec}; + +#[test] +fn test_deposit_success() { + let enviroment = create_vault_one_asset_hodl_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 100_000_0_000_000i128; + + let users = IntegrationTest::generate_random_users(&setup.env, 1); + let user = &users[0]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user, &user_starting_balance); + let user_balance = enviroment.token.balance(user); + assert_eq!(user_balance, user_starting_balance); + + let deposit_amount = 10_000_0_000_000i128; + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); + + let vault_balance = enviroment.token.balance(&enviroment.vault_contract.address); + assert_eq!(vault_balance, deposit_amount); + + let user_balance_after_deposit = enviroment.token.balance(user); + assert_eq!(user_balance_after_deposit, user_starting_balance - deposit_amount); + + let df_balance = enviroment.vault_contract.balance(&user); + assert_eq!(df_balance, deposit_amount - MINIMUM_LIQUIDITY); + + let total_supply = enviroment.vault_contract.total_supply(); + assert_eq!(total_supply, deposit_amount); +} + +#[test] +#[should_panic(expected = "HostError: Error(Contract, #10)")] +fn test_deposit_insufficient_balance() { + let enviroment = create_vault_one_asset_hodl_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 5_000_0_000_000i128; // Less than deposit amount + + let users = IntegrationTest::generate_random_users(&setup.env, 1); + let user = &users[0]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user, &user_starting_balance); + let user_balance = enviroment.token.balance(user); + assert_eq!(user_balance, user_starting_balance); + + let deposit_amount = 10_000_0_000_000i128; + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); +} + +#[test] +fn test_deposit_multiple_users() { + let enviroment = create_vault_one_asset_hodl_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 100_000_0_000_000i128; + + let users = IntegrationTest::generate_random_users(&setup.env, 2); + let user1 = &users[0]; + let user2 = &users[1]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user1, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user1, &user_starting_balance); + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user2, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user2, &user_starting_balance); + + let user1_balance = enviroment.token.balance(user1); + let user2_balance = enviroment.token.balance(user2); + assert_eq!(user1_balance, user_starting_balance); + assert_eq!(user2_balance, user_starting_balance); + + let deposit_amount = 10_000_0_000_000i128; + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user1.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user1.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user1.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user1, &false); + + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user2.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user2.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user2.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user2, &false); + + let vault_balance = enviroment.token.balance(&enviroment.vault_contract.address); + assert_eq!(vault_balance, deposit_amount * 2); + + let user1_balance_after_deposit = enviroment.token.balance(user1); + let user2_balance_after_deposit = enviroment.token.balance(user2); + assert_eq!(user1_balance_after_deposit, user_starting_balance - deposit_amount); + assert_eq!(user2_balance_after_deposit, user_starting_balance - deposit_amount); + + let df_balance_user1 = enviroment.vault_contract.balance(&user1); + let df_balance_user2 = enviroment.vault_contract.balance(&user2); + assert_eq!(df_balance_user1, deposit_amount - MINIMUM_LIQUIDITY); + assert_eq!(df_balance_user2, deposit_amount); + + let total_supply = enviroment.vault_contract.total_supply(); + assert_eq!(total_supply, deposit_amount * 2); +} + +#[test] +fn test_deposit_zero_amount() { + let enviroment = create_vault_one_asset_hodl_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 100_000_0_000_000i128; + + let users = IntegrationTest::generate_random_users(&setup.env, 1); + let user = &users[0]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user, &user_starting_balance); + let user_balance = enviroment.token.balance(user); + assert_eq!(user_balance, user_starting_balance); + + let deposit_amount = 0i128; + let result = enviroment.vault_contract.mock_all_auths().try_deposit( + &svec![&setup.env, deposit_amount], + &svec![&setup.env, deposit_amount], + &user, + &false + ); + + assert_eq!(result, Err(Ok(VaultContractError::InsufficientAmount))); +} + +#[test] +fn test_deposit_negative_amount() { + let enviroment = create_vault_one_asset_hodl_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 100_000_0_000_000i128; + + let users = IntegrationTest::generate_random_users(&setup.env, 1); + let user = &users[0]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user, &user_starting_balance); + let user_balance = enviroment.token.balance(user); + assert_eq!(user_balance, user_starting_balance); + + let deposit_amount = -10_000_0_000_000i128; + let result = enviroment.vault_contract.mock_all_auths().try_deposit( + &svec![&setup.env, deposit_amount], + &svec![&setup.env, deposit_amount], + &user, + &false + ); + + assert_eq!(result, Err(Ok(VaultContractError::NegativeNotAllowed))); +} + +#[test] +fn test_deposit_insufficient_minimum_liquidity() { + let enviroment = create_vault_one_asset_hodl_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 100_000_0_000_000i128; + + let users = IntegrationTest::generate_random_users(&setup.env, 1); + let user = &users[0]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user, &user_starting_balance); + let user_balance = enviroment.token.balance(user); + assert_eq!(user_balance, user_starting_balance); + + let deposit_amount = MINIMUM_LIQUIDITY - 1; + let result = enviroment.vault_contract.mock_all_auths().try_deposit( + &svec![&setup.env, deposit_amount], + &svec![&setup.env, deposit_amount], + &user, + &false + ); + + assert_eq!(result, Err(Ok(VaultContractError::InsufficientAmount))); +} \ No newline at end of file diff --git a/apps/contracts/integration-test/src/test/vault_one_hodl_strategy/invest.rs b/apps/contracts/integration-test/src/test/vault_one_hodl_strategy/invest.rs new file mode 100644 index 00000000..faec6852 --- /dev/null +++ b/apps/contracts/integration-test/src/test/vault_one_hodl_strategy/invest.rs @@ -0,0 +1,426 @@ +use crate::{setup::create_vault_one_asset_hodl_strategy, test::{IntegrationTest, ONE_YEAR_IN_SECONDS}, vault::{defindex_vault_contract::{AssetInvestmentAllocation, StrategyInvestment}, VaultContractError, MINIMUM_LIQUIDITY}}; +use soroban_sdk::{testutils::{Ledger, MockAuth, MockAuthInvoke, Address as _}, vec as svec, Address, IntoVal, Vec}; + +#[test] +fn test_invest_success() { + let enviroment = create_vault_one_asset_hodl_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 100_000_0_000_000i128; + + let users = IntegrationTest::generate_random_users(&setup.env, 1); + let user = &users[0]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user, &user_starting_balance); + let user_balance = enviroment.token.balance(user); + assert_eq!(user_balance, user_starting_balance); + + let deposit_amount = 10_000_0_000_000i128; + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); + + let df_balance = enviroment.vault_contract.balance(&user); + assert_eq!(df_balance, deposit_amount - MINIMUM_LIQUIDITY); + + // Create investment strategies for the deposited tokens + let investments = svec![ + &setup.env, + Some(AssetInvestmentAllocation { + asset: enviroment.token.address.clone(), + strategy_investments: svec![ + &setup.env, + Some(StrategyInvestment { + strategy: enviroment.strategy_contract.address.clone(), + amount: deposit_amount, + }), + ], + }), + ]; + + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &enviroment.manager.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "invest", + args: ( + Vec::from_array(&setup.env,[ + Some( + AssetInvestmentAllocation { + asset: enviroment.token.address.clone(), + strategy_investments: + svec![&setup.env, + Some(StrategyInvestment { + strategy: enviroment.strategy_contract.address.clone(), + amount: deposit_amount, + }) + ] + } + ) + ]), + ).into_val(&setup.env), + sub_invokes: &[] + }, + }]) + .invest(&investments); + + setup.env.ledger().set_timestamp(setup.env.ledger().timestamp() + ONE_YEAR_IN_SECONDS); + + let token_balance_after_invest = enviroment.token.balance(&enviroment.vault_contract.address); + assert_eq!(token_balance_after_invest, 0); + + let strategy_balance = enviroment.strategy_contract.balance(&enviroment.vault_contract.address); + assert_eq!(strategy_balance, deposit_amount); +} + +#[test] +fn test_invest_exceeding_investing_lenght() { + let enviroment = create_vault_one_asset_hodl_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 100_000_0_000_000i128; + + let users = IntegrationTest::generate_random_users(&setup.env, 1); + let user = &users[0]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user, &user_starting_balance); + let user_balance = enviroment.token.balance(user); + assert_eq!(user_balance, user_starting_balance); + + let deposit_amount = 10_000_0_000_000i128; + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); + + let df_balance = enviroment.vault_contract.balance(&user); + assert_eq!(df_balance, deposit_amount - MINIMUM_LIQUIDITY); + + let asset_address_2 = Address::generate(&setup.env); + let strategy_address_2 = Address::generate(&setup.env); + // Create investment strategies exceeding the allowed number + let investments = svec![ + &setup.env, + Some(AssetInvestmentAllocation { + asset: enviroment.token.address.clone(), + strategy_investments: svec![ + &setup.env, + Some(StrategyInvestment { + strategy: enviroment.strategy_contract.address.clone(), + amount: deposit_amount, + }), + ], + }), + Some(AssetInvestmentAllocation { + asset: asset_address_2.clone(), + strategy_investments: svec![ + &setup.env, + Some(StrategyInvestment { + strategy: strategy_address_2.clone(), + amount: deposit_amount, + }), + ], + }), + ]; + + let result = enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &enviroment.manager.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "invest", + args: ( + Vec::from_array(&setup.env,[ + Some( + AssetInvestmentAllocation { + asset: enviroment.token.address.clone(), + strategy_investments: + svec![&setup.env, + Some(StrategyInvestment { + strategy: enviroment.strategy_contract.address.clone(), + amount: deposit_amount, + }) + ] + } + ), + Some( + AssetInvestmentAllocation { + asset: asset_address_2.clone(), + strategy_investments: + svec![&setup.env, + Some(StrategyInvestment { + strategy: strategy_address_2.clone(), + amount: deposit_amount, + }) + ] + } + ) + ]), + ).into_val(&setup.env), + sub_invokes: &[] + }, + }]) + .try_invest(&investments); + + assert_eq!(result, Err(Ok(VaultContractError::WrongInvestmentLength))); +} + +#[test] +#[should_panic(expected = "HostError: Error(Contract, #10)")] +fn test_invest_insufficient_balance() { + let enviroment = create_vault_one_asset_hodl_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 5_000_0_000_000i128; // Less than deposit amount + + let users = IntegrationTest::generate_random_users(&setup.env, 1); + let user = &users[0]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user, &user_starting_balance); + let user_balance = enviroment.token.balance(user); + assert_eq!(user_balance, user_starting_balance); + + let deposit_amount = 10_000_0_000_000i128; + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); +} + +#[test] +fn test_invest_multiple_users() { + let enviroment = create_vault_one_asset_hodl_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 100_000_0_000_000i128; + + let users = IntegrationTest::generate_random_users(&setup.env, 2); + let user1 = &users[0]; + let user2 = &users[1]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user1, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user1, &user_starting_balance); + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user2, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user2, &user_starting_balance); + + let user1_balance = enviroment.token.balance(user1); + let user2_balance = enviroment.token.balance(user2); + assert_eq!(user1_balance, user_starting_balance); + assert_eq!(user2_balance, user_starting_balance); + + let deposit_amount = 10_000_0_000_000i128; + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user1.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user1.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user1.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user1, &false); + + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user2.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user2.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user2.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user2, &false); + + let df_balance_user1 = enviroment.vault_contract.balance(&user1); + let df_balance_user2 = enviroment.vault_contract.balance(&user2); + assert_eq!(df_balance_user1, deposit_amount - MINIMUM_LIQUIDITY); + assert_eq!(df_balance_user2, deposit_amount); + + // Create investment strategies for the deposited tokens + let investments = svec![ + &setup.env, + Some(AssetInvestmentAllocation { + asset: enviroment.token.address.clone(), + strategy_investments: svec![ + &setup.env, + Some(StrategyInvestment { + strategy: enviroment.strategy_contract.address.clone(), + amount: deposit_amount*2, + }), + ], + }), + ]; + + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &enviroment.manager.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "invest", + args: ( + Vec::from_array(&setup.env,[ + Some( + AssetInvestmentAllocation { + asset: enviroment.token.address.clone(), + strategy_investments: + svec![&setup.env, + Some(StrategyInvestment { + strategy: enviroment.strategy_contract.address.clone(), + amount: deposit_amount*2, + }) + ] + } + ) + ]), + ).into_val(&setup.env), + sub_invokes: &[] + }, + }]) + .invest(&investments); + + setup.env.ledger().set_timestamp(setup.env.ledger().timestamp() + ONE_YEAR_IN_SECONDS); + + let token_balance_after_invest = enviroment.token.balance(&enviroment.vault_contract.address); + assert_eq!(token_balance_after_invest, 0); + + let strategy_balance = enviroment.strategy_contract.balance(&enviroment.vault_contract.address); + assert_eq!(strategy_balance, deposit_amount * 2); +} \ No newline at end of file diff --git a/apps/contracts/integration-test/src/test/vault_one_hodl_strategy/mod.rs b/apps/contracts/integration-test/src/test/vault_one_hodl_strategy/mod.rs new file mode 100644 index 00000000..61f3a5a6 --- /dev/null +++ b/apps/contracts/integration-test/src/test/vault_one_hodl_strategy/mod.rs @@ -0,0 +1,3 @@ +mod deposit; +mod withdraw; +mod invest; \ No newline at end of file diff --git a/apps/contracts/integration-test/src/test/vault_one_hodl_strategy/withdraw.rs b/apps/contracts/integration-test/src/test/vault_one_hodl_strategy/withdraw.rs new file mode 100644 index 00000000..e3601633 --- /dev/null +++ b/apps/contracts/integration-test/src/test/vault_one_hodl_strategy/withdraw.rs @@ -0,0 +1,728 @@ +use crate::{setup::{create_vault_one_asset_hodl_strategy, VAULT_FEE}, test::{IntegrationTest, DEFINDEX_FEE, ONE_YEAR_IN_SECONDS}, vault::{defindex_vault_contract::{AssetInvestmentAllocation, StrategyInvestment}, VaultContractError, MINIMUM_LIQUIDITY}}; +use soroban_sdk::{testutils::{Ledger, MockAuth, MockAuthInvoke}, vec as svec, IntoVal, Vec}; + +#[test] +fn test_withdraw_no_invest_success() { + let enviroment = create_vault_one_asset_hodl_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 100_000_0_000_000i128; + + let users = IntegrationTest::generate_random_users(&setup.env, 1); + let user = &users[0]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user, &user_starting_balance); + let user_balance = enviroment.token.balance(user); + assert_eq!(user_balance, user_starting_balance); + + let deposit_amount = 10_000_0_000_000i128; + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); + + let df_balance = enviroment.vault_contract.balance(&user); + assert_eq!(df_balance, deposit_amount - MINIMUM_LIQUIDITY); + + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "withdraw", + args: ( + df_balance.clone(), + user.clone() + ).into_val(&setup.env), + sub_invokes: &[] + }, + }]) + .withdraw(&df_balance, &user); + + let df_balance = enviroment.vault_contract.balance(&user); + assert_eq!(df_balance, 0); + + let vault_balance_after_withdraw = enviroment.token.balance(&enviroment.vault_contract.address); + assert_eq!(vault_balance_after_withdraw, MINIMUM_LIQUIDITY); + + let user_balance_after_withdraw = enviroment.token.balance(user); + assert_eq!(user_balance_after_withdraw, user_starting_balance - MINIMUM_LIQUIDITY); + + let df_balance = enviroment.vault_contract.balance(&user); + assert_eq!(df_balance, 0); + + let total_supply = enviroment.vault_contract.total_supply(); + assert_eq!(total_supply, MINIMUM_LIQUIDITY); +} + +#[test] +fn test_withdraw_partial_success() { + let enviroment = create_vault_one_asset_hodl_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 100_000_0_000_000i128; + + let users = IntegrationTest::generate_random_users(&setup.env, 1); + let user = &users[0]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user, &user_starting_balance); + let user_balance = enviroment.token.balance(user); + assert_eq!(user_balance, user_starting_balance); + + let deposit_amount = 10_000_0_000_000i128; + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); + + let df_balance = enviroment.vault_contract.balance(&user); + assert_eq!(df_balance, deposit_amount - MINIMUM_LIQUIDITY); + + let withdraw_amount = df_balance / 2; + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "withdraw", + args: ( + withdraw_amount.clone(), + user.clone() + ).into_val(&setup.env), + sub_invokes: &[] + }, + }]) + .withdraw(&withdraw_amount, &user); + + let df_balance = enviroment.vault_contract.balance(&user); + assert_eq!(df_balance, withdraw_amount); + + let vault_balance_after_withdraw = enviroment.token.balance(&enviroment.vault_contract.address); + assert_eq!(vault_balance_after_withdraw, deposit_amount - withdraw_amount); + + let user_balance_after_withdraw = enviroment.token.balance(user); + assert_eq!(user_balance_after_withdraw, user_starting_balance - (deposit_amount - withdraw_amount)); +} + +#[test] +fn test_withdraw_insufficient_balance() { + let enviroment = create_vault_one_asset_hodl_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 100_000_0_000_000i128; + + let users = IntegrationTest::generate_random_users(&setup.env, 1); + let user = &users[0]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user, &user_starting_balance); + let user_balance = enviroment.token.balance(user); + assert_eq!(user_balance, user_starting_balance); + + let deposit_amount = 10_000_0_000_000i128; + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); + + let df_balance = enviroment.vault_contract.balance(&user); + assert_eq!(df_balance, deposit_amount - MINIMUM_LIQUIDITY); + + let withdraw_amount = df_balance + 1; // Attempt to withdraw more than the balance + let result = enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "withdraw", + args: ( + withdraw_amount.clone(), + user.clone() + ).into_val(&setup.env), + sub_invokes: &[] + }, + }]) + .try_withdraw(&withdraw_amount, &user); + assert_eq!(result, Err(Ok(VaultContractError::InsufficientBalance))); + + let df_balance = enviroment.vault_contract.balance(&user); + assert_eq!(df_balance, deposit_amount - MINIMUM_LIQUIDITY); + + let vault_balance_after_withdraw = enviroment.token.balance(&enviroment.vault_contract.address); + assert_eq!(vault_balance_after_withdraw, deposit_amount); + + let user_balance_after_withdraw = enviroment.token.balance(user); + assert_eq!(user_balance_after_withdraw, user_starting_balance - deposit_amount); +} + +#[test] +fn test_withdraw_after_invest() { + let enviroment = create_vault_one_asset_hodl_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 10_000_0_000_000i128; + + let users = IntegrationTest::generate_random_users(&setup.env, 1); + let user = &users[0]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user, &user_starting_balance); + let user_balance = enviroment.token.balance(user); + assert_eq!(user_balance, user_starting_balance); + + let deposit_amount = 10_000_0_000_000i128; + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user, &false); + + let user_balance_after_deposit = enviroment.token.balance(user); + assert_eq!(user_balance_after_deposit, 0); + + let df_balance = enviroment.vault_contract.balance(&user); + assert_eq!(df_balance, deposit_amount - MINIMUM_LIQUIDITY); + + // Create investment strategies for the deposited tokens + let investments = svec![ + &setup.env, + Some(AssetInvestmentAllocation { + asset: enviroment.token.address.clone(), + strategy_investments: svec![ + &setup.env, + Some(StrategyInvestment { + strategy: enviroment.strategy_contract.address.clone(), + amount: deposit_amount, + }), + ], + }), + ]; + + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &enviroment.manager.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "invest", + args: ( + Vec::from_array(&setup.env,[ + Some( + AssetInvestmentAllocation { + asset: enviroment.token.address.clone(), + strategy_investments: + svec![&setup.env, + Some(StrategyInvestment { + strategy: enviroment.strategy_contract.address.clone(), + amount: deposit_amount, + }) + ] + } + ) + ]), + ).into_val(&setup.env), + sub_invokes: &[] + }, + }]) + .invest(&investments); + + setup.env.ledger().set_timestamp(setup.env.ledger().timestamp() + ONE_YEAR_IN_SECONDS); + + let token_balance_after_invest = enviroment.token.balance(&enviroment.vault_contract.address); + assert_eq!(token_balance_after_invest, 0); + + let strategy_balance = enviroment.strategy_contract.balance(&enviroment.vault_contract.address); + assert_eq!(strategy_balance, deposit_amount); + + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "withdraw", + args: ( + df_balance.clone(), + user.clone() + ).into_val(&setup.env), + sub_invokes: &[] + }, + }]) + .withdraw(&df_balance, &user); + + let df_balance = enviroment.vault_contract.balance(&user); + assert_eq!(df_balance, 0); + + let token_balance_after_withdraw = enviroment.token.balance(&enviroment.vault_contract.address); + assert_eq!(token_balance_after_withdraw, 0); + + let charged_fee = (deposit_amount - MINIMUM_LIQUIDITY) * (DEFINDEX_FEE as i128 + VAULT_FEE as i128) / 10000; + let expected_amount = deposit_amount - MINIMUM_LIQUIDITY - charged_fee; + + let user_balance_after_withdraw = enviroment.token.balance(user); + assert_eq!(user_balance_after_withdraw, expected_amount); + + let strategy_balance = enviroment.strategy_contract.balance(&enviroment.vault_contract.address); + assert_eq!(strategy_balance, charged_fee + MINIMUM_LIQUIDITY); + + let vault_balance_after_withdraw = enviroment.token.balance(&enviroment.vault_contract.address); + assert_eq!(vault_balance_after_withdraw, 0); +} + +#[test] +fn test_withdraw_multiple_users() { + let enviroment = create_vault_one_asset_hodl_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 100_000_0_000_000i128; + + let users = IntegrationTest::generate_random_users(&setup.env, 2); + let user1 = &users[0]; + let user2 = &users[1]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user1, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user1, &user_starting_balance); + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user2, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user2, &user_starting_balance); + + let user1_balance = enviroment.token.balance(user1); + let user2_balance = enviroment.token.balance(user2); + assert_eq!(user1_balance, user_starting_balance); + assert_eq!(user2_balance, user_starting_balance); + + let deposit_amount = 10_000_0_000_000i128; + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user1.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user1.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user1.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user1, &false); + + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user2.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user2.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user2.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user2, &false); + + let df_balance_user1 = enviroment.vault_contract.balance(&user1); + let df_balance_user2 = enviroment.vault_contract.balance(&user2); + assert_eq!(df_balance_user1, deposit_amount - MINIMUM_LIQUIDITY); + assert_eq!(df_balance_user2, deposit_amount); + + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user1.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "withdraw", + args: ( + df_balance_user1.clone(), + user1.clone() + ).into_val(&setup.env), + sub_invokes: &[] + }, + }]) + .withdraw(&df_balance_user1, &user1); + + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user2.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "withdraw", + args: ( + df_balance_user2.clone(), + user2.clone() + ).into_val(&setup.env), + sub_invokes: &[] + }, + }]) + .withdraw(&df_balance_user2, &user2); + + let df_balance_user1 = enviroment.vault_contract.balance(&user1); + let df_balance_user2 = enviroment.vault_contract.balance(&user2); + assert_eq!(df_balance_user1, 0); + assert_eq!(df_balance_user2, 0); + + let vault_balance_after_withdraw = enviroment.token.balance(&enviroment.vault_contract.address); + assert_eq!(vault_balance_after_withdraw, MINIMUM_LIQUIDITY); + + let user1_balance_after_withdraw = enviroment.token.balance(user1); + let user2_balance_after_withdraw = enviroment.token.balance(user2); + assert_eq!(user1_balance_after_withdraw, user_starting_balance - MINIMUM_LIQUIDITY); + assert_eq!(user2_balance_after_withdraw, user_starting_balance); + + let total_supply = enviroment.vault_contract.total_supply(); + assert_eq!(total_supply, MINIMUM_LIQUIDITY); +} + +#[test] +fn test_withdraw_after_invest_multiple_users() { + let enviroment = create_vault_one_asset_hodl_strategy(); + let setup = enviroment.setup; + + let user_starting_balance = 10_000_0_000_000i128; + + let users = IntegrationTest::generate_random_users(&setup.env, 2); + let user1 = &users[0]; + let user2 = &users[1]; + + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user1, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user1, &user_starting_balance); + enviroment.token_admin_client.mock_auths(&[MockAuth { + address: &enviroment.token_admin.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "mint", + args: (user2, user_starting_balance,).into_val(&setup.env), + sub_invokes: &[], + }, + }]).mint(user2, &user_starting_balance); + + let user1_balance = enviroment.token.balance(user1); + let user2_balance = enviroment.token.balance(user2); + assert_eq!(user1_balance, user_starting_balance); + assert_eq!(user2_balance, user_starting_balance); + + let deposit_amount = 10_000_0_000_000i128; + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user1.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user1.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user1.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user1, &false); + + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user2.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "deposit", + args: ( + Vec::from_array(&setup.env,[deposit_amount]), + Vec::from_array(&setup.env,[deposit_amount]), + user2.clone() + ).into_val(&setup.env), + sub_invokes: &[ + MockAuthInvoke { + contract: &enviroment.token.address.clone(), + fn_name: "transfer", + args: ( + user2.clone(), + &enviroment.vault_contract.address.clone(), + deposit_amount + ).into_val(&setup.env), + sub_invokes: &[] + } + ] + }, + }]) + .deposit(&svec![&setup.env, deposit_amount], &svec![&setup.env, deposit_amount], &user2, &false); + + let df_balance_user1 = enviroment.vault_contract.balance(&user1); + let df_balance_user2 = enviroment.vault_contract.balance(&user2); + assert_eq!(df_balance_user1, deposit_amount - MINIMUM_LIQUIDITY); + assert_eq!(df_balance_user2, deposit_amount); + + // Create investment strategies for the deposited tokens + let investments = svec![ + &setup.env, + Some(AssetInvestmentAllocation { + asset: enviroment.token.address.clone(), + strategy_investments: svec![ + &setup.env, + Some(StrategyInvestment { + strategy: enviroment.strategy_contract.address.clone(), + amount: deposit_amount * 2, + }), + ], + }), + ]; + + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &enviroment.manager.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "invest", + args: ( + Vec::from_array(&setup.env,[ + Some( + AssetInvestmentAllocation { + asset: enviroment.token.address.clone(), + strategy_investments: + svec![&setup.env, + Some(StrategyInvestment { + strategy: enviroment.strategy_contract.address.clone(), + amount: deposit_amount * 2, + }) + ] + } + ) + ]), + ).into_val(&setup.env), + sub_invokes: &[] + }, + }]) + .invest(&investments); + + setup.env.ledger().set_timestamp(setup.env.ledger().timestamp() + ONE_YEAR_IN_SECONDS); + + let token_balance_after_invest = enviroment.token.balance(&enviroment.vault_contract.address); + assert_eq!(token_balance_after_invest, 0); + + let strategy_balance = enviroment.strategy_contract.balance(&enviroment.vault_contract.address); + assert_eq!(strategy_balance, deposit_amount * 2); + + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user1.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "withdraw", + args: ( + df_balance_user1.clone(), + user1.clone() + ).into_val(&setup.env), + sub_invokes: &[] + }, + }]) + .withdraw(&df_balance_user1, &user1); + + enviroment.vault_contract + .mock_auths(&[MockAuth { + address: &user2.clone(), + invoke: &MockAuthInvoke { + contract: &enviroment.vault_contract.address.clone(), + fn_name: "withdraw", + args: ( + df_balance_user2.clone(), + user2.clone() + ).into_val(&setup.env), + sub_invokes: &[] + }, + }]) + .withdraw(&df_balance_user2, &user2); + + let df_balance_user1 = enviroment.vault_contract.balance(&user1); + let df_balance_user2 = enviroment.vault_contract.balance(&user2); + assert_eq!(df_balance_user1, 0); + assert_eq!(df_balance_user2, 0); + + let token_balance_after_withdraw = enviroment.token.balance(&enviroment.vault_contract.address); + assert_eq!(token_balance_after_withdraw, 0); + + let charged_fee_user1 = (deposit_amount - MINIMUM_LIQUIDITY) * (DEFINDEX_FEE as i128 + VAULT_FEE as i128) / 10000; + let expected_amount_user1 = deposit_amount - MINIMUM_LIQUIDITY - charged_fee_user1; + + let charged_fee_user2 = deposit_amount * (DEFINDEX_FEE as i128 + VAULT_FEE as i128) / 10000; + let expected_amount_user2 = deposit_amount - charged_fee_user2; + + let user1_balance_after_withdraw = enviroment.token.balance(user1); + let user2_balance_after_withdraw = enviroment.token.balance(user2); + assert_eq!(user1_balance_after_withdraw, expected_amount_user1); + assert_eq!(user2_balance_after_withdraw, expected_amount_user2); + + let strategy_balance = enviroment.strategy_contract.balance(&enviroment.vault_contract.address); + assert_eq!(strategy_balance, charged_fee_user1 + charged_fee_user2 + MINIMUM_LIQUIDITY); + + let vault_balance_after_withdraw = enviroment.token.balance(&enviroment.vault_contract.address); + assert_eq!(vault_balance_after_withdraw, 0); +} \ No newline at end of file diff --git a/apps/contracts/strategies/blend/src/test.rs b/apps/contracts/strategies/blend/src/test.rs index ba5282eb..09398008 100644 --- a/apps/contracts/strategies/blend/src/test.rs +++ b/apps/contracts/strategies/blend/src/test.rs @@ -73,7 +73,4 @@ impl<'a> HodlStrategyTest<'a> { // } } -mod initialize; -mod deposit; -mod events; -mod withdraw; \ No newline at end of file +mod blend; \ No newline at end of file diff --git a/apps/contracts/strategies/blend/src/test/deposit.rs b/apps/contracts/strategies/blend/src/test/blend/deposit.rs similarity index 100% rename from apps/contracts/strategies/blend/src/test/deposit.rs rename to apps/contracts/strategies/blend/src/test/blend/deposit.rs diff --git a/apps/contracts/strategies/blend/src/test/events.rs b/apps/contracts/strategies/blend/src/test/blend/events.rs similarity index 100% rename from apps/contracts/strategies/blend/src/test/events.rs rename to apps/contracts/strategies/blend/src/test/blend/events.rs diff --git a/apps/contracts/strategies/blend/src/test/initialize.rs b/apps/contracts/strategies/blend/src/test/blend/initialize.rs similarity index 100% rename from apps/contracts/strategies/blend/src/test/initialize.rs rename to apps/contracts/strategies/blend/src/test/blend/initialize.rs diff --git a/apps/contracts/strategies/blend/src/test/blend/mod.rs b/apps/contracts/strategies/blend/src/test/blend/mod.rs new file mode 100644 index 00000000..75e4b2f2 --- /dev/null +++ b/apps/contracts/strategies/blend/src/test/blend/mod.rs @@ -0,0 +1,4 @@ +mod deposit; +mod events; +mod initialize; +mod withdraw; \ No newline at end of file diff --git a/apps/contracts/strategies/blend/src/test/withdraw.rs b/apps/contracts/strategies/blend/src/test/blend/withdraw.rs similarity index 100% rename from apps/contracts/strategies/blend/src/test/withdraw.rs rename to apps/contracts/strategies/blend/src/test/blend/withdraw.rs diff --git a/apps/contracts/strategies/fixed_apr/src/test.rs b/apps/contracts/strategies/fixed_apr/src/test.rs index 3885aeb8..203ca66a 100644 --- a/apps/contracts/strategies/fixed_apr/src/test.rs +++ b/apps/contracts/strategies/fixed_apr/src/test.rs @@ -58,7 +58,4 @@ impl<'a> FixAprStrategyTest<'a> { } } -mod initialize; -mod deposit; -mod harvest; -mod withdraw; \ No newline at end of file +mod fixed_apr; \ No newline at end of file diff --git a/apps/contracts/strategies/fixed_apr/src/test/deposit.rs b/apps/contracts/strategies/fixed_apr/src/test/fixed_apr/deposit.rs similarity index 100% rename from apps/contracts/strategies/fixed_apr/src/test/deposit.rs rename to apps/contracts/strategies/fixed_apr/src/test/fixed_apr/deposit.rs diff --git a/apps/contracts/strategies/fixed_apr/src/test/harvest.rs b/apps/contracts/strategies/fixed_apr/src/test/fixed_apr/harvest.rs similarity index 100% rename from apps/contracts/strategies/fixed_apr/src/test/harvest.rs rename to apps/contracts/strategies/fixed_apr/src/test/fixed_apr/harvest.rs diff --git a/apps/contracts/strategies/fixed_apr/src/test/initialize.rs b/apps/contracts/strategies/fixed_apr/src/test/fixed_apr/initialize.rs similarity index 100% rename from apps/contracts/strategies/fixed_apr/src/test/initialize.rs rename to apps/contracts/strategies/fixed_apr/src/test/fixed_apr/initialize.rs diff --git a/apps/contracts/strategies/fixed_apr/src/test/fixed_apr/mod.rs b/apps/contracts/strategies/fixed_apr/src/test/fixed_apr/mod.rs new file mode 100644 index 00000000..8bf6f532 --- /dev/null +++ b/apps/contracts/strategies/fixed_apr/src/test/fixed_apr/mod.rs @@ -0,0 +1,4 @@ +mod deposit; +mod harvest; +mod initialize; +mod withdraw; \ No newline at end of file diff --git a/apps/contracts/strategies/fixed_apr/src/test/withdraw.rs b/apps/contracts/strategies/fixed_apr/src/test/fixed_apr/withdraw.rs similarity index 100% rename from apps/contracts/strategies/fixed_apr/src/test/withdraw.rs rename to apps/contracts/strategies/fixed_apr/src/test/fixed_apr/withdraw.rs diff --git a/apps/contracts/strategies/hodl/src/test.rs b/apps/contracts/strategies/hodl/src/test.rs index f8944046..134c5e63 100644 --- a/apps/contracts/strategies/hodl/src/test.rs +++ b/apps/contracts/strategies/hodl/src/test.rs @@ -57,7 +57,4 @@ impl<'a> HodlStrategyTest<'a> { // } } -mod initialize; -mod deposit; -mod events; -mod withdraw; \ No newline at end of file +mod hodl; \ No newline at end of file diff --git a/apps/contracts/strategies/hodl/src/test/deposit.rs b/apps/contracts/strategies/hodl/src/test/hodl/deposit.rs similarity index 100% rename from apps/contracts/strategies/hodl/src/test/deposit.rs rename to apps/contracts/strategies/hodl/src/test/hodl/deposit.rs diff --git a/apps/contracts/strategies/hodl/src/test/events.rs b/apps/contracts/strategies/hodl/src/test/hodl/events.rs similarity index 100% rename from apps/contracts/strategies/hodl/src/test/events.rs rename to apps/contracts/strategies/hodl/src/test/hodl/events.rs diff --git a/apps/contracts/strategies/hodl/src/test/initialize.rs b/apps/contracts/strategies/hodl/src/test/hodl/initialize.rs similarity index 100% rename from apps/contracts/strategies/hodl/src/test/initialize.rs rename to apps/contracts/strategies/hodl/src/test/hodl/initialize.rs diff --git a/apps/contracts/strategies/hodl/src/test/hodl/mod.rs b/apps/contracts/strategies/hodl/src/test/hodl/mod.rs new file mode 100644 index 00000000..75e4b2f2 --- /dev/null +++ b/apps/contracts/strategies/hodl/src/test/hodl/mod.rs @@ -0,0 +1,4 @@ +mod deposit; +mod events; +mod initialize; +mod withdraw; \ No newline at end of file diff --git a/apps/contracts/strategies/hodl/src/test/withdraw.rs b/apps/contracts/strategies/hodl/src/test/hodl/withdraw.rs similarity index 100% rename from apps/contracts/strategies/hodl/src/test/withdraw.rs rename to apps/contracts/strategies/hodl/src/test/hodl/withdraw.rs diff --git a/apps/contracts/vault/src/test.rs b/apps/contracts/vault/src/test.rs index de9bb0d3..8f9f2222 100755 --- a/apps/contracts/vault/src/test.rs +++ b/apps/contracts/vault/src/test.rs @@ -167,11 +167,4 @@ impl<'a> DeFindexVaultTest<'a> { } } -mod initialize; -mod deposit; -mod admin; -mod invest; -mod withdraw; -mod emergency_withdraw; -mod rebalance; -mod deposit_and_invest; +mod vault; diff --git a/apps/contracts/vault/src/test/admin.rs b/apps/contracts/vault/src/test/vault/admin.rs similarity index 97% rename from apps/contracts/vault/src/test/admin.rs rename to apps/contracts/vault/src/test/vault/admin.rs index 7860682c..af96b289 100644 --- a/apps/contracts/vault/src/test/admin.rs +++ b/apps/contracts/vault/src/test/vault/admin.rs @@ -12,7 +12,7 @@ extern crate alloc; use alloc::vec; #[test] -fn test_set_new_fee_receiver_by_fee_receiver() { +fn set_new_fee_receiver_by_fee_receiver() { let test = DeFindexVaultTest::setup(); let strategy_params_token0 = create_strategy_params_token0(&test); let strategy_params_token1 = create_strategy_params_token1(&test); @@ -77,7 +77,7 @@ fn test_set_new_fee_receiver_by_fee_receiver() { } #[test] -fn test_set_new_fee_receiver_by_manager() { +fn set_new_fee_receiver_by_manager() { let test = DeFindexVaultTest::setup(); let strategy_params_token0 = create_strategy_params_token0(&test); let strategy_params_token1 = create_strategy_params_token1(&test); @@ -142,7 +142,7 @@ fn test_set_new_fee_receiver_by_manager() { #[test] #[should_panic(expected = "HostError: Error(Contract, #130)")] // Unauthorized -fn test_set_new_fee_receiver_by_emergency_manager() { +fn set_new_fee_receiver_by_emergency_manager() { let test = DeFindexVaultTest::setup(); let strategy_params_token0 = create_strategy_params_token0(&test); let strategy_params_token1 = create_strategy_params_token1(&test); @@ -184,7 +184,7 @@ fn test_set_new_fee_receiver_by_emergency_manager() { #[test] #[should_panic(expected = "HostError: Error(Contract, #130)")] // Unauthorized -fn test_set_new_fee_receiver_invalid_sender() { +fn set_new_fee_receiver_invalid_sender() { let test = DeFindexVaultTest::setup(); let strategy_params_token0 = create_strategy_params_token0(&test); let strategy_params_token1 = create_strategy_params_token1(&test); @@ -223,7 +223,7 @@ fn test_set_new_fee_receiver_invalid_sender() { } #[test] -fn test_set_new_manager_by_manager() { +fn set_new_manager_by_manager() { let test = DeFindexVaultTest::setup(); let strategy_params_token0 = create_strategy_params_token0(&test); let strategy_params_token1 = create_strategy_params_token1(&test); diff --git a/apps/contracts/vault/src/test/vault/deposit.rs b/apps/contracts/vault/src/test/vault/deposit.rs new file mode 100644 index 00000000..e86e118c --- /dev/null +++ b/apps/contracts/vault/src/test/vault/deposit.rs @@ -0,0 +1,1031 @@ +use soroban_sdk::{vec as sorobanvec, InvokeError, Map, String, Vec}; + +use crate::test::defindex_vault::{AssetStrategySet, ContractError}; +use crate::test::{ + create_strategy_params_token0, create_strategy_params_token1, DeFindexVaultTest, +}; + +// Test deposit not yet initialized +#[test] +fn not_yet_initialized() { + let test = DeFindexVaultTest::setup(); + let users = DeFindexVaultTest::generate_random_users(&test.env, 1); + + let result = test.defindex_contract.try_deposit( + &sorobanvec![&test.env, 100i128], + &sorobanvec![&test.env, 100i128], + &users[0], + &false, + ); + + assert_eq!(result, Err(Ok(ContractError::NotInitialized))); +} + + +#[test] +fn amounts_desired_less_length() { + 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 amount = 1000i128; + + let users = DeFindexVaultTest::generate_random_users(&test.env, 1); + + let response = test.defindex_contract.try_deposit( + &sorobanvec![&test.env, amount], // wrong amount desired + &sorobanvec![&test.env, amount, amount], + &users[0], + &false, + ); + + assert_eq!(response, Err(Ok(ContractError::WrongAmountsLength))); +} + +// test deposit amount desired more length +#[test] +fn amounts_desired_more_length() { + 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() + } + ]; + + 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 = 1000i128; + + let users = DeFindexVaultTest::generate_random_users(&test.env, 1); + + let response = test.defindex_contract.try_deposit( + &sorobanvec![&test.env, amount, amount], // wrong amount desired + &sorobanvec![&test.env, amount], + &users[0], + &false, + ); + + assert_eq!(response, Err(Ok(ContractError::WrongAmountsLength))); +} + +// test deposit amount min less length +#[test] +fn amounts_min_less_length() { + 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 amount = 1000i128; + + let users = DeFindexVaultTest::generate_random_users(&test.env, 1); + + let response = test.defindex_contract.try_deposit( + &sorobanvec![&test.env, amount, amount], + &sorobanvec![&test.env, amount], // wrong amount min + &users[0], + &false, + ); + + assert_eq!(response, Err(Ok(ContractError::WrongAmountsLength))); +} + + +// test deposit amount min more length +#[test] +fn amounts_min_more_length() { + 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 amount = 1000i128; + + let users = DeFindexVaultTest::generate_random_users(&test.env, 1); + + let response = test.defindex_contract.try_deposit( + &sorobanvec![&test.env, amount, amount], + &sorobanvec![&test.env, amount, amount, amount], // wrong amount min + &users[0], + &false, + ); + + assert_eq!(response, Err(Ok(ContractError::WrongAmountsLength))); +} + +// test amount desired negative +#[test] +fn amounts_desired_negative() { + 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 amount = 1000i128; + + let users = DeFindexVaultTest::generate_random_users(&test.env, 1); + + let response = test.defindex_contract.try_deposit( + &sorobanvec![&test.env, -amount, amount], + &sorobanvec![&test.env, amount, amount], + &users[0], + &false, + ); + + assert_eq!(response, Err(Ok(ContractError::NegativeNotAllowed))); +} + +// test deposit one asset success +#[test] +fn one_asset_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, + ); + + // check balances after deposit + let df_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(df_balance, amount - 1000); + + let user_balance = test.token0.balance(&users[0]); + assert_eq!(user_balance, 0i128); + + //map shuould be map + let mut expected_map = Map::new(&test.env); + expected_map.set(test.token0.address.clone(), amount); + + // check that all the assets are in the vault + let vault_balance = test.token0.balance(&test.defindex_contract.address); + assert_eq!(vault_balance, amount); + + // check that fetch_total_managed_funds returns correct amount + let total_managed_funds = test.defindex_contract.fetch_total_managed_funds(); + assert_eq!(total_managed_funds, expected_map); + + // check current idle funds, + let current_idle_funds = test.defindex_contract.fetch_current_idle_funds(); + assert_eq!(current_idle_funds, expected_map); + + //map shuould be map + let mut expected_map = Map::new(&test.env); + expected_map.set(test.token0.address.clone(), 0i128); + + // check that current invested funds is now 0, funds still in idle funds + let current_invested_funds = test.defindex_contract.fetch_current_invested_funds(); + assert_eq!(current_invested_funds, expected_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 + test.defindex_contract.deposit( + &sorobanvec![&test.env, amount2], + &sorobanvec![&test.env, amount2], + &users[0], + &false, + ); + + //map shuould be map + let mut expected_map = Map::new(&test.env); + expected_map.set(test.token0.address.clone(), amount + amount2); + + // 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); + + // check that all the assets are in the vault + 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 total_managed_funds = test.defindex_contract.fetch_total_managed_funds(); + assert_eq!(total_managed_funds, expected_map); + + // check current idle funds + let current_idle_funds = test.defindex_contract.fetch_current_idle_funds(); + assert_eq!(current_idle_funds, expected_map); + + + //map shuould be map + let mut expected_map = Map::new(&test.env); + expected_map.set(test.token0.address.clone(), 0i128); + + // check that current invested funds is now 0, funds still in idle funds + let current_invested_funds = test.defindex_contract.fetch_current_invested_funds(); + assert_eq!(current_invested_funds, expected_map); + + + +} + +// test deposit one asset with minimum more than desired +#[test] +fn one_asset_min_more_than_desired() { + 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 + let result=test.defindex_contract.try_deposit( + &sorobanvec![&test.env, amount], + &sorobanvec![&test.env, amount + 1], + &users[0], + &false, + ); + // this should fail + assert_eq!(result, Err(Ok(ContractError::InsufficientAmount))); + +} + +// test deposit of several asset, considering different proportion of assets +#[test] +fn several_assets_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); + let user_balance0 = test.token0.balance(&users[0]); + assert_eq!(user_balance0, amount0); + let user_balance1 = test.token1.balance(&users[0]); + assert_eq!(user_balance1, amount1); + + let df_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(df_balance, 0i128); + + // deposit + let deposit_result=test.defindex_contract.deposit( + &sorobanvec![&test.env, amount0, amount1], + &sorobanvec![&test.env, amount0, amount1], + &users[0], + &false, + ); + + // check deposit result + assert_eq!(deposit_result, (sorobanvec![&test.env, amount0, amount1], amount0 + amount1)); + + // check balances after deposit + let df_balance = test.defindex_contract.balance(&users[0]); + // For first deposit, a minimum amount LIQUIDITY OF 1000 is being locked in the contract + assert_eq!(df_balance, amount0 + amount1 - 1000); + + // check that the vault holds 1000 shares + let vault_df_shares = test.defindex_contract.balance(&test.defindex_contract.address); + assert_eq!(vault_df_shares, 1000i128); + + let user_balance0 = test.token0.balance(&users[0]); + assert_eq!(user_balance0,0i128); + let user_balance1 = test.token1.balance(&users[0]); + assert_eq!(user_balance1,0i128); + + // check vault balance of asset 0 + let vault_balance0 = test.token0.balance(&test.defindex_contract.address); + 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, amount1); + + //map shuould be map + let mut expected_map = Map::new(&test.env); + expected_map.set(test.token0.address.clone(), amount0); + expected_map.set(test.token1.address.clone(), amount1); + + // check that fetch_total_managed_funds returns correct amount + let total_managed_funds = test.defindex_contract.fetch_total_managed_funds(); + assert_eq!(total_managed_funds, expected_map); + + // check current idle funds + let current_idle_funds = test.defindex_contract.fetch_current_idle_funds(); + assert_eq!(current_idle_funds, expected_map); + + //map shuould be map + let mut expected_map = Map::new(&test.env); + expected_map.set(test.token0.address.clone(), 0i128); + expected_map.set(test.token1.address.clone(), 0i128); + + // check that current invested funds is now 0, funds still in idle funds + let current_invested_funds = test.defindex_contract.fetch_current_invested_funds(); + assert_eq!(current_invested_funds, expected_map); + + + // 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, 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], + &sorobanvec![&test.env, 0i128, 0i128], + &users[1], + &false, + ); + + // 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, 3*amount0); + // check vault balance of asset 1 + let vault_balance1 = test.token1.balance(&test.defindex_contract.address); + assert_eq!(vault_balance1, 3*amount1); + + //map shuould be map + let mut expected_map = Map::new(&test.env); + expected_map.set(test.token0.address.clone(), 3*amount0); + expected_map.set(test.token1.address.clone(), 3*amount1); + + // check that fetch_total_managed_funds returns correct amount + let total_managed_funds = test.defindex_contract.fetch_total_managed_funds(); + assert_eq!(total_managed_funds, expected_map); + + // check current idle funds + let current_idle_funds = test.defindex_contract.fetch_current_idle_funds(); + assert_eq!(current_idle_funds, expected_map); + + //map shuould be map + let mut expected_map = Map::new(&test.env); + expected_map.set(test.token0.address.clone(), 0i128); + expected_map.set(test.token1.address.clone(), 0i128); + + // check that current invested funds is now 0, funds still in idle funds + let current_invested_funds = test.defindex_contract.fetch_current_invested_funds(); + assert_eq!(current_invested_funds, expected_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], + &false, + ); + + // 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 deposit of several asset, imposing a minimum amount greater than optimal for asset 0 +#[test] +fn several_assets_min_greater_than_optimal() { + 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); + let user_balance0 = test.token0.balance(&users[0]); + assert_eq!(user_balance0, amount0); + let user_balance1 = test.token1.balance(&users[0]); + assert_eq!(user_balance1, amount1); + + let df_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(df_balance, 0i128); + + // deposit + let deposit_result=test.defindex_contract.try_deposit( + &sorobanvec![&test.env, amount0, amount1], + &sorobanvec![&test.env, amount0 + 1, amount1], + &users[0], + &false, + ); + + // this should fail + assert_eq!(deposit_result, Err(Ok(ContractError::InsufficientAmount))); + + // now we manage to deposit + test.defindex_contract.deposit( + &sorobanvec![&test.env, amount0, amount1], + &sorobanvec![&test.env, amount0, amount1], + &users[0], + &false, + ); + + // check deposit result + + // and now will try again with minimum more than optimal + + // 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 + test.token0_admin_client.mint(&users[0], &amount0_new); + test.token1_admin_client.mint(&users[0], &amount1_new); + + let deposit_result=test.defindex_contract.try_deposit( + &sorobanvec![&test.env, amount0_new, amount1_new], + &sorobanvec![&test.env, amount0*2+1, amount1*2], + &users[0], + &false, + ); + + // this should fail + + assert_eq!(deposit_result, Err(Ok(ContractError::InsufficientAmount))); + +} + +//test deposit amounts_min greater than amounts_desired +#[test] +fn amounts_min_greater_than_amounts_desired(){ + 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); + let user_balance0 = test.token0.balance(&users[0]); + assert_eq!(user_balance0, amount0); + let user_balance1 = test.token1.balance(&users[0]); + assert_eq!(user_balance1, amount1); + + let df_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(df_balance, 0i128); + + // deposit + let deposit_result=test.defindex_contract.try_deposit( + &sorobanvec![&test.env, amount0, amount1], + &sorobanvec![&test.env, amount0 + 1, amount1 + 1], + &users[0], + &false + ); + + // this should fail + assert_eq!(deposit_result, Err(Ok(ContractError::InsufficientAmount))); +} + +//Test token transfer from user to vault on deposit +#[test] +fn transfers_tokens_from_user_to_vault(){ + 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); + let user_balance0 = test.token0.balance(&users[0]); + assert_eq!(user_balance0, amount0); + let user_balance1 = test.token1.balance(&users[0]); + assert_eq!(user_balance1, amount1); + + let df_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(df_balance, 0i128); + + // deposit + test.defindex_contract.deposit( + &sorobanvec![&test.env, amount0, amount1], + &sorobanvec![&test.env, amount0, amount1], + &users[0], + &false + ); + + // check balances after deposit + let df_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(df_balance, amount0 + amount1 - 1000); + + let user_balance0 = test.token0.balance(&users[0]); + assert_eq!(user_balance0, 0i128); +} + +#[test] +fn arithmetic_error() { + let test = DeFindexVaultTest::setup(); + test.env.mock_all_auths(); + let strategy_params_token0 = create_strategy_params_token0(&test); + + // Initialize with 1 asset + 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"), + ); + + // Mock the environment to provoke a division by zero + let mut mock_map = Map::new(&test.env); + mock_map.set(test.token0.address.clone(), 0i128); // Total funds for token0 is 0 + + let amount = 123456789i128; + + let users = DeFindexVaultTest::generate_random_users(&test.env, 1); + + // Mint tokens to user + test.token0_admin_client.mint(&users[0], &amount); + + let large_amount = i128::MAX / 2; + test.token0_admin_client.mint(&users[0], &large_amount); + + //first deposit to overflow the balance + test.defindex_contract.deposit( + &sorobanvec![&test.env, large_amount], + &sorobanvec![&test.env, large_amount], + &users[0], + &false + ); + + // Try to deposit a large amount + let result = test.defindex_contract.try_deposit( + &sorobanvec![&test.env, large_amount], + &sorobanvec![&test.env, large_amount], + &users[0], + &false + ); + + // Verify that the returned error is ContractError::ArithmeticError + assert_eq!(result, Err(Ok(ContractError::ArithmeticError))); +} + +//all amounts are cero +#[test] +fn amounts_desired_zero() { + let test = DeFindexVaultTest::setup(); + test.env.mock_all_auths(); + let strategy_params_token0 = create_strategy_params_token0(&test); + + // Initialize with 1 asset + 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); + + // Mint tokens to user + test.token0_admin_client.mint(&users[0], &amount); + + // Balances before deposit + let user_balance_before = test.token0.balance(&users[0]); + assert_eq!(user_balance_before, amount); + + let vault_balance_before = test.token0.balance(&test.defindex_contract.address); + assert_eq!(vault_balance_before, 0i128); + + let df_balance_before = test.defindex_contract.balance(&users[0]); + assert_eq!(df_balance_before, 0i128); + + // Attempt to deposit with amounts_desired all set to 0 + let deposit_result = test.defindex_contract.try_deposit( + &sorobanvec![&test.env, 0i128], + &sorobanvec![&test.env, 0i128], + &users[0], + &false + ); + + // Verify that the returned error is ContractError::InsufficientAmount + assert_eq!(deposit_result, Err(Ok(ContractError::InsufficientAmount))); +} + + + // Deposit with insufficient funds and check for specific error message +#[test] +fn insufficient_funds_with_error_message() { + 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, 1); + + // Mint tokens to user + test.token0_admin_client.mint(&users[0], &amount0); + test.token1_admin_client.mint(&users[0], &amount1); + + // Balances before deposit + let user_balance0 = test.token0.balance(&users[0]); + assert_eq!(user_balance0, amount0); + let user_balance1 = test.token1.balance(&users[0]); + assert_eq!(user_balance1, amount1); + + let df_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(df_balance, 0i128); + + // Attempt to deposit more than available balance + let deposit_result = test.defindex_contract.try_deposit( + &sorobanvec![&test.env, amount0 + 1, amount1 + 1], + &sorobanvec![&test.env, amount0 + 1, amount1 + 1], + &users[0], + &false + ); + + if deposit_result == Err(Err(InvokeError::Contract(10))) { + return; + } else { + panic!("Expected error not returned"); + } + +} \ No newline at end of file diff --git a/apps/contracts/vault/src/test/deposit_and_invest.rs b/apps/contracts/vault/src/test/vault/deposit_and_invest.rs similarity index 99% rename from apps/contracts/vault/src/test/deposit_and_invest.rs rename to apps/contracts/vault/src/test/vault/deposit_and_invest.rs index a85ff29b..d070dad4 100644 --- a/apps/contracts/vault/src/test/deposit_and_invest.rs +++ b/apps/contracts/vault/src/test/vault/deposit_and_invest.rs @@ -7,7 +7,7 @@ use crate::test::{ // test deposit one asset success #[test] -fn deposit_and_invest_one_asset_success() { +fn one_asset_success() { let test = DeFindexVaultTest::setup(); test.env.mock_all_auths(); let strategy_params_token0 = create_strategy_params_token0(&test); @@ -129,7 +129,7 @@ fn deposit_and_invest_one_asset_success() { // test deposit of several asset, considering different proportion of assets #[test] -fn deposit_and_invest_several_assets_success() { +fn several_assets_success() { let test = DeFindexVaultTest::setup(); test.env.mock_all_auths(); let strategy_params_token0 = create_strategy_params_token0(&test); diff --git a/apps/contracts/vault/src/test/emergency_withdraw.rs b/apps/contracts/vault/src/test/vault/emergency_withdraw.rs similarity index 98% rename from apps/contracts/vault/src/test/emergency_withdraw.rs rename to apps/contracts/vault/src/test/vault/emergency_withdraw.rs index cf74ec8b..0620bd97 100644 --- a/apps/contracts/vault/src/test/emergency_withdraw.rs +++ b/apps/contracts/vault/src/test/vault/emergency_withdraw.rs @@ -10,7 +10,7 @@ use crate::test::{ }; #[test] -fn test_emergency_withdraw_success() { +fn withdraw_success() { let test = DeFindexVaultTest::setup(); test.env.mock_all_auths(); let strategy_params_token0 = create_strategy_params_token0(&test); diff --git a/apps/contracts/vault/src/test/initialize.rs b/apps/contracts/vault/src/test/vault/initialize.rs similarity index 95% rename from apps/contracts/vault/src/test/initialize.rs rename to apps/contracts/vault/src/test/vault/initialize.rs index c6e774b6..b8945444 100644 --- a/apps/contracts/vault/src/test/initialize.rs +++ b/apps/contracts/vault/src/test/vault/initialize.rs @@ -8,7 +8,7 @@ use crate::test::{ #[test] -fn test_initialize_and_get_roles() { +fn initialize_and_get_roles() { let test = DeFindexVaultTest::setup(); let strategy_params_token0 = create_strategy_params_token0(&test); let strategy_params_token1 = create_strategy_params_token1(&test); @@ -48,7 +48,7 @@ fn test_initialize_and_get_roles() { // Test that if strategy does support other asset we get an error when initializing #[test] -fn test_initialize_with_unsupported_strategy() { +fn initialize_with_unsupported_strategy() { let test = DeFindexVaultTest::setup(); let strategy_params_token0 = create_strategy_params_token0(&test); @@ -84,7 +84,7 @@ fn test_initialize_with_unsupported_strategy() { // test that if we try to initialize with an empty asset allocation fails #[test] -fn test_initialize_with_empty_asset_allocation() { +fn initialize_with_empty_asset_allocation() { let test = DeFindexVaultTest::setup(); // let strategy_params_token0 = create_strategy_params_token0(&test); @@ -106,7 +106,7 @@ fn test_initialize_with_empty_asset_allocation() { } #[test] -fn test_get_roles_not_yet_initialized() { +fn get_roles_not_yet_initialized() { let test = DeFindexVaultTest::setup(); let manager_role = test.defindex_contract.try_get_manager(); let fee_receiver_role = test.defindex_contract.try_get_manager(); @@ -118,7 +118,7 @@ fn test_get_roles_not_yet_initialized() { } #[test] -fn test_initialize_twice() { +fn initialize_twice() { let test = DeFindexVaultTest::setup(); let strategy_params_token0 = create_strategy_params_token0(&test); let strategy_params_token1 = create_strategy_params_token1(&test); @@ -166,7 +166,7 @@ fn test_initialize_twice() { } #[test] -fn test_withdraw_not_yet_initialized() { +fn withdraw_not_yet_initialized() { let test = DeFindexVaultTest::setup(); let users = DeFindexVaultTest::generate_random_users(&test.env, 1); @@ -175,7 +175,7 @@ fn test_withdraw_not_yet_initialized() { } #[test] -fn test_emergency_withdraw_not_yet_initialized() { +fn emergency_withdraw_not_yet_initialized() { let test = DeFindexVaultTest::setup(); let users = DeFindexVaultTest::generate_random_users(&test.env, 1); diff --git a/apps/contracts/vault/src/test/invest.rs b/apps/contracts/vault/src/test/vault/invest.rs similarity index 98% rename from apps/contracts/vault/src/test/invest.rs rename to apps/contracts/vault/src/test/vault/invest.rs index b6f7d93f..d88d6dee 100644 --- a/apps/contracts/vault/src/test/invest.rs +++ b/apps/contracts/vault/src/test/vault/invest.rs @@ -16,7 +16,7 @@ use crate::test::{ // check that invest can only be called after initialized #[test] -fn test_invest_not_yet_initialized() { +fn not_yet_initialized() { let test = DeFindexVaultTest::setup(); let asset_investments = vec![ @@ -41,7 +41,7 @@ fn test_invest_not_yet_initialized() { // try to invest with a wrong AssetInvestmentAllocation length #[test] -fn test_invest_wrong_asset_investment_length() { +fn wrong_asset_investment_length() { let test = DeFindexVaultTest::setup(); let strategy_params_token0 = create_strategy_params_token0(&test); @@ -120,7 +120,7 @@ fn test_invest_wrong_asset_investment_length() { // check that fails if strategy length is wrong #[test] -fn test_invest_wrong_strategy_length() { +fn wrong_strategy_length() { let test = DeFindexVaultTest::setup(); let strategy_params_token0 = create_strategy_params_token0(&test); @@ -224,7 +224,7 @@ fn test_invest_wrong_strategy_length() { // check that fails if asset address is wrong #[test] -fn test_invest_wrong_asset_address() { +fn wrong_asset_address() { let test = DeFindexVaultTest::setup(); let strategy_params_token0 = create_strategy_params_token0(&test); @@ -281,7 +281,7 @@ fn test_invest_wrong_asset_address() { // check that we cannot invest with negative amounts #[test] -fn test_invest_negative_amount() { +fn negative_amount() { let test = DeFindexVaultTest::setup(); let strategy_params_token0 = create_strategy_params_token0(&test); @@ -338,7 +338,7 @@ fn test_invest_negative_amount() { // check that we cannot invest in paused strategy. Will initialize with paused strategy and then try to invest #[test] -fn test_invest_paused_strategy() { +fn paused_strategy() { let test = DeFindexVaultTest::setup(); @@ -394,7 +394,7 @@ fn test_invest_paused_strategy() { // invest in strategy should work #[test] -fn test_invest_in_strategy() { +fn in_strategy() { let test = DeFindexVaultTest::setup(); let strategy_params_token0 = create_strategy_params_token0(&test); @@ -558,7 +558,7 @@ fn test_invest_in_strategy() { #[test] #[should_panic(expected = "HostError: Error(Contract, #10)")] // balance is not sufficient to spend // we get the error from the token contract -fn test_invest_more_than_idle_funds() { +fn more_than_idle_funds() { let test = DeFindexVaultTest::setup(); let strategy_params_token0 = create_strategy_params_token0(&test); @@ -651,7 +651,7 @@ fn test_invest_more_than_idle_funds() { // invest without mock aut, mocking only specific auths #[test] -fn test_invest_without_mock_all_auths() { +fn without_mock_all_auths() { let test = DeFindexVaultTest::setup(); let strategy_params_token0 = create_strategy_params_token0(&test); diff --git a/apps/contracts/vault/src/test/vault/mod.rs b/apps/contracts/vault/src/test/vault/mod.rs new file mode 100644 index 00000000..0e1a89b6 --- /dev/null +++ b/apps/contracts/vault/src/test/vault/mod.rs @@ -0,0 +1,8 @@ +mod admin; +mod deposit_and_invest; +mod deposit; +mod emergency_withdraw; +mod initialize; +mod invest; +mod rebalance; +mod withdraw; \ No newline at end of file diff --git a/apps/contracts/vault/src/test/vault/rebalance.rs b/apps/contracts/vault/src/test/vault/rebalance.rs new file mode 100644 index 00000000..64006e25 --- /dev/null +++ b/apps/contracts/vault/src/test/vault/rebalance.rs @@ -0,0 +1,387 @@ +use soroban_sdk::{vec as sorobanvec, InvokeError, String, Vec}; + +use crate::test::{ + create_strategy_params_token0, + defindex_vault::{ + ActionType, + AssetStrategySet, + Instruction, + AssetInvestmentAllocation, + StrategyInvestment, + OptionalSwapDetailsExactIn, + OptionalSwapDetailsExactOut, + }, + DeFindexVaultTest, +}; +use crate::test::defindex_vault::ContractError; + +#[test] +fn multi_instructions() { + let test = DeFindexVaultTest::setup(); + test.env.mock_all_auths(); + let strategy_params_token0 = create_strategy_params_token0(&test); + 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 = 987654321i128; + + let users = DeFindexVaultTest::generate_random_users(&test.env, 1); + + 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); + + test.defindex_contract.deposit( + &sorobanvec![&test.env, amount], + &sorobanvec![&test.env, amount], + &users[0], + &false + ); + + let df_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(df_balance, amount - 1000); + + let investments = sorobanvec![ + &test.env, + Some(AssetInvestmentAllocation { + asset: test.token0.address.clone(), + strategy_investments: sorobanvec![ + &test.env, + Some(StrategyInvestment { + strategy: test.strategy_client_token0.address.clone(), + amount: amount, + }), + ], + }), + ]; + + test.defindex_contract.invest(&investments); + + let vault_balance = test.token0.balance(&test.defindex_contract.address); + assert_eq!(vault_balance, 0); + + // REBALANCE + + let instruction_amount_0 = 200i128; + let instruction_amount_1 = 100i128; + + let instructions = sorobanvec![ + &test.env, + Instruction { + action: ActionType::Withdraw, + strategy: Some(test.strategy_client_token0.address.clone()), + amount: Some(instruction_amount_0), + swap_details_exact_in: OptionalSwapDetailsExactIn::None, + swap_details_exact_out: OptionalSwapDetailsExactOut::None, + }, + Instruction { + action: ActionType::Invest, + strategy: Some(test.strategy_client_token0.address.clone()), + amount: Some(instruction_amount_1), + swap_details_exact_in: OptionalSwapDetailsExactIn::None, + swap_details_exact_out: OptionalSwapDetailsExactOut::None, + } + ]; + + test.defindex_contract.rebalance(&instructions); + + let vault_balance = test.token0.balance(&test.defindex_contract.address); + assert_eq!(vault_balance, instruction_amount_1); +} + +#[test] +fn one_instruction() { + let test = DeFindexVaultTest::setup(); + test.env.mock_all_auths(); + let strategy_params_token0 = create_strategy_params_token0(&test); + 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, "TestVault"), + &String::from_str(&test.env, "TSTV"), + ); + let amount = 987654321i128; + + let users = DeFindexVaultTest::generate_random_users(&test.env, 1); + + 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); + + test.defindex_contract.deposit( + &sorobanvec![&test.env, amount], + &sorobanvec![&test.env, amount], + &users[0], + &false + ); + + let df_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(df_balance, amount - 1000); + + let investments = sorobanvec![ + &test.env, + Some(AssetInvestmentAllocation { + asset: test.token0.address.clone(), + strategy_investments: sorobanvec![ + &test.env, + Some(StrategyInvestment { + strategy: test.strategy_client_token0.address.clone(), + amount: amount, + }), + ], + }), + ]; + + test.defindex_contract.invest(&investments); + + let vault_balance = test.token0.balance(&test.defindex_contract.address); + assert_eq!(vault_balance, 0); + + // REBALANCE + + let instruction_amount_0 = 200i128; + + let instructions = sorobanvec![ + &test.env, + Instruction { + action: ActionType::Withdraw, + strategy: Some(test.strategy_client_token0.address.clone()), + amount: Some(instruction_amount_0), + swap_details_exact_in: OptionalSwapDetailsExactIn::None, + swap_details_exact_out: OptionalSwapDetailsExactOut::None, + }, + ]; + + test.defindex_contract.rebalance(&instructions); + + let vault_balance = test.token0.balance(&test.defindex_contract.address); + assert_eq!(vault_balance, instruction_amount_0); +} + +#[test] +fn empty_instructions(){ + let test = DeFindexVaultTest::setup(); + test.env.mock_all_auths(); + + let strategy_params_token0 = create_strategy_params_token0(&test); + 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, + &100u32, + &test.defindex_protocol_receiver, + &test.defindex_factory, + &String::from_str(&test.env, "testVault"), + &String::from_str(&test.env, "TSTV"), + ); + let amount: i128 = 987654321; + let users = DeFindexVaultTest::generate_random_users(&test.env, 1); + test.token0_admin_client.mint(&users[0], &amount); + let vault_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(vault_balance, 0i128); + + test.defindex_contract.deposit( + &sorobanvec![&test.env, amount], + &sorobanvec![&test.env, amount], + &users[0], + &false + ); + let df_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(df_balance, amount - 1000); + + let instructions = sorobanvec![ + &test.env, + Instruction { + action: ActionType::Withdraw, + strategy: None, + amount: None, + swap_details_exact_in: OptionalSwapDetailsExactIn::None, + swap_details_exact_out: OptionalSwapDetailsExactOut::None, + }, + ]; + let rebalance = test.defindex_contract.try_rebalance(&instructions); + assert_eq!(rebalance, Err(Ok(ContractError::MissingInstructionData))); + + let no_strategy_instructions = sorobanvec![ + &test.env, + Instruction { + action: ActionType::Withdraw, + strategy: Some(test.strategy_client_token0.address.clone()), + amount: None, + swap_details_exact_in: OptionalSwapDetailsExactIn::None, + swap_details_exact_out: OptionalSwapDetailsExactOut::None, + }, + ]; + let rebalance = test.defindex_contract.try_rebalance(&no_strategy_instructions); + assert_eq!(rebalance, Err(Ok(ContractError::MissingInstructionData))); + + let no_amount_instructions = sorobanvec![ + &test.env, + Instruction { + action: ActionType::Withdraw, + strategy: Some(test.strategy_client_token0.address.clone()), + amount: None, + swap_details_exact_in: OptionalSwapDetailsExactIn::None, + swap_details_exact_out: OptionalSwapDetailsExactOut::None, + }, + ]; + let rebalance = test.defindex_contract.try_rebalance(&no_amount_instructions); + assert_eq!(rebalance, Err(Ok(ContractError::MissingInstructionData))); +} + +#[test] +fn no_instructions(){ + let test = DeFindexVaultTest::setup(); + test.env.mock_all_auths(); + + let strategy_params_token0 = create_strategy_params_token0(&test); + 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, + &100u32, + &test.defindex_protocol_receiver, + &test.defindex_factory, + &String::from_str(&test.env, "testVault"), + &String::from_str(&test.env, "TSTV"), + ); + let amount: i128 = 987654321; + let users = DeFindexVaultTest::generate_random_users(&test.env, 1); + test.token0_admin_client.mint(&users[0], &amount); + let vault_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(vault_balance, 0i128); + + test.defindex_contract.deposit( + &sorobanvec![&test.env, amount], + &sorobanvec![&test.env, amount], + &users[0], + &false + ); + let df_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(df_balance, amount - 1000); + + let rebalance = test.defindex_contract.try_rebalance(&sorobanvec![&test.env]); + assert_eq!(rebalance, Err(Ok(ContractError::NoInstructions))); +} + +#[test] +fn insufficient_balance(){ + let test = DeFindexVaultTest::setup(); + test.env.mock_all_auths(); + + let strategy_params_token0 = create_strategy_params_token0(&test); + 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, + &100u32, + &test.defindex_protocol_receiver, + &test.defindex_factory, + &String::from_str(&test.env, "testVault"), + &String::from_str(&test.env, "TSTV"), + ); + let amount: i128 = 987654321; + let users = DeFindexVaultTest::generate_random_users(&test.env, 1); + test.token0_admin_client.mint(&users[0], &amount); + let vault_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(vault_balance, 0i128); + + test.defindex_contract.deposit( + &sorobanvec![&test.env, amount], + &sorobanvec![&test.env, amount], + &users[0], + &false + ); + let df_balance: i128 = test.defindex_contract.balance(&users[0]); + assert_eq!(df_balance, amount - 1000); + + let withdraw_instructions = sorobanvec![ + &test.env, + Instruction { + action: ActionType::Withdraw, + strategy: Some(test.strategy_client_token0.address.clone()), + amount: Some(amount + 1), + swap_details_exact_in: OptionalSwapDetailsExactIn::None, + swap_details_exact_out: OptionalSwapDetailsExactOut::None, + }, + ]; + let rebalance = test.defindex_contract.try_rebalance(&withdraw_instructions); + assert_eq!(rebalance, Err(Ok(ContractError::StrategyWithdrawError))); + + let invest_instructions = sorobanvec!( + &test.env, + Instruction { + action: ActionType::Invest, + strategy: Some(test.strategy_client_token0.address.clone()), + amount: Some(amount + 1), + swap_details_exact_in: OptionalSwapDetailsExactIn::None, + swap_details_exact_out: OptionalSwapDetailsExactOut::None, + }, + ); + let rebalance = test.defindex_contract.try_rebalance(&invest_instructions); + if rebalance == Err(Err(InvokeError::Contract(10))) { + return; + } else { + panic!("Expected error not returned"); + } +} + diff --git a/apps/contracts/vault/src/test/withdraw.rs b/apps/contracts/vault/src/test/vault/withdraw.rs similarity index 98% rename from apps/contracts/vault/src/test/withdraw.rs rename to apps/contracts/vault/src/test/vault/withdraw.rs index 38edb80f..953e0e2c 100644 --- a/apps/contracts/vault/src/test/withdraw.rs +++ b/apps/contracts/vault/src/test/vault/withdraw.rs @@ -13,7 +13,7 @@ use crate::test::{ }; #[test] -fn test_withdraw_from_idle_success() { +fn withdraw_from_idle_success() { let test = DeFindexVaultTest::setup(); test.env.mock_all_auths(); let strategy_params_token0 = create_strategy_params_token0(&test); @@ -127,7 +127,7 @@ fn test_withdraw_from_idle_success() { } #[test] -fn test_withdraw_from_strategy_success() { +fn withdraw_from_strategy_success() { let test = DeFindexVaultTest::setup(); test.env.mock_all_auths(); let strategy_params_token0 = create_strategy_params_token0(&test); From c8a6275223250f7ded96652c85ba57aa453a3fa1 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Mon, 2 Dec 2024 15:29:42 -0300 Subject: [PATCH 07/14] =?UTF-8?q?=F0=9F=94=A5Removed=20unused=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/contracts/src/tests/testTwoStrategiesVault.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/contracts/src/tests/testTwoStrategiesVault.ts b/apps/contracts/src/tests/testTwoStrategiesVault.ts index 79fab09f..1d58fb74 100644 --- a/apps/contracts/src/tests/testTwoStrategiesVault.ts +++ b/apps/contracts/src/tests/testTwoStrategiesVault.ts @@ -10,7 +10,7 @@ import { randomBytes } from "crypto"; import { AddressBook } from "../utils/address_book.js"; import { airdropAccount, invokeContract, invokeCustomContract } from "../utils/contract.js"; import { config } from "../utils/env_config.js"; -import { ActionType, AssetInvestmentAllocation, depositToVault, getVaultBalanceInStrategy, Instruction, investVault, rebalanceVault, fetchParsedCurrentIdleFunds, fetchCurrentInvestedFunds } from "./vault.js"; +import { ActionType, AssetInvestmentAllocation, depositToVault, Instruction, investVault, rebalanceVault, fetchParsedCurrentIdleFunds, fetchCurrentInvestedFunds } from "./vault.js"; import { checkUserBalance } from "./strategy.js"; const soroswapUSDC = new Address("CAAFIHB4I7WQMJMKC22CZVQNNX7EONWSOMT6SUXK6I3G3F6J4XFRWNDI"); From 82b69395d5ca76de36a55aea08a4b19f27faab4f Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Mon, 2 Dec 2024 15:39:38 -0300 Subject: [PATCH 08/14] =?UTF-8?q?=E2=9C=A8=20Implemented=20one=20strategy?= =?UTF-8?q?=20vault=20deployment=20and=20token=20minting=20functionality?= =?UTF-8?q?=20in=20testOnlyVault?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/contracts/src/tests/testOnlyVault.ts | 129 +++++++++++++++++++++- 1 file changed, 127 insertions(+), 2 deletions(-) diff --git a/apps/contracts/src/tests/testOnlyVault.ts b/apps/contracts/src/tests/testOnlyVault.ts index 3975d07d..244a8066 100644 --- a/apps/contracts/src/tests/testOnlyVault.ts +++ b/apps/contracts/src/tests/testOnlyVault.ts @@ -1,4 +1,129 @@ -import { depositToVault } from "./vault.js"; +import { + Address, + Asset, + Keypair, + nativeToScVal, + scValToNative, + xdr +} from "@stellar/stellar-sdk"; +import { randomBytes } from "crypto"; +import { AddressBook } from "../utils/address_book.js"; +import { airdropAccount, invokeContract, invokeCustomContract } from "../utils/contract.js"; +import { config } from "../utils/env_config.js"; +import { depositToVault} from "./vault.js"; +import { checkUserBalance } from "./strategy.js"; +const soroswapUSDC = new Address("CAAFIHB4I7WQMJMKC22CZVQNNX7EONWSOMT6SUXK6I3G3F6J4XFRWNDI"); + +const network = process.argv[2]; +const addressBook = AddressBook.loadFromFile(network); +const hodl_strategy = addressBook.getContractId("hodl_strategy"); +const fixed_apr_strategy = addressBook.getContractId("fixed_apr_strategy"); +const xlm: Asset = Asset.native() + +const loadedConfig = config(network); // modify the address to the deployed vault -await depositToVault("CBUTM5B7CG7ISTX5PD5XMDDMSTKFRGVXNKRMZO3TZW5IFGHN5274JJY7", [986754321]); +export async function deployVault(addressBook: AddressBook) { + if (network !== "mainnet") await airdropAccount(loadedConfig.admin); + let account = await loadedConfig.horizonRpc.loadAccount( + loadedConfig.admin.publicKey() + ); + console.log("publicKey", loadedConfig.admin.publicKey()); + let balance = account.balances.filter((item) => item.asset_type === "native"); + console.log("Current Admin account balance:", balance[0].balance); + + console.log("-------------------------------------------------------"); + console.log("Create Vault on Factory"); + console.log("-------------------------------------------------------"); + + console.log("Setting Emergengy Manager, Fee Receiver and Manager accounts"); + const emergencyManager = loadedConfig.getUser("DEFINDEX_EMERGENCY_MANAGER_SECRET_KEY"); + if (network !== "mainnet") await airdropAccount(emergencyManager); + + const feeReceiver = loadedConfig.getUser("DEFINDEX_FEE_RECEIVER_SECRET_KEY"); + if (network !== "mainnet") await airdropAccount(feeReceiver); + + const manager = loadedConfig.getUser("DEFINDEX_MANAGER_SECRET_KEY"); + if (network !== "mainnet") await airdropAccount(manager); + + const assets = [ + { + address: soroswapUSDC, + strategies: [ + { + name: "Hodl Strategy", + address: addressBook.getContractId("hodl_strategy"), + paused: false + } + ] + } + ]; + + const assetAllocations = assets.map((asset) => { + return xdr.ScVal.scvMap([ + new xdr.ScMapEntry({ + key: xdr.ScVal.scvSymbol("address"), + val: asset.address.toScVal(), + }), + new xdr.ScMapEntry({ + key: xdr.ScVal.scvSymbol("strategies"), + val: xdr.ScVal.scvVec( + asset.strategies.map((strategy) => + xdr.ScVal.scvMap([ + new xdr.ScMapEntry({ + key: xdr.ScVal.scvSymbol("address"), + val: new Address(strategy.address).toScVal(), + }), + new xdr.ScMapEntry({ + key: xdr.ScVal.scvSymbol("name"), + val: nativeToScVal(strategy.name, { type: "string" }), + }), + new xdr.ScMapEntry({ + key: xdr.ScVal.scvSymbol("paused"), + val: nativeToScVal(false, { type: "bool" }), + }), + ]) + ) + ), + }), + ]); + }); + + const createDeFindexParams: xdr.ScVal[] = [ + new Address(emergencyManager.publicKey()).toScVal(), + new Address(feeReceiver.publicKey()).toScVal(), + nativeToScVal(100, { type: "u32" }), + nativeToScVal("HODL FIXED Vault", { type: "string" }), + nativeToScVal("HDFXVLT", { type: "string" }), + new Address(manager.publicKey()).toScVal(), + xdr.ScVal.scvVec(assetAllocations), + nativeToScVal(randomBytes(32)), + ]; + + const result = await invokeContract( + 'defindex_factory', + addressBook, + 'create_defindex_vault', + createDeFindexParams, + loadedConfig.admin + ); + + console.log('🚀 « DeFindex Vault created with address:', scValToNative(result.returnValue)); + return scValToNative(result.returnValue); +} +const testUser = Keypair.random(); +if (network !== "mainnet") await airdropAccount(testUser); +const initialAmount = 10000_0_000_000; + +const mintToken = async () => { + await invokeCustomContract( + soroswapUSDC.toString(), + "mint", + [new Address(testUser.publicKey()).toScVal(), nativeToScVal(initialAmount, { type: "i128" })], + loadedConfig.getUser("SOROSWAP_MINT_SECRET_KEY") + ) +} + +await mintToken(); +const vaultAddress = await deployVault(addressBook); +await depositToVault(vaultAddress, [986754321], testUser); \ No newline at end of file From ea9762001c5ed5457f25353f1092987fbbcf8b9a Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:55:58 -0300 Subject: [PATCH 09/14] =?UTF-8?q?=E2=9C=A8=20Add=20invest=20&=20withdraw?= =?UTF-8?q?=20cases=20to=20insufficient=20balance=20rebalance=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/contracts/vault/src/test/rebalance.rs | 57 +++++++++++++++++----- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/apps/contracts/vault/src/test/rebalance.rs b/apps/contracts/vault/src/test/rebalance.rs index 19bb9cf4..ecea4880 100644 --- a/apps/contracts/vault/src/test/rebalance.rs +++ b/apps/contracts/vault/src/test/rebalance.rs @@ -1,17 +1,9 @@ use soroban_sdk::{vec as sorobanvec, InvokeError, String, Vec}; use crate::test::{ - create_strategy_params_token0, - defindex_vault::{ - ActionType, - AssetStrategySet, - Instruction, - AssetInvestmentAllocation, - StrategyInvestment, - OptionalSwapDetailsExactIn, - OptionalSwapDetailsExactOut, - }, - DeFindexVaultTest, + create_strategy_params_token0, defindex_vault::{ + ActionType, AssetInvestmentAllocation, AssetStrategySet, Instruction, OptionalSwapDetailsExactIn, OptionalSwapDetailsExactOut, StrategyInvestment + }, invest, DeFindexVaultTest }; use crate::test::defindex_vault::ContractError; @@ -333,7 +325,7 @@ fn rebalance_insufficient_balance(){ &test.manager, &test.emergency_manager, &test.vault_fee_receiver, - &100u32, + &10u32, &test.defindex_protocol_receiver, &test.defindex_factory, &String::from_str(&test.env, "testVault"), @@ -342,9 +334,45 @@ fn rebalance_insufficient_balance(){ let amount: i128 = 987654321; let users = DeFindexVaultTest::generate_random_users(&test.env, 1); test.token0_admin_client.mint(&users[0], &amount); + + //Balance should be 0 let vault_balance = test.defindex_contract.balance(&users[0]); assert_eq!(vault_balance, 0i128); + //Withdraw with no funds + let withdraw_no_funds_instructions = sorobanvec![ + &test.env, + Instruction { + action: ActionType::Withdraw, + strategy: Some(test.strategy_client_token0.address.clone()), + amount: Some(amount + 1), + swap_details_exact_in: OptionalSwapDetailsExactIn::None, + swap_details_exact_out: OptionalSwapDetailsExactOut::None, + }, + ]; + + let withdraw_no_funds = test.defindex_contract.try_rebalance(&withdraw_no_funds_instructions); + assert_eq!(withdraw_no_funds, Err(Ok(ContractError::StrategyWithdrawError))); //Contract should respond 'Insuficient balance'? + + //Invest with no funds + let invest_no_funds_instructions = sorobanvec![ + &test.env, + Instruction { + action: ActionType::Invest, + strategy: Some(test.strategy_client_token0.address.clone()), + amount: Some(1), + swap_details_exact_in: OptionalSwapDetailsExactIn::None, + swap_details_exact_out: OptionalSwapDetailsExactOut::None, + }, + ]; + let invest_no_funds = test.defindex_contract.try_rebalance(&invest_no_funds_instructions); + + //Contract should fail with error #10 no balance or panic the test + if invest_no_funds != Err(Err(InvokeError::Contract(10))) { + panic!("Expected error not returned"); + } + + //Deposit 987654321 stroops test.defindex_contract.deposit( &sorobanvec![&test.env, amount], &sorobanvec![&test.env, amount], @@ -354,6 +382,7 @@ fn rebalance_insufficient_balance(){ let df_balance: i128 = test.defindex_contract.balance(&users[0]); assert_eq!(df_balance, amount - 1000); + //Withdraw more than available let withdraw_instructions = sorobanvec![ &test.env, Instruction { @@ -377,6 +406,8 @@ fn rebalance_insufficient_balance(){ swap_details_exact_out: OptionalSwapDetailsExactOut::None, }, ); + + //Contract should fail with error #10 no balance let rebalance = test.defindex_contract.try_rebalance(&invest_instructions); if rebalance == Err(Err(InvokeError::Contract(10))) { return; @@ -385,3 +416,5 @@ fn rebalance_insufficient_balance(){ } } + + From 211875c23d9dad58652aded8f5b74f8a0669ce39 Mon Sep 17 00:00:00 2001 From: chopan123 Date: Mon, 2 Dec 2024 17:02:26 -0300 Subject: [PATCH 10/14] more control on fees --- .../02-contracts/01-vault-contract.md | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 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 911e48cc..84007931 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 @@ -150,7 +150,7 @@ The fees collected are from the gains of the strategies. Thus, it is a performan The DeFindex fee collection process is designed to track fees in the vault until distribution, with fees originating from the strategy gains. This ensures an organized and accountable fee handling system. #### General Overview -Fees are charged on a per-strategy basis, meaning each strategy independently calculates its gains and the corresponding fees. These fees are then collected and distributed to the protocol and manager. The fee percentages are fixed per vault and they are decided when creating it. +Fees are charged on a per-strategy basis, meaning each strategy independently calculates its gains and the corresponding fees. These fees are then collected and distributed to the protocol and manager. The fee percentages are fixed per vault and they are decided when creating it. However, the manager can change the ratio of the Vault fees at any time. #### Detailed Workflow @@ -190,25 +190,42 @@ fn report_all_strategies() { ``` - **Usage**: The `report_all_strategies()` function is invoked during key operations such as rebalancing, deposits, or withdrawals to ensure accurate gain tracking. -#### Fee Distribution -Once gains are tracked, fees are calculated and distributed accordingly. After distribution, the gains and losses for each strategy are reset to 0. +#### Fee Locking and Distribution +Once gains are tracked, fees can be inspected and/or locked for future distribution. So, the manager can see the current gains and losses, and decide if he wants to change the ratio of the fees, before locking them. + +The locking process is done by the manager calling the `lock_fees()` function. -**Pseudocode for Fee Distribution**: ```rust -fn distribute_fees() { +fn lock_fees() { for strategy in strategies { - let gains_or_losses = get_gains_or_losses(strategy); 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; - transfer_from_strategy(strategy.asset, protocol_fee_receiver, protocol_fee); - transfer_from_strategy(strategy.asset, vault_fee_receiver, vault_fee); + lock_fee(strategy.asset, protocol_fee_receiver, protocol_fee); + lock_fee(strategy.asset, vault_fee_receiver, vault_fee); reset_gains_or_losses(strategy); } } } ``` -This function is public and can be called by anyone. + +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. + +Then, the fees are distributed to the protocol and manager, whenever a person calls the `distribute_fees()` function. + +**Pseudocode for Fee Distribution**: +```rust +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); + reset_locked_fees(strategy); + } + } +} +``` #### Displaying User Balances To provide users with an accurate view of their balances, any outstanding fees should be deducted offchain from the total assets when showing the current balances. From 80d71792e6d5ece7f8e752cb6c68e97f4393a975 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Tue, 3 Dec 2024 11:43:48 -0300 Subject: [PATCH 11/14] =?UTF-8?q?=F0=9F=9A=9A=20Moved=20rebalance=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/contracts/vault/src/test/deposit.rs | 1031 ----------------- apps/contracts/vault/src/test/rebalance.rs | 420 ------- .../vault/src/test/vault/rebalance.rs | 57 +- 3 files changed, 45 insertions(+), 1463 deletions(-) delete mode 100644 apps/contracts/vault/src/test/deposit.rs delete mode 100644 apps/contracts/vault/src/test/rebalance.rs diff --git a/apps/contracts/vault/src/test/deposit.rs b/apps/contracts/vault/src/test/deposit.rs deleted file mode 100644 index 1b3e1648..00000000 --- a/apps/contracts/vault/src/test/deposit.rs +++ /dev/null @@ -1,1031 +0,0 @@ -use soroban_sdk::{vec as sorobanvec, InvokeError, Map, String, Vec}; - -use crate::test::defindex_vault::{AssetStrategySet, ContractError}; -use crate::test::{ - create_strategy_params_token0, create_strategy_params_token1, DeFindexVaultTest, -}; - -// Test deposit not yet initialized -#[test] -fn test_deposit_not_yet_initialized() { - let test = DeFindexVaultTest::setup(); - let users = DeFindexVaultTest::generate_random_users(&test.env, 1); - - let result = test.defindex_contract.try_deposit( - &sorobanvec![&test.env, 100i128], - &sorobanvec![&test.env, 100i128], - &users[0], - &false, - ); - - assert_eq!(result, Err(Ok(ContractError::NotInitialized))); -} - - -#[test] -fn deposit_amounts_desired_less_length() { - 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 amount = 1000i128; - - let users = DeFindexVaultTest::generate_random_users(&test.env, 1); - - let response = test.defindex_contract.try_deposit( - &sorobanvec![&test.env, amount], // wrong amount desired - &sorobanvec![&test.env, amount, amount], - &users[0], - &false, - ); - - assert_eq!(response, Err(Ok(ContractError::WrongAmountsLength))); -} - -// test deposit amount desired more length -#[test] -fn deposit_amounts_desired_more_length() { - 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() - } - ]; - - 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 = 1000i128; - - let users = DeFindexVaultTest::generate_random_users(&test.env, 1); - - let response = test.defindex_contract.try_deposit( - &sorobanvec![&test.env, amount, amount], // wrong amount desired - &sorobanvec![&test.env, amount], - &users[0], - &false, - ); - - assert_eq!(response, Err(Ok(ContractError::WrongAmountsLength))); -} - -// test deposit amount min less length -#[test] -fn deposit_amounts_min_less_length() { - 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 amount = 1000i128; - - let users = DeFindexVaultTest::generate_random_users(&test.env, 1); - - let response = test.defindex_contract.try_deposit( - &sorobanvec![&test.env, amount, amount], - &sorobanvec![&test.env, amount], // wrong amount min - &users[0], - &false, - ); - - assert_eq!(response, Err(Ok(ContractError::WrongAmountsLength))); -} - - -// test deposit amount min more length -#[test] -fn deposit_amounts_min_more_length() { - 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 amount = 1000i128; - - let users = DeFindexVaultTest::generate_random_users(&test.env, 1); - - let response = test.defindex_contract.try_deposit( - &sorobanvec![&test.env, amount, amount], - &sorobanvec![&test.env, amount, amount, amount], // wrong amount min - &users[0], - &false, - ); - - assert_eq!(response, Err(Ok(ContractError::WrongAmountsLength))); -} - -// test amount desired negative -#[test] -fn deposit_amounts_desired_negative() { - 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 amount = 1000i128; - - let users = DeFindexVaultTest::generate_random_users(&test.env, 1); - - let response = test.defindex_contract.try_deposit( - &sorobanvec![&test.env, -amount, amount], - &sorobanvec![&test.env, amount, amount], - &users[0], - &false, - ); - - assert_eq!(response, Err(Ok(ContractError::NegativeNotAllowed))); -} - -// test deposit one asset success -#[test] -fn deposit_one_asset_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, - ); - - // check balances after deposit - let df_balance = test.defindex_contract.balance(&users[0]); - assert_eq!(df_balance, amount - 1000); - - let user_balance = test.token0.balance(&users[0]); - assert_eq!(user_balance, 0i128); - - //map shuould be map - let mut expected_map = Map::new(&test.env); - expected_map.set(test.token0.address.clone(), amount); - - // check that all the assets are in the vault - let vault_balance = test.token0.balance(&test.defindex_contract.address); - assert_eq!(vault_balance, amount); - - // check that fetch_total_managed_funds returns correct amount - let total_managed_funds = test.defindex_contract.fetch_total_managed_funds(); - assert_eq!(total_managed_funds, expected_map); - - // check current idle funds, - let current_idle_funds = test.defindex_contract.fetch_current_idle_funds(); - assert_eq!(current_idle_funds, expected_map); - - //map shuould be map - let mut expected_map = Map::new(&test.env); - expected_map.set(test.token0.address.clone(), 0i128); - - // check that current invested funds is now 0, funds still in idle funds - let current_invested_funds = test.defindex_contract.fetch_current_invested_funds(); - assert_eq!(current_invested_funds, expected_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 - test.defindex_contract.deposit( - &sorobanvec![&test.env, amount2], - &sorobanvec![&test.env, amount2], - &users[0], - &false, - ); - - //map shuould be map - let mut expected_map = Map::new(&test.env); - expected_map.set(test.token0.address.clone(), amount + amount2); - - // 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); - - // check that all the assets are in the vault - 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 total_managed_funds = test.defindex_contract.fetch_total_managed_funds(); - assert_eq!(total_managed_funds, expected_map); - - // check current idle funds - let current_idle_funds = test.defindex_contract.fetch_current_idle_funds(); - assert_eq!(current_idle_funds, expected_map); - - - //map shuould be map - let mut expected_map = Map::new(&test.env); - expected_map.set(test.token0.address.clone(), 0i128); - - // check that current invested funds is now 0, funds still in idle funds - let current_invested_funds = test.defindex_contract.fetch_current_invested_funds(); - assert_eq!(current_invested_funds, expected_map); - - - -} - -// test deposit one asset with minimum more than desired -#[test] -fn deposit_one_asset_min_more_than_desired() { - 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 - let result=test.defindex_contract.try_deposit( - &sorobanvec![&test.env, amount], - &sorobanvec![&test.env, amount + 1], - &users[0], - &false, - ); - // this should fail - assert_eq!(result, Err(Ok(ContractError::InsufficientAmount))); - -} - -// test deposit of several asset, considering different proportion of assets -#[test] -fn deposit_several_assets_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); - let user_balance0 = test.token0.balance(&users[0]); - assert_eq!(user_balance0, amount0); - let user_balance1 = test.token1.balance(&users[0]); - assert_eq!(user_balance1, amount1); - - let df_balance = test.defindex_contract.balance(&users[0]); - assert_eq!(df_balance, 0i128); - - // deposit - let deposit_result=test.defindex_contract.deposit( - &sorobanvec![&test.env, amount0, amount1], - &sorobanvec![&test.env, amount0, amount1], - &users[0], - &false, - ); - - // check deposit result - assert_eq!(deposit_result, (sorobanvec![&test.env, amount0, amount1], amount0 + amount1)); - - // check balances after deposit - let df_balance = test.defindex_contract.balance(&users[0]); - // For first deposit, a minimum amount LIQUIDITY OF 1000 is being locked in the contract - assert_eq!(df_balance, amount0 + amount1 - 1000); - - // check that the vault holds 1000 shares - let vault_df_shares = test.defindex_contract.balance(&test.defindex_contract.address); - assert_eq!(vault_df_shares, 1000i128); - - let user_balance0 = test.token0.balance(&users[0]); - assert_eq!(user_balance0,0i128); - let user_balance1 = test.token1.balance(&users[0]); - assert_eq!(user_balance1,0i128); - - // check vault balance of asset 0 - let vault_balance0 = test.token0.balance(&test.defindex_contract.address); - 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, amount1); - - //map shuould be map - let mut expected_map = Map::new(&test.env); - expected_map.set(test.token0.address.clone(), amount0); - expected_map.set(test.token1.address.clone(), amount1); - - // check that fetch_total_managed_funds returns correct amount - let total_managed_funds = test.defindex_contract.fetch_total_managed_funds(); - assert_eq!(total_managed_funds, expected_map); - - // check current idle funds - let current_idle_funds = test.defindex_contract.fetch_current_idle_funds(); - assert_eq!(current_idle_funds, expected_map); - - //map shuould be map - let mut expected_map = Map::new(&test.env); - expected_map.set(test.token0.address.clone(), 0i128); - expected_map.set(test.token1.address.clone(), 0i128); - - // check that current invested funds is now 0, funds still in idle funds - let current_invested_funds = test.defindex_contract.fetch_current_invested_funds(); - assert_eq!(current_invested_funds, expected_map); - - - // 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, 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], - &sorobanvec![&test.env, 0i128, 0i128], - &users[1], - &false, - ); - - // 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, 3*amount0); - // check vault balance of asset 1 - let vault_balance1 = test.token1.balance(&test.defindex_contract.address); - assert_eq!(vault_balance1, 3*amount1); - - //map shuould be map - let mut expected_map = Map::new(&test.env); - expected_map.set(test.token0.address.clone(), 3*amount0); - expected_map.set(test.token1.address.clone(), 3*amount1); - - // check that fetch_total_managed_funds returns correct amount - let total_managed_funds = test.defindex_contract.fetch_total_managed_funds(); - assert_eq!(total_managed_funds, expected_map); - - // check current idle funds - let current_idle_funds = test.defindex_contract.fetch_current_idle_funds(); - assert_eq!(current_idle_funds, expected_map); - - //map shuould be map - let mut expected_map = Map::new(&test.env); - expected_map.set(test.token0.address.clone(), 0i128); - expected_map.set(test.token1.address.clone(), 0i128); - - // check that current invested funds is now 0, funds still in idle funds - let current_invested_funds = test.defindex_contract.fetch_current_invested_funds(); - assert_eq!(current_invested_funds, expected_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], - &false, - ); - - // 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 deposit of several asset, imposing a minimum amount greater than optimal for asset 0 -#[test] -fn deposit_several_assets_min_greater_than_optimal() { - 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); - let user_balance0 = test.token0.balance(&users[0]); - assert_eq!(user_balance0, amount0); - let user_balance1 = test.token1.balance(&users[0]); - assert_eq!(user_balance1, amount1); - - let df_balance = test.defindex_contract.balance(&users[0]); - assert_eq!(df_balance, 0i128); - - // deposit - let deposit_result=test.defindex_contract.try_deposit( - &sorobanvec![&test.env, amount0, amount1], - &sorobanvec![&test.env, amount0 + 1, amount1], - &users[0], - &false, - ); - - // this should fail - assert_eq!(deposit_result, Err(Ok(ContractError::InsufficientAmount))); - - // now we manage to deposit - test.defindex_contract.deposit( - &sorobanvec![&test.env, amount0, amount1], - &sorobanvec![&test.env, amount0, amount1], - &users[0], - &false, - ); - - // check deposit result - - // and now will try again with minimum more than optimal - - // 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 - test.token0_admin_client.mint(&users[0], &amount0_new); - test.token1_admin_client.mint(&users[0], &amount1_new); - - let deposit_result=test.defindex_contract.try_deposit( - &sorobanvec![&test.env, amount0_new, amount1_new], - &sorobanvec![&test.env, amount0*2+1, amount1*2], - &users[0], - &false, - ); - - // this should fail - - assert_eq!(deposit_result, Err(Ok(ContractError::InsufficientAmount))); - -} - -//test deposit amounts_min greater than amounts_desired -#[test] -fn deposit_amounts_min_greater_than_amounts_desired(){ - 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); - let user_balance0 = test.token0.balance(&users[0]); - assert_eq!(user_balance0, amount0); - let user_balance1 = test.token1.balance(&users[0]); - assert_eq!(user_balance1, amount1); - - let df_balance = test.defindex_contract.balance(&users[0]); - assert_eq!(df_balance, 0i128); - - // deposit - let deposit_result=test.defindex_contract.try_deposit( - &sorobanvec![&test.env, amount0, amount1], - &sorobanvec![&test.env, amount0 + 1, amount1 + 1], - &users[0], - &false - ); - - // this should fail - assert_eq!(deposit_result, Err(Ok(ContractError::InsufficientAmount))); -} - -//Test token transfer from user to vault on deposit -#[test] -fn deposit_transfers_tokens_from_user_to_vault(){ - 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); - let user_balance0 = test.token0.balance(&users[0]); - assert_eq!(user_balance0, amount0); - let user_balance1 = test.token1.balance(&users[0]); - assert_eq!(user_balance1, amount1); - - let df_balance = test.defindex_contract.balance(&users[0]); - assert_eq!(df_balance, 0i128); - - // deposit - test.defindex_contract.deposit( - &sorobanvec![&test.env, amount0, amount1], - &sorobanvec![&test.env, amount0, amount1], - &users[0], - &false - ); - - // check balances after deposit - let df_balance = test.defindex_contract.balance(&users[0]); - assert_eq!(df_balance, amount0 + amount1 - 1000); - - let user_balance0 = test.token0.balance(&users[0]); - assert_eq!(user_balance0, 0i128); -} - -#[test] -fn test_deposit_arithmetic_error() { - let test = DeFindexVaultTest::setup(); - test.env.mock_all_auths(); - let strategy_params_token0 = create_strategy_params_token0(&test); - - // Initialize with 1 asset - 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"), - ); - - // Mock the environment to provoke a division by zero - let mut mock_map = Map::new(&test.env); - mock_map.set(test.token0.address.clone(), 0i128); // Total funds for token0 is 0 - - let amount = 123456789i128; - - let users = DeFindexVaultTest::generate_random_users(&test.env, 1); - - // Mint tokens to user - test.token0_admin_client.mint(&users[0], &amount); - - let large_amount = i128::MAX / 2; - test.token0_admin_client.mint(&users[0], &large_amount); - - //first deposit to overflow the balance - test.defindex_contract.deposit( - &sorobanvec![&test.env, large_amount], - &sorobanvec![&test.env, large_amount], - &users[0], - &false - ); - - // Try to deposit a large amount - let result = test.defindex_contract.try_deposit( - &sorobanvec![&test.env, large_amount], - &sorobanvec![&test.env, large_amount], - &users[0], - &false - ); - - // Verify that the returned error is ContractError::ArithmeticError - assert_eq!(result, Err(Ok(ContractError::ArithmeticError))); -} - -//all amounts are cero -#[test] -fn deposit_amounts_desired_zero() { - let test = DeFindexVaultTest::setup(); - test.env.mock_all_auths(); - let strategy_params_token0 = create_strategy_params_token0(&test); - - // Initialize with 1 asset - 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); - - // Mint tokens to user - test.token0_admin_client.mint(&users[0], &amount); - - // Balances before deposit - let user_balance_before = test.token0.balance(&users[0]); - assert_eq!(user_balance_before, amount); - - let vault_balance_before = test.token0.balance(&test.defindex_contract.address); - assert_eq!(vault_balance_before, 0i128); - - let df_balance_before = test.defindex_contract.balance(&users[0]); - assert_eq!(df_balance_before, 0i128); - - // Attempt to deposit with amounts_desired all set to 0 - let deposit_result = test.defindex_contract.try_deposit( - &sorobanvec![&test.env, 0i128], - &sorobanvec![&test.env, 0i128], - &users[0], - &false - ); - - // Verify that the returned error is ContractError::InsufficientAmount - assert_eq!(deposit_result, Err(Ok(ContractError::InsufficientAmount))); -} - - - // Deposit with insufficient funds and check for specific error message -#[test] -fn deposit_insufficient_funds_with_error_message() { - 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, 1); - - // Mint tokens to user - test.token0_admin_client.mint(&users[0], &amount0); - test.token1_admin_client.mint(&users[0], &amount1); - - // Balances before deposit - let user_balance0 = test.token0.balance(&users[0]); - assert_eq!(user_balance0, amount0); - let user_balance1 = test.token1.balance(&users[0]); - assert_eq!(user_balance1, amount1); - - let df_balance = test.defindex_contract.balance(&users[0]); - assert_eq!(df_balance, 0i128); - - // Attempt to deposit more than available balance - let deposit_result = test.defindex_contract.try_deposit( - &sorobanvec![&test.env, amount0 + 1, amount1 + 1], - &sorobanvec![&test.env, amount0 + 1, amount1 + 1], - &users[0], - &false - ); - - if deposit_result == Err(Err(InvokeError::Contract(10))) { - return; - } else { - panic!("Expected error not returned"); - } - -} \ No newline at end of file diff --git a/apps/contracts/vault/src/test/rebalance.rs b/apps/contracts/vault/src/test/rebalance.rs deleted file mode 100644 index ecea4880..00000000 --- a/apps/contracts/vault/src/test/rebalance.rs +++ /dev/null @@ -1,420 +0,0 @@ -use soroban_sdk::{vec as sorobanvec, InvokeError, String, Vec}; - -use crate::test::{ - create_strategy_params_token0, defindex_vault::{ - ActionType, AssetInvestmentAllocation, AssetStrategySet, Instruction, OptionalSwapDetailsExactIn, OptionalSwapDetailsExactOut, StrategyInvestment - }, invest, DeFindexVaultTest -}; -use crate::test::defindex_vault::ContractError; - -#[test] -fn rebalance_multi_instructions() { - let test = DeFindexVaultTest::setup(); - test.env.mock_all_auths(); - let strategy_params_token0 = create_strategy_params_token0(&test); - 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 = 987654321i128; - - let users = DeFindexVaultTest::generate_random_users(&test.env, 1); - - 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); - - test.defindex_contract.deposit( - &sorobanvec![&test.env, amount], - &sorobanvec![&test.env, amount], - &users[0], - &false - ); - - let df_balance = test.defindex_contract.balance(&users[0]); - assert_eq!(df_balance, amount - 1000); - - let investments = sorobanvec![ - &test.env, - Some(AssetInvestmentAllocation { - asset: test.token0.address.clone(), - strategy_investments: sorobanvec![ - &test.env, - Some(StrategyInvestment { - strategy: test.strategy_client_token0.address.clone(), - amount: amount, - }), - ], - }), - ]; - - test.defindex_contract.invest(&investments); - - let vault_balance = test.token0.balance(&test.defindex_contract.address); - assert_eq!(vault_balance, 0); - - // REBALANCE - - let instruction_amount_0 = 200i128; - let instruction_amount_1 = 100i128; - - let instructions = sorobanvec![ - &test.env, - Instruction { - action: ActionType::Withdraw, - strategy: Some(test.strategy_client_token0.address.clone()), - amount: Some(instruction_amount_0), - swap_details_exact_in: OptionalSwapDetailsExactIn::None, - swap_details_exact_out: OptionalSwapDetailsExactOut::None, - }, - Instruction { - action: ActionType::Invest, - strategy: Some(test.strategy_client_token0.address.clone()), - amount: Some(instruction_amount_1), - swap_details_exact_in: OptionalSwapDetailsExactIn::None, - swap_details_exact_out: OptionalSwapDetailsExactOut::None, - } - ]; - - test.defindex_contract.rebalance(&instructions); - - let vault_balance = test.token0.balance(&test.defindex_contract.address); - assert_eq!(vault_balance, instruction_amount_1); -} - -#[test] -fn rebalance_one_instruction() { - let test = DeFindexVaultTest::setup(); - test.env.mock_all_auths(); - let strategy_params_token0 = create_strategy_params_token0(&test); - 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, "TestVault"), - &String::from_str(&test.env, "TSTV"), - ); - let amount = 987654321i128; - - let users = DeFindexVaultTest::generate_random_users(&test.env, 1); - - 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); - - test.defindex_contract.deposit( - &sorobanvec![&test.env, amount], - &sorobanvec![&test.env, amount], - &users[0], - &false - ); - - let df_balance = test.defindex_contract.balance(&users[0]); - assert_eq!(df_balance, amount - 1000); - - let investments = sorobanvec![ - &test.env, - Some(AssetInvestmentAllocation { - asset: test.token0.address.clone(), - strategy_investments: sorobanvec![ - &test.env, - Some(StrategyInvestment { - strategy: test.strategy_client_token0.address.clone(), - amount: amount, - }), - ], - }), - ]; - - test.defindex_contract.invest(&investments); - - let vault_balance = test.token0.balance(&test.defindex_contract.address); - assert_eq!(vault_balance, 0); - - // REBALANCE - - let instruction_amount_0 = 200i128; - - let instructions = sorobanvec![ - &test.env, - Instruction { - action: ActionType::Withdraw, - strategy: Some(test.strategy_client_token0.address.clone()), - amount: Some(instruction_amount_0), - swap_details_exact_in: OptionalSwapDetailsExactIn::None, - swap_details_exact_out: OptionalSwapDetailsExactOut::None, - }, - ]; - - test.defindex_contract.rebalance(&instructions); - - let vault_balance = test.token0.balance(&test.defindex_contract.address); - assert_eq!(vault_balance, instruction_amount_0); -} - -#[test] -fn rebalance_empty_instructions(){ - let test = DeFindexVaultTest::setup(); - test.env.mock_all_auths(); - - let strategy_params_token0 = create_strategy_params_token0(&test); - 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, - &100u32, - &test.defindex_protocol_receiver, - &test.defindex_factory, - &String::from_str(&test.env, "testVault"), - &String::from_str(&test.env, "TSTV"), - ); - let amount: i128 = 987654321; - let users = DeFindexVaultTest::generate_random_users(&test.env, 1); - test.token0_admin_client.mint(&users[0], &amount); - let vault_balance = test.defindex_contract.balance(&users[0]); - assert_eq!(vault_balance, 0i128); - - test.defindex_contract.deposit( - &sorobanvec![&test.env, amount], - &sorobanvec![&test.env, amount], - &users[0], - &false - ); - let df_balance = test.defindex_contract.balance(&users[0]); - assert_eq!(df_balance, amount - 1000); - - let instructions = sorobanvec![ - &test.env, - Instruction { - action: ActionType::Withdraw, - strategy: None, - amount: None, - swap_details_exact_in: OptionalSwapDetailsExactIn::None, - swap_details_exact_out: OptionalSwapDetailsExactOut::None, - }, - ]; - let rebalance = test.defindex_contract.try_rebalance(&instructions); - assert_eq!(rebalance, Err(Ok(ContractError::MissingInstructionData))); - - let no_strategy_instructions = sorobanvec![ - &test.env, - Instruction { - action: ActionType::Withdraw, - strategy: Some(test.strategy_client_token0.address.clone()), - amount: None, - swap_details_exact_in: OptionalSwapDetailsExactIn::None, - swap_details_exact_out: OptionalSwapDetailsExactOut::None, - }, - ]; - let rebalance = test.defindex_contract.try_rebalance(&no_strategy_instructions); - assert_eq!(rebalance, Err(Ok(ContractError::MissingInstructionData))); - - let no_amount_instructions = sorobanvec![ - &test.env, - Instruction { - action: ActionType::Withdraw, - strategy: Some(test.strategy_client_token0.address.clone()), - amount: None, - swap_details_exact_in: OptionalSwapDetailsExactIn::None, - swap_details_exact_out: OptionalSwapDetailsExactOut::None, - }, - ]; - let rebalance = test.defindex_contract.try_rebalance(&no_amount_instructions); - assert_eq!(rebalance, Err(Ok(ContractError::MissingInstructionData))); -} - -#[test] -fn rebalance_no_instructions(){ - let test = DeFindexVaultTest::setup(); - test.env.mock_all_auths(); - - let strategy_params_token0 = create_strategy_params_token0(&test); - 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, - &100u32, - &test.defindex_protocol_receiver, - &test.defindex_factory, - &String::from_str(&test.env, "testVault"), - &String::from_str(&test.env, "TSTV"), - ); - let amount: i128 = 987654321; - let users = DeFindexVaultTest::generate_random_users(&test.env, 1); - test.token0_admin_client.mint(&users[0], &amount); - let vault_balance = test.defindex_contract.balance(&users[0]); - assert_eq!(vault_balance, 0i128); - - test.defindex_contract.deposit( - &sorobanvec![&test.env, amount], - &sorobanvec![&test.env, amount], - &users[0], - &false - ); - let df_balance = test.defindex_contract.balance(&users[0]); - assert_eq!(df_balance, amount - 1000); - - let rebalance = test.defindex_contract.try_rebalance(&sorobanvec![&test.env]); - assert_eq!(rebalance, Err(Ok(ContractError::NoInstructions))); -} - -#[test] -fn rebalance_insufficient_balance(){ - let test = DeFindexVaultTest::setup(); - test.env.mock_all_auths(); - - let strategy_params_token0 = create_strategy_params_token0(&test); - 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, - &10u32, - &test.defindex_protocol_receiver, - &test.defindex_factory, - &String::from_str(&test.env, "testVault"), - &String::from_str(&test.env, "TSTV"), - ); - let amount: i128 = 987654321; - let users = DeFindexVaultTest::generate_random_users(&test.env, 1); - test.token0_admin_client.mint(&users[0], &amount); - - //Balance should be 0 - let vault_balance = test.defindex_contract.balance(&users[0]); - assert_eq!(vault_balance, 0i128); - - //Withdraw with no funds - let withdraw_no_funds_instructions = sorobanvec![ - &test.env, - Instruction { - action: ActionType::Withdraw, - strategy: Some(test.strategy_client_token0.address.clone()), - amount: Some(amount + 1), - swap_details_exact_in: OptionalSwapDetailsExactIn::None, - swap_details_exact_out: OptionalSwapDetailsExactOut::None, - }, - ]; - - let withdraw_no_funds = test.defindex_contract.try_rebalance(&withdraw_no_funds_instructions); - assert_eq!(withdraw_no_funds, Err(Ok(ContractError::StrategyWithdrawError))); //Contract should respond 'Insuficient balance'? - - //Invest with no funds - let invest_no_funds_instructions = sorobanvec![ - &test.env, - Instruction { - action: ActionType::Invest, - strategy: Some(test.strategy_client_token0.address.clone()), - amount: Some(1), - swap_details_exact_in: OptionalSwapDetailsExactIn::None, - swap_details_exact_out: OptionalSwapDetailsExactOut::None, - }, - ]; - let invest_no_funds = test.defindex_contract.try_rebalance(&invest_no_funds_instructions); - - //Contract should fail with error #10 no balance or panic the test - if invest_no_funds != Err(Err(InvokeError::Contract(10))) { - panic!("Expected error not returned"); - } - - //Deposit 987654321 stroops - test.defindex_contract.deposit( - &sorobanvec![&test.env, amount], - &sorobanvec![&test.env, amount], - &users[0], - &false - ); - let df_balance: i128 = test.defindex_contract.balance(&users[0]); - assert_eq!(df_balance, amount - 1000); - - //Withdraw more than available - let withdraw_instructions = sorobanvec![ - &test.env, - Instruction { - action: ActionType::Withdraw, - strategy: Some(test.strategy_client_token0.address.clone()), - amount: Some(amount + 1), - swap_details_exact_in: OptionalSwapDetailsExactIn::None, - swap_details_exact_out: OptionalSwapDetailsExactOut::None, - }, - ]; - let rebalance = test.defindex_contract.try_rebalance(&withdraw_instructions); - assert_eq!(rebalance, Err(Ok(ContractError::StrategyWithdrawError))); - - let invest_instructions = sorobanvec!( - &test.env, - Instruction { - action: ActionType::Invest, - strategy: Some(test.strategy_client_token0.address.clone()), - amount: Some(amount + 1), - swap_details_exact_in: OptionalSwapDetailsExactIn::None, - swap_details_exact_out: OptionalSwapDetailsExactOut::None, - }, - ); - - //Contract should fail with error #10 no balance - let rebalance = test.defindex_contract.try_rebalance(&invest_instructions); - if rebalance == Err(Err(InvokeError::Contract(10))) { - return; - } else { - panic!("Expected error not returned"); - } -} - - - diff --git a/apps/contracts/vault/src/test/vault/rebalance.rs b/apps/contracts/vault/src/test/vault/rebalance.rs index 64006e25..cffb956e 100644 --- a/apps/contracts/vault/src/test/vault/rebalance.rs +++ b/apps/contracts/vault/src/test/vault/rebalance.rs @@ -1,17 +1,9 @@ use soroban_sdk::{vec as sorobanvec, InvokeError, String, Vec}; use crate::test::{ - create_strategy_params_token0, - defindex_vault::{ - ActionType, - AssetStrategySet, - Instruction, - AssetInvestmentAllocation, - StrategyInvestment, - OptionalSwapDetailsExactIn, - OptionalSwapDetailsExactOut, - }, - DeFindexVaultTest, + create_strategy_params_token0, defindex_vault::{ + ActionType, AssetInvestmentAllocation, AssetStrategySet, Instruction, OptionalSwapDetailsExactIn, OptionalSwapDetailsExactOut, StrategyInvestment + }, invest, DeFindexVaultTest }; use crate::test::defindex_vault::ContractError; @@ -333,7 +325,7 @@ fn insufficient_balance(){ &test.manager, &test.emergency_manager, &test.vault_fee_receiver, - &100u32, + &10u32, &test.defindex_protocol_receiver, &test.defindex_factory, &String::from_str(&test.env, "testVault"), @@ -342,9 +334,45 @@ fn insufficient_balance(){ let amount: i128 = 987654321; let users = DeFindexVaultTest::generate_random_users(&test.env, 1); test.token0_admin_client.mint(&users[0], &amount); + + //Balance should be 0 let vault_balance = test.defindex_contract.balance(&users[0]); assert_eq!(vault_balance, 0i128); + //Withdraw with no funds + let withdraw_no_funds_instructions = sorobanvec![ + &test.env, + Instruction { + action: ActionType::Withdraw, + strategy: Some(test.strategy_client_token0.address.clone()), + amount: Some(amount + 1), + swap_details_exact_in: OptionalSwapDetailsExactIn::None, + swap_details_exact_out: OptionalSwapDetailsExactOut::None, + }, + ]; + + let withdraw_no_funds = test.defindex_contract.try_rebalance(&withdraw_no_funds_instructions); + assert_eq!(withdraw_no_funds, Err(Ok(ContractError::StrategyWithdrawError))); //Contract should respond 'Insuficient balance'? + + //Invest with no funds + let invest_no_funds_instructions = sorobanvec![ + &test.env, + Instruction { + action: ActionType::Invest, + strategy: Some(test.strategy_client_token0.address.clone()), + amount: Some(1), + swap_details_exact_in: OptionalSwapDetailsExactIn::None, + swap_details_exact_out: OptionalSwapDetailsExactOut::None, + }, + ]; + let invest_no_funds = test.defindex_contract.try_rebalance(&invest_no_funds_instructions); + + //Contract should fail with error #10 no balance or panic the test + if invest_no_funds != Err(Err(InvokeError::Contract(10))) { + panic!("Expected error not returned"); + } + + //Deposit 987654321 stroops test.defindex_contract.deposit( &sorobanvec![&test.env, amount], &sorobanvec![&test.env, amount], @@ -354,6 +382,7 @@ fn insufficient_balance(){ let df_balance: i128 = test.defindex_contract.balance(&users[0]); assert_eq!(df_balance, amount - 1000); + //Withdraw more than available let withdraw_instructions = sorobanvec![ &test.env, Instruction { @@ -377,6 +406,8 @@ fn insufficient_balance(){ swap_details_exact_out: OptionalSwapDetailsExactOut::None, }, ); + + //Contract should fail with error #10 no balance let rebalance = test.defindex_contract.try_rebalance(&invest_instructions); if rebalance == Err(Err(InvokeError::Contract(10))) { return; @@ -385,3 +416,5 @@ fn insufficient_balance(){ } } + + From f4ad0d5491542f60148ddc71a89cfb83720c2778 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Tue, 3 Dec 2024 13:17:56 -0300 Subject: [PATCH 12/14] =?UTF-8?q?=E2=9C=85Add=20Hodl=20tests=20for=20depos?= =?UTF-8?q?it,=20events=20&=20withdraw?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategies/hodl/src/test/hodl/deposit.rs | 5 +- .../strategies/hodl/src/test/hodl/events.rs | 117 +++++++++++++++++- .../strategies/hodl/src/test/hodl/withdraw.rs | 56 ++++++++- 3 files changed, 171 insertions(+), 7 deletions(-) diff --git a/apps/contracts/strategies/hodl/src/test/hodl/deposit.rs b/apps/contracts/strategies/hodl/src/test/hodl/deposit.rs index 3267f974..7f075993 100644 --- a/apps/contracts/strategies/hodl/src/test/hodl/deposit.rs +++ b/apps/contracts/strategies/hodl/src/test/hodl/deposit.rs @@ -18,7 +18,10 @@ fn deposit_with_negative_amount() { // check auth #[test] fn deposit_mock_auths() { - todo!() + let test = HodlStrategyTest::setup(); + let init_fn_args: Vec = (0,).into_val(&test.env); + test.strategy.initialize(&test.token.address, &init_fn_args); + test.env.mock_all_auths(); } #[test] diff --git a/apps/contracts/strategies/hodl/src/test/hodl/events.rs b/apps/contracts/strategies/hodl/src/test/hodl/events.rs index 239a9bd1..bd903034 100644 --- a/apps/contracts/strategies/hodl/src/test/hodl/events.rs +++ b/apps/contracts/strategies/hodl/src/test/hodl/events.rs @@ -1,6 +1,117 @@ -// TODO: Write tests for events + +use soroban_sdk::{symbol_short, testutils::Events, vec, IntoVal, Vec, Val}; +use crate::test::HodlStrategyTest; +use crate::event::{InitializedEvent, DepositEvent, HarvestEvent, WithdrawEvent}; + +#[test] +fn initialized (){ + let test = HodlStrategyTest::setup(); + let init_fn_args: Vec = (0,).into_val(&test.env); + test.strategy.initialize(&test.token.address, &init_fn_args); + + let initialized_event = test.env.events().all().last().unwrap(); + let expected_initialized_event = InitializedEvent { + asset: test.token.address, + }; + + assert_eq!( + vec![&test.env, initialized_event.clone()], + vec![ + &test.env, + ( + test.strategy.address.clone(), + ("HodlStrategy", symbol_short!("init")).into_val(&test.env), + (expected_initialized_event).into_val(&test.env) + ) + ] + ); +} + +#[test] +fn deposit() { + let test = HodlStrategyTest::setup(); + let init_fn_args: Vec = (0,).into_val(&test.env); + test.strategy.initialize(&test.token.address, &init_fn_args); + + let amount = 123456; + test.strategy.deposit(&amount, &test.user); + + let deposit_event = test.env.events().all().last().unwrap(); + let expected_deposit_event = DepositEvent { + amount, + from: test.user, + }; + + assert_eq!( + vec![&test.env, deposit_event.clone()], + vec![ + &test.env, + ( + test.strategy.address.clone(), + ("HodlStrategy", symbol_short!("deposit")).into_val(&test.env), + (expected_deposit_event).into_val(&test.env) + ) + ] + ); +} #[test] -fn test_events() { - todo!() +fn withdraw() { + let test = HodlStrategyTest::setup(); + let init_fn_args: Vec = (0,).into_val(&test.env); + test.strategy.initialize(&test.token.address, &init_fn_args); + let amount_to_deposit = 987654321; + test.strategy.deposit(&amount_to_deposit, &test.user); + + let amount_to_withdraw = 123456; + test.strategy.withdraw(&amount_to_withdraw, &test.user); + let withdraw_event = test.env.events().all().last().unwrap(); + let expected_withdraw_event = WithdrawEvent { + amount: amount_to_withdraw, + from: test.user, + }; + + assert_eq!( + vec![&test.env, withdraw_event.clone()], + vec![ + &test.env, + ( + test.strategy.address.clone(), + ("HodlStrategy", symbol_short!("withdraw")).into_val(&test.env), + (expected_withdraw_event).into_val(&test.env) + ) + ] + ); + + + +} + +#[test] +fn harvest(){ + let test = HodlStrategyTest::setup(); + let init_fn_args: Vec = (0,).into_val(&test.env); + test.strategy.initialize(&test.token.address, &init_fn_args); + + let amount = 123456; + test.strategy.deposit(&amount, &test.user); + test.strategy.harvest(&test.user); + + let harvest_event = test.env.events().all().last().unwrap(); + let expected_harvest_event = HarvestEvent { + amount: 0i128, + from: test.user, + }; + + assert_eq!( + vec![&test.env, harvest_event.clone()], + vec![ + &test.env, + ( + test.strategy.address.clone(), + ("HodlStrategy", symbol_short!("harvest")).into_val(&test.env), + (expected_harvest_event).into_val(&test.env) + ) + ] + ); } \ No newline at end of file diff --git a/apps/contracts/strategies/hodl/src/test/hodl/withdraw.rs b/apps/contracts/strategies/hodl/src/test/hodl/withdraw.rs index dd8aa9d5..d4201220 100644 --- a/apps/contracts/strategies/hodl/src/test/hodl/withdraw.rs +++ b/apps/contracts/strategies/hodl/src/test/hodl/withdraw.rs @@ -1,5 +1,55 @@ - +use crate::test::HodlStrategyTest; +use crate::test::StrategyError; +use soroban_sdk::{IntoVal, Vec, Val}; #[test] fn withdraw() { - todo!() -} \ No newline at end of file + let test = HodlStrategyTest::setup(); + + // initialize + let init_fn_args: Vec = (0,).into_val(&test.env); + test.strategy.initialize(&test.token.address, &init_fn_args); + + let balance = test.token.balance(&test.user); + let amount = 123456; + + //Try to withdraw before depositing + let result = test.strategy.try_withdraw(&amount, &test.user); + assert_eq!(result, Err(Ok(StrategyError::InsufficientBalance))); + + // Deposit amount of token from the user to the strategy + test.strategy.deposit(&amount, &test.user); + + let user_balance_after_deposit = test.token.balance(&test.user); + assert_eq!(user_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 + test.strategy.withdraw(&amount_to_withdraw, &test.user); + + // Reading user balance in token + let balance = test.token.balance(&test.user); + assert_eq!(balance, user_balance_after_deposit + amount_to_withdraw); + + // Reading strategy balance in token + let balance = test.token.balance(&test.strategy.address); + assert_eq!(balance, amount - amount_to_withdraw); + + // Reading user balance on strategy contract + let user_balance = test.strategy.balance(&test.user); + assert_eq!(user_balance, amount - amount_to_withdraw); + + //withdraw more than the user has + let amount_to_withdraw = user_balance + 1; + let result = test.strategy.try_withdraw(&amount_to_withdraw, &test.user); + assert_eq!(result, Err(Ok(StrategyError::InsufficientBalance))); + +} From 1d8426f0dc6d3a4c48b43029a88598cab0c009dd Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Tue, 3 Dec 2024 13:25:57 -0300 Subject: [PATCH 13/14] =?UTF-8?q?=F0=9F=A9=B9Fix=20build?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/contracts/vault/src/test/vault/rebalance.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/contracts/vault/src/test/vault/rebalance.rs b/apps/contracts/vault/src/test/vault/rebalance.rs index cffb956e..7e43422d 100644 --- a/apps/contracts/vault/src/test/vault/rebalance.rs +++ b/apps/contracts/vault/src/test/vault/rebalance.rs @@ -3,7 +3,7 @@ use soroban_sdk::{vec as sorobanvec, InvokeError, String, Vec}; use crate::test::{ create_strategy_params_token0, defindex_vault::{ ActionType, AssetInvestmentAllocation, AssetStrategySet, Instruction, OptionalSwapDetailsExactIn, OptionalSwapDetailsExactOut, StrategyInvestment - }, invest, DeFindexVaultTest + }, DeFindexVaultTest }; use crate::test::defindex_vault::ContractError; From 43c1703a088668cbf1b4f4041d5a7141d7cf1646 Mon Sep 17 00:00:00 2001 From: chopan123 Date: Tue, 3 Dec 2024 13:30:07 -0300 Subject: [PATCH 14/14] updated introduction --- apps/docs/01-introduction/01-concepts.md | 35 ++++++++++++++++ .../01-introduction/02-core-concepts.md | 38 +++++++++++++++++ .../10-whitepaper/01-introduction/README.md | 42 ++++--------------- apps/docs/SUMMARY.md | 9 ++-- 4 files changed, 85 insertions(+), 39 deletions(-) create mode 100644 apps/docs/01-introduction/01-concepts.md create mode 100644 apps/docs/10-whitepaper/01-introduction/02-core-concepts.md diff --git a/apps/docs/01-introduction/01-concepts.md b/apps/docs/01-introduction/01-concepts.md new file mode 100644 index 00000000..f1837a11 --- /dev/null +++ b/apps/docs/01-introduction/01-concepts.md @@ -0,0 +1,35 @@ +## Concepts + +These are some concepts to understand the DeFindex protocol: +- **Vault:** +A **DeFindex Vault** is a smart contract that **defines a distribution** of an investment into **one or more strategies**. It works like an index fund or an ETF, where the underlying assets are invested in DeFi protocols. In order to be exposed to DeFi strategies, a user just needs to deposit assets into the Vault. Then, the Vault will take care of automatically investing those assets in the defined strategies. + +- **Strategy:** A strategy is a set of **steps** to be followed to execute an investment in one or several protocols. This could be as simple as just holding assets, or as complex as farming and auto-compound rewards automatically, leverage lending or leveraged farming strategies for borrowing and lending markets like Blend.Capital. + + Example of Leverage Lending: + ``` + Investing USDC in Blend, can be as simple as just depositing USDC in Blend harvest the BLND rewards and reinvest them (autocompounding), or it can be more complex with multiple steps: 1) Deposit 100% in Blend, 2) take a 50% loan in XLM, 3) Swap XLM for USDC, 4) Deposit more USDC. Then harvest BLND rewards. + ``` + +- **Rebalancing:** Rebalancing involves changing the allocation of funds between strategies of a DeFindex Vault. For example, a vault with 50% in two strategies could change to 80% and 20%, respectively. + +- **Shares** or **dfTokens:** Shares are fungible tokens issued to users upon depositing assets into a specific DeFindex Vault. They represent a proportional share of the total assets managed by the DeFindex Vault. Users can burn shares to withdraw their underlying assets, which might be liquidated based on current protocol strategies. + +- **Automated Market Makers (AMM):** AMMs are decentralized exchanges that use algorithms to set prices and facilitate trading. In DeFindex, AMM LP tokens represent liquidity provision in various trading pairs. Users can earn yields from trading fees and token incentives by holding or staking these LP tokens. + + Example: [Soroswap.Finance](https://soroswap.finance). + +- **Lending Platforms:** Lending platforms allow users to deposit assets in exchange for earning interest. DeFindex incorporates lending strategies to diversify asset allocation and maximize returns. Assets deposited in DeFindex can be lent out to earn additional yield. + + Example: [Blend Capital](https://blend.capital). + +- **Autocompounding:** Autocompounding is the process of reinvesting rewards automatically into the same strategy. This allows for changing from APR to APY! This allows for continuous growth of the investment without the need for manual intervention. Let's see an example: + + If a user deposits 100 USDC in a strategy with 30% APR, after one year the user will have 130 USDC. However, if the user reinvests the rewards every day, she will get more! Let's see how this works: + 1. A 30% APR is 0.082191781% per day. Because daily return is APR/365 = 0.082191781% + 2. If the user reinvests the rewards every day, after one year the user will have ~135 USDC. Because $$(1 + 0.00082191781)^{365} = 1,349692488$$ Meaning that instead of 30% APR, the user will have 34.96% APY. + + This shows how powerful the autocompounding is! + +- **Farming:** Farming is the process of earning rewards by staking assets in DeFi protocols. +- **Harvesting:** Harvesting is the process of collecting the rewards earned by the strategy. \ No newline at end of file diff --git a/apps/docs/10-whitepaper/01-introduction/02-core-concepts.md b/apps/docs/10-whitepaper/01-introduction/02-core-concepts.md new file mode 100644 index 00000000..614acd36 --- /dev/null +++ b/apps/docs/10-whitepaper/01-introduction/02-core-concepts.md @@ -0,0 +1,38 @@ +# Core Concepts + +These are some concepts to understand the DeFindex protocol: +- **Vault:** +A **DeFindex Vault** is a smart contract that **defines a distribution** of an investment into **one or more strategies**. It works like an index fund or an ETF, where the underlying assets are invested in DeFi protocols. In order to be exposed to DeFi strategies, a user just needs to deposit assets into the Vault. Then, the Vault will take care of automatically investing those assets in the defined strategies. + +- **Strategy:** A strategy is a set of **steps** to be followed to execute an investment in one or several protocols. This could be as simple as just holding assets, or as complex as farming and auto-compound rewards automatically, leverage lending or leveraged farming strategies for borrowing and lending markets like Blend.Capital. + + Example of Leverage Lending: + ``` + Investing USDC in Blend, can be as simple as just depositing USDC in Blend harvest the BLND rewards and reinvest them (autocompounding), or it can be more complex with multiple steps: 1) Deposit 100% in Blend, 2) take a 50% loan in XLM, 3) Swap XLM for USDC, 4) Deposit more USDC. Then harvest BLND rewards. + ``` + +- **Rebalancing:** Rebalancing involves changing the allocation of funds between strategies of a DeFindex Vault. For example, a vault with 50% in two strategies could change to 80% and 20%, respectively. + +- **Shares** or **dfTokens:** Shares are fungible tokens issued to users upon depositing assets into a specific DeFindex Vault. They represent a proportional share of the total assets managed by the DeFindex Vault. Users can burn shares to withdraw their underlying assets, which might be liquidated based on current protocol strategies. + +- **Automated Market Makers (AMM):** AMMs are decentralized exchanges that use algorithms to set prices and facilitate trading. In DeFindex, AMM LP tokens represent liquidity provision in various trading pairs. Users can earn yields from trading fees and token incentives by holding or staking these LP tokens. + + Example: [Soroswap.Finance](https://soroswap.finance). + +- **Lending Platforms:** Lending platforms allow users to deposit assets in exchange for earning interest. DeFindex incorporates lending strategies to diversify asset allocation and maximize returns. Assets deposited in DeFindex can be lent out to earn additional yield. + + Example: [Blend Capital](https://blend.capital). + +- **IDLE Assets:** DeFindex maintains its balance between invested and idle assets. Idle assets are kept liquid to ensure users can easily withdraw funds without disrupting ongoing investments. Also, if a strategy is unhealthy, Emergency Managers can liquidate risky positions and move all funds into idle assets in order to protect investors from unhealthy or risky strategies. + +- **Rescue funds:** Emergency Managers can liquidate risky positions and move all funds into idle assets in order to protect investors from unhealthy or risky strategies. + +- **Price Per Share (PPS):** Price Per Share (PPS) is a key metric that determines the value of one share (dfTokens) relative to the total assets managed by a DeFindex Vault. + +- **Autocompounding:** Autocompounding is the process of reinvesting rewards automatically into the same strategy. This allows for changing from APR to APY! This allows for continuous growth of the investment without the need for manual intervention. Let's see an example: + + If a user deposits 100 USDC in a strategy with 30% APR, after one year the user will have 130 USDC. However, if the user reinvests the rewards every day, she will get more! Let's see how this works: + 1. A 30% APR is 0.082191781% per day. Because daily return is APR/365 = 0.082191781% + 2. If the user reinvests the rewards every day, after one year the user will have ~135 USDC. Because $(1 + 0.00082191781)^{365} = 1,349692488$ Meaning that instead of 30% APR, the user will have 34.96% APY. + + This shows how powerful the autocompounding is! \ No newline at end of file diff --git a/apps/docs/10-whitepaper/01-introduction/README.md b/apps/docs/10-whitepaper/01-introduction/README.md index e3ba4df4..7b871c43 100644 --- a/apps/docs/10-whitepaper/01-introduction/README.md +++ b/apps/docs/10-whitepaper/01-introduction/README.md @@ -1,43 +1,15 @@ # Introduction -With the new Protocol 20 of Stellar, new Smart Contract based Descentralized Protocols have arised in the Stellar Blockchain. Automated Market Makers like **Soroswap.Finance**, or Lending and Borrowing protocols like **Blend Capital** are just the beggining of a new set of DeFi Protocols. +With the introduction of Protocol 20 of Stellar, new Smart Contract based Descentralized Protocols have arised in the Stellar Blockchain. Automated Market Makers like **Soroswap.Finance**, or Lending and Borrowing protocols like **Blend Capital** are just the beggining of a new set of DeFi Protocols. -These protocols allow from simple to complex investment stragtegies. Very simple strategies can be just to invest in a Soroswap.Finance AMM Constant Product Liquidity Pool, or just to lend USDC in a Blend Market and harvest the BLND reward to later swap those BLND harvested to USDC and reinvest them in the lending pool. Other very simple strategy can be to diversify investment in two assets and just hold those assets. +These protocols allow from simple to complex investment stragtegies. The most simple strategy can be holding assets, other simple strategies can be investing in a Soroswap.Finance AMM Constant Product Liquidity Pool. Also, a more complex strategy can be lending USDC in a Blend Market, harvest the BLND reward to later swap those BLND harvested to USDC and reinvest them in the lending pool. -A bit more complex strategy can be to diversify the investment in both protocols, or even, lend USDC, borrow XLM and then participate in a Liquidity Pool USDC/XLM with some reward program that can be harvested. +However, this is very time consuming and requires a lot of knowledge about the protocols and strategies. From one side, a crypto investor would need to spend a lot of time learning about the protocols and strategies, and then, would need to manually manage their investments. From another side, it's just too complex for non-expert users, or even for wallets users that prefer a very simple interface. -However, for this to be easyly done by non-experienced users, or even by wallets users that prefer to have a very simple interface, an interface Smart Contract protocol is needed. +That's why DeFindex was created. DeFindex is a protocol where users can define how their **investments are distributed** among **multiple DeFi protocols and strategies**. The definition of this distribution and its rules involves the creation of a vault, which works like an index fund or an ETF, where the underlying assets are invested in DeFi protocols. -DeFindex is a protocol where users can define how their **investments are distributed** among **multiple DeFi protocols and strategies**. The definition of this distribution and its rules involves the creation of an index. The distribution refers to the specification of percentage allocations to protocols and strategies. +This is particularly useful for wallets users (even web2 users) that prefer a very simple interface, where wallet builders can integrate DeFindex in their wallets, to offer a DeFi investing service to their users. Also, for expert users that prefer to have a very easy way to diversify their investments among multiple protocols and strategies. -## Core Concepts +We want to make DeFi investing easy, simple and accessible for everyone! -- **Index:** -An index ("DeFindex") or **DeFindex Vault** is a smart contract that **defines a distribution** of an investment into **one or more strategies**. A DeFindex has a fixed list of strategies where investments can be made and the percentage distribution can be fixed?? or variable. Changing the distribution percentage is called rebalancing. - -- **Strategy:** A strategy is a set of **steps** to be followed to execute an investment in one or several protocols. This can include farming and auto-compound rewards automatically, leverage lending or leveraged farming strategies for borrowing and lending markets like Blend.Capital. - - Example of Leverage Lending: - ``` - Investing USDC in Blend, can be as simple as just depositing USDC in Blend harvest the BLND rewards and reinvest them (autocompounding), or it can be more complex with multiple steps: 1) Deposit 100% in Blend, 2) take a 50% loan in XLM, 3) Swap XLM for USDC, 4) Deposit more USDC. Then harvest BLND rewards. - ``` - -- **Rebalancing:** Rebalancing involves changing the strategy distributions ratio of a DeFindex. For example, an index with 50% in two protocols could change to 80% and 20%, respectively. This process moves all the investment in the protocols to achieve the desired percentages. - - Rebalancing can be made in order to allways achieve a desired strategy distribution ratio, or in order to change the desired distribution ratio to a new one. - -- **dfTokens:** dfTokens are fungible tokens issued to users upon depositing assets into a specific DeFindex Vault. They represent a proportional share of the total assets managed by the DeFindex Valut. Users can burn dfTokens to withdraw their underlying assets, which might be liquidated based on current protocol strategies. - -- **Automated Market Makers (AMM):** AMMs are decentralized exchanges that use algorithms to set prices and facilitate trading. In DeFindex, AMM LP tokens represent liquidity provision in various trading pairs. Users can earn yields from trading fees and token incentives by holding or staking these LP tokens. - - Example: [Soroswap.Finance](https://soroswap.finance). - -- **Lending Platforms:** Lending platforms allow users to deposit assets in exchange for earning interest. DeFindex incorporates lending strategies to diversify asset allocation and maximize returns. Assets deposited in DeFindex can be lent out to earn additional yield. - - Example: [Blend Capital](https://blend.capital). - -- **IDLE Assets:** DeFindex maintains a balance between invested and idle assets. Idle assets are kept liquid to ensure users can easily withdraw funds without disrupting ongoing investments. The Minimum Idle Amount is the threshold of liquid assets required to support smooth operations and withdrawals. - -- **Emergengy Withdraw:** Emergency Managers can liquidate risky positions and move all funds into idle assets in order to protect investors from unhealthy or risky strategies., - -- **Price Per Share (PPS):** Price Per Share (PPS) is a key metric that determines the value of one dfTokens relative to the total assets managed by a DeFindex Vault. +In this whitepaper we will explain core concepts to understand DeFindex. Then, design decisions, how it works, which contracts are involved, the current state of the art. \ No newline at end of file diff --git a/apps/docs/SUMMARY.md b/apps/docs/SUMMARY.md index 7c30811c..c388a399 100644 --- a/apps/docs/SUMMARY.md +++ b/apps/docs/SUMMARY.md @@ -2,14 +2,12 @@ ## DeFindex Protocol * [Introduction](README.md) -* [Concepts](01-introduction/README.md) +* [Concepts](01-introduction/01-concepts.md) ## Whitepaper * [Whitepaper](10-whitepaper/README.md) * [Introduction](10-whitepaper/01-introduction/README.md) - * [State of the Art](10-whitepaper/02-state-of-the-art/README.md) - * [Yearn Finance](10-whitepaper/02-state-of-the-art/01-yearn-finance.md) - * [TokenSets](10-whitepaper/02-state-of-the-art/02-tokensets.md) + * [Core Concepts](10-whitepaper/01-introduction/02-core-concepts.md) * [The DeFindex Approach](10-whitepaper/03-the-defindex-approach/README.md) * [Design Decisions](10-whitepaper/03-the-defindex-approach/01-design-decisions.md) * [Smart Contracts](10-whitepaper/03-the-defindex-approach/02-contracts/README.MD) @@ -17,6 +15,9 @@ * [DeFindex Strategy](./10-whitepaper/03-the-defindex-approach/02-contracts/02-strategy-contract.md) * [DeFindex Zapper](./10-whitepaper/03-the-defindex-approach/02-contracts/02-zapper-contract.md) * [Strategy Examples](./10-whitepaper/04-strategies-examples.md) + * [State of the Art](10-whitepaper/02-state-of-the-art/README.md) + * [Yearn Finance](10-whitepaper/02-state-of-the-art/01-yearn-finance.md) + * [TokenSets](10-whitepaper/02-state-of-the-art/02-tokensets.md) * [Appendix](./10-whitepaper/10-appendix/README.md) * [Why Not Swap in Deposit](./10-whitepaper/10-appendix/01-why-we-cant-swap-on-deposit-or-withdraw.md)