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] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Renamed=20tests=20&=20crea?= =?UTF-8?q?te=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);