From 47d8ba83b79765ea4270feaf2f1208cd1e6f0640 Mon Sep 17 00:00:00 2001 From: chopan123 Date: Wed, 30 Oct 2024 09:31:44 -0300 Subject: [PATCH 01/19] added strategy balance check --- apps/contracts/src/test.ts | 5 +++- apps/contracts/src/tests/strategy.ts | 43 ++++++++++++++++++++++++++++ apps/contracts/src/tests/vault.ts | 2 +- 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 apps/contracts/src/tests/strategy.ts diff --git a/apps/contracts/src/test.ts b/apps/contracts/src/test.ts index e37c3e03..51d0ee50 100644 --- a/apps/contracts/src/test.ts +++ b/apps/contracts/src/test.ts @@ -11,6 +11,7 @@ import { AddressBook } from "./utils/address_book.js"; import { airdropAccount, invokeContract } from "./utils/contract.js"; import { config } from "./utils/env_config.js"; import { getDfTokenBalance, depositToVault } from "./tests/vault.js"; +import { checkUserBalance } from "./tests/strategy.js"; export async function test_factory(addressBook: AddressBook) { @@ -109,7 +110,9 @@ const passphrase = network === "mainnet" ? Networks.PUBLIC : network === "testne const loadedConfig = config(network); const deployedVault = await test_factory(addressBook); -await depositToVault(deployedVault); +const [user] = await depositToVault(deployedVault); +const strategyBalance = await checkUserBalance(addressBook.getContractId("hodl_strategy"), user.publicKey(), user) +console.log('πŸš€ ~ strategyBalance:', strategyBalance); await depositToVault(deployedVault); // await getDfTokenBalance("CCL54UEU2IGTCMIJOYXELIMVA46CLT3N5OG35XN45APXDZYHYLABF53N", "GDAMXOJUSW6O67UVI6U4LBHI5IIJFUKQVDHPKNFKOIYRLYB2LA6YDAFI", loadedConfig.admin) diff --git a/apps/contracts/src/tests/strategy.ts b/apps/contracts/src/tests/strategy.ts new file mode 100644 index 00000000..ae916970 --- /dev/null +++ b/apps/contracts/src/tests/strategy.ts @@ -0,0 +1,43 @@ +import { + Address, + scValToNative, + xdr, + Keypair +} from "@stellar/stellar-sdk"; +import { invokeCustomContract } from "../utils/contract.js"; + +/** + * Description: Retrieves the balance for a specified user from the contract. + * + * @param {string} contractAddress - The address of the deployed contract. + * @param {string} userPublicKey - The public key of the user whose balance to check. + * @param {Keypair} [source] - Optional; the Keypair instance for authorization if required. + * @returns {Promise} The balance of the specified user in the contract. + * @throws Will throw an error if the balance retrieval fails. + * @example + * const balance = await checkUserBalance("CCE7MLKC7R6TIQA37A7EHWEUC3AIXIH5DSOQUSVAARCWDD7257HS4RUG", "GB6JL..."); + */ + +export async function checkUserBalance(contractAddress: string, userPublicKey: string, source?: Keypair): Promise { + const userAddress = new Address(userPublicKey).toScVal(); + const methodName = "balance"; + + try { + // Call the `balance` method from the contract + const result = await invokeCustomContract( + contractAddress, + methodName, + [userAddress], + source ? source : Keypair.random() + ); + + // Convert the result to a native JavaScript number + const balance = scValToNative(result.returnValue) as number; + console.log(`Balance for user ${userPublicKey}:`, balance); + + return balance; + } catch (error) { + console.error(`Failed to retrieve balance for user ${userPublicKey}:`, error); + throw error; + } +} diff --git a/apps/contracts/src/tests/vault.ts b/apps/contracts/src/tests/vault.ts index 5ef8934e..01bad070 100644 --- a/apps/contracts/src/tests/vault.ts +++ b/apps/contracts/src/tests/vault.ts @@ -55,7 +55,6 @@ export async function depositToVault(deployedVault: string, user?: Keypair) { console.error("❌ Balance failed:", error); } try { - // TODO: Would this work on Mainnet or Standalone? How does it know which network to use? const result = await invokeCustomContract( deployedVault, "deposit", @@ -76,6 +75,7 @@ export async function depositToVault(deployedVault: string, user?: Keypair) { } catch (error) { console.error("❌ Balance failed:", error); } + return [newUser]; } /** From c8fe281fee0a16b32f05f92c393800c07e067b36 Mon Sep 17 00:00:00 2001 From: chopan123 Date: Wed, 30 Oct 2024 09:33:49 -0300 Subject: [PATCH 02/19] docs: updated readme --- apps/contracts/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/contracts/README.md b/apps/contracts/README.md index db900e43..fff530c2 100644 --- a/apps/contracts/README.md +++ b/apps/contracts/README.md @@ -32,7 +32,7 @@ Make sure you have the hodl strategy deployed, if not, you can run: yarn deploy-hodl ``` -to test the factory to deploy a DeFindex Vault +to test the factory to deploy a DeFindex Vault, and deposit there two times, you can run: ```bash yarn test From e4e4abd81062f032d2e03e79cf8ffa74f73f39ab Mon Sep 17 00:00:00 2001 From: chopan123 Date: Wed, 30 Oct 2024 15:41:10 -0300 Subject: [PATCH 03/19] Trying to withdraw from Vault --- apps/contracts/package.json | 3 +- apps/contracts/src/test.ts | 35 ++++++++-- apps/contracts/src/tests/dev.ts | 10 +++ apps/contracts/src/tests/vault.ts | 106 ++++++++++++++++++++++++------ 4 files changed, 128 insertions(+), 26 deletions(-) create mode 100644 apps/contracts/src/tests/dev.ts diff --git a/apps/contracts/package.json b/apps/contracts/package.json index 329bffed..3bfb5bda 100644 --- a/apps/contracts/package.json +++ b/apps/contracts/package.json @@ -8,7 +8,8 @@ "deploy-hodl": "tsc && node dist/deploy_hodl.js", "publish-addresses": "tsc && node dist/publish_addresses.js", "test": "tsc && node dist/test.js", - "test-vault": "tsc && node dist/tests/testOnlyVault.js" + "test-vault": "tsc && node dist/tests/testOnlyVault.js", + "test-dev": "tsc && node dist/tests/dev.js" }, "type": "module", "devDependencies": { diff --git a/apps/contracts/src/test.ts b/apps/contracts/src/test.ts index 2d626e64..5f82e85c 100644 --- a/apps/contracts/src/test.ts +++ b/apps/contracts/src/test.ts @@ -7,7 +7,7 @@ import { xdr } from "@stellar/stellar-sdk"; import { randomBytes } from "crypto"; -import { depositToVault } from "./tests/vault.js"; +import { depositToVault, withdrawFromVault } from "./tests/vault.js"; import { AddressBook } from "./utils/address_book.js"; import { airdropAccount, invokeContract } from "./utils/contract.js"; import { config } from "./utils/env_config.js"; @@ -110,9 +110,36 @@ const passphrase = network === "mainnet" ? Networks.PUBLIC : network === "testne const loadedConfig = config(network); const deployedVault = await test_factory(addressBook); -const [user] = await depositToVault(deployedVault); -const strategyBalance = await checkUserBalance(addressBook.getContractId("hodl_strategy"), user.publicKey(), user) -console.log('πŸš€ ~ strategyBalance:', strategyBalance); +// Step 1: Deposit to vault and capture initial balances +const { user, balanceBefore: depositBalanceBefore, result: depositResult, balanceAfter: depositBalanceAfter } = await depositToVault(deployedVault); + +// Step 2: Check strategy balance after deposit +const strategyBalanceAfterDeposit = await checkUserBalance(addressBook.getContractId("hodl_strategy"), user.publicKey(), user); + +// Step 3: Withdraw from the vault +const { balanceBefore: withdrawBalanceBefore, result: withdrawResult, balanceAfter: withdrawBalanceAfter } = await withdrawFromVault(deployedVault, BigInt(5000000), user); + +// Step 4: Check strategy balance after withdrawal +const strategyBalanceAfterWithdraw = await checkUserBalance(addressBook.getContractId("hodl_strategy"), user.publicKey(), user); + +// Log a table with all balances +console.table([ + { + Operation: "Deposit", + "Balance Before": depositBalanceBefore, + "Deposit Result": depositResult, + "Balance After": depositBalanceAfter, + "Strategy Balance After": strategyBalanceAfterDeposit + }, + { + Operation: "Withdraw", + "Balance Before": withdrawBalanceBefore, + "Withdraw Result": withdrawResult, + "Balance After": withdrawBalanceAfter, + "Strategy Balance After": strategyBalanceAfterWithdraw + } +]); + await depositToVault(deployedVault); // await getDfTokenBalance("CCL54UEU2IGTCMIJOYXELIMVA46CLT3N5OG35XN45APXDZYHYLABF53N", "GDAMXOJUSW6O67UVI6U4LBHI5IIJFUKQVDHPKNFKOIYRLYB2LA6YDAFI", loadedConfig.admin) diff --git a/apps/contracts/src/tests/dev.ts b/apps/contracts/src/tests/dev.ts new file mode 100644 index 00000000..d370a0d7 --- /dev/null +++ b/apps/contracts/src/tests/dev.ts @@ -0,0 +1,10 @@ +import { airdropAccount } from "../utils/contract.js"; +import { withdrawFromVault } from "./vault.js"; +import { Keypair } from "@stellar/stellar-sdk"; + +const user = Keypair.fromSecret("SA77N6PLHDFRYDNYE3YJQBPTRNODMVYP5WWF2SG42DXB52GW2FWOG2B3") +const contract = "CCNWF3D7FJCZKYCAD6FAO3JNPRHG6SVXHO5YTFDZRXSPOJXL6TIBWP3Y" + +const badUser = Keypair.random() +await airdropAccount(badUser); +await withdrawFromVault(contract, BigInt(100000000), badUser) \ No newline at end of file diff --git a/apps/contracts/src/tests/vault.ts b/apps/contracts/src/tests/vault.ts index 01bad070..ef25c5a1 100644 --- a/apps/contracts/src/tests/vault.ts +++ b/apps/contracts/src/tests/vault.ts @@ -1,15 +1,14 @@ /** - * Description: Tests the vault by creating a new user, airdropping funds, and making a deposit. + * Description: Deposits a specified amount to the vault for a user and returns the user details along with pre- and post-deposit balances. * * @param {string} deployedVault - The address of the deployed vault contract. - * @returns {Promise} Logs the result of the deposit action. + * @param {Keypair} [user] - The user Keypair making the deposit. If not provided, a new user will be created. + * @returns {Promise<{ user: Keypair, balanceBefore: number, result: any, balanceAfter: number }>} Returns an object with the user, balance before, deposit result, and balance after. * @throws Will throw an error if the deposit fails or any step encounters an issue. * @example - * await test_vault("CCE7MLKC7R6TIQA37A7EHWEUC3AIXIH5DSOQUSVAARCWDD7257HS4RUG"); + * const { user, balanceBefore, result, balanceAfter } = await depositToVault("CCE7MLKC7R6TIQA37A7EHWEUC3AIXIH5DSOQUSVAARCWDD7257HS4RUG", user); */ -// ./tests/vault.ts - import { Address, nativeToScVal, @@ -25,15 +24,18 @@ import { config } from "../utils/env_config.js"; const network = process.argv[2]; export async function depositToVault(deployedVault: string, user?: Keypair) { - // Create and fund a new user account + // Create and fund a new user account if not provided const newUser = user ? user : Keypair.random(); console.log('πŸš€ ~ depositToVault ~ newUser.publicKey():', newUser.publicKey()); console.log('πŸš€ ~ depositToVault ~ newUser.secret():', newUser.secret()); if (network !== "mainnet") await airdropAccount(newUser); - console.log("New user publicKey:", newUser.publicKey()); + let balanceBefore: number; + let balanceAfter: number; + let result: any; + // Define deposit parameters const depositAmount = BigInt(10000000); // 1 XLM in stroops (1 XLM = 10^7 stroops) const amountsDesired = [depositAmount]; @@ -44,40 +46,42 @@ export async function depositToVault(deployedVault: string, user?: Keypair) { xdr.ScVal.scvVec(amountsMin.map((min) => nativeToScVal(min, { type: "i128" }))), (new Address(newUser.publicKey())).toScVal() ]; - // console.log('πŸš€ ~ depositToVault ~ depositParams:', depositParams); try { - - // Check the user's balance after the deposit - const balanceBefore = await getDfTokenBalance(deployedVault, newUser.publicKey(), newUser); - console.log("πŸ”’ Β« dfToken balance before deposit:", balanceBefore) + // Check the user's balance before the deposit + balanceBefore = await getDfTokenBalance(deployedVault, newUser.publicKey(), newUser); + console.log("πŸ”’ Β« dfToken balance before deposit:", balanceBefore); } catch (error) { - console.error("❌ Balance failed:", error); + console.error("❌ Balance check before deposit failed:", error); + throw error; } + try { - const result = await invokeCustomContract( + result = await invokeCustomContract( deployedVault, "deposit", depositParams, newUser ); - console.log("πŸš€ Β« Deposit successful:", scValToNative(result.returnValue)); - } catch (error) { console.error("❌ Deposit failed:", error); + throw error; } - try { + try { // Check the user's balance after the deposit - const balanceAfter = await getDfTokenBalance(deployedVault, newUser.publicKey(), newUser); - console.log("πŸ”’ Β« dfToken balance after deposit:", balanceAfter) + balanceAfter = await getDfTokenBalance(deployedVault, newUser.publicKey(), newUser); + console.log("πŸ”’ Β« dfToken balance after deposit:", balanceAfter); } catch (error) { - console.error("❌ Balance failed:", error); + console.error("❌ Balance check after deposit failed:", error); + throw error; } - return [newUser]; + + return { user: newUser, balanceBefore, result, balanceAfter }; } + /** * Description: Retrieves the dfToken balance for a specified user from the vault contract. * @@ -109,3 +113,63 @@ export async function getDfTokenBalance(deployedVault: string, userPublicKey: st throw error; } } + +/** + * Description: Withdraws a specified amount from the vault for the user and returns the pre- and post-withdrawal balances. + * + * @param {string} deployedVault - The address of the deployed vault contract. + * @param {BigInt} withdrawAmount - The amount in stroops to withdraw (1 XLM = 10^7 stroops). + * @param {Keypair} user - The user Keypair requesting the withdrawal. + * @returns {Promise<{ balanceBefore: number, result: any, balanceAfter: number }>} Returns an object with balance before, the withdrawal result, and balance after. + * @throws Will throw an error if the withdrawal fails or any step encounters an issue. + * @example + * const { balanceBefore, result, balanceAfter } = await withdrawFromVault("CCE7MLKC7R6TIQA37A7EHWEUC3AIXIH5DSOQUSVAARCWDD7257HS4RUG", 10000000, user); + */ + +export async function withdrawFromVault(deployedVault: string, withdrawAmount: BigInt, user: Keypair) { + console.log('πŸš€ ~ withdrawFromVault ~ User publicKey:', user.publicKey()); + + let balanceBefore: number; + let balanceAfter: number; + let result: any; + + try { + // Check the user's balance before the withdrawal + balanceBefore = await getDfTokenBalance(deployedVault, user.publicKey(), user); + console.log("πŸ”’ Β« dfToken balance before withdraw:", balanceBefore); + } catch (error) { + console.error("❌ Balance check before withdraw failed:", error); + throw error; + } + + // Define withdraw parameters + const amountsToWithdraw = [withdrawAmount]; + const withdrawParams: xdr.ScVal[] = [ + xdr.ScVal.scvVec(amountsToWithdraw.map((amount) => nativeToScVal(amount, { type: "i128" }))), + (new Address(user.publicKey())).toScVal() + ]; + + try { + result = await invokeCustomContract( + deployedVault, + "withdraw", + withdrawParams, + user + ); + console.log("πŸš€ Β« Withdrawal successful:", scValToNative(result.returnValue)); + } catch (error) { + console.error("❌ Withdrawal failed:", error); + throw error; + } + + try { + // Check the user's balance after the withdrawal + balanceAfter = await getDfTokenBalance(deployedVault, user.publicKey(), user); + console.log("πŸ”’ Β« dfToken balance after withdraw:", balanceAfter); + } catch (error) { + console.error("❌ Balance check after withdraw failed:", error); + throw error; + } + + return { balanceBefore, result, balanceAfter }; +} From f086e15a018f17495f8754dae1d2065343685b22 Mon Sep 17 00:00:00 2001 From: chopan123 Date: Wed, 30 Oct 2024 15:42:17 -0300 Subject: [PATCH 04/19] refactor, --- apps/contracts/src/tests/dev.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/contracts/src/tests/dev.ts b/apps/contracts/src/tests/dev.ts index d370a0d7..b75012e0 100644 --- a/apps/contracts/src/tests/dev.ts +++ b/apps/contracts/src/tests/dev.ts @@ -4,7 +4,8 @@ import { Keypair } from "@stellar/stellar-sdk"; const user = Keypair.fromSecret("SA77N6PLHDFRYDNYE3YJQBPTRNODMVYP5WWF2SG42DXB52GW2FWOG2B3") const contract = "CCNWF3D7FJCZKYCAD6FAO3JNPRHG6SVXHO5YTFDZRXSPOJXL6TIBWP3Y" +await withdrawFromVault(contract, BigInt(10000), user) -const badUser = Keypair.random() -await airdropAccount(badUser); -await withdrawFromVault(contract, BigInt(100000000), badUser) \ No newline at end of file +// const badUser = Keypair.random() +// await airdropAccount(badUser); +// await withdrawFromVault(contract, BigInt(1000), badUser) \ No newline at end of file From 45d9f232e63adf4b7e563a29de8ef48886d2bb60 Mon Sep 17 00:00:00 2001 From: chopan123 Date: Mon, 4 Nov 2024 11:07:11 -0300 Subject: [PATCH 05/19] test for withdraw failing due to invalidAction --- apps/contracts/src/tests/dev.ts | 6 +++++- apps/contracts/src/tests/vault.ts | 20 ++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/apps/contracts/src/tests/dev.ts b/apps/contracts/src/tests/dev.ts index b75012e0..9177b236 100644 --- a/apps/contracts/src/tests/dev.ts +++ b/apps/contracts/src/tests/dev.ts @@ -4,8 +4,12 @@ import { Keypair } from "@stellar/stellar-sdk"; const user = Keypair.fromSecret("SA77N6PLHDFRYDNYE3YJQBPTRNODMVYP5WWF2SG42DXB52GW2FWOG2B3") const contract = "CCNWF3D7FJCZKYCAD6FAO3JNPRHG6SVXHO5YTFDZRXSPOJXL6TIBWP3Y" -await withdrawFromVault(contract, BigInt(10000), user) +console.log("πŸš€ ~ file: dev.ts ~ line 6 ~ user", user.publicKey()) +const withdrawResult = + await withdrawFromVault(contract, 10000, user) +// await withdrawFromVault(contract, BigInt(10000), user) +console.log('πŸš€ ~ withdrawResult:', withdrawResult); // const badUser = Keypair.random() // await airdropAccount(badUser); // await withdrawFromVault(contract, BigInt(1000), badUser) \ No newline at end of file diff --git a/apps/contracts/src/tests/vault.ts b/apps/contracts/src/tests/vault.ts index ef25c5a1..c94681e0 100644 --- a/apps/contracts/src/tests/vault.ts +++ b/apps/contracts/src/tests/vault.ts @@ -118,7 +118,7 @@ export async function getDfTokenBalance(deployedVault: string, userPublicKey: st * Description: Withdraws a specified amount from the vault for the user and returns the pre- and post-withdrawal balances. * * @param {string} deployedVault - The address of the deployed vault contract. - * @param {BigInt} withdrawAmount - The amount in stroops to withdraw (1 XLM = 10^7 stroops). + * @param {BigInt | number} withdrawAmount - The amount in stroops to withdraw (1 XLM = 10^7 stroops). * @param {Keypair} user - The user Keypair requesting the withdrawal. * @returns {Promise<{ balanceBefore: number, result: any, balanceAfter: number }>} Returns an object with balance before, the withdrawal result, and balance after. * @throws Will throw an error if the withdrawal fails or any step encounters an issue. @@ -126,7 +126,7 @@ export async function getDfTokenBalance(deployedVault: string, userPublicKey: st * const { balanceBefore, result, balanceAfter } = await withdrawFromVault("CCE7MLKC7R6TIQA37A7EHWEUC3AIXIH5DSOQUSVAARCWDD7257HS4RUG", 10000000, user); */ -export async function withdrawFromVault(deployedVault: string, withdrawAmount: BigInt, user: Keypair) { +export async function withdrawFromVault(deployedVault: string, withdrawAmount: BigInt | number, user: Keypair) { console.log('πŸš€ ~ withdrawFromVault ~ User publicKey:', user.publicKey()); let balanceBefore: number; @@ -173,3 +173,19 @@ export async function withdrawFromVault(deployedVault: string, withdrawAmount: B return { balanceBefore, result, balanceAfter }; } + +/** + * Retrieves the current idle funds of the vault. + * + * @param {string} deployedVault - The address of the deployed vault contract. + * @returns {Promise>} A promise that resolves with a map of asset addresses to idle amounts. + */ +export async function fetchCurrentIdleFunds(deployedVault: string, user: Keypair): Promise> { + try { + const result = await invokeCustomContract(deployedVault, "fetch_current_idle_funds", [], user); + return result.map(scValToNative); // Convert result to native format if needed + } catch (error) { + console.error("❌ Failed to fetch current idle funds:", error); + throw error; + } +} From d6adfa55b34a0f695ea68974fa04a0b2f1ca42b2 Mon Sep 17 00:00:00 2001 From: esteblock Date: Mon, 4 Nov 2024 12:37:24 -0300 Subject: [PATCH 06/19] check for empty secret key in .env --- apps/contracts/src/utils/env_config.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/contracts/src/utils/env_config.ts b/apps/contracts/src/utils/env_config.ts index 96996bf6..89083d03 100644 --- a/apps/contracts/src/utils/env_config.ts +++ b/apps/contracts/src/utils/env_config.ts @@ -81,10 +81,11 @@ class EnvConfig { horizon_rpc_url === undefined || (network != "mainnet" && friendbot_url === undefined) || passphrase === undefined || - admin === undefined + admin === undefined || + admin === "" ) { throw new Error( - "Error: Configuration is missing required fields, include " + "Error: Configuration is missing required fields. Please check your .env file." ); } From c8a0cd350e73e02536aee876cd7bba9006e87a2b Mon Sep 17 00:00:00 2001 From: esteblock Date: Mon, 4 Nov 2024 14:52:21 -0300 Subject: [PATCH 07/19] test initialize function@ --- apps/contracts/src/test.ts | 21 ++++- apps/contracts/strategies/hodl/src/lib.rs | 11 ++- apps/contracts/strategies/hodl/src/test.rs | 87 ++++--------------- .../strategies/hodl/src/test/deposit.rs | 51 +++++++++++ .../strategies/hodl/src/test/initialize.rs | 17 ++++ 5 files changed, 111 insertions(+), 76 deletions(-) create mode 100644 apps/contracts/strategies/hodl/src/test/deposit.rs create mode 100644 apps/contracts/strategies/hodl/src/test/initialize.rs diff --git a/apps/contracts/src/test.ts b/apps/contracts/src/test.ts index 5f82e85c..55c70a39 100644 --- a/apps/contracts/src/test.ts +++ b/apps/contracts/src/test.ts @@ -27,6 +27,7 @@ export async function test_factory(addressBook: AddressBook) { console.log("Testing Create DeFindex on Factory"); console.log("-------------------------------------------------------"); + console.log("Setting Emergengy Manager, Fee Receiver and Manager accounts"); const emergencyManager = loadedConfig.getUser("DEFINDEX_EMERGENCY_MANAGER_SECRET_KEY"); if (network !== "mainnet") await airdropAccount(emergencyManager); @@ -109,15 +110,33 @@ const passphrase = network === "mainnet" ? Networks.PUBLIC : network === "testne const loadedConfig = config(network); +// Step 0: Deploy the vault const deployedVault = await test_factory(addressBook); +console.log(" -- ") +console.log(" -- ") +console.log("Step 0: Deployed Vault:", deployedVault); +console.log(" -- ") +console.log(" -- ") + + // Step 1: Deposit to vault and capture initial balances const { user, balanceBefore: depositBalanceBefore, result: depositResult, balanceAfter: depositBalanceAfter } = await depositToVault(deployedVault); +console.log(" -- ") +console.log(" -- ") +console.log("Step 1: Deposited to Vault using user:", user.publicKey(), "with balance before:", depositBalanceBefore, "and balance after:", depositBalanceAfter); +console.log(" -- ") +console.log(" -- ") // Step 2: Check strategy balance after deposit const strategyBalanceAfterDeposit = await checkUserBalance(addressBook.getContractId("hodl_strategy"), user.publicKey(), user); +console.log(" -- ") +console.log(" -- ") +console.log("Step 2: Strategy balance after deposit:", strategyBalanceAfterDeposit); +console.log(" -- ") +console.log(" -- ") // Step 3: Withdraw from the vault -const { balanceBefore: withdrawBalanceBefore, result: withdrawResult, balanceAfter: withdrawBalanceAfter } = await withdrawFromVault(deployedVault, BigInt(5000000), user); +const { balanceBefore: withdrawBalanceBefore, result: withdrawResult, balanceAfter: withdrawBalanceAfter } = await withdrawFromVault(deployedVault, BigInt(0), user); // Step 4: Check strategy balance after withdrawal const strategyBalanceAfterWithdraw = await checkUserBalance(addressBook.getContractId("hodl_strategy"), user.publicKey(), user); diff --git a/apps/contracts/strategies/hodl/src/lib.rs b/apps/contracts/strategies/hodl/src/lib.rs index a6a68bdf..e6dcb819 100644 --- a/apps/contracts/strategies/hodl/src/lib.rs +++ b/apps/contracts/strategies/hodl/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -use balance::{read_balance, receive_balance, spend_balance}; use soroban_sdk::{ contract, contractimpl, Address, Env, String, Val, Vec}; use soroban_sdk::token::Client as TokenClient; @@ -7,6 +6,9 @@ use soroban_sdk::token::Client as TokenClient; mod balance; mod storage; +use balance::{read_balance, receive_balance, spend_balance}; + + use storage::{ extend_instance_ttl, get_underlying_asset, is_initialized, set_initialized, set_underlying_asset }; @@ -98,13 +100,11 @@ impl DeFindexStrategyTrait for HodlStrategy { check_nonnegative_amount(amount)?; extend_instance_ttl(&e); + spend_balance(&e, from.clone(), amount); + let contract_address = e.current_contract_address(); - let underlying_asset = get_underlying_asset(&e); TokenClient::new(&e, &underlying_asset).transfer(&contract_address, &from, &amount); - - spend_balance(&e, from.clone(), amount); - event::emit_withdraw(&e, String::from_str(&e, STARETEGY_NAME), amount, from); Ok(amount) @@ -114,7 +114,6 @@ impl DeFindexStrategyTrait for HodlStrategy { e: Env, from: Address, ) -> Result { - from.require_auth(); check_initialized(&e)?; extend_instance_ttl(&e); diff --git a/apps/contracts/strategies/hodl/src/test.rs b/apps/contracts/strategies/hodl/src/test.rs index 3b4ee3cc..e3794edd 100644 --- a/apps/contracts/strategies/hodl/src/test.rs +++ b/apps/contracts/strategies/hodl/src/test.rs @@ -1,15 +1,13 @@ #![cfg(test)] extern crate std; -use crate::{HodlStrategy, HodlStrategyClient}; -use soroban_sdk::token::{ - StellarAssetClient as SorobanTokenAdminClient, TokenClient as SorobanTokenClient, -}; -use soroban_sdk::{IntoVal, Val}; +use crate::{HodlStrategy, HodlStrategyClient, StrategyError}; + +use soroban_sdk::token::{TokenClient, StellarAssetClient}; + use soroban_sdk::{ Env, Address, testutils::Address as _, - Vec, }; use std::vec; @@ -19,23 +17,15 @@ fn create_hodl_strategy<'a>(e: &Env) -> HodlStrategyClient<'a> { } // Create Test Token -pub(crate) fn create_token_contract<'a>(e: &Env, admin: &Address) -> SorobanTokenClient<'a> { - SorobanTokenClient::new(e, &e.register_stellar_asset_contract(admin.clone())) -} - -pub(crate) fn get_token_admin_client<'a>( - e: &Env, - address: &Address, -) -> SorobanTokenAdminClient<'a> { - SorobanTokenAdminClient::new(e, address) +pub(crate) fn create_token_contract<'a>(e: &Env, admin: &Address) -> TokenClient<'a> { + TokenClient::new(e, &e.register_stellar_asset_contract_v2(admin.clone()).address()) } - pub struct HodlStrategyTest<'a> { env: Env, strategy: HodlStrategyClient<'a>, - token0_admin_client: SorobanTokenAdminClient<'a>, - token0: SorobanTokenClient<'a>, + token: TokenClient<'a>, + user: Address, } impl<'a> HodlStrategyTest<'a> { @@ -43,20 +33,20 @@ impl<'a> HodlStrategyTest<'a> { let env = Env::default(); env.mock_all_auths(); + let strategy = create_hodl_strategy(&env); - - let token0_admin = Address::generate(&env); - let token0 = create_token_contract(&env, &token0_admin); - let token0_admin_client = get_token_admin_client(&env, &token0.address.clone()); + let admin = Address::generate(&env); + let token = create_token_contract(&env, &admin); + let user = Address::generate(&env); - let init_fn_args: Vec = (0,).into_val(&env); - strategy.initialize(&token0.address, &init_fn_args); + // Mint 1,000,000,000 to user + StellarAssetClient::new(&env, &token.address).mint(&user, &1_000_000_000); HodlStrategyTest { env, strategy, - token0_admin_client, - token0, + token, + user } } @@ -69,46 +59,5 @@ impl<'a> HodlStrategyTest<'a> { } } -#[test] -fn test_deposit_and_withdrawal_flow() { - let test = HodlStrategyTest::setup(); - let users = HodlStrategyTest::generate_random_users(&test.env, 1); - - let amount: i128 = 10_000_000; - // Minting token 0 to the user - test.token0_admin_client.mint(&users[0], &amount); - - // Reading user 0 balance - let balance = test.token0.balance(&users[0]); - assert_eq!(balance, amount); - - // Depositing token 0 to the strategy from user - test.strategy.deposit(&amount, &users[0]); - - // Reading user 0 balance - let balance = test.token0.balance(&users[0]); - assert_eq!(balance, 0); - - // Reading strategy balance - let balance = test.token0.balance(&test.strategy.address); - assert_eq!(balance, amount); - - // Reading user balance on strategy contract - let user_balance = test.strategy.balance(&users[0]); - assert_eq!(user_balance, amount); - - // Withdrawing token 0 from the strategy to user - test.strategy.withdraw(&amount, &users[0]); - - // Reading user 0 balance - let balance = test.token0.balance(&users[0]); - assert_eq!(balance, amount); - - // Reading strategy balance - let balance = test.token0.balance(&test.strategy.address); - assert_eq!(balance, 0); - - // Reading user balance on strategy contract - let user_balance = test.strategy.balance(&users[0]); - assert_eq!(user_balance, 0); -} \ No newline at end of file +mod initialize; +mod deposit; \ No newline at end of file diff --git a/apps/contracts/strategies/hodl/src/test/deposit.rs b/apps/contracts/strategies/hodl/src/test/deposit.rs new file mode 100644 index 00000000..6f5ab3d0 --- /dev/null +++ b/apps/contracts/strategies/hodl/src/test/deposit.rs @@ -0,0 +1,51 @@ +use crate::test::HodlStrategyTest; +use crate::test::StrategyError; + +#[test] +fn test_deposit_and_withdrawal_flow() { + let test = HodlStrategyTest::setup(); + let users = HodlStrategyTest::generate_random_users(&test.env, 1); + + // try deposit, harves, withdraw and balance function should return NotInitialized error before being initialize + + let result = test.strategy.try_deposit(&10_000_000, &users[0]); + assert_eq!(result, Err(Ok(StrategyError::NotInitialized))); + + + let amount: i128 = 1_000_000; + + + // Reading user 0 balance + let balance = test.token.balance(&users[0]); + assert_eq!(balance, amount); + + // Depositing token 0 to the strategy from user + test.strategy.deposit(&amount, &users[0]); + + // Reading user 0 balance + let balance = test.token.balance(&users[0]); + assert_eq!(balance, 0); + + // Reading strategy balance + let balance = test.token.balance(&test.strategy.address); + assert_eq!(balance, amount); + + // Reading user balance on strategy contract + let user_balance = test.strategy.balance(&users[0]); + assert_eq!(user_balance, amount); + + // Withdrawing token 0 from the strategy to user + test.strategy.withdraw(&amount, &users[0]); + + // Reading user 0 balance + let balance = test.token.balance(&users[0]); + assert_eq!(balance, amount); + + // Reading strategy balance + let balance = test.token.balance(&test.strategy.address); + assert_eq!(balance, 0); + + // Reading user balance on strategy contract + let user_balance = test.strategy.balance(&users[0]); + assert_eq!(user_balance, 0); +} \ No newline at end of file diff --git a/apps/contracts/strategies/hodl/src/test/initialize.rs b/apps/contracts/strategies/hodl/src/test/initialize.rs new file mode 100644 index 00000000..0bd3b746 --- /dev/null +++ b/apps/contracts/strategies/hodl/src/test/initialize.rs @@ -0,0 +1,17 @@ +// Cannot Initialize twice +extern crate std; +use soroban_sdk::{IntoVal, Vec, Val}; +use crate::test::HodlStrategyTest; +use crate::test::StrategyError; + +#[test] +fn cannot_initialize_twice() { + let test = HodlStrategyTest::setup(); + + let init_fn_args: Vec = (0,).into_val(&test.env); + + test.strategy.initialize(&test.token.address, &init_fn_args); + let result = test.strategy.try_initialize(&test.token.address , &init_fn_args); + assert_eq!(result, Err(Ok(StrategyError::AlreadyInitialized))); + +} \ No newline at end of file From 876e4d976239b6310a382ef2f38d69830296c322 Mon Sep 17 00:00:00 2001 From: esteblock Date: Mon, 4 Nov 2024 15:10:49 -0300 Subject: [PATCH 08/19] add error when spending more balance and test it --- apps/contracts/strategies/core/src/error.rs | 1 + apps/contracts/strategies/hodl/src/balance.rs | 7 ++- apps/contracts/strategies/hodl/src/lib.rs | 27 +++++--- apps/contracts/strategies/hodl/src/test.rs | 16 +++-- .../strategies/hodl/src/test/deposit.rs | 62 +++++++++++-------- 5 files changed, 69 insertions(+), 44 deletions(-) diff --git a/apps/contracts/strategies/core/src/error.rs b/apps/contracts/strategies/core/src/error.rs index bb0f8ddc..309cccba 100644 --- a/apps/contracts/strategies/core/src/error.rs +++ b/apps/contracts/strategies/core/src/error.rs @@ -11,6 +11,7 @@ pub enum StrategyError { // Validation Errors NegativeNotAllowed = 410, InvalidArgument = 411, + InsufficientBalance = 412, // Protocol Errors ProtocolAddressNotFound = 420, diff --git a/apps/contracts/strategies/hodl/src/balance.rs b/apps/contracts/strategies/hodl/src/balance.rs index 88aa258b..1f5751be 100644 --- a/apps/contracts/strategies/hodl/src/balance.rs +++ b/apps/contracts/strategies/hodl/src/balance.rs @@ -1,6 +1,7 @@ use soroban_sdk::{Address, Env}; use crate::storage::{DataKey, INSTANCE_BUMP_AMOUNT, INSTANCE_LIFETIME_THRESHOLD}; +use crate::StrategyError; pub fn read_balance(e: &Env, addr: Address) -> i128 { let key = DataKey::Balance(addr); @@ -31,10 +32,12 @@ pub fn receive_balance(e: &Env, addr: Address, amount: i128) { write_balance(e, addr, new_balance); } -pub fn spend_balance(e: &Env, addr: Address, amount: i128) { +pub fn spend_balance(e: &Env, addr: Address, amount: i128) -> Result<(), StrategyError> { + let balance = read_balance(e, addr.clone()); if balance < amount { - panic!("insufficient balance"); + return Err(StrategyError::InsufficientBalance); } write_balance(e, addr, balance - amount); + Ok(()) } diff --git a/apps/contracts/strategies/hodl/src/lib.rs b/apps/contracts/strategies/hodl/src/lib.rs index e6dcb819..52393c53 100644 --- a/apps/contracts/strategies/hodl/src/lib.rs +++ b/apps/contracts/strategies/hodl/src/lib.rs @@ -1,18 +1,31 @@ #![no_std] use soroban_sdk::{ - contract, contractimpl, Address, Env, String, Val, Vec}; -use soroban_sdk::token::Client as TokenClient; + contract, + contractimpl, + Address, + Env, + String, + token::Client as TokenClient, + Val, + Vec}; mod balance; mod storage; -use balance::{read_balance, receive_balance, spend_balance}; - +use balance::{ + read_balance, + receive_balance, + spend_balance}; use storage::{ - extend_instance_ttl, get_underlying_asset, is_initialized, set_initialized, set_underlying_asset + extend_instance_ttl, + get_underlying_asset, + is_initialized, + set_initialized, + set_underlying_asset }; -use defindex_strategy_core::{DeFindexStrategyTrait, StrategyError, event}; + +pub use defindex_strategy_core::{DeFindexStrategyTrait, StrategyError, event}; pub fn check_nonnegative_amount(amount: i128) -> Result<(), StrategyError> { if amount < 0 { @@ -100,7 +113,7 @@ impl DeFindexStrategyTrait for HodlStrategy { check_nonnegative_amount(amount)?; extend_instance_ttl(&e); - spend_balance(&e, from.clone(), amount); + spend_balance(&e, from.clone(), amount)?; let contract_address = e.current_contract_address(); let underlying_asset = get_underlying_asset(&e); diff --git a/apps/contracts/strategies/hodl/src/test.rs b/apps/contracts/strategies/hodl/src/test.rs index e3794edd..3f9fabd2 100644 --- a/apps/contracts/strategies/hodl/src/test.rs +++ b/apps/contracts/strategies/hodl/src/test.rs @@ -1,5 +1,4 @@ #![cfg(test)] -extern crate std; use crate::{HodlStrategy, HodlStrategyClient, StrategyError}; use soroban_sdk::token::{TokenClient, StellarAssetClient}; @@ -9,7 +8,6 @@ use soroban_sdk::{ Address, testutils::Address as _, }; -use std::vec; // Base Strategy Contract fn create_hodl_strategy<'a>(e: &Env) -> HodlStrategyClient<'a> { @@ -50,13 +48,13 @@ impl<'a> HodlStrategyTest<'a> { } } - pub(crate) fn generate_random_users(e: &Env, users_count: u32) -> vec::Vec
{ - let mut users = vec![]; - for _c in 0..users_count { - users.push(Address::generate(e)); - } - users - } + // pub(crate) fn generate_random_users(e: &Env, users_count: u32) -> vec::Vec
{ + // let mut users = vec![]; + // for _c in 0..users_count { + // users.push(Address::generate(e)); + // } + // users + // } } mod initialize; diff --git a/apps/contracts/strategies/hodl/src/test/deposit.rs b/apps/contracts/strategies/hodl/src/test/deposit.rs index 6f5ab3d0..b85d0b2d 100644 --- a/apps/contracts/strategies/hodl/src/test/deposit.rs +++ b/apps/contracts/strategies/hodl/src/test/deposit.rs @@ -1,51 +1,61 @@ use crate::test::HodlStrategyTest; use crate::test::StrategyError; +use soroban_sdk::{IntoVal, Vec, Val}; + #[test] fn test_deposit_and_withdrawal_flow() { let test = HodlStrategyTest::setup(); - let users = HodlStrategyTest::generate_random_users(&test.env, 1); + // let users = HodlStrategyTest::generate_random_users(&test.env, 1); - // try deposit, harves, withdraw and balance function should return NotInitialized error before being initialize + // try deposit should return NotInitialized error before being initialize - let result = test.strategy.try_deposit(&10_000_000, &users[0]); + let result = test.strategy.try_deposit(&10_000_000, &test.user); assert_eq!(result, Err(Ok(StrategyError::NotInitialized))); + // initialize + let init_fn_args: Vec = (0,).into_val(&test.env); + test.strategy.initialize(&test.token.address, &init_fn_args); - let amount: i128 = 1_000_000; - + // Initial user token balance + let balance = test.token.balance(&test.user); - // Reading user 0 balance - let balance = test.token.balance(&users[0]); - assert_eq!(balance, amount); + let amount = 123456; - // Depositing token 0 to the strategy from user - test.strategy.deposit(&amount, &users[0]); + // Deposit amount of token from the user to the strategy + test.strategy.deposit(&amount, &test.user); - // Reading user 0 balance - let balance = test.token.balance(&users[0]); - assert_eq!(balance, 0); + let balance_after_deposit = test.token.balance(&test.user); + assert_eq!(balance_after_deposit, balance - amount); // Reading strategy balance - let balance = test.token.balance(&test.strategy.address); - assert_eq!(balance, amount); + let strategy_balance_after_deposit = test.token.balance(&test.strategy.address); + assert_eq!(strategy_balance_after_deposit, amount); // Reading user balance on strategy contract - let user_balance = test.strategy.balance(&users[0]); - assert_eq!(user_balance, amount); + let user_balance_on_strategy = test.strategy.balance(&test.user); + assert_eq!(user_balance_on_strategy, amount); - // Withdrawing token 0 from the strategy to user - test.strategy.withdraw(&amount, &users[0]); - // Reading user 0 balance - let balance = test.token.balance(&users[0]); - assert_eq!(balance, amount); + let amount_to_withdraw = 100_000; + // Withdrawing token from the strategy to user + test.strategy.withdraw(&amount_to_withdraw, &test.user); - // Reading strategy balance + // Reading user balance in token + let balance = test.token.balance(&test.user); + assert_eq!(balance, balance_after_deposit + amount_to_withdraw); + + // Reading strategy balance in token let balance = test.token.balance(&test.strategy.address); - assert_eq!(balance, 0); + assert_eq!(balance, amount - amount_to_withdraw); // Reading user balance on strategy contract - let user_balance = test.strategy.balance(&users[0]); - assert_eq!(user_balance, 0); + let user_balance = test.strategy.balance(&test.user); + assert_eq!(user_balance, amount - amount_to_withdraw); + + // now we will want to withdraw more of the remaining balance + let amount_to_withdraw = 200_000; + let result = test.strategy.try_withdraw(&amount_to_withdraw, &test.user); + assert_eq!(result, Err(Ok(StrategyError::InsufficientBalance))); + } \ No newline at end of file From 1549b91247014896ac021fb4d027183b89a7f453 Mon Sep 17 00:00:00 2001 From: esteblock Date: Mon, 4 Nov 2024 15:17:50 -0300 Subject: [PATCH 09/19] clean warnings --- apps/contracts/defindex/src/interface.rs | 2 +- apps/contracts/defindex/src/investment.rs | 2 +- apps/contracts/defindex/src/test.rs | 4 ++-- apps/contracts/defindex/src/test/admin.rs | 2 +- apps/contracts/defindex/src/test/initialize.rs | 2 +- apps/contracts/defindex/src/test/withdraw.rs | 2 +- apps/contracts/defindex/src/utils.rs | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/contracts/defindex/src/interface.rs b/apps/contracts/defindex/src/interface.rs index f3873a90..9dc76739 100644 --- a/apps/contracts/defindex/src/interface.rs +++ b/apps/contracts/defindex/src/interface.rs @@ -1,7 +1,7 @@ use soroban_sdk::{Address, Env, Map, String, Vec}; use crate::{ - models::{AssetAllocation, DexDistribution, Instruction, Investment}, + models::{AssetAllocation, Instruction, Investment}, ContractError, }; diff --git a/apps/contracts/defindex/src/investment.rs b/apps/contracts/defindex/src/investment.rs index 3882dd27..adeb3930 100644 --- a/apps/contracts/defindex/src/investment.rs +++ b/apps/contracts/defindex/src/investment.rs @@ -1,6 +1,6 @@ use soroban_sdk::{Address, Env, Map, Vec}; -use crate::{models::Investment, strategies::{get_strategy_asset, get_strategy_client, invest_in_strategy}, utils::check_nonnegative_amount, ContractError}; +use crate::{models::Investment, strategies::{get_strategy_asset, invest_in_strategy}, utils::check_nonnegative_amount, ContractError}; pub fn prepare_investment(e: &Env, investments: Vec, idle_funds: Map) -> Result, ContractError> { let mut total_investment_per_asset: Map = Map::new(e); diff --git a/apps/contracts/defindex/src/test.rs b/apps/contracts/defindex/src/test.rs index d1bd4ad7..84044528 100755 --- a/apps/contracts/defindex/src/test.rs +++ b/apps/contracts/defindex/src/test.rs @@ -20,7 +20,7 @@ fn create_hodl_strategy<'a>(e: & Env, asset: & Address) -> HodlStrategyClient<'a hodl_strategy } -// DeFindex Vault Contract +// DeFindex Vault Contract pub mod defindex_vault { soroban_sdk::contractimport!(file = "../target/wasm32-unknown-unknown/release/defindex_vault.optimized.wasm"); pub type DeFindexVaultClient<'a> = Client<'a>; @@ -52,7 +52,7 @@ fn create_defindex_vault<'a>( // Create Test Token pub(crate) fn create_token_contract<'a>(e: &Env, admin: &Address) -> SorobanTokenClient<'a> { - SorobanTokenClient::new(e, &e.register_stellar_asset_contract(admin.clone())) + SorobanTokenClient::new(e, &e.register_stellar_asset_contract_v2(admin.clone()).address()) } pub(crate) fn get_token_admin_client<'a>( diff --git a/apps/contracts/defindex/src/test/admin.rs b/apps/contracts/defindex/src/test/admin.rs index ce047843..384694be 100644 --- a/apps/contracts/defindex/src/test/admin.rs +++ b/apps/contracts/defindex/src/test/admin.rs @@ -1,5 +1,5 @@ use soroban_sdk::{ - testutils::{Address as _, AuthorizedFunction, AuthorizedInvocation, MockAuth, MockAuthInvoke}, vec as sorobanvec, Address, IntoVal, String, Symbol, Vec + testutils::{AuthorizedFunction, AuthorizedInvocation, MockAuth, MockAuthInvoke}, vec as sorobanvec, IntoVal, String, Symbol, Vec }; use crate::test::{create_strategy_params, defindex_vault::AssetAllocation, DeFindexVaultTest}; diff --git a/apps/contracts/defindex/src/test/initialize.rs b/apps/contracts/defindex/src/test/initialize.rs index 9af00d37..68075239 100644 --- a/apps/contracts/defindex/src/test/initialize.rs +++ b/apps/contracts/defindex/src/test/initialize.rs @@ -1,4 +1,4 @@ -use soroban_sdk::{vec as sorobanvec, Address, String, Vec}; +use soroban_sdk::{vec as sorobanvec, String, Vec}; use crate::test::{create_strategy_params, defindex_vault::{AssetAllocation, ContractError}, DeFindexVaultTest}; diff --git a/apps/contracts/defindex/src/test/withdraw.rs b/apps/contracts/defindex/src/test/withdraw.rs index fc2209ca..38890e61 100644 --- a/apps/contracts/defindex/src/test/withdraw.rs +++ b/apps/contracts/defindex/src/test/withdraw.rs @@ -1,4 +1,4 @@ -use soroban_sdk::{vec as sorobanvec, Address, String, Vec}; +use soroban_sdk::{vec as sorobanvec, String, Vec}; use crate::test::{create_strategy_params, defindex_vault::{AssetAllocation, Investment}, DeFindexVaultTest}; diff --git a/apps/contracts/defindex/src/utils.rs b/apps/contracts/defindex/src/utils.rs index 3fab13cf..82bb5900 100644 --- a/apps/contracts/defindex/src/utils.rs +++ b/apps/contracts/defindex/src/utils.rs @@ -1,4 +1,4 @@ -use soroban_sdk::{contracttype, panic_with_error, Address, Env, Map, Vec}; +use soroban_sdk::{panic_with_error, Address, Env, Map, Vec}; use crate::{ access::{AccessControl, AccessControlTrait, RolesDataKey}, funds::{fetch_invested_funds_for_asset, fetch_invested_funds_for_strategy, fetch_total_managed_funds}, models::AssetAllocation, token::VaultToken, ContractError From 05f01eedf471154426f30053b12fb36e0609b9fc Mon Sep 17 00:00:00 2001 From: esteblock Date: Mon, 4 Nov 2024 15:36:29 -0300 Subject: [PATCH 10/19] improved withdraw test --- apps/contracts/defindex/src/test.rs | 4 +- apps/contracts/defindex/src/test/withdraw.rs | 72 ++++++++++++++++++-- 2 files changed, 67 insertions(+), 9 deletions(-) diff --git a/apps/contracts/defindex/src/test.rs b/apps/contracts/defindex/src/test.rs index 84044528..b23401cd 100755 --- a/apps/contracts/defindex/src/test.rs +++ b/apps/contracts/defindex/src/test.rs @@ -7,11 +7,11 @@ use soroban_sdk::{testutils::Address as _, vec as sorobanvec, Address, Env, Stri use std::vec; // DeFindex Hodl Strategy Contract -mod hodl_strategy { +pub mod hodl_strategy { soroban_sdk::contractimport!(file = "../target/wasm32-unknown-unknown/release/hodl_strategy.optimized.wasm"); pub type HodlStrategyClient<'a> = Client<'a>; } -use hodl_strategy::HodlStrategyClient; +use hodl_strategy::{HodlStrategyClient}; fn create_hodl_strategy<'a>(e: & Env, asset: & Address) -> HodlStrategyClient<'a> { let contract_address = &e.register_contract_wasm(None, hodl_strategy::WASM); diff --git a/apps/contracts/defindex/src/test/withdraw.rs b/apps/contracts/defindex/src/test/withdraw.rs index 38890e61..2079a843 100644 --- a/apps/contracts/defindex/src/test/withdraw.rs +++ b/apps/contracts/defindex/src/test/withdraw.rs @@ -1,6 +1,7 @@ use soroban_sdk::{vec as sorobanvec, String, Vec}; use crate::test::{create_strategy_params, defindex_vault::{AssetAllocation, Investment}, DeFindexVaultTest}; +use super::hodl_strategy::StrategyError; #[test] fn test_withdraw_from_idle_success() { @@ -26,7 +27,7 @@ fn test_withdraw_from_idle_success() { &String::from_str(&test.env, "dfToken"), &String::from_str(&test.env, "DFT"), ); - let amount = 1000i128; + let amount = 1234567890i128; let users = DeFindexVaultTest::generate_random_users(&test.env, 1); @@ -38,18 +39,75 @@ fn test_withdraw_from_idle_success() { 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]); + // Deposit + let amount_to_deposit = 567890i128; + test.defindex_contract.deposit(&sorobanvec![&test.env, amount_to_deposit], &sorobanvec![&test.env, amount_to_deposit], &users[0]); + + // Check Balances after deposit + + // Token balance of user + let user_balance = test.token0.balance(&users[0]); + assert_eq!(user_balance, amount - amount_to_deposit); + + // Token balance of vault should be amount_to_deposit + // Because balances are still in indle, balances are not in strategy, but in idle + + let vault_balance = test.token0.balance(&test.defindex_contract.address); + assert_eq!(vault_balance, amount_to_deposit); + + // Token balance of hodl strategy should be 0 (all in idle) + let strategy_balance = test.token0.balance(&test.strategy_client.address); + assert_eq!(strategy_balance, 0); + // Df balance of user should be equal to deposited amount let df_balance = test.defindex_contract.balance(&users[0]); - assert_eq!(df_balance, amount); + assert_eq!(df_balance, amount_to_deposit); - test.defindex_contract.withdraw(&df_balance, &users[0]); + // user decides to withdraw a portion of deposited amount + let amount_to_withdraw = 123456i128; + test.defindex_contract.withdraw(&amount_to_withdraw, &users[0]); - let df_balance = test.defindex_contract.balance(&users[0]); - assert_eq!(df_balance, 0i128); + // Check Balances after withdraw + // Token balance of user should be amount - amount_to_deposit + amount_to_withdraw let user_balance = test.token0.balance(&users[0]); - assert_eq!(user_balance, amount); + assert_eq!(user_balance, amount - amount_to_deposit + amount_to_withdraw); + + // Token balance of vault should be amount_to_deposit - amount_to_withdraw + let vault_balance = test.token0.balance(&test.defindex_contract.address); + assert_eq!(vault_balance, amount_to_deposit - amount_to_withdraw); + + // Token balance of hodl strategy should be 0 (all in idle) + let strategy_balance = test.token0.balance(&test.strategy_client.address); + assert_eq!(strategy_balance, 0); + + // Df balance of user should be equal to deposited amount - amount_to_withdraw + let df_balance = test.defindex_contract.balance(&users[0]); + assert_eq!(df_balance, amount_to_deposit - amount_to_withdraw); + + + // user tries to withdraw more than deposited amount + let amount_to_withdraw_more = amount_to_deposit + 1; + let result = test.defindex_contract.try_withdraw(&amount_to_withdraw_more, &users[0]); + // just check if is error + assert_eq!(result.is_err(), true); + + // TODO test corresponding error + + // withdraw remaining balance + test.defindex_contract.withdraw(&(amount_to_deposit - amount_to_withdraw), &users[0]); + + // // result is err + + // assert_eq!(result, Err(Ok(StrategyError::InsufficientBalance))); + + // result should be error from contract + + // let df_balance = test.defindex_contract.balance(&users[0]); + // assert_eq!(df_balance, 0i128); + + // let user_balance = test.token0.balance(&users[0]); + // assert_eq!(user_balance, amount); } #[test] From 687a1e23232541537a898dd636d9f5d3d602c08f Mon Sep 17 00:00:00 2001 From: esteblock Date: Mon, 4 Nov 2024 16:12:23 -0300 Subject: [PATCH 11/19] test deposit withdraw directly to strategy and fix withdraw from vault --- apps/contracts/src/deploy_hodl.ts | 2 +- apps/contracts/src/test.ts | 69 ++++++++++++-- apps/contracts/src/tests/strategy.ts | 104 +++++++++++++++++++++- apps/contracts/src/tests/testOnlyVault.ts | 2 +- apps/contracts/src/tests/vault.ts | 15 ++-- 5 files changed, 179 insertions(+), 13 deletions(-) diff --git a/apps/contracts/src/deploy_hodl.ts b/apps/contracts/src/deploy_hodl.ts index a3bda80e..ff71ad4e 100644 --- a/apps/contracts/src/deploy_hodl.ts +++ b/apps/contracts/src/deploy_hodl.ts @@ -47,7 +47,7 @@ export async function deployContracts(addressBook: AddressBook) { const emptyVecScVal = xdr.ScVal.scvVec([]); - console.log("Initializing Soroswap Adapter"); + console.log("Initializing DeFindex HODL Strategy"); await invokeContract( "hodl_strategy", addressBook, diff --git a/apps/contracts/src/test.ts b/apps/contracts/src/test.ts index 55c70a39..d8e91ae7 100644 --- a/apps/contracts/src/test.ts +++ b/apps/contracts/src/test.ts @@ -4,14 +4,15 @@ import { nativeToScVal, Networks, scValToNative, - xdr + xdr, + Keypair } from "@stellar/stellar-sdk"; import { randomBytes } from "crypto"; import { depositToVault, withdrawFromVault } from "./tests/vault.js"; import { AddressBook } from "./utils/address_book.js"; import { airdropAccount, invokeContract } from "./utils/contract.js"; import { config } from "./utils/env_config.js"; -import { checkUserBalance } from "./tests/strategy.js"; +import { checkUserBalance, depositToStrategy, withdrawFromStrategy} from "./tests/strategy.js"; export async function test_factory(addressBook: AddressBook) { @@ -110,6 +111,41 @@ const passphrase = network === "mainnet" ? Networks.PUBLIC : network === "testne const loadedConfig = config(network); +// Deposit directly on Strategy + +const myUser = Keypair.random(); +if (network !== "mainnet") await airdropAccount(myUser); + console.log("New user publicKey:", myUser.publicKey()); +const{ + user: strategyUser, + balanceBefore: balanceBeforeStrategyDeposit, + result: strategyDepositResult, + balanceAfter: balanceAfterStrategyDeposit} + = await depositToStrategy(addressBook.getContractId("hodl_strategy"), myUser, 1234567890); + + +console.log(" -- ") +console.log(" -- ") +console.log("Step 0: Deposited directly in Strategy, with balance before:", balanceBeforeStrategyDeposit, "and balance after:", balanceAfterStrategyDeposit); + +console.log(" -- ") +console.log(" -- ") + +// Withdraw directly from strategy +const { + user: strategyWithdrawUser, + balanceBefore: balanceBeforeStrategyWithdraw, + result: strategyWithdrawResult, + balanceAfter: balanceAfterStrategyWithdraw +} = await withdrawFromStrategy(addressBook.getContractId("hodl_strategy"), myUser, 567890); + +console.log(" -- ") +console.log(" -- ") +console.log("Step 1: Withdrawn directly from Strategy, with balance before:", balanceBeforeStrategyWithdraw, "and balance after:", balanceAfterStrategyWithdraw); +console.log(" -- ") +console.log(" -- ") + + // Step 0: Deploy the vault const deployedVault = await test_factory(addressBook); console.log(" -- ") @@ -120,7 +156,8 @@ console.log(" -- ") // Step 1: Deposit to vault and capture initial balances -const { user, balanceBefore: depositBalanceBefore, result: depositResult, balanceAfter: depositBalanceAfter } = await depositToVault(deployedVault); +const { user, balanceBefore: depositBalanceBefore, result: depositResult, balanceAfter: depositBalanceAfter } + = await depositToVault(deployedVault, 987654321); console.log(" -- ") console.log(" -- ") console.log("Step 1: Deposited to Vault using user:", user.publicKey(), "with balance before:", depositBalanceBefore, "and balance after:", depositBalanceAfter); @@ -135,8 +172,30 @@ console.log("Step 2: Strategy balance after deposit:", strategyBalanceAfterDepos console.log(" -- ") console.log(" -- ") +// check vault balance of XLM after deposit +let xlmContractId: string; + switch (network) { + case "testnet": + xlmContractId = xlm.contractId(Networks.TESTNET); + break; + case "mainnet": + xlmContractId = xlm.contractId(Networks.PUBLIC); + break; + default: + console.log("Invalid network:", network, "It should be either testnet or mainnet"); + throw new Error("Invalid network"); + } +const xlmAddress = new Address(xlmContractId); +const vaultBalanceAfterDeposit = await checkUserBalance(xlmContractId, deployedVault, user); +console.log(" -- ") +console.log(" -- ") +console.log("Step 2: Vault balance after deposit:", vaultBalanceAfterDeposit); +console.log(" -- ") +console.log(" -- ") + // Step 3: Withdraw from the vault -const { balanceBefore: withdrawBalanceBefore, result: withdrawResult, balanceAfter: withdrawBalanceAfter } = await withdrawFromVault(deployedVault, BigInt(0), user); +const { balanceBefore: withdrawBalanceBefore, result: withdrawResult, balanceAfter: withdrawBalanceAfter } + = await withdrawFromVault(deployedVault, 4321, user); // Step 4: Check strategy balance after withdrawal const strategyBalanceAfterWithdraw = await checkUserBalance(addressBook.getContractId("hodl_strategy"), user.publicKey(), user); @@ -159,7 +218,7 @@ console.table([ } ]); -await depositToVault(deployedVault); +await depositToVault(deployedVault, 98765421); // await getDfTokenBalance("CCL54UEU2IGTCMIJOYXELIMVA46CLT3N5OG35XN45APXDZYHYLABF53N", "GDAMXOJUSW6O67UVI6U4LBHI5IIJFUKQVDHPKNFKOIYRLYB2LA6YDAFI", loadedConfig.admin) // await depositToVault("CCIOE3BLPYOYDFB5KALLDXED2CZT3GJDZSHY453U4TTOIRZLAKMKZPLR"); diff --git a/apps/contracts/src/tests/strategy.ts b/apps/contracts/src/tests/strategy.ts index ae916970..18c86afa 100644 --- a/apps/contracts/src/tests/strategy.ts +++ b/apps/contracts/src/tests/strategy.ts @@ -2,7 +2,8 @@ import { Address, scValToNative, xdr, - Keypair + Keypair, + nativeToScVal, } from "@stellar/stellar-sdk"; import { invokeCustomContract } from "../utils/contract.js"; @@ -41,3 +42,104 @@ export async function checkUserBalance(contractAddress: string, userPublicKey: s throw error; } } + + + +export async function depositToStrategy(deployedStrategy: string, user: Keypair, amount: number) { + + let balanceBefore: number; + let balanceAfter: number; + let result: any; + + // Define deposit parameters + const depositAmount = BigInt(amount); + const amountsDesired = [depositAmount]; + const amountsMin = [BigInt(0)]; // Minimum amount for transaction to succeed + + const depositParams: xdr.ScVal[] = [ + nativeToScVal(depositAmount, { type: "i128" }), + (new Address(user.publicKey())).toScVal() + ]; + + try { + // Check the user's strategy + balanceBefore = await checkUserBalance(deployedStrategy, user.publicKey(), user); + console.log("πŸ”’ Β« strategy balance before deposit:", balanceBefore); + } catch (error) { + console.error("❌ Balance check before deposit failed:", error); + throw error; + } + + try { + result = await invokeCustomContract( + deployedStrategy, + "deposit", + depositParams, + user + ); + console.log("πŸš€ Β« Deposit successful:", scValToNative(result.returnValue)); + } catch (error) { + console.error("❌ Deposit failed:", error); + throw error; + } + + try { + // Check the user's balance after the deposit + balanceAfter = await checkUserBalance(deployedStrategy, user.publicKey(), user); + console.log("πŸ”’ Β« dfToken balance after deposit:", balanceAfter); + } catch (error) { + console.error("❌ Balance check after deposit failed:", error); + throw error; + } + + return { user, balanceBefore, result, balanceAfter }; +} + + +export async function withdrawFromStrategy(deployedStrategy: string, user: Keypair, amount: number) { + let balanceBefore: number; + let balanceAfter: number; + let result: any; + + // Define withdraw parameters + const withdrawAmount = BigInt(amount); + const amountsToWithdraw = [withdrawAmount]; + + const withdrawParams: xdr.ScVal[] = [ + nativeToScVal(amount, { type: "i128" }), + (new Address(user.publicKey())).toScVal() + ]; + + try { + // Check the user's balance before the withdrawal + balanceBefore = await checkUserBalance(deployedStrategy, user.publicKey(), user); + console.log("πŸ”’ Β« strategy balance before withdraw:", balanceBefore); + } catch (error) { + console.error("❌ Balance check before withdraw failed:", error); + throw error; + } + + try { + result = await invokeCustomContract( + deployedStrategy, + "withdraw", + withdrawParams, + user + ); + console.log("πŸš€ Β« Withdrawal successful:", scValToNative(result.returnValue)); + } catch (error) { + console.error("❌ Withdrawal failed:", error); + throw error; + } + + try { + // Check the user's balance after the withdrawal + balanceAfter = await checkUserBalance(deployedStrategy, user.publicKey(), user); + console.log("πŸ”’ Β« strategy balance after withdraw:", balanceAfter); + } catch (error) { + console.error("❌ Balance check after withdraw failed:", error); + throw error; + } + + return { user, balanceBefore, result, balanceAfter }; +} \ No newline at end of file diff --git a/apps/contracts/src/tests/testOnlyVault.ts b/apps/contracts/src/tests/testOnlyVault.ts index 97649f0c..430c3c62 100644 --- a/apps/contracts/src/tests/testOnlyVault.ts +++ b/apps/contracts/src/tests/testOnlyVault.ts @@ -1,4 +1,4 @@ import { depositToVault } from "./vault.js"; // modify the address to the deployed vault -await depositToVault("CBUTM5B7CG7ISTX5PD5XMDDMSTKFRGVXNKRMZO3TZW5IFGHN5274JJY7"); +await depositToVault("CBUTM5B7CG7ISTX5PD5XMDDMSTKFRGVXNKRMZO3TZW5IFGHN5274JJY7", 986754321); diff --git a/apps/contracts/src/tests/vault.ts b/apps/contracts/src/tests/vault.ts index c94681e0..83104d0d 100644 --- a/apps/contracts/src/tests/vault.ts +++ b/apps/contracts/src/tests/vault.ts @@ -23,7 +23,7 @@ import { config } from "../utils/env_config.js"; const network = process.argv[2]; -export async function depositToVault(deployedVault: string, user?: Keypair) { +export async function depositToVault(deployedVault: string, amount: number, user?: Keypair, ) { // Create and fund a new user account if not provided const newUser = user ? user : Keypair.random(); console.log('πŸš€ ~ depositToVault ~ newUser.publicKey():', newUser.publicKey()); @@ -37,7 +37,7 @@ export async function depositToVault(deployedVault: string, user?: Keypair) { let result: any; // Define deposit parameters - const depositAmount = BigInt(10000000); // 1 XLM in stroops (1 XLM = 10^7 stroops) + const depositAmount = BigInt(amount); // 1 XLM in stroops (1 XLM = 10^7 stroops) const amountsDesired = [depositAmount]; const amountsMin = [BigInt(0)]; // Minimum amount for transaction to succeed @@ -126,7 +126,7 @@ export async function getDfTokenBalance(deployedVault: string, userPublicKey: st * const { balanceBefore, result, balanceAfter } = await withdrawFromVault("CCE7MLKC7R6TIQA37A7EHWEUC3AIXIH5DSOQUSVAARCWDD7257HS4RUG", 10000000, user); */ -export async function withdrawFromVault(deployedVault: string, withdrawAmount: BigInt | number, user: Keypair) { +export async function withdrawFromVault(deployedVault: string, withdrawAmount: number, user: Keypair) { console.log('πŸš€ ~ withdrawFromVault ~ User publicKey:', user.publicKey()); let balanceBefore: number; @@ -143,9 +143,14 @@ export async function withdrawFromVault(deployedVault: string, withdrawAmount: B } // Define withdraw parameters - const amountsToWithdraw = [withdrawAmount]; + // const amountsToWithdraw = [BigInt(withdrawAmount)]; + // const withdrawParams: xdr.ScVal[] = [ + // xdr.ScVal.scvVec(amountsToWithdraw.map((amount) => nativeToScVal(amount, { type: "i128" }))), + // (new Address(user.publicKey())).toScVal() + // ]; + const withdrawParams: xdr.ScVal[] = [ - xdr.ScVal.scvVec(amountsToWithdraw.map((amount) => nativeToScVal(amount, { type: "i128" }))), + nativeToScVal(BigInt(withdrawAmount), { type: "i128" }), (new Address(user.publicKey())).toScVal() ]; From 302f323d2a4cd27b1f08ad50fb4eede43cf3bf1b Mon Sep 17 00:00:00 2001 From: chopan123 Date: Tue, 5 Nov 2024 09:32:26 -0300 Subject: [PATCH 12/19] scf tracker done --- scf-tracker.md | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 scf-tracker.md diff --git a/scf-tracker.md b/scf-tracker.md new file mode 100644 index 00000000..f27416b6 --- /dev/null +++ b/scf-tracker.md @@ -0,0 +1,89 @@ +## [D1] Factory Smart Contract + +- **Brief Description:** + Improve the current smart contract developed at the Consensus EasyA Hackathon by enhancing security and solving duplicated data issues. We plan to apply for audits by banks and incorporate their feedback. + +- **How to Measure Completion:** + Code is reviewed, tested, and available on GitHub. Successfully passes security audits. +- **Result:** + - βœ… Code available on [GitHub](https://github.com/paltalabs/defindex/tree/main/apps/contracts/factory) + - πŸ› οΈ Security Audits +--- + +## [D2] DeFindex Smart Contract + +- **Brief Description:** + This contract allocates different DeFi protocols and collects the fees to be paid. We aim to enhance the Consensus EasyA Hackathon contracts by improving security, adding fees, liquidity pool tokens, rebalancing, and optional admin functions. Optimization for Soroban CPU instruction limits and bank audits for feedback will be implemented. + +- **How to Measure Completion:** + Contract will be available on [GitHub](https://github.com/paltalabs). Code is reviewed, tested, and successfully passes security audits. + +- **Result:** + - βœ… Code available on [GitHub](https://github.com/paltalabs/defindex/tree/main/apps/contracts/defindex) + - πŸ› οΈ Security Audits + +--- + +## [D3] Adapter Smart Contracts + +- **Brief Description:** + Enable any DeFi protocol to connect to the DeFindex Contract. Engage with the Stellar community to promote Smart Contract standards for interoperability. Improve security, optimize CPU instructions, and review interfaces. Publish the adapter struct at crates.io for community use. Create specific adapters for Blend, Phoenix, Xycloans, and Soroswap. + +- **How to Measure Completion:** + Contract and Adapter Struct will be available on [GitHub](https://github.com/paltalabs). Adapter Struct will be published at crates.io. Documentation will be available on the website, and a standard proposal will be available as a SEP. + +- **Result:** + - βœ… Code available on [GitHub](https://github.com/paltalabs/defindex/tree/main/apps/contracts/strategies) + - πŸ› οΈ Adapter Struct published at crates.io + - πŸ› οΈ SEP proposal + +--- + +## [D4] Index Creator Dashboard (Frontend) + +- **Brief Description:** + Create a user-friendly dashboard where index creators (wallet providers or others) can create different DeFindexes with specific allocations to protocols and strategies. Adapter creators (DeFi protocols) can upgrade their adapters if they pass audit checks. A simple governance system will allow for adapter approvals (similar to the soroswap token-list). + +- **How to Measure Completion:** + Dashboard will be available as a Dapp. DeFi protocols can propose their adapters, and index creators can easily allocate to selected DeFi protocols and strategies. + +- **Result:** + - βœ… Code available on [GitHub](https://github.com/paltalabs/defindex/tree/main/apps/dapp) +--- + +## [D5] Tutorials for DeFi Protocols and Index Creators + +- **Brief Description:** + Create tutorials on how to create a DeFindex using the dashboard, how to implement it on a mobile or web app, and how to create an adapter. + +- **How to Measure Completion:** + Tutorials will be available in the docs section of the website, published on Medium and dev.to, and shared on the Stellar Discord Server. + +- **Result:** + - πŸ› οΈ Tutorials available on the website and published on Medium and dev.to + - πŸ› οΈ Shared on the Stellar Discord Server +--- + +## [D6] Flutter SDK + +- **Brief Description:** + Improve the existing Flutter SDK from the Consensus EasyA Hackathon to allow any Flutter-based wallet to integrate DeFindex. Collaborate with Meru and Beans App for feedback, and integrate with Meru. + +- **How to Measure Completion:** + Code will be available on [GitHub](https://github.com/paltalabs). A Flutter app can call a DeFindex Smart Contract instance with less than 10 lines of code. + +- **Result:** + - πŸ› οΈ Code available on [GitHub](https://github.com/paltalabs/defindex/tree/main/packages/defindex-dart-sdk) + +--- + +## [D7] TypeScript SDK + +- **Brief Description:** + Develop a TypeScript SDK for web dapps to integrate DeFindexes. + +- **How to Measure Completion:** + Code will be available on [GitHub](https://github.com/paltalabs). A React app can call a DeFindex Smart Contract instance with less than 10 lines of code. + +- **Result:** + - πŸ› οΈ Code available on [GitHub](https://github.com/paltalabs/defindex/tree/main/packages/defindex-sdk) From f9d6a09336a20e69a7fd0ad870df8bb67ce9fc6e Mon Sep 17 00:00:00 2001 From: esteblock Date: Tue, 5 Nov 2024 09:40:08 -0300 Subject: [PATCH 13/19] complete with pending tests --- apps/contracts/strategies/hodl/src/lib.rs | 5 ++++- apps/contracts/strategies/hodl/src/test.rs | 4 +++- .../strategies/hodl/src/test/deposit.rs | 20 ++++++++++++++++++- .../strategies/hodl/src/test/events.rs | 6 ++++++ .../strategies/hodl/src/test/initialize.rs | 6 +++++- .../strategies/hodl/src/test/withdraw.rs | 5 +++++ 6 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 apps/contracts/strategies/hodl/src/test/events.rs create mode 100644 apps/contracts/strategies/hodl/src/test/withdraw.rs diff --git a/apps/contracts/strategies/hodl/src/lib.rs b/apps/contracts/strategies/hodl/src/lib.rs index 52393c53..648947a6 100644 --- a/apps/contracts/strategies/hodl/src/lib.rs +++ b/apps/contracts/strategies/hodl/src/lib.rs @@ -25,7 +25,10 @@ use storage::{ set_underlying_asset }; -pub use defindex_strategy_core::{DeFindexStrategyTrait, StrategyError, event}; +pub use defindex_strategy_core::{ + DeFindexStrategyTrait, + StrategyError, + event}; pub fn check_nonnegative_amount(amount: i128) -> Result<(), StrategyError> { if amount < 0 { diff --git a/apps/contracts/strategies/hodl/src/test.rs b/apps/contracts/strategies/hodl/src/test.rs index 3f9fabd2..f8944046 100644 --- a/apps/contracts/strategies/hodl/src/test.rs +++ b/apps/contracts/strategies/hodl/src/test.rs @@ -58,4 +58,6 @@ impl<'a> HodlStrategyTest<'a> { } mod initialize; -mod deposit; \ No newline at end of file +mod deposit; +mod events; +mod withdraw; \ No newline at end of file diff --git a/apps/contracts/strategies/hodl/src/test/deposit.rs b/apps/contracts/strategies/hodl/src/test/deposit.rs index b85d0b2d..3267f974 100644 --- a/apps/contracts/strategies/hodl/src/test/deposit.rs +++ b/apps/contracts/strategies/hodl/src/test/deposit.rs @@ -2,9 +2,27 @@ use crate::test::HodlStrategyTest; use crate::test::StrategyError; use soroban_sdk::{IntoVal, Vec, Val}; +// test deposit with negative amount +#[test] +fn deposit_with_negative_amount() { + let test = HodlStrategyTest::setup(); + let init_fn_args: Vec = (0,).into_val(&test.env); + test.strategy.initialize(&test.token.address, &init_fn_args); + + let amount = -123456; + + let result = test.strategy.try_deposit(&amount, &test.user); + assert_eq!(result, Err(Ok(StrategyError::NegativeNotAllowed))); +} + +// check auth +#[test] +fn deposit_mock_auths() { + todo!() +} #[test] -fn test_deposit_and_withdrawal_flow() { +fn deposit_and_withdrawal_flow() { let test = HodlStrategyTest::setup(); // let users = HodlStrategyTest::generate_random_users(&test.env, 1); diff --git a/apps/contracts/strategies/hodl/src/test/events.rs b/apps/contracts/strategies/hodl/src/test/events.rs new file mode 100644 index 00000000..239a9bd1 --- /dev/null +++ b/apps/contracts/strategies/hodl/src/test/events.rs @@ -0,0 +1,6 @@ +// TODO: Write tests for events + +#[test] +fn test_events() { + todo!() +} \ No newline at end of file diff --git a/apps/contracts/strategies/hodl/src/test/initialize.rs b/apps/contracts/strategies/hodl/src/test/initialize.rs index 0bd3b746..41037473 100644 --- a/apps/contracts/strategies/hodl/src/test/initialize.rs +++ b/apps/contracts/strategies/hodl/src/test/initialize.rs @@ -13,5 +13,9 @@ fn cannot_initialize_twice() { test.strategy.initialize(&test.token.address, &init_fn_args); let result = test.strategy.try_initialize(&test.token.address , &init_fn_args); assert_eq!(result, Err(Ok(StrategyError::AlreadyInitialized))); - + + // get asset should return underlying asset + + let underlying_asset = test.strategy.asset(); + assert_eq!(underlying_asset, test.token.address); } \ No newline at end of file diff --git a/apps/contracts/strategies/hodl/src/test/withdraw.rs b/apps/contracts/strategies/hodl/src/test/withdraw.rs new file mode 100644 index 00000000..dd8aa9d5 --- /dev/null +++ b/apps/contracts/strategies/hodl/src/test/withdraw.rs @@ -0,0 +1,5 @@ + +#[test] +fn withdraw() { + todo!() +} \ No newline at end of file From 4ef4360668a5c690220bbaf33fd33667c8aeca73 Mon Sep 17 00:00:00 2001 From: esteblock Date: Tue, 5 Nov 2024 11:34:06 -0300 Subject: [PATCH 14/19] minted shares formula --- .../02-contracts/01-vault-contract.md | 94 ++++++++++++++++++- 1 file changed, 89 insertions(+), 5 deletions(-) diff --git a/apps/docs/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract.md b/apps/docs/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract.md index 47992ad7..f08ec9ba 100644 --- a/apps/docs/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract.md +++ b/apps/docs/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract.md @@ -105,14 +105,98 @@ The Emergency Manager has the authority to withdraw assets from the DeFindex in ### Management Every DeFindex has a manager, who is responsible for managing the DeFindex. The Manager can ebalance the Vault, and invest IDLE funds in strategies. -### Fee Collection -The revenues generated by the strategies are collected as shares of the DeFindex. These shares, or dfTokens, are minted whenever a deposit or withdrawal takes place. +### Fees: Receivers, Structure and Collection -The deployer can set a management fee, which can later be adjusted by the Fee Receiver. Additionally, **palta**labs charges a 0.5% APR on the vault shares. +**Fee Receivers:** Every DeFindex user will pay a Management Fee for the total amount of funds managed by the DeFindex Vault. These fees will finally be received by two different fee receivers: -The recommended initial setup suggests a fee of **0.5%-2% APR on these shares**. For example, if a DeFindex has 100 shares and the fee is set at 0.5% APR, the fees collected annually would be 1 share, split as 0.5 for the Fee Receiver and 0.5 for **palta**labs. The **palta**labs fee is defined in the Factory contract. +1. **DeFindex Protocol Fee Receiver**. For creating the protocol. +2. **Vault Fee Receiver**. For creating the vault. -These allocations are recalculated and minted whenever a user deposits, withdraws from the DeFindex, or when rebalancing occurs. +**Fee Structure:** +The Total Management fee is the sum of **DeFindex Fee** and the **Vault Fee**. This sum can be from 0.5% to 2.5%, and it is defined as follows: + +1. **The DeFindex Fee** is fixed for all DeFindex Vaults, and this fee is fixed to 0.5% APR. This fee is fixed in the DeFindex Factory Contract. +2. **The Vault Fee** is set by the Vault Deployer. The recommended initial setup suggests a fee of **0.5% - 2% APR**. + +**Fee Collection:** +In order for a vault to collect fees, the DeFindex vault will mint and collect new shares (or dfTokens) whenever a deposit or withdrawal takes place. + +**Mathematical Explanation** +Lets consider $V_0$, total value locked at $t_0$, total shares $s_0$ at $t_0$ and total management fee of $f$. +This fee $f$ its considered as APR. + +After one period (one year), new shares $s_f$ should be minted in order to pay this fee. + +$s_1 = s_0 + s_f$ + +$V_1 =V_0$ as we consider that no new funds where added + +$s_f$ should comply with: + +The value of the mited share $\frac{V_0}{s_1} * s_f$ should be equal of the fraction of the total value locked $V_0 * f$ + +$\frac{V_0}{s_1} * s_f = V_0 * f$ + +so this gets simplified + +$s_f = f \cdot s_1 = f \cdot (s_0 + s_f)$ + +$s_f = \frac{f \cdot s_0}{(1-f)}$ + + + + + +**Example:** +For example, if a Vault has 100 shares and the Vault Fee is set at 0.5% APR, the annually collected fees would be 1 share, splited as 0.5 for the Fee Receiver and 0.5 for the DeFindex Protocol. + +total funds:100 (does not change) +previous shares: 1 +Yβ‰ˆ1.0101 + +100/(100+Y) *Y = 1 USDC +total funds/(previous total shars+fee shares) *fee shares = fee rate of total funds + + + + +t0 amount0 +t1 amount0 cobra fee por el tiempo (t1-t0) y se suma amount1 +t2 (amount0 y amount 1) cobra fee por el tiempo (t2-t1) + +100 shares, 100 USDC, price per share = 100/100 = 1 + +despues de un aΓ±o, deposito de 100. +sin fee: se mintean 100 shares +new shares: 200, new funds: 200 USDC + +con fee: se mintean X shares al depositor, Y shares al fee receiver +total shares: (100 + X + Y) +total funds: (200 USDC) +value of fee shares (Y) +200/(100+X+Y) *Y = 1 USDC +200/(100+X+Y) *X = 100 USDC +X=101.0101 +Y=1.0101 + +si no se deposita nada +100/(100+Y) *Y = 1 USDC +100/(100+X+Y) *X = 100 USDC + + +300/(100+X+Y) *Y = 1 USDC +300/(100+X+Y) *X = 200 USDC + + + + +mint x shares, total new shares = (100+x) +my value of this is x/(100+x) = 1% + + +101 shares, 1 share is equal to 1/101 + +These allocations are recalculated and minted whenever a user deposits, withdraws from the DeFindex. For instance, let's say a DeFindex starts with an initial value of 1 USDC per share and 100 shares (dfTokens). These 100 USDC are invested in a lending protocol offering an 8% APY. The DeFindex also has a 0.5% APR fee. After one year, the investment grows to 108 USDC. Additionally, 1 dfToken is minted as a fee. This means the DeFindex now holds 101 dfTokens backed by 108 USDC, making the price per share approximately 1.07 USDC. As a result, a user holding 100 dfTokens will have a value equivalent to around 107 USDC, while the collected fee will be backed by about 1.07 USDC. From 60d3f0b2e2336ef8115a4eb760ae009795dd6f3f Mon Sep 17 00:00:00 2001 From: esteblock Date: Tue, 5 Nov 2024 15:20:22 -0300 Subject: [PATCH 15/19] better doc --- .../02-contracts/01-vault-contract.md | 161 +++++++++++------- 1 file changed, 99 insertions(+), 62 deletions(-) diff --git a/apps/docs/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract.md b/apps/docs/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract.md index f08ec9ba..2b17ad36 100644 --- a/apps/docs/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract.md +++ b/apps/docs/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract.md @@ -6,14 +6,14 @@ While anyone can invest in a DeFindex, only the Manager and Emergency Manager ha The contract also holds funds not currently invested in any strategy, known as **IDLE funds**. These funds act as a safety buffer, allowing the Emergency Manager to withdraw assets from underperforming or unhealthy strategies and store them as IDLE funds. (also to enable fast small withdrawals) -### Underlying Assets +## Underlying Assets Each DeFindex Vault will use a defined set of underlying assets to be invested in one or more strategies. Because Strategies are the only one that know exactly the current balance of the asset, the Vault relies on the strategies in order to know the exact total balance for each underlying asset.?? Or if the Vault executes Strategies at its own name (auth), it should execute a speficic `get_assets_balance` function in the strategy contract to know exactely how many assets it has at a specific moment. -### Initialization +## Initialization The DeFindex Vault contract is structured with specific roles and strategies for managing assets effectively. The key roles include the **Fee Receiver**, **Manager**, and **Emergency Manager**, each responsible for different tasks in managing the Vault. Additionally, a predefined set of strategies determines how assets will be allocated within the Vault. A management fee is also established at the time of initialization, which can later be adjusted by the Fee Receiver. Further details on fee handling are explained later in the document. The allocation ratios for these strategies are not set during the deployment but are defined during the first deposit made into the Vault. For example, imagine a scenario where the Vault is set up to allocate 20% of its assets to a USDC lending pool (like Blend), 30% to another USDC lending pool (such as YieldBlox), and 50% to a USDC-XLM liquidity pool on an Automated Market Maker (AMM) platform (like Soroswap). @@ -32,7 +32,7 @@ In summary: -### Investing: Deposit +## Investing: Deposit When a user deposits assets into the DeFindex, they receive dfTokens that represent their proportional share of the DeFindex's assets. These dfTokens can later be burned to withdraw the corresponding assets. Upon calling the `deposit()` function, **the assets are transferred to the DeFindex in accordance with the current asset ratio**. For example, if the current ratio is 1 token A, 2 tokens B, and 3 tokens C for each dfToken, this ratio is maintained when assets are deposited. In return, the user receives dfTokens that represent their participation in the DeFindex Vault. @@ -43,7 +43,7 @@ Thus, the price per dfToken reflects a multi-asset price. For instance, using th -### Withdrawals +## Withdrawals When a user wishes to withdraw funds, they must burn a corresponding amount of dfTokens (shares) to receive their **assets at the ratio of the time of withdrawal**. If there are sufficient **IDLE funds** available, the withdrawal is fulfilled directly from these IDLE funds. If additional assets are needed beyond what is available in the IDLE funds, a liquidation process is triggered to release the required assets. @@ -81,14 +81,14 @@ Where: - $a_{i, \text{IDLE}}$: Amount of asset $i$ to get from the IDLE funds - $a_{i, \text{Strategy}}$: Amount of asset $i$ to get from the strategies -### Rebalancing +## Rebalancing Rebalancing is overseen by the **Manager**, who adjusts the allocation of funds between different strategies to maintain or change the ratio of underlying assets. For example, a DeFindex might start with a ratio of 2 USDC to 1 XLM, as initially set by the Deployer. However, this ratio can be modified by the Manager based on strategy performance or market conditions. Upon deployment, the Deployer establishes the initial strategies and asset ratios for the DeFindex. The Manager has the authority to adjust these ratios as needed to respond to evolving conditions or to optimize performance. To ensure accurate representation of asset proportions, strategies are required to **report** the amount of each underlying asset they hold. This reporting ensures that when dfTokens are minted or redeemed, the DeFindex maintains the correct asset ratios in line with the current balance and strategy allocations. -#### Functions +### Functions - `assets()`: Returns the assets addresses and amount of each of them in the DeFindex (and hence its current ratio). `[[adress0, amount0], [address1, amount1]]`. TODO: Separate in 2 functions. - `withdraw_from_strategies`: Allows the Manager to withdraw assets from one or more strategies, letting them as IDLE funds. @@ -99,106 +99,143 @@ To ensure accurate representation of asset proportions, strategies are required Then, a rebalance execution will withdraw assets from the strategies, swap them, and invest them back in the strategies. - `emergency_withdraw`: Allows the Emergency Manager to withdraw all assets from a specific Strategy. As arguments, it receives the the address of a Strategy. It also turns off the strategy. -### Emergency Management +## Emergency Management The Emergency Manager has the authority to withdraw assets from the DeFindex in case of an emergency. This role is designed to protect users' assets in the event of a critical situation, such as a hack of a underlying protocol or a if a strategy gets unhealthy. The Emergency Manager can withdraw assets from the Strategy and store them as IDLE funds inside the Vault until the situation is resolved. -### Management +## Management Every DeFindex has a manager, who is responsible for managing the DeFindex. The Manager can ebalance the Vault, and invest IDLE funds in strategies. +## Fees -### Fees: Receivers, Structure and Collection +### Fee Receivers +The DeFindex protocol defines two distinct fee receivers to reward both the creators of the DeFindex Protocol and the deployers of individual Vaults: -**Fee Receivers:** Every DeFindex user will pay a Management Fee for the total amount of funds managed by the DeFindex Vault. These fees will finally be received by two different fee receivers: +1. **DeFindex Protocol Fee Receiver**: Receives a fixed protocol fee of 0.5% APR. +2. **Vault Fee Receiver**: Receives a fee set by the vault deployer, typically recommended between 0.5% and 2% APR. -1. **DeFindex Protocol Fee Receiver**. For creating the protocol. -2. **Vault Fee Receiver**. For creating the vault. +The Total Management Fee consists of both the protocol fee and the vault fee. Thus, each Vault has a total APR fee rate $f_{\text{total}}$ such that: -**Fee Structure:** -The Total Management fee is the sum of **DeFindex Fee** and the **Vault Fee**. This sum can be from 0.5% to 2.5%, and it is defined as follows: +$$ +f_{\text{total}} = f_{\text{DeFindex}} + f_{\text{Vault}} +$$ + +where $f_{\text{DeFindex}} = 0.5\%$ is a fixed fee that goes to the DeFindex Protocol Fee Receiver address, and $f_{\text{Vault}}$ is a variable APR, typically between 0.5% and 2%, that goes to the Vault Fee Receiver address. -1. **The DeFindex Fee** is fixed for all DeFindex Vaults, and this fee is fixed to 0.5% APR. This fee is fixed in the DeFindex Factory Contract. -2. **The Vault Fee** is set by the Vault Deployer. The recommended initial setup suggests a fee of **0.5% - 2% APR**. +### Fee Collection Methodology -**Fee Collection:** -In order for a vault to collect fees, the DeFindex vault will mint and collect new shares (or dfTokens) whenever a deposit or withdrawal takes place. +The fee collection process mints new shares, or dfTokens, to cover the accrued management fees. These shares are calculated based on the elapsed time since the last fee assessment, ensuring fees are accrued based on the actual period of asset management. The fee collection is triggered whenever there is a vault interaction, such as a `deposit`, `withdrawal`, or even an explicit `fee_collection` call, with calculations based on the time elapsed since the last fee assessment. -**Mathematical Explanation** -Lets consider $V_0$, total value locked at $t_0$, total shares $s_0$ at $t_0$ and total management fee of $f$. -This fee $f$ its considered as APR. +### Mathematical Derivation of New Fees -After one period (one year), new shares $s_f$ should be minted in order to pay this fee. +Let: -$s_1 = s_0 + s_f$ +- $V_0$ be the Total Value Locked (TVL) at the last assessment, +- $s_0$ be the Total Shares (dfTokens) at the last assessment, +- $f_{\text{total}}$ be the Total Management Fee (APR). -$V_1 =V_0$ as we consider that no new funds where added +Over a time period $\Delta t$, the fees due for collection are derived as a value represented by newly minted shares. -$s_f$ should comply with: +To mint new shares for fee distribution, we calculate the required number of new shares, $s_f$, that correspond to the total management fee over the elapsed period. -The value of the mited share $\frac{V_0}{s_1} * s_f$ should be equal of the fraction of the total value locked $V_0 * f$ +After a period $\Delta t$ (expressed in seconds), and after the fee collection process the new total shares $s_1$ should be: -$\frac{V_0}{s_1} * s_f = V_0 * f$ +$$ +s_1 = s_0 + s_f +$$ -so this gets simplified +Since `fee_collection` is always called before any `deposit` or `withdrawal`, we assume that the Total Value $V_1$ remains equal to $V_0$. -$s_f = f \cdot s_1 = f \cdot (s_0 + s_f)$ +We establish the following condition to ensure the number of minted shares accurately reflects the management fee accrued over $\Delta t$. The value of the new minted shares $s_f$ should equal the prorated APR fee share of the total value of the vault. In mathematical terms: -$s_f = \frac{f \cdot s_0}{(1-f)}$ +$$ +\frac{V_0}{s_1} \times s_f = V_0 \times f_{\text{total}} \times \frac{\Delta t}{\text{SECONDS PER YEAR}} +$$ +Rearranging terms, we get: +$$ +s_f = \frac{f_{\text{total}} \times s_0 \times \Delta t}{\text{SECONDS PER YEAR} - f_{\text{total}} \times \Delta t} +$$ +This equation gives the precise quantity of new shares $s_f$ to mint as dfTokens for the management fee over the period $\Delta t$. +### Distribution of Fees -**Example:** -For example, if a Vault has 100 shares and the Vault Fee is set at 0.5% APR, the annually collected fees would be 1 share, splited as 0.5 for the Fee Receiver and 0.5 for the DeFindex Protocol. +Once the total fees, $s_f$, are calculated, the shares are split proportionally between the DeFindex Protocol Fee Receiver and the Vault Fee Receiver. This is done by calculating the ratio of each fee receiver’s APR to the total APR: -total funds:100 (does not change) -previous shares: 1 -Yβ‰ˆ1.0101 +$$ +s_{\text{DeFindex}} = \frac{s_f \times f_{\text{DeFindex}}}{f_{\text{total}}} +$$ -100/(100+Y) *Y = 1 USDC -total funds/(previous total shars+fee shares) *fee shares = fee rate of total funds +$$ +s_{\text{Vault}} = s_f - s_{\text{DeFindex}} +$$ + +This ensures that each fee receiver is allocated their respective share of dfTokens based on their fee contribution to $f_{\text{total}}$. The dfTokens are then minted to each receiver’s address as a direct representation of the fees collected. + + +### Example + +Suppose a DeFindex vault begins with an initial value of 1 USDC per share and a total of 100 shares (dfTokens), representing an investment of 100 USDC. This investment is placed in a lending protocol with an 8% APY. The DeFindex protocol has a total management fee of 1% APR, split between a 0.5% protocol fee and a 0.5% vault fee. + +After one year, the investment grows to 108 USDC due to the 8% APY. + +#### Step 1: Calculate the Shares to Mint for Fees + +Using the formula: + +$ +s_f = \frac{f_{\text{total}} \times s_0 \times \Delta t}{\text{SECONDS PER YEAR} - f_{\text{total}} \times \Delta t} +$ + +where: +- \( f_{\text{total}} = 0.01 \) (1% APR management fee), +- \( s_0 = 100 \) (initial shares), +- \( \Delta t = \text{SECONDS PER YEAR} \) (since this example spans a full year), +we calculate \( s_f \), the number of shares to mint for the fee collection. +Substituting values: +$ +s_f = \frac{0.01 \times 100 \times \text{SECONDS PER YEAR}}{\text{SECONDS PER YEAR} - (0.01 \times \text{SECONDS PER YEAR})} +$ -t0 amount0 -t1 amount0 cobra fee por el tiempo (t1-t0) y se suma amount1 -t2 (amount0 y amount 1) cobra fee por el tiempo (t2-t1) +Simplifying: -100 shares, 100 USDC, price per share = 100/100 = 1 +$ +s_f = \frac{1 \times \text{SECONDS PER YEAR}}{0.99 \times \text{SECONDS PER YEAR}} \approx 1.0101 +$ -despues de un aΓ±o, deposito de 100. -sin fee: se mintean 100 shares -new shares: 200, new funds: 200 USDC +Thus, approximately 1.01 dfTokens are minted as fees. -con fee: se mintean X shares al depositor, Y shares al fee receiver -total shares: (100 + X + Y) -total funds: (200 USDC) -value of fee shares (Y) -200/(100+X+Y) *Y = 1 USDC -200/(100+X+Y) *X = 100 USDC -X=101.0101 -Y=1.0101 +#### Step 2: Update Total Shares and Calculate Price per Share -si no se deposita nada -100/(100+Y) *Y = 1 USDC -100/(100+X+Y) *X = 100 USDC +With the fee tokens minted, the total dfTokens increase from 100 to 101.01. +The vault now holds 108 USDC backing 101.01 dfTokens, so the new price per share is: -300/(100+X+Y) *Y = 1 USDC -300/(100+X+Y) *X = 200 USDC +$ +\text{Price per Share} = \frac{108}{101.01} \approx 1.069 \, \text{USDC} +$ +#### Step 3: Determine the Value for a User Holding 100 dfTokens +For a user holding 100 dfTokens, the value of their holdings after one year is approximately: +$ +100 \, \text{dfTokens} \times 1.069 \, \text{USDC per share} = 106.9 \, \text{USDC} +$ -mint x shares, total new shares = (100+x) -my value of this is x/(100+x) = 1% +The remaining 1.01 dfTokens represent the collected fee, backed by around: +$ +1.01 \, \text{dfTokens} \times 1.069 \, \text{USDC per share} \approx 1.08 \, \text{USDC} +\] -101 shares, 1 share is equal to 1/101 +--- -These allocations are recalculated and minted whenever a user deposits, withdraws from the DeFindex. +This breakdown clarifies how the investment grows and the management fee is deducted by minting new dfTokens, resulting in a proportional share value for both users and fee recipients. -For instance, let's say a DeFindex starts with an initial value of 1 USDC per share and 100 shares (dfTokens). These 100 USDC are invested in a lending protocol offering an 8% APY. The DeFindex also has a 0.5% APR fee. After one year, the investment grows to 108 USDC. Additionally, 1 dfToken is minted as a fee. This means the DeFindex now holds 101 dfTokens backed by 108 USDC, making the price per share approximately 1.07 USDC. As a result, a user holding 100 dfTokens will have a value equivalent to around 107 USDC, while the collected fee will be backed by about 1.07 USDC. It is expected that the Fee Receiver is associated with the manager, allowing the entity managing the Vault to be compensated through the Fee Receiver. In other words, the Fee Receiver could be the manager using the same address, or it could be a different entity such as a streaming contract, a DAO, or another party. From 40f123f72291288707e44337333f9a597b96aaea Mon Sep 17 00:00:00 2001 From: esteblock Date: Tue, 5 Nov 2024 15:21:34 -0300 Subject: [PATCH 16/19] change folder name, from defindex to vault for DeFindexVault Contract --- apps/contracts/Cargo.toml | 2 +- apps/contracts/Makefile | 2 +- apps/contracts/defindex/src/constants.rs | 2 - apps/contracts/defindex/src/investment.rs | 43 ---- apps/contracts/{defindex => vault}/Cargo.toml | 0 apps/contracts/{defindex => vault}/Makefile | 0 .../{defindex => vault}/src/access.rs | 0 .../{defindex => vault}/src/aggregator.rs | 48 +++-- apps/contracts/vault/src/constants.rs | 2 + .../{defindex => vault}/src/error.rs | 2 +- .../{defindex => vault}/src/events.rs | 13 +- apps/contracts/{defindex => vault}/src/fee.rs | 47 ++-- .../{defindex => vault}/src/funds.rs | 37 ++-- .../{defindex => vault}/src/interface.rs | 35 +-- apps/contracts/vault/src/investment.rs | 54 +++++ apps/contracts/{defindex => vault}/src/lib.rs | 202 +++++++++++------- .../{defindex => vault}/src/models.rs | 2 +- .../{defindex => vault}/src/storage.rs | 27 ++- .../{defindex => vault}/src/strategies.rs | 47 +++- .../contracts/{defindex => vault}/src/test.rs | 32 +-- .../{defindex => vault}/src/test/admin.rs | 3 +- .../{defindex => vault}/src/test/deposit.rs | 25 +-- .../src/test/emergency_withdraw.rs | 35 ++- .../src/test/initialize.rs | 6 +- .../{defindex => vault}/src/test/rebalance.rs | 33 +-- .../{defindex => vault}/src/test/withdraw.rs | 66 +++--- .../src/token/allowance.rs | 0 .../{defindex => vault}/src/token/balance.rs | 0 .../{defindex => vault}/src/token/contract.rs | 0 .../{defindex => vault}/src/token/metadata.rs | 0 .../{defindex => vault}/src/token/mod.rs | 0 .../src/token/storage_types.rs | 0 .../src/token/total_supply.rs | 0 .../{defindex => vault}/src/utils.rs | 28 ++- scf-tracker.md | 2 +- 35 files changed, 485 insertions(+), 310 deletions(-) delete mode 100644 apps/contracts/defindex/src/constants.rs delete mode 100644 apps/contracts/defindex/src/investment.rs rename apps/contracts/{defindex => vault}/Cargo.toml (100%) rename apps/contracts/{defindex => vault}/Makefile (100%) rename apps/contracts/{defindex => vault}/src/access.rs (100%) rename apps/contracts/{defindex => vault}/src/aggregator.rs (65%) create mode 100644 apps/contracts/vault/src/constants.rs rename apps/contracts/{defindex => vault}/src/error.rs (96%) rename apps/contracts/{defindex => vault}/src/events.rs (97%) rename apps/contracts/{defindex => vault}/src/fee.rs (72%) rename apps/contracts/{defindex => vault}/src/funds.rs (96%) rename apps/contracts/{defindex => vault}/src/interface.rs (95%) create mode 100644 apps/contracts/vault/src/investment.rs rename apps/contracts/{defindex => vault}/src/lib.rs (87%) rename apps/contracts/{defindex => vault}/src/models.rs (99%) rename apps/contracts/{defindex => vault}/src/storage.rs (81%) rename apps/contracts/{defindex => vault}/src/strategies.rs (80%) rename apps/contracts/{defindex => vault}/src/test.rs (87%) rename apps/contracts/{defindex => vault}/src/test/admin.rs (99%) rename apps/contracts/{defindex => vault}/src/test/deposit.rs (96%) rename apps/contracts/{defindex => vault}/src/test/emergency_withdraw.rs (79%) rename apps/contracts/{defindex => vault}/src/test/initialize.rs (97%) rename apps/contracts/{defindex => vault}/src/test/rebalance.rs (82%) rename apps/contracts/{defindex => vault}/src/test/withdraw.rs (84%) rename apps/contracts/{defindex => vault}/src/token/allowance.rs (100%) rename apps/contracts/{defindex => vault}/src/token/balance.rs (100%) rename apps/contracts/{defindex => vault}/src/token/contract.rs (100%) rename apps/contracts/{defindex => vault}/src/token/metadata.rs (100%) rename apps/contracts/{defindex => vault}/src/token/mod.rs (100%) rename apps/contracts/{defindex => vault}/src/token/storage_types.rs (100%) rename apps/contracts/{defindex => vault}/src/token/total_supply.rs (100%) rename apps/contracts/{defindex => vault}/src/utils.rs (93%) diff --git a/apps/contracts/Cargo.toml b/apps/contracts/Cargo.toml index dd4a992d..9dee20c8 100644 --- a/apps/contracts/Cargo.toml +++ b/apps/contracts/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["strategies/*", "defindex", "factory"] +members = ["strategies/*", "vault", "factory"] exclude = [ "strategies/external_wasms", ] diff --git a/apps/contracts/Makefile b/apps/contracts/Makefile index 9c88fe8c..30807186 100644 --- a/apps/contracts/Makefile +++ b/apps/contracts/Makefile @@ -1,4 +1,4 @@ -SUBDIRS = strategies defindex factory +SUBDIRS = strategies vault factory default: build diff --git a/apps/contracts/defindex/src/constants.rs b/apps/contracts/defindex/src/constants.rs deleted file mode 100644 index 772d4f54..00000000 --- a/apps/contracts/defindex/src/constants.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub(crate) const MAX_BPS: i128 = 10_000; -pub(crate) const SECONDS_PER_YEAR: i128 = 31_536_000; \ No newline at end of file diff --git a/apps/contracts/defindex/src/investment.rs b/apps/contracts/defindex/src/investment.rs deleted file mode 100644 index adeb3930..00000000 --- a/apps/contracts/defindex/src/investment.rs +++ /dev/null @@ -1,43 +0,0 @@ -use soroban_sdk::{Address, Env, Map, Vec}; - -use crate::{models::Investment, strategies::{get_strategy_asset, invest_in_strategy}, utils::check_nonnegative_amount, ContractError}; - -pub fn prepare_investment(e: &Env, investments: Vec, idle_funds: Map) -> Result, ContractError> { - let mut total_investment_per_asset: Map = Map::new(e); - - for investment in investments.iter() { - let strategy_address = &investment.strategy; - let amount_to_invest = investment.amount; - check_nonnegative_amount(amount_to_invest.clone())?; - - // Find the corresponding asset for the strategy - let asset = get_strategy_asset(&e, strategy_address)?; - - // Track investment per asset - let current_investment = total_investment_per_asset - .get(asset.address.clone()) - .unwrap_or(0); - let updated_investment = current_investment.checked_add(amount_to_invest).ok_or(ContractError::Overflow)?; - - total_investment_per_asset.set(asset.address.clone(), updated_investment); - - // Check if total investment exceeds idle funds - let idle_balance = idle_funds.get(asset.address.clone()).unwrap_or(0); - if updated_investment > idle_balance { - return Err(ContractError::NotEnoughIdleFunds); - } - } - - Ok(total_investment_per_asset) -} - -pub fn execute_investment(e: &Env, investments: Vec) -> Result<(), ContractError> { - for investment in investments.iter() { - let strategy_address = &investment.strategy; - let amount_to_invest = &investment.amount; - - invest_in_strategy(e, strategy_address, amount_to_invest)? - } - - Ok(()) -} \ No newline at end of file diff --git a/apps/contracts/defindex/Cargo.toml b/apps/contracts/vault/Cargo.toml similarity index 100% rename from apps/contracts/defindex/Cargo.toml rename to apps/contracts/vault/Cargo.toml diff --git a/apps/contracts/defindex/Makefile b/apps/contracts/vault/Makefile similarity index 100% rename from apps/contracts/defindex/Makefile rename to apps/contracts/vault/Makefile diff --git a/apps/contracts/defindex/src/access.rs b/apps/contracts/vault/src/access.rs similarity index 100% rename from apps/contracts/defindex/src/access.rs rename to apps/contracts/vault/src/access.rs diff --git a/apps/contracts/defindex/src/aggregator.rs b/apps/contracts/vault/src/aggregator.rs similarity index 65% rename from apps/contracts/defindex/src/aggregator.rs rename to apps/contracts/vault/src/aggregator.rs index 70f5050c..c10e34db 100644 --- a/apps/contracts/defindex/src/aggregator.rs +++ b/apps/contracts/vault/src/aggregator.rs @@ -1,14 +1,18 @@ use soroban_sdk::{vec, Address, Env, IntoVal, Symbol, Val, Vec}; -use crate::{models::DexDistribution, storage::{get_assets, get_factory}, ContractError}; +use crate::{ + models::DexDistribution, + storage::{get_assets, get_factory}, + ContractError, +}; fn fetch_aggregator_address(e: &Env) -> Address { let factory_address = get_factory(e); e.invoke_contract( - &factory_address, - &Symbol::new(&e, "aggregator"), - Vec::new(&e) + &factory_address, + &Symbol::new(&e, "aggregator"), + Vec::new(&e), ) } @@ -16,8 +20,16 @@ fn is_supported_asset(e: &Env, token: &Address) -> bool { let assets = get_assets(e); assets.iter().any(|asset| &asset.address == token) } - -pub fn internal_swap_exact_tokens_for_tokens(e: &Env, token_in: &Address, token_out: &Address, amount_in: &i128, amount_out_min: &i128, distribution: &Vec, deadline: &u64) -> Result<(), ContractError> { + +pub fn internal_swap_exact_tokens_for_tokens( + e: &Env, + token_in: &Address, + token_out: &Address, + amount_in: &i128, + amount_out_min: &i128, + distribution: &Vec, + deadline: &u64, +) -> Result<(), ContractError> { let aggregator_address = fetch_aggregator_address(e); // Check if both tokens are supported by the vault @@ -35,13 +47,21 @@ pub fn internal_swap_exact_tokens_for_tokens(e: &Env, token_in: &Address, token_ init_args.push_back(deadline.into_val(e)); e.invoke_contract( - &aggregator_address, - &Symbol::new(&e, "swap_exact_tokens_for_tokens"), - Vec::new(&e) + &aggregator_address, + &Symbol::new(&e, "swap_exact_tokens_for_tokens"), + Vec::new(&e), ) } -pub fn internal_swap_tokens_for_exact_tokens(e: &Env, token_in: &Address, token_out: &Address, amount_out: &i128, amount_in_max: &i128, distribution: &Vec, deadline: &u64) -> Result<(), ContractError> { +pub fn internal_swap_tokens_for_exact_tokens( + e: &Env, + token_in: &Address, + token_out: &Address, + amount_out: &i128, + amount_in_max: &i128, + distribution: &Vec, + deadline: &u64, +) -> Result<(), ContractError> { let aggregator_address = fetch_aggregator_address(e); // Check if both tokens are supported by the vault @@ -59,8 +79,8 @@ pub fn internal_swap_tokens_for_exact_tokens(e: &Env, token_in: &Address, token_ init_args.push_back(deadline.into_val(e)); e.invoke_contract( - &aggregator_address, - &Symbol::new(&e, "swap_tokens_for_exact_tokens"), - Vec::new(&e) + &aggregator_address, + &Symbol::new(&e, "swap_tokens_for_exact_tokens"), + Vec::new(&e), ) -} \ No newline at end of file +} diff --git a/apps/contracts/vault/src/constants.rs b/apps/contracts/vault/src/constants.rs new file mode 100644 index 00000000..1428e1b8 --- /dev/null +++ b/apps/contracts/vault/src/constants.rs @@ -0,0 +1,2 @@ +pub(crate) const MAX_BPS: i128 = 10_000; +pub(crate) const SECONDS_PER_YEAR: i128 = 31_536_000; diff --git a/apps/contracts/defindex/src/error.rs b/apps/contracts/vault/src/error.rs similarity index 96% rename from apps/contracts/defindex/src/error.rs rename to apps/contracts/vault/src/error.rs index 24b4b6f0..7a2fec42 100644 --- a/apps/contracts/defindex/src/error.rs +++ b/apps/contracts/vault/src/error.rs @@ -8,7 +8,7 @@ pub enum ContractError { NotInitialized = 100, AlreadyInitialized = 101, InvalidRatio = 102, - StrategyDoesNotSupportAsset=103, + StrategyDoesNotSupportAsset = 103, // Validation Errors (11x) NegativeNotAllowed = 110, diff --git a/apps/contracts/defindex/src/events.rs b/apps/contracts/vault/src/events.rs similarity index 97% rename from apps/contracts/defindex/src/events.rs rename to apps/contracts/vault/src/events.rs index deca5b5d..880b6a45 100644 --- a/apps/contracts/defindex/src/events.rs +++ b/apps/contracts/vault/src/events.rs @@ -1,6 +1,6 @@ //! Definition of the Events used in the DeFindex Vault contract -use soroban_sdk::{contracttype, symbol_short, Address, Env, Vec}; use crate::models::AssetAllocation; +use soroban_sdk::{contracttype, symbol_short, Address, Env, Vec}; // INITIALIZED VAULT EVENT #[contracttype] @@ -178,9 +178,7 @@ pub struct ManagerChangedEvent { /// Publishes a `ManagerChangedEvent` to the event stream. pub(crate) fn emit_manager_changed_event(e: &Env, new_manager: Address) { - let event = ManagerChangedEvent { - new_manager, - }; + let event = ManagerChangedEvent { new_manager }; e.events() .publish(("DeFindexVault", symbol_short!("nmanager")), event); @@ -194,10 +192,7 @@ pub struct EmergencyManagerChangedEvent { } /// Publishes an `EmergencyManagerChangedEvent` to the event stream. -pub(crate) fn emit_emergency_manager_changed_event( - e: &Env, - new_emergency_manager: Address, -) { +pub(crate) fn emit_emergency_manager_changed_event(e: &Env, new_emergency_manager: Address) { let event = EmergencyManagerChangedEvent { new_emergency_manager, }; @@ -233,4 +228,4 @@ pub(crate) fn emit_fees_minted_event( e.events() .publish(("DeFindexVault", symbol_short!("mfees")), event); -} \ No newline at end of file +} diff --git a/apps/contracts/defindex/src/fee.rs b/apps/contracts/vault/src/fee.rs similarity index 72% rename from apps/contracts/defindex/src/fee.rs rename to apps/contracts/vault/src/fee.rs index 3d2e765d..1cd57a06 100644 --- a/apps/contracts/defindex/src/fee.rs +++ b/apps/contracts/vault/src/fee.rs @@ -1,23 +1,30 @@ use soroban_sdk::{Address, Env, Map, Symbol, Vec}; -use crate::{access::AccessControl, constants::{MAX_BPS, SECONDS_PER_YEAR}, events, funds::fetch_total_managed_funds, storage::{get_defindex_receiver, get_factory, get_last_fee_assesment, get_vault_share, set_last_fee_assesment}, token::internal_mint, utils::calculate_dftokens_from_asset_amounts, ContractError}; +use crate::{ + access::AccessControl, + constants::{MAX_BPS, SECONDS_PER_YEAR}, + events, + funds::fetch_total_managed_funds, + storage::{ + get_defindex_receiver, get_factory, get_last_fee_assesment, get_vault_share, + set_last_fee_assesment, + }, + token::internal_mint, + utils::calculate_dftokens_from_asset_amounts, + ContractError, +}; /// Fetches the current fee rate from the factory contract. /// The fee rate is expressed in basis points (BPS). fn fetch_fee_rate(e: &Env) -> u32 { - let factory_address = get_factory(e); - // Interacts with the factory contract to get the fee rate. - e.invoke_contract( - &factory_address, - &Symbol::new(&e, "fee_rate"), - Vec::new(&e) - ) + let factory_address = get_factory(e); + // Interacts with the factory contract to get the fee rate. + e.invoke_contract(&factory_address, &Symbol::new(&e, "fee_rate"), Vec::new(&e)) } fn calculate_fees(e: &Env, time_elapsed: u64, fee_rate: u32) -> Result { - let total_managed_funds = fetch_total_managed_funds(e); // Get total managed funds per asset - + let seconds_per_year = SECONDS_PER_YEAR; // 365 days in seconds let mut total_fees_per_asset: Map = Map::new(&e); @@ -28,10 +35,10 @@ fn calculate_fees(e: &Env, time_elapsed: u64, fee_rate: u32) -> Result Result Result<(), ContractError> { let current_timestamp = e.ledger().timestamp(); - let last_fee_assessment = get_last_fee_assesment(e); + let last_fee_assessment = get_last_fee_assesment(e); let time_elapsed = current_timestamp.checked_sub(last_fee_assessment).unwrap(); @@ -64,19 +71,25 @@ pub fn collect_fees(e: &Env) -> Result<(), ContractError> { fn mint_fees(e: &Env, total_fees: i128) -> Result<(), ContractError> { let access_control = AccessControl::new(&e); - + let vault_fee_receiver = access_control.get_fee_receiver()?; let defindex_receiver = get_defindex_receiver(e); let vault_share_bps = get_vault_share(e); let vault_shares = (total_fees * vault_share_bps as i128) / MAX_BPS; - + let defindex_shares = total_fees - vault_shares; internal_mint(e.clone(), vault_fee_receiver.clone(), vault_shares); internal_mint(e.clone(), defindex_receiver.clone(), defindex_shares); - events::emit_fees_minted_event(e, defindex_receiver, defindex_shares, vault_fee_receiver, vault_shares); + events::emit_fees_minted_event( + e, + defindex_receiver, + defindex_shares, + vault_fee_receiver, + vault_shares, + ); Ok(()) -} \ No newline at end of file +} diff --git a/apps/contracts/defindex/src/funds.rs b/apps/contracts/vault/src/funds.rs similarity index 96% rename from apps/contracts/defindex/src/funds.rs rename to apps/contracts/vault/src/funds.rs index 830ffa13..f02d879b 100644 --- a/apps/contracts/defindex/src/funds.rs +++ b/apps/contracts/vault/src/funds.rs @@ -5,28 +5,28 @@ use crate::models::AssetAllocation; use crate::storage::get_assets; use crate::strategies::get_strategy_client; -// Funds for AssetAllocation +// Funds for AssetAllocation /// Fetches the idle funds for a given asset. Idle funds refer to the balance of the asset /// that is currently not invested in any strategies. -/// +/// /// # Arguments /// * `e` - The current environment instance. /// * `asset` - The asset for which idle funds are being fetched. -/// +/// /// # Returns /// * The idle balance (i128) of the asset in the current contract address. fn fetch_idle_funds_for_asset(e: &Env, asset: &AssetAllocation) -> i128 { TokenClient::new(e, &asset.address).balance(&e.current_contract_address()) } -/// Fetches the total funds that are invested for a given asset. +/// Fetches the total funds that are invested for a given asset. /// It iterates through all the strategies associated with the asset and sums their balances. -/// +/// /// # Arguments /// * `e` - The current environment instance. /// * `asset` - The asset for which invested funds are being fetched. -/// +/// /// # Returns /// * The total invested balance (i128) of the asset across all strategies. pub fn fetch_invested_funds_for_strategy(e: &Env, strategy_address: &Address) -> i128 { @@ -34,13 +34,13 @@ pub fn fetch_invested_funds_for_strategy(e: &Env, strategy_address: &Address) -> strategy_client.balance(&e.current_contract_address()) } -/// Fetches the total funds that are invested for a given asset. +/// Fetches the total funds that are invested for a given asset. /// It iterates through all the strategies associated with the asset and sums their balances. -/// +/// /// # Arguments /// * `e` - The current environment instance. /// * `asset` - The asset for which invested funds are being fetched. -/// +/// /// # Returns /// * The total invested balance (i128) of the asset across all strategies. pub fn fetch_invested_funds_for_asset(e: &Env, asset: &AssetAllocation) -> i128 { @@ -51,15 +51,14 @@ pub fn fetch_invested_funds_for_asset(e: &Env, asset: &AssetAllocation) -> i128 invested_funds } - // Pub functions -/// Fetches the current idle funds for all assets managed by the contract. +/// Fetches the current idle funds for all assets managed by the contract. /// It returns a map where the key is the asset's address and the value is the idle balance. -/// +/// /// # Arguments /// * `e` - The current environment instance. -/// +/// /// # Returns /// * A map where each entry represents an asset's address and its corresponding idle balance. pub fn fetch_current_idle_funds(e: &Env) -> Map { @@ -71,12 +70,12 @@ pub fn fetch_current_idle_funds(e: &Env) -> Map { map } -/// Fetches the current invested funds for all assets managed by the contract. +/// Fetches the current invested funds for all assets managed by the contract. /// It returns a map where the key is the asset's address and the value is the invested balance. -/// +/// /// # Arguments /// * `e` - The current environment instance. -/// +/// /// # Returns /// * A map where each entry represents an asset's address and its corresponding invested balance. pub fn fetch_current_invested_funds(e: &Env) -> Map { @@ -92,12 +91,12 @@ pub fn fetch_current_invested_funds(e: &Env) -> Map { } /// Fetches the total managed funds for all assets. This includes both idle and invested funds. -/// It returns a map where the key is the asset's address and the value is the total managed balance +/// It returns a map where the key is the asset's address and the value is the total managed balance /// (idle + invested). With this map we can calculate the current managed funds ratio. -/// +/// /// # Arguments /// * `e` - The current environment instance. -/// +/// /// # Returns /// * A map where each entry represents an asset's address and its total managed balance. pub fn fetch_total_managed_funds(e: &Env) -> Map { diff --git a/apps/contracts/defindex/src/interface.rs b/apps/contracts/vault/src/interface.rs similarity index 95% rename from apps/contracts/defindex/src/interface.rs rename to apps/contracts/vault/src/interface.rs index 9dc76739..dafdc998 100644 --- a/apps/contracts/defindex/src/interface.rs +++ b/apps/contracts/vault/src/interface.rs @@ -6,12 +6,11 @@ use crate::{ }; pub trait VaultTrait { - /// Initializes the DeFindex Vault contract with the required parameters. /// /// This function sets the roles for emergency manager, fee receiver, and manager. /// It also stores the list of assets to be managed by the vault, including strategies for each asset. - /// + /// /// # Arguments: /// * `e` - The environment. /// * `assets` - A vector of `AssetAllocation` structs representing the assets and their associated strategies. @@ -86,7 +85,11 @@ pub trait VaultTrait { /// /// # Returns: /// * `Result<(), ContractError>` - Ok if successful, otherwise returns a ContractError. - fn emergency_withdraw(e: Env, strategy_address: Address, caller: Address) -> Result<(), ContractError>; + fn emergency_withdraw( + e: Env, + strategy_address: Address, + caller: Address, + ) -> Result<(), ContractError>; /// Pauses a strategy to prevent it from being used in the vault. /// @@ -100,7 +103,11 @@ pub trait VaultTrait { /// /// # Returns: /// * `Result<(), ContractError>` - Ok if successful, otherwise returns a ContractError. - fn pause_strategy(e: Env, strategy_address: Address, caller: Address) -> Result<(), ContractError>; + fn pause_strategy( + e: Env, + strategy_address: Address, + caller: Address, + ) -> Result<(), ContractError>; /// Unpauses a previously paused strategy. /// @@ -114,7 +121,11 @@ pub trait VaultTrait { /// /// # Returns: /// * `Result<(), ContractError>` - Ok if successful, otherwise returns a ContractError. - fn unpause_strategy(e: Env, strategy_address: Address, caller: Address) -> Result<(), ContractError>; + fn unpause_strategy( + e: Env, + strategy_address: Address, + caller: Address, + ) -> Result<(), ContractError>; /// Retrieves the list of assets managed by the DeFindex Vault. /// @@ -136,7 +147,7 @@ pub trait VaultTrait { /// # Returns: /// * `Map` - A map of asset addresses to their total managed amounts. fn fetch_total_managed_funds(e: &Env) -> Map; - + /// Returns the current invested funds, representing the total assets allocated to strategies. /// /// This function provides a map where the key is the asset address and the value is the total amount @@ -164,7 +175,6 @@ pub trait VaultTrait { // TODO: DELETE THIS, USED FOR TESTING /// Temporary method for testing purposes. fn get_asset_amounts_for_dftokens(e: Env, df_token: i128) -> Map; - } pub trait AdminInterfaceTrait { @@ -234,25 +244,24 @@ pub trait AdminInterfaceTrait { } pub trait VaultManagementTrait { - /// Invests the vault's idle funds into the specified strategies. - /// + /// /// # Arguments: /// * `e` - The environment. /// * `investment` - A vector of `Investment` structs representing the amount to invest in each strategy. /// * `caller` - The address of the caller. - /// + /// /// # Returns: /// * `Result<(), ContractError>` - Ok if successful, otherwise returns a ContractError. fn invest(e: Env, investment: Vec) -> Result<(), ContractError>; /// Rebalances the vault by executing a series of instructions. - /// + /// /// # Arguments: /// * `e` - The environment. /// * `instructions` - A vector of `Instruction` structs representing actions (withdraw, invest, swap, zapper) to be taken. - /// + /// /// # Returns: /// * `Result<(), ContractError>` - Ok if successful, otherwise returns a ContractError. fn rebalance(e: Env, instructions: Vec) -> Result<(), ContractError>; -} \ No newline at end of file +} diff --git a/apps/contracts/vault/src/investment.rs b/apps/contracts/vault/src/investment.rs new file mode 100644 index 00000000..889a41e8 --- /dev/null +++ b/apps/contracts/vault/src/investment.rs @@ -0,0 +1,54 @@ +use soroban_sdk::{Address, Env, Map, Vec}; + +use crate::{ + models::Investment, + strategies::{get_strategy_asset, invest_in_strategy}, + utils::check_nonnegative_amount, + ContractError, +}; + +pub fn prepare_investment( + e: &Env, + investments: Vec, + idle_funds: Map, +) -> Result, ContractError> { + let mut total_investment_per_asset: Map = Map::new(e); + + for investment in investments.iter() { + let strategy_address = &investment.strategy; + let amount_to_invest = investment.amount; + check_nonnegative_amount(amount_to_invest.clone())?; + + // Find the corresponding asset for the strategy + let asset = get_strategy_asset(&e, strategy_address)?; + + // Track investment per asset + let current_investment = total_investment_per_asset + .get(asset.address.clone()) + .unwrap_or(0); + let updated_investment = current_investment + .checked_add(amount_to_invest) + .ok_or(ContractError::Overflow)?; + + total_investment_per_asset.set(asset.address.clone(), updated_investment); + + // Check if total investment exceeds idle funds + let idle_balance = idle_funds.get(asset.address.clone()).unwrap_or(0); + if updated_investment > idle_balance { + return Err(ContractError::NotEnoughIdleFunds); + } + } + + Ok(total_investment_per_asset) +} + +pub fn execute_investment(e: &Env, investments: Vec) -> Result<(), ContractError> { + for investment in investments.iter() { + let strategy_address = &investment.strategy; + let amount_to_invest = &investment.amount; + + invest_in_strategy(e, strategy_address, amount_to_invest)? + } + + Ok(()) +} diff --git a/apps/contracts/defindex/src/lib.rs b/apps/contracts/vault/src/lib.rs similarity index 87% rename from apps/contracts/defindex/src/lib.rs rename to apps/contracts/vault/src/lib.rs index 044b1470..e2ea5c59 100755 --- a/apps/contracts/defindex/src/lib.rs +++ b/apps/contracts/vault/src/lib.rs @@ -1,9 +1,8 @@ #![no_std] -use aggregator::{internal_swap_exact_tokens_for_tokens, internal_swap_tokens_for_exact_tokens}; -use fee::collect_fees; -use investment::{execute_investment, prepare_investment}; use soroban_sdk::{ - contract, contractimpl, panic_with_error, token::{TokenClient, TokenInterface}, Address, Env, Map, String, Vec + contract, contractimpl, panic_with_error, + token::{TokenClient, TokenInterface}, + Address, Env, Map, String, Vec, }; use soroban_token_sdk::metadata::TokenMetadata; @@ -24,16 +23,28 @@ mod token; mod utils; use access::{AccessControl, AccessControlTrait, RolesDataKey}; +use aggregator::{internal_swap_exact_tokens_for_tokens, internal_swap_tokens_for_exact_tokens}; +use fee::collect_fees; use funds::{fetch_current_idle_funds, fetch_current_invested_funds, fetch_total_managed_funds}; -use interface::{AdminInterfaceTrait, VaultTrait, VaultManagementTrait}; -use models::{ActionType, AssetAllocation, Instruction, Investment, OptionalSwapDetailsExactIn, OptionalSwapDetailsExactOut}; +use interface::{AdminInterfaceTrait, VaultManagementTrait, VaultTrait}; +use investment::{execute_investment, prepare_investment}; +use models::{ + ActionType, AssetAllocation, Instruction, Investment, OptionalSwapDetailsExactIn, + OptionalSwapDetailsExactOut, +}; use storage::{ - get_assets, set_asset, set_defindex_receiver, set_factory, set_last_fee_assesment, set_total_assets, set_vault_share + get_assets, set_asset, set_defindex_receiver, set_factory, set_last_fee_assesment, + set_total_assets, set_vault_share, +}; +use strategies::{ + get_asset_allocation_from_address, get_strategy_asset, get_strategy_client, + get_strategy_struct, invest_in_strategy, pause_strategy, unpause_strategy, + withdraw_from_strategy, }; -use strategies::{get_asset_allocation_from_address, get_strategy_asset, get_strategy_client, get_strategy_struct, invest_in_strategy, pause_strategy, unpause_strategy, withdraw_from_strategy}; -use token::{internal_mint, internal_burn, write_metadata, VaultToken}; +use token::{internal_burn, internal_mint, write_metadata, VaultToken}; use utils::{ - calculate_asset_amounts_for_dftokens, calculate_deposit_amounts_and_shares_to_mint, calculate_withdrawal_amounts, check_initialized, check_nonnegative_amount + calculate_asset_amounts_for_dftokens, calculate_deposit_amounts_and_shares_to_mint, + calculate_withdrawal_amounts, check_initialized, check_nonnegative_amount, }; pub use error::ContractError; @@ -47,7 +58,7 @@ impl VaultTrait for DeFindexVault { /// /// This function sets the roles for emergency manager, fee receiver, and manager. /// It also stores the list of assets to be managed by the vault, including strategies for each asset. - /// + /// /// # Arguments: /// * `e` - The environment. /// * `assets` - A vector of `AssetAllocation` structs representing the assets and their associated strategies. @@ -92,10 +103,11 @@ impl VaultTrait for DeFindexVault { // Store Assets Objects let total_assets = assets.len(); + // TODO Require minimum 1 asset set_total_assets(&e, total_assets as u32); for (i, asset) in assets.iter().enumerate() { // for every asset, we need to check that the list of strategyes indeed support this asset - + // TODO Fix, currently failing // for strategy in asset.strategies.iter() { // let strategy_client = DeFindexStrategyClient::new(&e, &strategy.address); @@ -119,7 +131,14 @@ impl VaultTrait for DeFindexVault { }, ); - events::emit_initialized_vault(&e, emergency_manager, fee_receiver, manager, defindex_receiver, assets); + events::emit_initialized_vault( + &e, + emergency_manager, + fee_receiver, + manager, + defindex_receiver, + assets, + ); Ok(()) } @@ -148,16 +167,20 @@ impl VaultTrait for DeFindexVault { from.require_auth(); // Set LastFeeAssessment if it is the first deposit - if VaultToken::total_supply(e.clone())==0{ + if VaultToken::total_supply(e.clone()) == 0 { set_last_fee_assesment(&e, &e.ledger().timestamp()); } // get assets let assets = get_assets(&e); - // assets lenght should be equal to amounts_desired and amounts_min length + // lenght should be equal tolength + let assets_length = assets.len(); + let asset = t_assets(&e); let assets_length = assets.len(); + + // assets lenght should be equal to amounts_desired and amounts_min length if assets_length != amounts_desired.len() || assets_length != amounts_min.len() { - panic_with_error!(&e, ContractError::WrongAmuntsLength); + panic_with_error!(&e, ContractError::WrongAmountsLength); } // for every amount desired, check non negative @@ -167,20 +190,23 @@ impl VaultTrait for DeFindexVault { // for amount min is not necesary to check if it is negative let (amounts, shares_to_mint) = if assets_length == 1 { - // If Total Assets == 1 - let shares = if VaultToken::total_supply(e.clone())==0{ + // If Total Assets == 1 + let shares = if VaultToken::total_supply(e.clone()) == 0 { // TODO In this case we might also want to mint a MINIMUM LIQUIDITY to be locked forever in the contract // this might be for security and practical reasons as well // shares will be equal to the amount desired to deposit, just for simplicity amounts_desired.get(0).unwrap() // here we have already check same lenght - } else{ + } else { // in this case we will mint a share proportional to the total managed funds let total_managed_funds = fetch_total_managed_funds(&e); - VaultToken::total_supply(e.clone()) * amounts_desired.get(0).unwrap() / total_managed_funds.get(assets.get(0).unwrap().address.clone()).unwrap() + VaultToken::total_supply(e.clone()) * amounts_desired.get(0).unwrap() + / + .get(assets.get(0).unwrap().address.clone()) + .unwrap() }; (amounts_desired, shares) } else { - // If Total Assets > 1 + // If Total Assets > 1 calculate_deposit_amounts_and_shares_to_mint( &e, &assets, @@ -227,59 +253,62 @@ impl VaultTrait for DeFindexVault { check_initialized(&e)?; check_nonnegative_amount(df_amount)?; from.require_auth(); - + // Check if the user has enough dfTokens let df_user_balance = VaultToken::balance(e.clone(), from.clone()); if df_user_balance < df_amount { return Err(ContractError::InsufficientBalance); } - + // Calculate the withdrawal amounts for each asset based on the dfToken amount let asset_amounts = calculate_asset_amounts_for_dftokens(&e, df_amount); // Burn the dfTokens after calculating the withdrawal amounts (so total supply is correct) internal_burn(e.clone(), from.clone(), df_amount); - + // Create a map to store the total amounts to transfer for each asset address let mut total_amounts_to_transfer: Map = Map::new(&e); - + // Get idle funds for each asset (Map) let idle_funds = fetch_current_idle_funds(&e); - + // Loop through each asset and handle the withdrawal for (asset_address, required_amount) in asset_amounts.iter() { // Check idle funds for this asset let idle_balance = idle_funds.get(asset_address.clone()).unwrap_or(0); let mut remaining_amount = required_amount; - + // Withdraw as much as possible from idle funds first if idle_balance > 0 { if idle_balance >= required_amount { // Idle funds cover the full amount total_amounts_to_transfer.set(asset_address.clone(), required_amount); - continue; // No need to withdraw from the strategy + continue; // No need to withdraw from the strategy } else { // Partial withdrawal from idle funds total_amounts_to_transfer.set(asset_address.clone(), idle_balance); - remaining_amount = required_amount - idle_balance; // Update remaining amount + remaining_amount = required_amount - idle_balance; // Update remaining amount } } - + // Find the corresponding asset address for this strategy let asset_allocation = get_asset_allocation_from_address(&e, asset_address.clone())?; - let withdrawal_amounts = calculate_withdrawal_amounts(&e, remaining_amount, asset_allocation); + let withdrawal_amounts = + calculate_withdrawal_amounts(&e, remaining_amount, asset_allocation); for (strategy_address, amount) in withdrawal_amounts.iter() { // TODO: What if the withdraw method exceeds the instructions limit? since im trying to ithdraw from all strategies of all assets... withdraw_from_strategy(&e, &strategy_address, &amount)?; - + // Update the total amounts to transfer map - let current_amount = total_amounts_to_transfer.get(strategy_address.clone()).unwrap_or(0); + let current_amount = total_amounts_to_transfer + .get(strategy_address.clone()) + .unwrap_or(0); total_amounts_to_transfer.set(asset_address.clone(), current_amount + amount); } } - + // Perform the transfers for the total amounts let mut amounts_withdrawn: Vec = Vec::new(&e); for (asset_address, total_amount) in total_amounts_to_transfer.iter() { @@ -295,7 +324,7 @@ impl VaultTrait for DeFindexVault { // fees assesment collect_fees(&e)?; - + Ok(amounts_withdrawn) } @@ -312,31 +341,38 @@ impl VaultTrait for DeFindexVault { /// /// # Returns: /// * `Result<(), ContractError>` - Ok if successful, otherwise returns a ContractError. - fn emergency_withdraw(e: Env, strategy_address: Address, caller: Address) -> Result<(), ContractError> { + fn emergency_withdraw( + e: Env, + strategy_address: Address, + caller: Address, + ) -> Result<(), ContractError> { check_initialized(&e)?; - + // Ensure the caller is the Manager or Emergency Manager let access_control = AccessControl::new(&e); - access_control.require_any_role(&[RolesDataKey::EmergencyManager, RolesDataKey::Manager], &caller); - + access_control.require_any_role( + &[RolesDataKey::EmergencyManager, RolesDataKey::Manager], + &caller, + ); + // Find the strategy and its associated asset let asset = get_strategy_asset(&e, &strategy_address)?; // This ensures that the vault has this strategy in its list of assets let strategy = get_strategy_struct(&strategy_address, &asset)?; - + // Withdraw all assets from the strategy let strategy_client = get_strategy_client(&e, strategy.address.clone()); let strategy_balance = strategy_client.balance(&e.current_contract_address()); - + if strategy_balance > 0 { strategy_client.withdraw(&strategy_balance, &e.current_contract_address()); //TODO: Should we check if the idle funds are corresponding to the strategy balance withdrawed? } - + // Pause the strategy pause_strategy(&e, strategy_address.clone())?; - + events::emit_emergency_withdraw_event(&e, caller, strategy_address, strategy_balance); Ok(()) } @@ -353,17 +389,24 @@ impl VaultTrait for DeFindexVault { /// /// # Returns: /// * `Result<(), ContractError>` - Ok if successful, otherwise returns a ContractError. - fn pause_strategy(e: Env, strategy_address: Address, caller: Address) -> Result<(), ContractError> { + fn pause_strategy( + e: Env, + strategy_address: Address, + caller: Address, + ) -> Result<(), ContractError> { // Ensure the caller is the Manager or Emergency Manager // TODO: Should check if the strategy has any amount invested on it, and return an error if it has, should we let the manager to pause a strategy with funds invested? let access_control = AccessControl::new(&e); - access_control.require_any_role(&[RolesDataKey::EmergencyManager, RolesDataKey::Manager], &caller); + access_control.require_any_role( + &[RolesDataKey::EmergencyManager, RolesDataKey::Manager], + &caller, + ); pause_strategy(&e, strategy_address.clone())?; events::emit_strategy_paused_event(&e, strategy_address, caller); Ok(()) } - + /// Unpauses a previously paused strategy. /// /// This function unpauses a strategy by setting its `paused` field to `false`, allowing it to be used @@ -376,10 +419,17 @@ impl VaultTrait for DeFindexVault { /// /// # Returns: /// * `Result<(), ContractError>` - Ok if successful, otherwise returns a ContractError. - fn unpause_strategy(e: Env, strategy_address: Address, caller: Address) -> Result<(), ContractError> { + fn unpause_strategy( + e: Env, + strategy_address: Address, + caller: Address, + ) -> Result<(), ContractError> { // Ensure the caller is the Manager or Emergency Manager let access_control = AccessControl::new(&e); - access_control.require_any_role(&[RolesDataKey::EmergencyManager, RolesDataKey::Manager], &caller); + access_control.require_any_role( + &[RolesDataKey::EmergencyManager, RolesDataKey::Manager], + &caller, + ); unpause_strategy(&e, strategy_address.clone())?; events::emit_strategy_unpaused_event(&e, strategy_address, caller); @@ -540,35 +590,35 @@ impl AdminInterfaceTrait for DeFindexVault { #[contractimpl] impl VaultManagementTrait for DeFindexVault { /// Invests the vault's idle funds into the specified strategies. - /// + /// /// # Arguments: /// * `e` - The environment. /// * `investment` - A vector of `Investment` structs representing the amount to invest in each strategy. /// * `caller` - The address of the caller. - /// + /// /// # Returns: /// * `Result<(), ContractError>` - Ok if successful, otherwise returns a ContractError. fn invest(e: Env, investments: Vec) -> Result<(), ContractError> { check_initialized(&e)?; - + let access_control = AccessControl::new(&e); access_control.require_role(&RolesDataKey::Manager); e.current_contract_address().require_auth(); - + // Get the current idle funds for all assets let idle_funds = fetch_current_idle_funds(&e); - + // Prepare investments based on current idle funds // This checks if the total investment exceeds the idle funds prepare_investment(&e, investments.clone(), idle_funds)?; - + // Now proceed with the actual investments if all checks passed execute_investment(&e, investments)?; // auto invest mockup // if auto_invest { // let idle_funds = fetch_current_idle_funds(&e); - + // // Prepare investments based on current ratios of invested funds // let investments = calculate_investments_based_on_ratios(&e); // prepare_investment(&e, investments.clone(), idle_funds)?; @@ -578,20 +628,20 @@ impl VaultManagementTrait for DeFindexVault { } /// Rebalances the vault by executing a series of instructions. - /// + /// /// # Arguments: /// * `e` - The environment. /// * `instructions` - A vector of `Instruction` structs representing actions (withdraw, invest, swap, zapper) to be taken. - /// + /// /// # Returns: /// * `Result<(), ContractError>` - Ok if successful, otherwise returns a ContractError. fn rebalance(e: Env, instructions: Vec) -> Result<(), ContractError> { check_initialized(&e)?; - + let access_control = AccessControl::new(&e); access_control.require_role(&RolesDataKey::Manager); e.current_contract_address().require_auth(); - + for instruction in instructions.iter() { match instruction.action { ActionType::Withdraw => match (&instruction.strategy, &instruction.amount) { @@ -609,13 +659,13 @@ impl VaultManagementTrait for DeFindexVault { ActionType::SwapExactIn => match &instruction.swap_details_exact_in { OptionalSwapDetailsExactIn::Some(swap_details) => { internal_swap_exact_tokens_for_tokens( - &e, - &swap_details.token_in, - &swap_details.token_out, - &swap_details.amount_in, - &swap_details.amount_out_min, - &swap_details.distribution, - &swap_details.deadline + &e, + &swap_details.token_in, + &swap_details.token_out, + &swap_details.amount_in, + &swap_details.amount_out_min, + &swap_details.distribution, + &swap_details.deadline, )?; } _ => return Err(ContractError::MissingInstructionData), @@ -623,23 +673,23 @@ impl VaultManagementTrait for DeFindexVault { ActionType::SwapExactOut => match &instruction.swap_details_exact_out { OptionalSwapDetailsExactOut::Some(swap_details) => { internal_swap_tokens_for_exact_tokens( - &e, - &swap_details.token_in, - &swap_details.token_out, - &swap_details.amount_out, - &swap_details.amount_in_max, - &swap_details.distribution, - &swap_details.deadline + &e, + &swap_details.token_in, + &swap_details.token_out, + &swap_details.amount_out, + &swap_details.amount_in_max, + &swap_details.distribution, + &swap_details.deadline, )?; } _ => return Err(ContractError::MissingInstructionData), }, ActionType::Zapper => { // TODO: Implement Zapper instructions - }, + } } } - + Ok(()) } -} \ No newline at end of file +} diff --git a/apps/contracts/defindex/src/models.rs b/apps/contracts/vault/src/models.rs similarity index 99% rename from apps/contracts/defindex/src/models.rs rename to apps/contracts/vault/src/models.rs index e5c182bb..042ec976 100644 --- a/apps/contracts/defindex/src/models.rs +++ b/apps/contracts/vault/src/models.rs @@ -87,4 +87,4 @@ pub enum OptionalSwapDetailsExactIn { pub enum OptionalSwapDetailsExactOut { Some(SwapDetailsExactOut), None, -} \ No newline at end of file +} diff --git a/apps/contracts/defindex/src/storage.rs b/apps/contracts/vault/src/storage.rs similarity index 81% rename from apps/contracts/defindex/src/storage.rs rename to apps/contracts/vault/src/storage.rs index 525e2b81..3eaf9860 100644 --- a/apps/contracts/defindex/src/storage.rs +++ b/apps/contracts/vault/src/storage.rs @@ -5,8 +5,8 @@ use crate::models::AssetAllocation; #[derive(Clone)] #[contracttype] enum DataKey { - AssetAllocation(u32), // AssetAllocation Addresse by index - TotalAssets, // Total number of tokens + AssetAllocation(u32), // AssetAllocation Addresse by index + TotalAssets, // Total number of tokens DeFindexReceiver, Factory, LastFeeAssessment, @@ -15,11 +15,16 @@ enum DataKey { // Assets Management pub fn set_asset(e: &Env, index: u32, asset: &AssetAllocation) { - e.storage().instance().set(&DataKey::AssetAllocation(index), asset); + e.storage() + .instance() + .set(&DataKey::AssetAllocation(index), asset); } pub fn get_asset(e: &Env, index: u32) -> AssetAllocation { - e.storage().instance().get(&DataKey::AssetAllocation(index)).unwrap() + e.storage() + .instance() + .get(&DataKey::AssetAllocation(index)) + .unwrap() } pub fn set_total_assets(e: &Env, n: u32) { @@ -55,16 +60,11 @@ pub fn get_defindex_receiver(e: &Env) -> Address { // DeFindex Factory pub fn set_factory(e: &Env, address: &Address) { - e.storage() - .instance() - .set(&DataKey::Factory, address); + e.storage().instance().set(&DataKey::Factory, address); } pub fn get_factory(e: &Env) -> Address { - e.storage() - .instance() - .get(&DataKey::Factory) - .unwrap() + e.storage().instance().get(&DataKey::Factory).unwrap() } // Last Fee Assesment @@ -89,8 +89,5 @@ pub fn set_vault_share(e: &Env, vault_share: &u32) { } pub fn get_vault_share(e: &Env) -> u32 { - e.storage() - .instance() - .get(&DataKey::VaultShare) - .unwrap() + e.storage().instance().get(&DataKey::VaultShare).unwrap() } diff --git a/apps/contracts/defindex/src/strategies.rs b/apps/contracts/vault/src/strategies.rs similarity index 80% rename from apps/contracts/defindex/src/strategies.rs rename to apps/contracts/vault/src/strategies.rs index a1019745..cde32336 100644 --- a/apps/contracts/defindex/src/strategies.rs +++ b/apps/contracts/vault/src/strategies.rs @@ -1,18 +1,29 @@ use defindex_strategy_core::DeFindexStrategyClient; -use soroban_sdk::{Env, Address}; +use soroban_sdk::{Address, Env}; -use crate::{models::{AssetAllocation, Strategy}, storage::{get_asset, get_assets, get_total_assets, set_asset}, ContractError}; +use crate::{ + models::{AssetAllocation, Strategy}, + storage::{get_asset, get_assets, get_total_assets, set_asset}, + ContractError, +}; pub fn get_strategy_client(e: &Env, address: Address) -> DeFindexStrategyClient { DeFindexStrategyClient::new(&e, &address) } /// Finds the asset corresponding to the given strategy address. -pub fn get_strategy_asset(e: &Env, strategy_address: &Address) -> Result { +pub fn get_strategy_asset( + e: &Env, + strategy_address: &Address, +) -> Result { let assets = get_assets(e); for asset in assets.iter() { - if asset.strategies.iter().any(|strategy| &strategy.address == strategy_address) { + if asset + .strategies + .iter() + .any(|strategy| &strategy.address == strategy_address) + { return Ok(asset); } } @@ -21,7 +32,10 @@ pub fn get_strategy_asset(e: &Env, strategy_address: &Address) -> Result Result { +pub fn get_asset_allocation_from_address( + e: &Env, + asset_address: Address, +) -> Result { let assets = get_assets(e); for asset in assets.iter() { @@ -34,7 +48,10 @@ pub fn get_asset_allocation_from_address(e: &Env, asset_address: Address) -> Res } /// Finds the strategy struct corresponding to the given strategy address within the given asset. -pub fn get_strategy_struct(strategy_address: &Address, asset: &AssetAllocation) -> Result { +pub fn get_strategy_struct( + strategy_address: &Address, + asset: &AssetAllocation, +) -> Result { asset .strategies .iter() @@ -104,20 +121,28 @@ pub fn unpause_strategy(e: &Env, strategy_address: Address) -> Result<(), Contra Err(ContractError::StrategyNotFound) } -pub fn withdraw_from_strategy(e: &Env, strategy_address: &Address, amount: &i128) -> Result<(), ContractError> { +pub fn withdraw_from_strategy( + e: &Env, + strategy_address: &Address, + amount: &i128, +) -> Result<(), ContractError> { let strategy_client = get_strategy_client(e, strategy_address.clone()); - + match strategy_client.try_withdraw(amount, &e.current_contract_address()) { Ok(Ok(_)) => Ok(()), Ok(Err(_)) | Err(_) => Err(ContractError::StrategyWithdrawError), } } -pub fn invest_in_strategy(e: &Env, strategy_address: &Address, amount: &i128) -> Result<(), ContractError> { +pub fn invest_in_strategy( + e: &Env, + strategy_address: &Address, + amount: &i128, +) -> Result<(), ContractError> { let strategy_client = get_strategy_client(&e, strategy_address.clone()); - + match strategy_client.try_deposit(amount, &e.current_contract_address()) { Ok(Ok(_)) => Ok(()), Ok(Err(_)) | Err(_) => Err(ContractError::StrategyInvestError), } -} \ No newline at end of file +} diff --git a/apps/contracts/defindex/src/test.rs b/apps/contracts/vault/src/test.rs similarity index 87% rename from apps/contracts/defindex/src/test.rs rename to apps/contracts/vault/src/test.rs index b23401cd..4a9960a9 100755 --- a/apps/contracts/defindex/src/test.rs +++ b/apps/contracts/vault/src/test.rs @@ -8,28 +8,30 @@ use std::vec; // DeFindex Hodl Strategy Contract pub mod hodl_strategy { - soroban_sdk::contractimport!(file = "../target/wasm32-unknown-unknown/release/hodl_strategy.optimized.wasm"); + soroban_sdk::contractimport!( + file = "../target/wasm32-unknown-unknown/release/hodl_strategy.optimized.wasm" + ); pub type HodlStrategyClient<'a> = Client<'a>; } -use hodl_strategy::{HodlStrategyClient}; +use hodl_strategy::HodlStrategyClient; -fn create_hodl_strategy<'a>(e: & Env, asset: & Address) -> HodlStrategyClient<'a> { +fn create_hodl_strategy<'a>(e: &Env, asset: &Address) -> HodlStrategyClient<'a> { let contract_address = &e.register_contract_wasm(None, hodl_strategy::WASM); - let hodl_strategy = HodlStrategyClient::new(e, contract_address); + let hodl_strategy = HodlStrategyClient::new(e, contract_address); hodl_strategy.initialize(&asset, &sorobanvec![&e]); hodl_strategy } -// DeFindex Vault Contract +// DeFindex Vault Contract pub mod defindex_vault { - soroban_sdk::contractimport!(file = "../target/wasm32-unknown-unknown/release/defindex_vault.optimized.wasm"); + soroban_sdk::contractimport!( + file = "../target/wasm32-unknown-unknown/release/defindex_vault.optimized.wasm" + ); pub type DeFindexVaultClient<'a> = Client<'a>; } use defindex_vault::{DeFindexVaultClient, Strategy}; -fn create_defindex_vault<'a>( - e: & Env -) -> DeFindexVaultClient<'a> { +fn create_defindex_vault<'a>(e: &Env) -> DeFindexVaultClient<'a> { let address = &e.register_contract_wasm(None, defindex_vault::WASM); let client = DeFindexVaultClient::new(e, address); client @@ -52,7 +54,11 @@ fn create_defindex_vault<'a>( // Create Test Token pub(crate) fn create_token_contract<'a>(e: &Env, admin: &Address) -> SorobanTokenClient<'a> { - SorobanTokenClient::new(e, &e.register_stellar_asset_contract_v2(admin.clone()).address()) + SorobanTokenClient::new( + e, + &e.register_stellar_asset_contract_v2(admin.clone()) + .address(), + ) } pub(crate) fn get_token_admin_client<'a>( @@ -142,8 +148,8 @@ impl<'a> DeFindexVaultTest<'a> { } mod admin; -mod initialize; -mod withdraw; mod deposit; mod emergency_withdraw; -mod rebalance; \ No newline at end of file +mod initialize; +mod rebalance; +mod withdraw; diff --git a/apps/contracts/defindex/src/test/admin.rs b/apps/contracts/vault/src/test/admin.rs similarity index 99% rename from apps/contracts/defindex/src/test/admin.rs rename to apps/contracts/vault/src/test/admin.rs index 384694be..38fc84ef 100644 --- a/apps/contracts/defindex/src/test/admin.rs +++ b/apps/contracts/vault/src/test/admin.rs @@ -1,5 +1,6 @@ use soroban_sdk::{ - testutils::{AuthorizedFunction, AuthorizedInvocation, MockAuth, MockAuthInvoke}, vec as sorobanvec, IntoVal, String, Symbol, Vec + testutils::{AuthorizedFunction, AuthorizedInvocation, MockAuth, MockAuthInvoke}, + vec as sorobanvec, IntoVal, String, Symbol, Vec, }; use crate::test::{create_strategy_params, defindex_vault::AssetAllocation, DeFindexVaultTest}; diff --git a/apps/contracts/defindex/src/test/deposit.rs b/apps/contracts/vault/src/test/deposit.rs similarity index 96% rename from apps/contracts/defindex/src/test/deposit.rs rename to apps/contracts/vault/src/test/deposit.rs index b765e1d3..ccccf851 100644 --- a/apps/contracts/defindex/src/test/deposit.rs +++ b/apps/contracts/vault/src/test/deposit.rs @@ -1,15 +1,14 @@ use soroban_sdk::{vec as sorobanvec, String, Vec}; -use crate::test::{create_strategy_params, DeFindexVaultTest}; use crate::test::defindex_vault::{AssetAllocation, ContractError}; +use crate::test::{create_strategy_params, DeFindexVaultTest}; #[test] fn deposit_amounts_desired_wrong_length() { - let test = DeFindexVaultTest::setup(); test.env.mock_all_auths(); let strategy_params = create_strategy_params(&test); - + // initialize with 2 assets let assets: Vec = sorobanvec![ &test.env, @@ -35,26 +34,23 @@ fn deposit_amounts_desired_wrong_length() { &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]); + &sorobanvec![&test.env, amount, amount], + &users[0], + ); assert_eq!(response, Err(Ok(ContractError::WrongAmuntsLength))); - } - #[test] fn deposit_amounts_min_wrong_length() { todo!(); } - #[test] fn deposit_amounts_desired_negative() { todo!(); @@ -65,7 +61,6 @@ fn deposit_one_asset() { todo!(); } - #[test] fn deposit_several_assets() { todo!(); @@ -97,9 +92,9 @@ fn deposit_several_assets() { // &test.defindex_receiver, // ); // let amount = 1000i128; - + // 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); @@ -114,10 +109,10 @@ fn deposit_several_assets() { // assert_eq!(df_balance, amount); // test.defindex_contract.withdraw(&df_balance, &users[0]); - + // let df_balance = test.defindex_contract.balance(&users[0]); // assert_eq!(df_balance, 0i128); // let user_balance = test.token0.balance(&users[0]); // assert_eq!(user_balance, amount); -// } \ No newline at end of file +// } diff --git a/apps/contracts/defindex/src/test/emergency_withdraw.rs b/apps/contracts/vault/src/test/emergency_withdraw.rs similarity index 79% rename from apps/contracts/defindex/src/test/emergency_withdraw.rs rename to apps/contracts/vault/src/test/emergency_withdraw.rs index 1d2757f6..9df5992a 100644 --- a/apps/contracts/defindex/src/test/emergency_withdraw.rs +++ b/apps/contracts/vault/src/test/emergency_withdraw.rs @@ -1,6 +1,10 @@ use soroban_sdk::{vec as sorobanvec, String, Vec}; -use crate::test::{create_strategy_params, defindex_vault::{AssetAllocation, Investment}, DeFindexVaultTest}; +use crate::test::{ + create_strategy_params, + defindex_vault::{AssetAllocation, Investment}, + DeFindexVaultTest, +}; #[test] fn test_emergency_withdraw_success() { @@ -27,18 +31,22 @@ fn test_emergency_withdraw_success() { &String::from_str(&test.env, "DFT"), ); let amount = 1000i128; - + 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); - + // Deposit - test.defindex_contract.deposit(&sorobanvec![&test.env, amount], &sorobanvec![&test.env, amount], &users[0]); + test.defindex_contract.deposit( + &sorobanvec![&test.env, amount], + &sorobanvec![&test.env, amount], + &users[0], + ); let df_balance = test.defindex_contract.balance(&users[0]); assert_eq!(df_balance, amount); @@ -56,19 +64,26 @@ fn test_emergency_withdraw_success() { } ]; test.defindex_contract.invest(&investments); - + // Balance of the token0 on the vault should be 0 let vault_balance_of_token = test.token0.balance(&test.defindex_contract.address); assert_eq!(vault_balance_of_token, 0); // Balance of the strategy should be `amount` - let strategy_balance = test.strategy_client.balance(&test.defindex_contract.address); + let strategy_balance = test + .strategy_client + .balance(&test.defindex_contract.address); assert_eq!(strategy_balance, amount); - test.defindex_contract.emergency_withdraw(&strategy_params.first().unwrap().address, &test.emergency_manager); + test.defindex_contract.emergency_withdraw( + &strategy_params.first().unwrap().address, + &test.emergency_manager, + ); // Balance of the strategy should be 0 - let strategy_balance = test.strategy_client.balance(&test.defindex_contract.address); + let strategy_balance = test + .strategy_client + .balance(&test.defindex_contract.address); assert_eq!(strategy_balance, 0); // Balance of the token0 on the vault should be `amount` @@ -78,4 +93,4 @@ fn test_emergency_withdraw_success() { // check if strategy is paused let asset = test.defindex_contract.get_assets().first().unwrap(); assert_eq!(asset.strategies.first().unwrap().paused, true); -} \ No newline at end of file +} diff --git a/apps/contracts/defindex/src/test/initialize.rs b/apps/contracts/vault/src/test/initialize.rs similarity index 97% rename from apps/contracts/defindex/src/test/initialize.rs rename to apps/contracts/vault/src/test/initialize.rs index 68075239..621c63d8 100644 --- a/apps/contracts/defindex/src/test/initialize.rs +++ b/apps/contracts/vault/src/test/initialize.rs @@ -1,6 +1,10 @@ use soroban_sdk::{vec as sorobanvec, String, Vec}; -use crate::test::{create_strategy_params, defindex_vault::{AssetAllocation, ContractError}, DeFindexVaultTest}; +use crate::test::{ + create_strategy_params, + defindex_vault::{AssetAllocation, ContractError}, + DeFindexVaultTest, +}; #[test] fn test_initialize_and_get_roles() { diff --git a/apps/contracts/defindex/src/test/rebalance.rs b/apps/contracts/vault/src/test/rebalance.rs similarity index 82% rename from apps/contracts/defindex/src/test/rebalance.rs rename to apps/contracts/vault/src/test/rebalance.rs index 49e7bbd3..0743b471 100644 --- a/apps/contracts/defindex/src/test/rebalance.rs +++ b/apps/contracts/vault/src/test/rebalance.rs @@ -1,6 +1,13 @@ use soroban_sdk::{vec as sorobanvec, String, Vec}; -use crate::test::{create_strategy_params, defindex_vault::{ActionType, AssetAllocation, Instruction, Investment, OptionalSwapDetailsExactIn, OptionalSwapDetailsExactOut}, DeFindexVaultTest}; +use crate::test::{ + create_strategy_params, + defindex_vault::{ + ActionType, AssetAllocation, Instruction, Investment, OptionalSwapDetailsExactIn, + OptionalSwapDetailsExactOut, + }, + DeFindexVaultTest, +}; #[test] fn rebalance() { @@ -27,9 +34,9 @@ fn rebalance() { &String::from_str(&test.env, "DFT"), ); let amount = 1000i128; - + 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); @@ -37,19 +44,23 @@ fn rebalance() { 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]); + test.defindex_contract.deposit( + &sorobanvec![&test.env, amount], + &sorobanvec![&test.env, amount], + &users[0], + ); let df_balance = test.defindex_contract.balance(&users[0]); assert_eq!(df_balance, amount); let investments = sorobanvec![ - &test.env, + &test.env, Investment { - amount: amount, - strategy: test.strategy_client.address.clone() + amount: amount, + strategy: test.strategy_client.address.clone() } ]; - + test.defindex_contract.invest(&investments); let vault_balance = test.token0.balance(&test.defindex_contract.address); @@ -60,9 +71,8 @@ fn rebalance() { let instruction_amount_0 = 200i128; let instruction_amount_1 = 100i128; - let instructions = sorobanvec![ - &test.env, + &test.env, Instruction { action: ActionType::Withdraw, strategy: Some(test.strategy_client.address.clone()), @@ -83,5 +93,4 @@ fn rebalance() { let vault_balance = test.token0.balance(&test.defindex_contract.address); assert_eq!(vault_balance, instruction_amount_1); - -} \ No newline at end of file +} diff --git a/apps/contracts/defindex/src/test/withdraw.rs b/apps/contracts/vault/src/test/withdraw.rs similarity index 84% rename from apps/contracts/defindex/src/test/withdraw.rs rename to apps/contracts/vault/src/test/withdraw.rs index 2079a843..a135d468 100644 --- a/apps/contracts/defindex/src/test/withdraw.rs +++ b/apps/contracts/vault/src/test/withdraw.rs @@ -1,7 +1,11 @@ use soroban_sdk::{vec as sorobanvec, String, Vec}; -use crate::test::{create_strategy_params, defindex_vault::{AssetAllocation, Investment}, DeFindexVaultTest}; use super::hodl_strategy::StrategyError; +use crate::test::{ + create_strategy_params, + defindex_vault::{AssetAllocation, Investment}, + DeFindexVaultTest, +}; #[test] fn test_withdraw_from_idle_success() { @@ -28,9 +32,9 @@ fn test_withdraw_from_idle_success() { &String::from_str(&test.env, "DFT"), ); let amount = 1234567890i128; - + 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); @@ -41,14 +45,18 @@ fn test_withdraw_from_idle_success() { // Deposit let amount_to_deposit = 567890i128; - test.defindex_contract.deposit(&sorobanvec![&test.env, amount_to_deposit], &sorobanvec![&test.env, amount_to_deposit], &users[0]); + test.defindex_contract.deposit( + &sorobanvec![&test.env, amount_to_deposit], + &sorobanvec![&test.env, amount_to_deposit], + &users[0], + ); // Check Balances after deposit - + // Token balance of user let user_balance = test.token0.balance(&users[0]); assert_eq!(user_balance, amount - amount_to_deposit); - + // Token balance of vault should be amount_to_deposit // Because balances are still in indle, balances are not in strategy, but in idle @@ -65,13 +73,17 @@ fn test_withdraw_from_idle_success() { // user decides to withdraw a portion of deposited amount let amount_to_withdraw = 123456i128; - test.defindex_contract.withdraw(&amount_to_withdraw, &users[0]); - + test.defindex_contract + .withdraw(&amount_to_withdraw, &users[0]); + // Check Balances after withdraw // Token balance of user should be amount - amount_to_deposit + amount_to_withdraw let user_balance = test.token0.balance(&users[0]); - assert_eq!(user_balance, amount - amount_to_deposit + amount_to_withdraw); + assert_eq!( + user_balance, + amount - amount_to_deposit + amount_to_withdraw + ); // Token balance of vault should be amount_to_deposit - amount_to_withdraw let vault_balance = test.token0.balance(&test.defindex_contract.address); @@ -85,17 +97,19 @@ fn test_withdraw_from_idle_success() { let df_balance = test.defindex_contract.balance(&users[0]); assert_eq!(df_balance, amount_to_deposit - amount_to_withdraw); - // user tries to withdraw more than deposited amount let amount_to_withdraw_more = amount_to_deposit + 1; - let result = test.defindex_contract.try_withdraw(&amount_to_withdraw_more, &users[0]); + let result = test + .defindex_contract + .try_withdraw(&amount_to_withdraw_more, &users[0]); // just check if is error assert_eq!(result.is_err(), true); - + // TODO test corresponding error // withdraw remaining balance - test.defindex_contract.withdraw(&(amount_to_deposit - amount_to_withdraw), &users[0]); + test.defindex_contract + .withdraw(&(amount_to_deposit - amount_to_withdraw), &users[0]); // // result is err @@ -135,9 +149,9 @@ fn test_withdraw_from_strategy_success() { &String::from_str(&test.env, "DFT"), ); let amount = 1000i128; - + 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); @@ -146,29 +160,33 @@ fn test_withdraw_from_strategy_success() { 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]); + test.defindex_contract.deposit( + &sorobanvec![&test.env, amount], + &sorobanvec![&test.env, amount], + &users[0], + ); let df_balance = test.defindex_contract.balance(&users[0]); assert_eq!(df_balance, amount); let investments = sorobanvec![ - &test.env, + &test.env, Investment { - amount: amount, - strategy: test.strategy_client.address.clone() - }]; - - + amount: amount, + strategy: test.strategy_client.address.clone() + } + ]; + test.defindex_contract.invest(&investments); let vault_balance = test.token0.balance(&test.defindex_contract.address); assert_eq!(vault_balance, 0); test.defindex_contract.withdraw(&df_balance, &users[0]); - + let df_balance = test.defindex_contract.balance(&users[0]); assert_eq!(df_balance, 0i128); let user_balance = test.token0.balance(&users[0]); assert_eq!(user_balance, amount); -} \ No newline at end of file +} diff --git a/apps/contracts/defindex/src/token/allowance.rs b/apps/contracts/vault/src/token/allowance.rs similarity index 100% rename from apps/contracts/defindex/src/token/allowance.rs rename to apps/contracts/vault/src/token/allowance.rs diff --git a/apps/contracts/defindex/src/token/balance.rs b/apps/contracts/vault/src/token/balance.rs similarity index 100% rename from apps/contracts/defindex/src/token/balance.rs rename to apps/contracts/vault/src/token/balance.rs diff --git a/apps/contracts/defindex/src/token/contract.rs b/apps/contracts/vault/src/token/contract.rs similarity index 100% rename from apps/contracts/defindex/src/token/contract.rs rename to apps/contracts/vault/src/token/contract.rs diff --git a/apps/contracts/defindex/src/token/metadata.rs b/apps/contracts/vault/src/token/metadata.rs similarity index 100% rename from apps/contracts/defindex/src/token/metadata.rs rename to apps/contracts/vault/src/token/metadata.rs diff --git a/apps/contracts/defindex/src/token/mod.rs b/apps/contracts/vault/src/token/mod.rs similarity index 100% rename from apps/contracts/defindex/src/token/mod.rs rename to apps/contracts/vault/src/token/mod.rs diff --git a/apps/contracts/defindex/src/token/storage_types.rs b/apps/contracts/vault/src/token/storage_types.rs similarity index 100% rename from apps/contracts/defindex/src/token/storage_types.rs rename to apps/contracts/vault/src/token/storage_types.rs diff --git a/apps/contracts/defindex/src/token/total_supply.rs b/apps/contracts/vault/src/token/total_supply.rs similarity index 100% rename from apps/contracts/defindex/src/token/total_supply.rs rename to apps/contracts/vault/src/token/total_supply.rs diff --git a/apps/contracts/defindex/src/utils.rs b/apps/contracts/vault/src/utils.rs similarity index 93% rename from apps/contracts/defindex/src/utils.rs rename to apps/contracts/vault/src/utils.rs index 82bb5900..0d93e27d 100644 --- a/apps/contracts/defindex/src/utils.rs +++ b/apps/contracts/vault/src/utils.rs @@ -1,8 +1,14 @@ use soroban_sdk::{panic_with_error, Address, Env, Map, Vec}; use crate::{ - access::{AccessControl, AccessControlTrait, RolesDataKey}, funds::{fetch_invested_funds_for_asset, fetch_invested_funds_for_strategy, fetch_total_managed_funds}, models::AssetAllocation, token::VaultToken, ContractError - + access::{AccessControl, AccessControlTrait, RolesDataKey}, + funds::{ + fetch_invested_funds_for_asset, fetch_invested_funds_for_strategy, + fetch_total_managed_funds, + }, + models::AssetAllocation, + token::VaultToken, + ContractError, }; pub const DAY_IN_LEDGERS: u32 = 17280; @@ -51,7 +57,8 @@ pub fn calculate_withdrawal_amounts( let strategy_invested_funds = fetch_invested_funds_for_strategy(e, &strategy.address); - let strategy_share_of_withdrawal = (amount * strategy_invested_funds) / total_invested_in_strategies; + let strategy_share_of_withdrawal = + (amount * strategy_invested_funds) / total_invested_in_strategies; withdrawal_amounts.set(strategy.address.clone(), strategy_share_of_withdrawal); } @@ -131,7 +138,7 @@ pub fn calculate_optimal_amounts_and_shares_with_enforced_asset( // this might be the first deposit... in this case, the ratio will be enforced by the first depositor // TODO: might happen that the reserve_target is zero because everything is in one asset!? // in this case we ned to check the ratio - // TODO VERY DANGEROUS. + // TODO VERY DANGEROUS. } let amount_desired_target = amounts_desired.get(*i).unwrap(); // i128 @@ -148,22 +155,23 @@ pub fn calculate_optimal_amounts_and_shares_with_enforced_asset( } } //TODO: calculate the shares to mint = total_supply * amount_desired_target / reserve_target - let shares_to_mint = VaultToken::total_supply(e.clone()) * amount_desired_target / reserve_target; + let shares_to_mint = + VaultToken::total_supply(e.clone()) * amount_desired_target / reserve_target; (optimal_amounts, shares_to_mint) } /// Calculates the optimal amounts to deposit for a set of assets, along with the shares to mint. /// This function iterates over a list of assets and checks if the desired deposit amounts /// match the optimal deposit strategy, based on current managed funds and asset ratios. -/// +/// /// If the desired amount for a given asset cannot be achieved due to constraints (e.g., it's below the minimum amount), /// the function attempts to find an optimal solution by adjusting the amounts of subsequent assets. -/// +/// /// # Arguments /// * `e` - The current environment. /// * `assets` - A vector of assets for which deposits are being calculated. /// * `amounts_desired` - A vector of desired amounts for each asset. /// * `amounts_min` - A vector of minimum amounts for each asset, below which deposits are not allowed. -/// +/// /// # Returns /// A tuple containing: /// * A vector of optimal amounts to deposit for each asset. @@ -183,7 +191,7 @@ pub fn calculate_deposit_amounts_and_shares_to_mint( amounts_min: &Vec, ) -> (Vec, i128) { // Retrieve the total managed funds for each asset as a Map. - let total_managed_funds = fetch_total_managed_funds(e); + let total_managed_funds = fetch_total_managed_funds(e); // Iterate over each asset in the assets vector. for i in 0..assets.len() { @@ -232,4 +240,4 @@ pub fn calculate_deposit_amounts_and_shares_to_mint( // If no solution was found after iterating through all assets, throw an error. panic!("didn't find optimal amounts"); -} \ No newline at end of file +} diff --git a/scf-tracker.md b/scf-tracker.md index f27416b6..68079a7b 100644 --- a/scf-tracker.md +++ b/scf-tracker.md @@ -19,7 +19,7 @@ Contract will be available on [GitHub](https://github.com/paltalabs). Code is reviewed, tested, and successfully passes security audits. - **Result:** - - βœ… Code available on [GitHub](https://github.com/paltalabs/defindex/tree/main/apps/contracts/defindex) + - βœ… Code available on [GitHub](https://github.com/paltalabs/defindex/tree/main/apps/contracts/vault) - πŸ› οΈ Security Audits --- From b0423440052cb0a6df9a11ae1a849b4561ece5e8 Mon Sep 17 00:00:00 2001 From: esteblock Date: Tue, 5 Nov 2024 16:58:18 -0300 Subject: [PATCH 17/19] use new fee nomenclature --- apps/contracts/vault/src/access.rs | 10 ++-- apps/contracts/vault/src/error.rs | 2 +- apps/contracts/vault/src/events.rs | 18 +++--- apps/contracts/vault/src/fee.rs | 8 +-- apps/contracts/vault/src/interface.rs | 14 ++--- apps/contracts/vault/src/lib.rs | 58 ++++++++++--------- apps/contracts/vault/src/storage.rs | 14 ++--- apps/contracts/vault/src/test.rs | 12 ++-- apps/contracts/vault/src/test/admin.rs | 38 ++++++------ apps/contracts/vault/src/test/deposit.rs | 10 ++-- .../vault/src/test/emergency_withdraw.rs | 4 +- apps/contracts/vault/src/test/initialize.rs | 14 ++--- apps/contracts/vault/src/test/rebalance.rs | 4 +- apps/contracts/vault/src/test/withdraw.rs | 8 +-- .../02-contracts/01-vault-contract.md | 2 +- 15 files changed, 110 insertions(+), 106 deletions(-) diff --git a/apps/contracts/vault/src/access.rs b/apps/contracts/vault/src/access.rs index c601242a..dba6db02 100644 --- a/apps/contracts/vault/src/access.rs +++ b/apps/contracts/vault/src/access.rs @@ -6,7 +6,7 @@ use soroban_sdk::{contracttype, panic_with_error, Address, Env}; #[contracttype] pub enum RolesDataKey { EmergencyManager, // Role: Emergency Manager - FeeReceiver, // Role: Fee Receiver + VaultFeeReceiver, // Role: Fee Receiver Manager, // Role: Manager } @@ -82,13 +82,13 @@ impl AccessControlTrait for AccessControl { // Role-specific setters and getters impl AccessControl { - pub fn set_fee_receiver(&self, caller: &Address, fee_receiver: &Address) { - self.require_any_role(&[RolesDataKey::Manager, RolesDataKey::FeeReceiver], caller); - self.set_role(&RolesDataKey::FeeReceiver, fee_receiver); + pub fn set_fee_receiver(&self, caller: &Address, vault_fee_receiver: &Address) { + self.require_any_role(&[RolesDataKey::Manager, RolesDataKey::VaultFeeReceiver], caller); + self.set_role(&RolesDataKey::VaultFeeReceiver, vault_fee_receiver); } pub fn get_fee_receiver(&self) -> Result { - self.check_role(&RolesDataKey::FeeReceiver) + self.check_role(&RolesDataKey::VaultFeeReceiver) } pub fn set_manager(&self, manager: &Address) { diff --git a/apps/contracts/vault/src/error.rs b/apps/contracts/vault/src/error.rs index 7a2fec42..66874708 100644 --- a/apps/contracts/vault/src/error.rs +++ b/apps/contracts/vault/src/error.rs @@ -13,7 +13,7 @@ pub enum ContractError { // Validation Errors (11x) NegativeNotAllowed = 110, InsufficientBalance = 111, - WrongAmuntsLength = 112, + WrongAmountsLength = 112, NotEnoughIdleFunds = 113, InsufficientManagedFunds = 114, MissingInstructionData = 115, diff --git a/apps/contracts/vault/src/events.rs b/apps/contracts/vault/src/events.rs index 880b6a45..c7d46eb7 100644 --- a/apps/contracts/vault/src/events.rs +++ b/apps/contracts/vault/src/events.rs @@ -7,9 +7,9 @@ use soroban_sdk::{contracttype, symbol_short, Address, Env, Vec}; #[derive(Clone, Debug, Eq, PartialEq)] pub struct InitializedVaultEvent { pub emergency_manager: Address, - pub fee_receiver: Address, + pub vault_fee_receiver: Address, pub manager: Address, - pub defindex_receiver: Address, + pub defindex_protocol_receiver: Address, pub assets: Vec, } @@ -17,16 +17,16 @@ pub struct InitializedVaultEvent { pub(crate) fn emit_initialized_vault( e: &Env, emergency_manager: Address, - fee_receiver: Address, + vault_fee_receiver: Address, manager: Address, - defindex_receiver: Address, + defindex_protocol_receiver: Address, assets: Vec, ) { let event = InitializedVaultEvent { emergency_manager, - fee_receiver, + vault_fee_receiver, manager, - defindex_receiver, + defindex_protocol_receiver, assets, }; @@ -205,7 +205,7 @@ pub(crate) fn emit_emergency_manager_changed_event(e: &Env, new_emergency_manage #[contracttype] #[derive(Clone, Debug, Eq, PartialEq)] pub struct FeesMintedEvent { - pub defindex_receiver: Address, + pub defindex_protocol_receiver: Address, pub defindex_shares: i128, pub vault_receiver: Address, pub vault_shares: i128, @@ -214,13 +214,13 @@ pub struct FeesMintedEvent { /// Publishes an `EmergencyManagerChangedEvent` to the event stream. pub(crate) fn emit_fees_minted_event( e: &Env, - defindex_receiver: Address, + defindex_protocol_receiver: Address, defindex_shares: i128, vault_receiver: Address, vault_shares: i128, ) { let event = FeesMintedEvent { - defindex_receiver, + defindex_protocol_receiver, defindex_shares, vault_receiver, vault_shares, diff --git a/apps/contracts/vault/src/fee.rs b/apps/contracts/vault/src/fee.rs index 1cd57a06..7b0698d6 100644 --- a/apps/contracts/vault/src/fee.rs +++ b/apps/contracts/vault/src/fee.rs @@ -6,7 +6,7 @@ use crate::{ events, funds::fetch_total_managed_funds, storage::{ - get_defindex_receiver, get_factory, get_last_fee_assesment, get_vault_share, + get_defindex_protocol_fee_receiver, get_factory, get_last_fee_assesment, get_vault_share, set_last_fee_assesment, }, token::internal_mint, @@ -73,7 +73,7 @@ fn mint_fees(e: &Env, total_fees: i128) -> Result<(), ContractError> { let access_control = AccessControl::new(&e); let vault_fee_receiver = access_control.get_fee_receiver()?; - let defindex_receiver = get_defindex_receiver(e); + let defindex_protocol_receiver = get_defindex_protocol_fee_receiver(e); let vault_share_bps = get_vault_share(e); @@ -82,11 +82,11 @@ fn mint_fees(e: &Env, total_fees: i128) -> Result<(), ContractError> { let defindex_shares = total_fees - vault_shares; internal_mint(e.clone(), vault_fee_receiver.clone(), vault_shares); - internal_mint(e.clone(), defindex_receiver.clone(), defindex_shares); + internal_mint(e.clone(), defindex_protocol_receiver.clone(), defindex_shares); events::emit_fees_minted_event( e, - defindex_receiver, + defindex_protocol_receiver, defindex_shares, vault_fee_receiver, vault_shares, diff --git a/apps/contracts/vault/src/interface.rs b/apps/contracts/vault/src/interface.rs index dafdc998..28bc7104 100644 --- a/apps/contracts/vault/src/interface.rs +++ b/apps/contracts/vault/src/interface.rs @@ -16,9 +16,9 @@ pub trait VaultTrait { /// * `assets` - A vector of `AssetAllocation` structs representing the assets and their associated strategies. /// * `manager` - The address responsible for managing the vault. /// * `emergency_manager` - The address with emergency control over the vault. - /// * `fee_receiver` - The address that will receive fees from the vault. - /// * `vault_share` - The percentage of the vault's fees that will be sent to the DeFindex receiver. in BPS. - /// * `defindex_receiver` - The address that will receive fees for DeFindex from the vault. + /// * `vault_fee_receiver` - The address that will receive fees from the vault. + /// * `vault_fee` - The percentage of the vault's fees that will be sent to the DeFindex receiver. in BPS. + /// * `defindex_protocol_receiver` - The address that will receive fees for DeFindex from the vault. /// * `factory` - The address of the factory that deployed the vault. /// /// # Returns: @@ -28,9 +28,9 @@ pub trait VaultTrait { assets: Vec, manager: Address, emergency_manager: Address, - fee_receiver: Address, - vault_share: u32, - defindex_receiver: Address, + vault_fee_receiver: Address, + vault_fee: u32, + defindex_protocol_receiver: Address, factory: Address, vault_name: String, vault_symbol: String, @@ -185,7 +185,7 @@ pub trait AdminInterfaceTrait { /// # Arguments: /// * `e` - The environment. /// * `caller` - The address initiating the change (must be the manager or emergency manager). - /// * `fee_receiver` - The new fee receiver address. + /// * `vault_fee_receiver` - The new fee receiver address. /// /// # Returns: /// * `()` - No return value. diff --git a/apps/contracts/vault/src/lib.rs b/apps/contracts/vault/src/lib.rs index e2ea5c59..19f81b99 100755 --- a/apps/contracts/vault/src/lib.rs +++ b/apps/contracts/vault/src/lib.rs @@ -33,7 +33,7 @@ use models::{ OptionalSwapDetailsExactOut, }; use storage::{ - get_assets, set_asset, set_defindex_receiver, set_factory, set_last_fee_assesment, + get_assets, set_asset, set_defindex_protocol_fee_receiver, set_factory, set_last_fee_assesment, set_total_assets, set_vault_share, }; use strategies::{ @@ -53,32 +53,39 @@ pub use error::ContractError; pub struct DeFindexVault; #[contractimpl] -impl VaultTrait for DeFindexVault { +impl VaultTrait for DeFindexVault { /// Initializes the DeFindex Vault contract with the required parameters. /// - /// This function sets the roles for emergency manager, fee receiver, and manager. + /// This function sets the roles for manager, emergency manager, vault fee receiver, and manager. /// It also stores the list of assets to be managed by the vault, including strategies for each asset. /// - /// # Arguments: - /// * `e` - The environment. - /// * `assets` - A vector of `AssetAllocation` structs representing the assets and their associated strategies. - /// * `manager` - The address responsible for managing the vault. - /// * `emergency_manager` - The address with emergency control over the vault. - /// * `fee_receiver` - The address that will receive fees from the vault. - /// * `vault_share` - The percentage of the vault's fees that will be sent to the DeFindex receiver. in BPS. - /// * `defindex_receiver` - The address that will receive fees for DeFindex from the vault. - /// * `factory` - The address of the factory that deployed the vault. + /// # Arguments + /// - `assets`: List of asset allocations for the vault, including strategies associated with each asset. + /// - `manager`: Primary vault manager with permissions for vault control. + /// - `emergency_manager`: Address with emergency access for emergency control over the vault. + /// - `vault_fee_receiver`: Address designated to receive the vault fee receiver's portion of management fees. + /// - `vault_fee`: Vault-specific fee percentage in basis points (typically set at 0-2% APR). + /// - `defindex_protocol_receiver`: Address receiving DeFindex’s protocol-wide fee in basis points (0.5% APR). + /// - `factory`: Factory contract address for deployment linkage. + /// - `vault_name`: Name of the vault token to be displayed in metadata. + /// - `vault_symbol`: Symbol representing the vault’s token. + /// + /// # Returns + /// - `Result<(), ContractError>`: Returns `Ok(())` if initialization succeeds, or a `ContractError` if + /// any setup fails (e.g., strategy mismatch with asset). + /// + /// # Errors + /// - `ContractError::AlreadyInitialized`: If the vault has already been initialized. + /// - `ContractError::StrategyDoesNotSupportAsset`: If a strategy within an asset does not support the asset’s contract. /// - /// # Returns: - /// * `Result<(), ContractError>` - Ok if successful, otherwise returns a ContractError. fn initialize( e: Env, assets: Vec, manager: Address, emergency_manager: Address, - fee_receiver: Address, - vault_share: u32, - defindex_receiver: Address, + vault_fee_receiver: Address, + vault_fee: u32, + defindex_protocol_receiver: Address, factory: Address, vault_name: String, vault_symbol: String, @@ -89,14 +96,14 @@ impl VaultTrait for DeFindexVault { } access_control.set_role(&RolesDataKey::EmergencyManager, &emergency_manager); - access_control.set_role(&RolesDataKey::FeeReceiver, &fee_receiver); + access_control.set_role(&RolesDataKey::VaultFeeReceiver, &vault_fee_receiver); access_control.set_role(&RolesDataKey::Manager, &manager); // Set Vault Share (in basis points) - set_vault_share(&e, &vault_share); + set_vault_share(&e, &vault_fee); // Set Paltalabs Fee Receiver - set_defindex_receiver(&e, &defindex_receiver); + set_defindex_protocol_fee_receiver(&e, &defindex_protocol_receiver); // Set the factory address set_factory(&e, &factory); @@ -134,9 +141,9 @@ impl VaultTrait for DeFindexVault { events::emit_initialized_vault( &e, emergency_manager, - fee_receiver, + vault_fee_receiver, manager, - defindex_receiver, + defindex_protocol_receiver, assets, ); @@ -173,9 +180,6 @@ impl VaultTrait for DeFindexVault { // get assets let assets = get_assets(&e); - // lenght should be equal tolength - let assets_length = assets.len(); - let asset = t_assets(&e); let assets_length = assets.len(); // assets lenght should be equal to amounts_desired and amounts_min length @@ -201,7 +205,7 @@ impl VaultTrait for DeFindexVault { let total_managed_funds = fetch_total_managed_funds(&e); VaultToken::total_supply(e.clone()) * amounts_desired.get(0).unwrap() / - .get(assets.get(0).unwrap().address.clone()) + total_managed_funds.get(assets.get(0).unwrap().address.clone()) .unwrap() }; (amounts_desired, shares) @@ -505,7 +509,7 @@ impl AdminInterfaceTrait for DeFindexVault { /// # Arguments: /// * `e` - The environment. /// * `caller` - The address initiating the change (must be the manager or emergency manager). - /// * `fee_receiver` - The new fee receiver address. + /// * `vault_fee_receiver` - The new fee receiver address. /// /// # Returns: /// * `()` - No return value. diff --git a/apps/contracts/vault/src/storage.rs b/apps/contracts/vault/src/storage.rs index 3eaf9860..844dd5b4 100644 --- a/apps/contracts/vault/src/storage.rs +++ b/apps/contracts/vault/src/storage.rs @@ -7,7 +7,7 @@ use crate::models::AssetAllocation; enum DataKey { AssetAllocation(u32), // AssetAllocation Addresse by index TotalAssets, // Total number of tokens - DeFindexReceiver, + DeFindexProtocolFeeReceiver, Factory, LastFeeAssessment, VaultShare, @@ -45,16 +45,16 @@ pub fn get_assets(e: &Env) -> Vec { } // DeFindex Fee Receiver -pub fn set_defindex_receiver(e: &Env, address: &Address) { +pub fn set_defindex_protocol_fee_receiver(e: &Env, address: &Address) { e.storage() .instance() - .set(&DataKey::DeFindexReceiver, address); + .set(&DataKey::DeFindexProtocolFeeReceiver, address); } -pub fn get_defindex_receiver(e: &Env) -> Address { +pub fn get_defindex_protocol_fee_receiver(e: &Env) -> Address { e.storage() .instance() - .get(&DataKey::DeFindexReceiver) + .get(&DataKey::DeFindexProtocolFeeReceiver) .unwrap() } @@ -82,10 +82,10 @@ pub fn get_last_fee_assesment(e: &Env) -> u64 { } // Vault Share -pub fn set_vault_share(e: &Env, vault_share: &u32) { +pub fn set_vault_share(e: &Env, vault_fee: &u32) { e.storage() .instance() - .set(&DataKey::VaultShare, vault_share); + .set(&DataKey::VaultShare, vault_fee); } pub fn get_vault_share(e: &Env) -> u32 { diff --git a/apps/contracts/vault/src/test.rs b/apps/contracts/vault/src/test.rs index 4a9960a9..46bccd0c 100755 --- a/apps/contracts/vault/src/test.rs +++ b/apps/contracts/vault/src/test.rs @@ -88,8 +88,8 @@ pub struct DeFindexVaultTest<'a> { token1_admin_client: SorobanTokenAdminClient<'a>, token1: SorobanTokenClient<'a>, emergency_manager: Address, - fee_receiver: Address, - defindex_receiver: Address, + vault_fee_receiver: Address, + defindex_protocol_receiver: Address, manager: Address, strategy_client: HodlStrategyClient<'a>, } @@ -105,8 +105,8 @@ impl<'a> DeFindexVaultTest<'a> { let defindex_contract = create_defindex_vault(&env); let emergency_manager = Address::generate(&env); - let fee_receiver = Address::generate(&env); - let defindex_receiver = Address::generate(&env); + let vault_fee_receiver = Address::generate(&env); + let defindex_protocol_receiver = Address::generate(&env); let manager = Address::generate(&env); let token0_admin = Address::generate(&env); @@ -131,8 +131,8 @@ impl<'a> DeFindexVaultTest<'a> { token1_admin_client, token1, emergency_manager, - fee_receiver, - defindex_receiver, + vault_fee_receiver, + defindex_protocol_receiver, manager, strategy_client, } diff --git a/apps/contracts/vault/src/test/admin.rs b/apps/contracts/vault/src/test/admin.rs index 38fc84ef..50b70331 100644 --- a/apps/contracts/vault/src/test/admin.rs +++ b/apps/contracts/vault/src/test/admin.rs @@ -29,41 +29,41 @@ fn test_set_new_fee_receiver_by_fee_receiver() { &assets, &test.manager, &test.emergency_manager, - &test.fee_receiver, + &test.vault_fee_receiver, &2000u32, - &test.defindex_receiver, + &test.defindex_protocol_receiver, &test.defindex_factory, &String::from_str(&test.env, "dfToken"), &String::from_str(&test.env, "DFT"), ); let fee_receiver_role = test.defindex_contract.get_fee_receiver(); - assert_eq!(fee_receiver_role, test.fee_receiver); + assert_eq!(fee_receiver_role, test.vault_fee_receiver); let users = DeFindexVaultTest::generate_random_users(&test.env, 1); // Fee Receiver is setting the new fee receiver test.defindex_contract .mock_auths(&[MockAuth { - address: &test.fee_receiver, + address: &test.vault_fee_receiver, invoke: &MockAuthInvoke { contract: &test.defindex_contract.address.clone(), fn_name: "set_fee_receiver", - args: (&test.fee_receiver, &users[0]).into_val(&test.env), + args: (&test.vault_fee_receiver, &users[0]).into_val(&test.env), sub_invokes: &[], }, }]) - .set_fee_receiver(&test.fee_receiver, &users[0]); + .set_fee_receiver(&test.vault_fee_receiver, &users[0]); let expected_auth = AuthorizedInvocation { // Top-level authorized function is `deploy` with all the arguments. function: AuthorizedFunction::Contract(( test.defindex_contract.address.clone(), Symbol::new(&test.env, "set_fee_receiver"), - (&test.fee_receiver, users[0].clone()).into_val(&test.env), + (&test.vault_fee_receiver, users[0].clone()).into_val(&test.env), )), sub_invocations: vec![], }; - assert_eq!(test.env.auths(), vec![(test.fee_receiver, expected_auth)]); + assert_eq!(test.env.auths(), vec![(test.vault_fee_receiver, expected_auth)]); let new_fee_receiver_role = test.defindex_contract.get_fee_receiver(); assert_eq!(new_fee_receiver_role, users[0]); @@ -92,16 +92,16 @@ fn test_set_new_fee_receiver_by_manager() { &assets, &test.manager, &test.emergency_manager, - &test.fee_receiver, + &test.vault_fee_receiver, &2000u32, - &test.defindex_receiver, + &test.defindex_protocol_receiver, &test.defindex_factory, &String::from_str(&test.env, "dfToken"), &String::from_str(&test.env, "DFT"), ); let fee_receiver_role = test.defindex_contract.get_fee_receiver(); - assert_eq!(fee_receiver_role, test.fee_receiver); + assert_eq!(fee_receiver_role, test.vault_fee_receiver); let users = DeFindexVaultTest::generate_random_users(&test.env, 1); // Now Manager is setting the new fee receiver @@ -156,16 +156,16 @@ fn test_set_new_fee_receiver_by_emergency_manager() { &assets, &test.manager, &test.emergency_manager, - &test.fee_receiver, + &test.vault_fee_receiver, &2000u32, - &test.defindex_receiver, + &test.defindex_protocol_receiver, &test.defindex_factory, &String::from_str(&test.env, "dfToken"), &String::from_str(&test.env, "DFT"), ); let fee_receiver_role = test.defindex_contract.get_fee_receiver(); - assert_eq!(fee_receiver_role, test.fee_receiver); + assert_eq!(fee_receiver_role, test.vault_fee_receiver); let users = DeFindexVaultTest::generate_random_users(&test.env, 1); // Now Emergency Manager is setting the new fee receiver @@ -195,16 +195,16 @@ fn test_set_new_fee_receiver_invalid_sender() { &assets, &test.manager, &test.emergency_manager, - &test.fee_receiver, + &test.vault_fee_receiver, &2000u32, - &test.defindex_receiver, + &test.defindex_protocol_receiver, &test.defindex_factory, &String::from_str(&test.env, "dfToken"), &String::from_str(&test.env, "DFT"), ); let fee_receiver_role = test.defindex_contract.get_fee_receiver(); - assert_eq!(fee_receiver_role, test.fee_receiver); + assert_eq!(fee_receiver_role, test.vault_fee_receiver); let users = DeFindexVaultTest::generate_random_users(&test.env, 1); // Trying to set the new fee receiver with an invalid sender @@ -232,9 +232,9 @@ fn test_set_new_manager_by_manager() { &assets, &test.manager, &test.emergency_manager, - &test.fee_receiver, + &test.vault_fee_receiver, &2000u32, - &test.defindex_receiver, + &test.defindex_protocol_receiver, &test.defindex_factory, &String::from_str(&test.env, "dfToken"), &String::from_str(&test.env, "DFT"), diff --git a/apps/contracts/vault/src/test/deposit.rs b/apps/contracts/vault/src/test/deposit.rs index ccccf851..6568d6dd 100644 --- a/apps/contracts/vault/src/test/deposit.rs +++ b/apps/contracts/vault/src/test/deposit.rs @@ -26,9 +26,9 @@ fn deposit_amounts_desired_wrong_length() { &assets, &test.manager, &test.emergency_manager, - &test.fee_receiver, + &test.vault_fee_receiver, &2000u32, - &test.defindex_receiver, + &test.defindex_protocol_receiver, &test.defindex_factory, &String::from_str(&test.env, "dfToken"), &String::from_str(&test.env, "DFT"), @@ -43,7 +43,7 @@ fn deposit_amounts_desired_wrong_length() { &users[0], ); - assert_eq!(response, Err(Ok(ContractError::WrongAmuntsLength))); + assert_eq!(response, Err(Ok(ContractError::WrongAmountsLength))); } #[test] @@ -88,8 +88,8 @@ fn deposit_several_assets() { // &assets, // &test.manager, // &test.emergency_manager, -// &test.fee_receiver, -// &test.defindex_receiver, +// &test.vault_fee_receiver, +// &test.defindex_protocol_receiver, // ); // let amount = 1000i128; diff --git a/apps/contracts/vault/src/test/emergency_withdraw.rs b/apps/contracts/vault/src/test/emergency_withdraw.rs index 9df5992a..742ac034 100644 --- a/apps/contracts/vault/src/test/emergency_withdraw.rs +++ b/apps/contracts/vault/src/test/emergency_withdraw.rs @@ -23,9 +23,9 @@ fn test_emergency_withdraw_success() { &assets, &test.manager, &test.emergency_manager, - &test.fee_receiver, + &test.vault_fee_receiver, &2000u32, - &test.defindex_receiver, + &test.defindex_protocol_receiver, &test.defindex_factory, &String::from_str(&test.env, "dfToken"), &String::from_str(&test.env, "DFT"), diff --git a/apps/contracts/vault/src/test/initialize.rs b/apps/contracts/vault/src/test/initialize.rs index 621c63d8..0d0b6e5e 100644 --- a/apps/contracts/vault/src/test/initialize.rs +++ b/apps/contracts/vault/src/test/initialize.rs @@ -26,9 +26,9 @@ fn test_initialize_and_get_roles() { &assets, &test.manager, &test.emergency_manager, - &test.fee_receiver, + &test.vault_fee_receiver, &2000u32, - &test.defindex_receiver, + &test.defindex_protocol_receiver, &test.defindex_factory, &String::from_str(&test.env, "dfToken"), &String::from_str(&test.env, "DFT"), @@ -39,7 +39,7 @@ fn test_initialize_and_get_roles() { let emergency_manager_role = test.defindex_contract.get_emergency_manager(); assert_eq!(manager_role, test.manager); - assert_eq!(fee_receiver_role, test.fee_receiver); + assert_eq!(fee_receiver_role, test.vault_fee_receiver); assert_eq!(emergency_manager_role, test.emergency_manager); } @@ -76,9 +76,9 @@ fn test_initialize_twice() { &assets, &test.manager, &test.emergency_manager, - &test.fee_receiver, + &test.vault_fee_receiver, &2000u32, - &test.defindex_receiver, + &test.defindex_protocol_receiver, &test.defindex_factory, &String::from_str(&test.env, "dfToken"), &String::from_str(&test.env, "DFT"), @@ -88,9 +88,9 @@ fn test_initialize_twice() { &assets, &test.manager, &test.emergency_manager, - &test.fee_receiver, + &test.vault_fee_receiver, &2000u32, - &test.defindex_receiver, + &test.defindex_protocol_receiver, &test.defindex_factory, &String::from_str(&test.env, "dfToken"), &String::from_str(&test.env, "DFT"), diff --git a/apps/contracts/vault/src/test/rebalance.rs b/apps/contracts/vault/src/test/rebalance.rs index 0743b471..e5f89bb9 100644 --- a/apps/contracts/vault/src/test/rebalance.rs +++ b/apps/contracts/vault/src/test/rebalance.rs @@ -26,9 +26,9 @@ fn rebalance() { &assets, &test.manager, &test.emergency_manager, - &test.fee_receiver, + &test.vault_fee_receiver, &2000u32, - &test.defindex_receiver, + &test.defindex_protocol_receiver, &test.defindex_factory, &String::from_str(&test.env, "dfToken"), &String::from_str(&test.env, "DFT"), diff --git a/apps/contracts/vault/src/test/withdraw.rs b/apps/contracts/vault/src/test/withdraw.rs index a135d468..fa1d5a8e 100644 --- a/apps/contracts/vault/src/test/withdraw.rs +++ b/apps/contracts/vault/src/test/withdraw.rs @@ -24,9 +24,9 @@ fn test_withdraw_from_idle_success() { &assets, &test.manager, &test.emergency_manager, - &test.fee_receiver, + &test.vault_fee_receiver, &2000u32, - &test.defindex_receiver, + &test.defindex_protocol_receiver, &test.defindex_factory, &String::from_str(&test.env, "dfToken"), &String::from_str(&test.env, "DFT"), @@ -141,9 +141,9 @@ fn test_withdraw_from_strategy_success() { &assets, &test.manager, &test.emergency_manager, - &test.fee_receiver, + &test.vault_fee_receiver, &2000u32, - &test.defindex_receiver, + &test.defindex_protocol_receiver, &test.defindex_factory, &String::from_str(&test.env, "dfToken"), &String::from_str(&test.env, "DFT"), diff --git a/apps/docs/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract.md b/apps/docs/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract.md index 2b17ad36..e1144219 100644 --- a/apps/docs/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract.md +++ b/apps/docs/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract.md @@ -118,7 +118,7 @@ $$ f_{\text{total}} = f_{\text{DeFindex}} + f_{\text{Vault}} $$ -where $f_{\text{DeFindex}} = 0.5\%$ is a fixed fee that goes to the DeFindex Protocol Fee Receiver address, and $f_{\text{Vault}}$ is a variable APR, typically between 0.5% and 2%, that goes to the Vault Fee Receiver address. +where $f_{\text{DeFindex}} = 0.5\%$ is a fixed `defindex_fee` that goes to the DeFindex Protocol Fee Receiver address, and $f_{\text{Vault}}$ is a variable APR `vault_fee`, typically between 0.5% and 2%, that goes to the Vault Fee Receiver address. ### Fee Collection Methodology From 05e7df78e9f24855efa2f36c66d50bc4ae5ce05e Mon Sep 17 00:00:00 2001 From: esteblock Date: Wed, 6 Nov 2024 09:20:58 -0300 Subject: [PATCH 18/19] TEMP: Fix check that asset is supported by strategy.asset --- apps/contracts/vault/src/lib.rs | 20 ++++++------ apps/contracts/vault/src/storage.rs | 2 +- apps/contracts/vault/src/test.rs | 35 +++++++++++++++------ apps/contracts/vault/src/test/initialize.rs | 22 +++++++------ 4 files changed, 50 insertions(+), 29 deletions(-) diff --git a/apps/contracts/vault/src/lib.rs b/apps/contracts/vault/src/lib.rs index 19f81b99..bdc4ad6e 100755 --- a/apps/contracts/vault/src/lib.rs +++ b/apps/contracts/vault/src/lib.rs @@ -34,7 +34,7 @@ use models::{ }; use storage::{ get_assets, set_asset, set_defindex_protocol_fee_receiver, set_factory, set_last_fee_assesment, - set_total_assets, set_vault_share, + set_total_assets, set_vault_fee, }; use strategies::{ get_asset_allocation_from_address, get_strategy_asset, get_strategy_client, @@ -47,6 +47,8 @@ use utils::{ calculate_withdrawal_amounts, check_initialized, check_nonnegative_amount, }; +use defindex_strategy_core::DeFindexStrategyClient; + pub use error::ContractError; #[contract] @@ -99,8 +101,8 @@ impl VaultTrait for DeFindexVault { access_control.set_role(&RolesDataKey::VaultFeeReceiver, &vault_fee_receiver); access_control.set_role(&RolesDataKey::Manager, &manager); - // Set Vault Share (in basis points) - set_vault_share(&e, &vault_fee); + // Set Vault Fee (in basis points) + set_vault_fee(&e, &vault_fee); // Set Paltalabs Fee Receiver set_defindex_protocol_fee_receiver(&e, &defindex_protocol_receiver); @@ -116,12 +118,12 @@ impl VaultTrait for DeFindexVault { // for every asset, we need to check that the list of strategyes indeed support this asset // TODO Fix, currently failing - // for strategy in asset.strategies.iter() { - // let strategy_client = DeFindexStrategyClient::new(&e, &strategy.address); - // if strategy_client.asset() != asset.address { - // panic_with_error!(&e, ContractError::StrategyDoesNotSupportAsset); - // } - // } + for strategy in asset.strategies.iter() { + let strategy_client = DeFindexStrategyClient::new(&e, &strategy.address); + if strategy_client.asset() != asset.address { + panic_with_error!(&e, ContractError::StrategyDoesNotSupportAsset); + } + } set_asset(&e, i as u32, &asset); } diff --git a/apps/contracts/vault/src/storage.rs b/apps/contracts/vault/src/storage.rs index 844dd5b4..4027ddc7 100644 --- a/apps/contracts/vault/src/storage.rs +++ b/apps/contracts/vault/src/storage.rs @@ -82,7 +82,7 @@ pub fn get_last_fee_assesment(e: &Env) -> u64 { } // Vault Share -pub fn set_vault_share(e: &Env, vault_fee: &u32) { +pub fn set_vault_fee(e: &Env, vault_fee: &u32) { e.storage() .instance() .set(&DataKey::VaultShare, vault_fee); diff --git a/apps/contracts/vault/src/test.rs b/apps/contracts/vault/src/test.rs index 46bccd0c..e5549da0 100755 --- a/apps/contracts/vault/src/test.rs +++ b/apps/contracts/vault/src/test.rs @@ -68,12 +68,24 @@ pub(crate) fn get_token_admin_client<'a>( SorobanTokenAdminClient::new(e, address) } -pub(crate) fn create_strategy_params(test: &DeFindexVaultTest) -> Vec { +pub(crate) fn create_strategy_params_token0(test: &DeFindexVaultTest) -> Vec { sorobanvec![ &test.env, Strategy { name: String::from_str(&test.env, "Strategy 1"), - address: test.strategy_client.address.clone(), + address: test.strategy_client_token0.address.clone(), + paused: false, + } + ] +} + + +pub(crate) fn create_strategy_params_token1(test: &DeFindexVaultTest) -> Vec { + sorobanvec![ + &test.env, + Strategy { + name: String::from_str(&test.env, "Strategy 1"), + address: test.strategy_client_token1.address.clone(), paused: false, } ] @@ -91,7 +103,8 @@ pub struct DeFindexVaultTest<'a> { vault_fee_receiver: Address, defindex_protocol_receiver: Address, manager: Address, - strategy_client: HodlStrategyClient<'a>, + strategy_client_token0: HodlStrategyClient<'a>, + strategy_client_token1: HodlStrategyClient<'a>, } impl<'a> DeFindexVaultTest<'a> { @@ -120,7 +133,8 @@ impl<'a> DeFindexVaultTest<'a> { // token1_admin_client.mint(to, amount); - let strategy_client = create_hodl_strategy(&env, &token0.address); + let strategy_client_token0 = create_hodl_strategy(&env, &token0.address); + let strategy_client_token1 = create_hodl_strategy(&env, &token1.address); DeFindexVaultTest { env, @@ -134,7 +148,8 @@ impl<'a> DeFindexVaultTest<'a> { vault_fee_receiver, defindex_protocol_receiver, manager, - strategy_client, + strategy_client_token0, + strategy_client_token1, } } @@ -147,9 +162,9 @@ impl<'a> DeFindexVaultTest<'a> { } } -mod admin; -mod deposit; -mod emergency_withdraw; mod initialize; -mod rebalance; -mod withdraw; +// mod admin; +// mod deposit; +// mod emergency_withdraw; +// mod rebalance; +// mod withdraw; diff --git a/apps/contracts/vault/src/test/initialize.rs b/apps/contracts/vault/src/test/initialize.rs index 0d0b6e5e..c515b45f 100644 --- a/apps/contracts/vault/src/test/initialize.rs +++ b/apps/contracts/vault/src/test/initialize.rs @@ -1,7 +1,8 @@ use soroban_sdk::{vec as sorobanvec, String, Vec}; use crate::test::{ - create_strategy_params, + create_strategy_params_token0, + create_strategy_params_token1, defindex_vault::{AssetAllocation, ContractError}, DeFindexVaultTest, }; @@ -9,16 +10,17 @@ use crate::test::{ #[test] fn test_initialize_and_get_roles() { let test = DeFindexVaultTest::setup(); - let strategy_params = create_strategy_params(&test); + let strategy_params_token0 = create_strategy_params_token0(&test); + let strategy_params_token1 = create_strategy_params_token1(&test); let assets: Vec = sorobanvec![ &test.env, AssetAllocation { address: test.token0.address.clone(), - strategies: strategy_params.clone() + strategies: strategy_params_token0.clone() }, AssetAllocation { address: test.token1.address.clone(), - strategies: strategy_params.clone() + strategies: strategy_params_token1.clone() } ]; @@ -58,17 +60,18 @@ fn test_get_roles_not_yet_initialized() { #[test] fn test_initialize_twice() { let test = DeFindexVaultTest::setup(); - let strategy_params = create_strategy_params(&test); + let strategy_params_token0 = create_strategy_params_token0(&test); + let strategy_params_token1 = create_strategy_params_token1(&test); let assets: Vec = sorobanvec![ &test.env, AssetAllocation { address: test.token0.address.clone(), - strategies: strategy_params.clone() + strategies: strategy_params_token0.clone() }, AssetAllocation { address: test.token1.address.clone(), - strategies: strategy_params.clone() + strategies: strategy_params_token1.clone() } ]; @@ -127,10 +130,11 @@ fn test_emergency_withdraw_not_yet_initialized() { let test = DeFindexVaultTest::setup(); let users = DeFindexVaultTest::generate_random_users(&test.env, 1); - let strategy_params = create_strategy_params(&test); + let strategy_params_token1 = create_strategy_params_token1(&test); + let strategy_params_token1 = create_strategy_params_token1(&test); let result = test .defindex_contract - .try_emergency_withdraw(&strategy_params.first().unwrap().address, &users[0]); + .try_emergency_withdraw(&strategy_params_token1.first().unwrap().address, &users[0]); assert_eq!(result, Err(Ok(ContractError::NotInitialized))); } From 5f0b0de24e18bbfe21fb88dc94e46a3be1921096 Mon Sep 17 00:00:00 2001 From: esteblock Date: Wed, 6 Nov 2024 09:26:49 -0300 Subject: [PATCH 19/19] fix: check that asset is suported by strategy.asset --- apps/contracts/vault/src/access.rs | 7 ++- apps/contracts/vault/src/fee.rs | 6 ++- apps/contracts/vault/src/lib.rs | 10 ++--- apps/contracts/vault/src/storage.rs | 4 +- apps/contracts/vault/src/test.rs | 11 +++-- apps/contracts/vault/src/test/admin.rs | 45 ++++++++++++------- apps/contracts/vault/src/test/deposit.rs | 14 +++--- .../vault/src/test/emergency_withdraw.rs | 14 +++--- apps/contracts/vault/src/test/initialize.rs | 3 +- apps/contracts/vault/src/test/rebalance.rs | 12 ++--- apps/contracts/vault/src/test/withdraw.rs | 16 +++---- 11 files changed, 80 insertions(+), 62 deletions(-) diff --git a/apps/contracts/vault/src/access.rs b/apps/contracts/vault/src/access.rs index dba6db02..d4f00a96 100644 --- a/apps/contracts/vault/src/access.rs +++ b/apps/contracts/vault/src/access.rs @@ -6,7 +6,7 @@ use soroban_sdk::{contracttype, panic_with_error, Address, Env}; #[contracttype] pub enum RolesDataKey { EmergencyManager, // Role: Emergency Manager - VaultFeeReceiver, // Role: Fee Receiver + VaultFeeReceiver, // Role: Fee Receiver Manager, // Role: Manager } @@ -83,7 +83,10 @@ impl AccessControlTrait for AccessControl { // Role-specific setters and getters impl AccessControl { pub fn set_fee_receiver(&self, caller: &Address, vault_fee_receiver: &Address) { - self.require_any_role(&[RolesDataKey::Manager, RolesDataKey::VaultFeeReceiver], caller); + self.require_any_role( + &[RolesDataKey::Manager, RolesDataKey::VaultFeeReceiver], + caller, + ); self.set_role(&RolesDataKey::VaultFeeReceiver, vault_fee_receiver); } diff --git a/apps/contracts/vault/src/fee.rs b/apps/contracts/vault/src/fee.rs index 7b0698d6..36d4cd3e 100644 --- a/apps/contracts/vault/src/fee.rs +++ b/apps/contracts/vault/src/fee.rs @@ -82,7 +82,11 @@ fn mint_fees(e: &Env, total_fees: i128) -> Result<(), ContractError> { let defindex_shares = total_fees - vault_shares; internal_mint(e.clone(), vault_fee_receiver.clone(), vault_shares); - internal_mint(e.clone(), defindex_protocol_receiver.clone(), defindex_shares); + internal_mint( + e.clone(), + defindex_protocol_receiver.clone(), + defindex_shares, + ); events::emit_fees_minted_event( e, diff --git a/apps/contracts/vault/src/lib.rs b/apps/contracts/vault/src/lib.rs index bdc4ad6e..925c979d 100755 --- a/apps/contracts/vault/src/lib.rs +++ b/apps/contracts/vault/src/lib.rs @@ -55,7 +55,7 @@ pub use error::ContractError; pub struct DeFindexVault; #[contractimpl] -impl VaultTrait for DeFindexVault { +impl VaultTrait for DeFindexVault { /// Initializes the DeFindex Vault contract with the required parameters. /// /// This function sets the roles for manager, emergency manager, vault fee receiver, and manager. @@ -73,7 +73,7 @@ impl VaultTrait for DeFindexVault { /// - `vault_symbol`: Symbol representing the vault’s token. /// /// # Returns - /// - `Result<(), ContractError>`: Returns `Ok(())` if initialization succeeds, or a `ContractError` if + /// - `Result<(), ContractError>`: Returns `Ok(())` if initialization succeeds, or a `ContractError` if /// any setup fails (e.g., strategy mismatch with asset). /// /// # Errors @@ -120,7 +120,7 @@ impl VaultTrait for DeFindexVault { // TODO Fix, currently failing for strategy in asset.strategies.iter() { let strategy_client = DeFindexStrategyClient::new(&e, &strategy.address); - if strategy_client.asset() != asset.address { + if strategy_client.asset() != asset.address { panic_with_error!(&e, ContractError::StrategyDoesNotSupportAsset); } } @@ -206,8 +206,8 @@ impl VaultTrait for DeFindexVault { // in this case we will mint a share proportional to the total managed funds let total_managed_funds = fetch_total_managed_funds(&e); VaultToken::total_supply(e.clone()) * amounts_desired.get(0).unwrap() - / - total_managed_funds.get(assets.get(0).unwrap().address.clone()) + / total_managed_funds + .get(assets.get(0).unwrap().address.clone()) .unwrap() }; (amounts_desired, shares) diff --git a/apps/contracts/vault/src/storage.rs b/apps/contracts/vault/src/storage.rs index 4027ddc7..90df7c61 100644 --- a/apps/contracts/vault/src/storage.rs +++ b/apps/contracts/vault/src/storage.rs @@ -83,9 +83,7 @@ pub fn get_last_fee_assesment(e: &Env) -> u64 { // Vault Share pub fn set_vault_fee(e: &Env, vault_fee: &u32) { - e.storage() - .instance() - .set(&DataKey::VaultShare, vault_fee); + e.storage().instance().set(&DataKey::VaultShare, vault_fee); } pub fn get_vault_share(e: &Env) -> u32 { diff --git a/apps/contracts/vault/src/test.rs b/apps/contracts/vault/src/test.rs index e5549da0..bd168890 100755 --- a/apps/contracts/vault/src/test.rs +++ b/apps/contracts/vault/src/test.rs @@ -79,7 +79,6 @@ pub(crate) fn create_strategy_params_token0(test: &DeFindexVaultTest) -> Vec Vec { sorobanvec![ &test.env, @@ -162,9 +161,9 @@ impl<'a> DeFindexVaultTest<'a> { } } +mod admin; +mod deposit; +mod emergency_withdraw; mod initialize; -// mod admin; -// mod deposit; -// mod emergency_withdraw; -// mod rebalance; -// mod withdraw; +mod rebalance; +mod withdraw; diff --git a/apps/contracts/vault/src/test/admin.rs b/apps/contracts/vault/src/test/admin.rs index 50b70331..c7b64d08 100644 --- a/apps/contracts/vault/src/test/admin.rs +++ b/apps/contracts/vault/src/test/admin.rs @@ -3,7 +3,10 @@ use soroban_sdk::{ vec as sorobanvec, IntoVal, String, Symbol, Vec, }; -use crate::test::{create_strategy_params, defindex_vault::AssetAllocation, DeFindexVaultTest}; +use crate::test::{ + create_strategy_params_token0, create_strategy_params_token1, defindex_vault::AssetAllocation, + DeFindexVaultTest, +}; extern crate alloc; use alloc::vec; @@ -11,17 +14,18 @@ use alloc::vec; #[test] fn test_set_new_fee_receiver_by_fee_receiver() { let test = DeFindexVaultTest::setup(); - let strategy_params = create_strategy_params(&test); + let strategy_params_token0 = create_strategy_params_token0(&test); + let strategy_params_token1 = create_strategy_params_token1(&test); let assets: Vec = sorobanvec![ &test.env, AssetAllocation { address: test.token0.address.clone(), - strategies: strategy_params.clone() + strategies: strategy_params_token0.clone() }, AssetAllocation { address: test.token1.address.clone(), - strategies: strategy_params.clone() + strategies: strategy_params_token1.clone() } ]; @@ -63,7 +67,10 @@ fn test_set_new_fee_receiver_by_fee_receiver() { )), sub_invocations: vec![], }; - assert_eq!(test.env.auths(), vec![(test.vault_fee_receiver, expected_auth)]); + assert_eq!( + test.env.auths(), + vec![(test.vault_fee_receiver, expected_auth)] + ); let new_fee_receiver_role = test.defindex_contract.get_fee_receiver(); assert_eq!(new_fee_receiver_role, users[0]); @@ -72,7 +79,8 @@ fn test_set_new_fee_receiver_by_fee_receiver() { #[test] fn test_set_new_fee_receiver_by_manager() { let test = DeFindexVaultTest::setup(); - let strategy_params = create_strategy_params(&test); + let strategy_params_token0 = create_strategy_params_token0(&test); + let strategy_params_token1 = create_strategy_params_token1(&test); // let tokens: Vec
= sorobanvec![&test.env, test.token0.address.clone(), test.token1.address.clone()]; // let ratios: Vec = sorobanvec![&test.env, 1, 1]; @@ -80,11 +88,11 @@ fn test_set_new_fee_receiver_by_manager() { &test.env, AssetAllocation { address: test.token0.address.clone(), - strategies: strategy_params.clone() + strategies: strategy_params_token0.clone() }, AssetAllocation { address: test.token1.address.clone(), - strategies: strategy_params.clone() + strategies: strategy_params_token1.clone() } ]; @@ -136,7 +144,8 @@ fn test_set_new_fee_receiver_by_manager() { #[should_panic(expected = "HostError: Error(Contract, #130)")] // Unauthorized fn test_set_new_fee_receiver_by_emergency_manager() { let test = DeFindexVaultTest::setup(); - let strategy_params = create_strategy_params(&test); + let strategy_params_token0 = create_strategy_params_token0(&test); + let strategy_params_token1 = create_strategy_params_token1(&test); // let tokens: Vec
= sorobanvec![&test.env, test.token0.address.clone(), test.token1.address.clone()]; // let ratios: Vec = sorobanvec![&test.env, 1, 1]; @@ -144,11 +153,11 @@ fn test_set_new_fee_receiver_by_emergency_manager() { &test.env, AssetAllocation { address: test.token0.address.clone(), - strategies: strategy_params.clone() + strategies: strategy_params_token0.clone() }, AssetAllocation { address: test.token1.address.clone(), - strategies: strategy_params.clone() + strategies: strategy_params_token1.clone() } ]; @@ -177,17 +186,18 @@ fn test_set_new_fee_receiver_by_emergency_manager() { #[should_panic(expected = "HostError: Error(Contract, #130)")] // Unauthorized fn test_set_new_fee_receiver_invalid_sender() { let test = DeFindexVaultTest::setup(); - let strategy_params = create_strategy_params(&test); + let strategy_params_token0 = create_strategy_params_token0(&test); + let strategy_params_token1 = create_strategy_params_token1(&test); let assets: Vec = sorobanvec![ &test.env, AssetAllocation { address: test.token0.address.clone(), - strategies: strategy_params.clone() + strategies: strategy_params_token0.clone() }, AssetAllocation { address: test.token1.address.clone(), - strategies: strategy_params.clone() + strategies: strategy_params_token1.clone() } ]; @@ -215,16 +225,17 @@ fn test_set_new_fee_receiver_invalid_sender() { #[test] fn test_set_new_manager_by_manager() { let test = DeFindexVaultTest::setup(); - let strategy_params = create_strategy_params(&test); + let strategy_params_token0 = create_strategy_params_token0(&test); + let strategy_params_token1 = create_strategy_params_token1(&test); let assets: Vec = sorobanvec![ &test.env, AssetAllocation { address: test.token0.address.clone(), - strategies: strategy_params.clone() + strategies: strategy_params_token0.clone() }, AssetAllocation { address: test.token1.address.clone(), - strategies: strategy_params.clone() + strategies: strategy_params_token1.clone() } ]; diff --git a/apps/contracts/vault/src/test/deposit.rs b/apps/contracts/vault/src/test/deposit.rs index 6568d6dd..30bb840d 100644 --- a/apps/contracts/vault/src/test/deposit.rs +++ b/apps/contracts/vault/src/test/deposit.rs @@ -1,24 +1,27 @@ use soroban_sdk::{vec as sorobanvec, String, Vec}; use crate::test::defindex_vault::{AssetAllocation, ContractError}; -use crate::test::{create_strategy_params, DeFindexVaultTest}; +use crate::test::{ + create_strategy_params_token0, create_strategy_params_token1, DeFindexVaultTest, +}; #[test] fn deposit_amounts_desired_wrong_length() { let test = DeFindexVaultTest::setup(); test.env.mock_all_auths(); - let strategy_params = create_strategy_params(&test); + 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, AssetAllocation { address: test.token0.address.clone(), - strategies: strategy_params.clone() + strategies: strategy_params_token0.clone() }, AssetAllocation { address: test.token1.address.clone(), - strategies: strategy_params.clone() + strategies: strategy_params_token1.clone() } ]; @@ -74,7 +77,8 @@ fn deposit_several_assets() { // fn test_withdraw_success() { // let test = DeFindexVaultTest::setup(); // test.env.mock_all_auths(); -// let strategy_params = create_strategy_params(&test); +// let strategy_params_token0 = create_strategy_params_token0(&test); +// let strategy_params_token1 = create_strategy_params_token1(&test); // let assets: Vec = sorobanvec![ // &test.env, // AssetAllocation { diff --git a/apps/contracts/vault/src/test/emergency_withdraw.rs b/apps/contracts/vault/src/test/emergency_withdraw.rs index 742ac034..fee60772 100644 --- a/apps/contracts/vault/src/test/emergency_withdraw.rs +++ b/apps/contracts/vault/src/test/emergency_withdraw.rs @@ -1,7 +1,7 @@ use soroban_sdk::{vec as sorobanvec, String, Vec}; use crate::test::{ - create_strategy_params, + create_strategy_params_token0, defindex_vault::{AssetAllocation, Investment}, DeFindexVaultTest, }; @@ -10,12 +10,12 @@ use crate::test::{ fn test_emergency_withdraw_success() { let test = DeFindexVaultTest::setup(); test.env.mock_all_auths(); - let strategy_params = create_strategy_params(&test); + let strategy_params_token0 = create_strategy_params_token0(&test); let assets: Vec = sorobanvec![ &test.env, AssetAllocation { address: test.token0.address.clone(), - strategies: strategy_params.clone() + strategies: strategy_params_token0.clone() } ]; @@ -60,7 +60,7 @@ fn test_emergency_withdraw_success() { &test.env, Investment { amount: amount.clone(), - strategy: strategy_params.first().unwrap().address.clone() + strategy: strategy_params_token0.first().unwrap().address.clone() } ]; test.defindex_contract.invest(&investments); @@ -71,18 +71,18 @@ fn test_emergency_withdraw_success() { // Balance of the strategy should be `amount` let strategy_balance = test - .strategy_client + .strategy_client_token0 .balance(&test.defindex_contract.address); assert_eq!(strategy_balance, amount); test.defindex_contract.emergency_withdraw( - &strategy_params.first().unwrap().address, + &strategy_params_token0.first().unwrap().address, &test.emergency_manager, ); // Balance of the strategy should be 0 let strategy_balance = test - .strategy_client + .strategy_client_token0 .balance(&test.defindex_contract.address); assert_eq!(strategy_balance, 0); diff --git a/apps/contracts/vault/src/test/initialize.rs b/apps/contracts/vault/src/test/initialize.rs index c515b45f..df040047 100644 --- a/apps/contracts/vault/src/test/initialize.rs +++ b/apps/contracts/vault/src/test/initialize.rs @@ -1,8 +1,7 @@ use soroban_sdk::{vec as sorobanvec, String, Vec}; use crate::test::{ - create_strategy_params_token0, - create_strategy_params_token1, + create_strategy_params_token0, create_strategy_params_token1, defindex_vault::{AssetAllocation, ContractError}, DeFindexVaultTest, }; diff --git a/apps/contracts/vault/src/test/rebalance.rs b/apps/contracts/vault/src/test/rebalance.rs index e5f89bb9..5a39c049 100644 --- a/apps/contracts/vault/src/test/rebalance.rs +++ b/apps/contracts/vault/src/test/rebalance.rs @@ -1,7 +1,7 @@ use soroban_sdk::{vec as sorobanvec, String, Vec}; use crate::test::{ - create_strategy_params, + create_strategy_params_token0, defindex_vault::{ ActionType, AssetAllocation, Instruction, Investment, OptionalSwapDetailsExactIn, OptionalSwapDetailsExactOut, @@ -13,12 +13,12 @@ use crate::test::{ fn rebalance() { let test = DeFindexVaultTest::setup(); test.env.mock_all_auths(); - let strategy_params = create_strategy_params(&test); + let strategy_params_token0 = create_strategy_params_token0(&test); let assets: Vec = sorobanvec![ &test.env, AssetAllocation { address: test.token0.address.clone(), - strategies: strategy_params.clone() + strategies: strategy_params_token0.clone() } ]; @@ -57,7 +57,7 @@ fn rebalance() { &test.env, Investment { amount: amount, - strategy: test.strategy_client.address.clone() + strategy: test.strategy_client_token0.address.clone() } ]; @@ -75,14 +75,14 @@ fn rebalance() { &test.env, Instruction { action: ActionType::Withdraw, - strategy: Some(test.strategy_client.address.clone()), + 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.address.clone()), + 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, diff --git a/apps/contracts/vault/src/test/withdraw.rs b/apps/contracts/vault/src/test/withdraw.rs index fa1d5a8e..e28058a7 100644 --- a/apps/contracts/vault/src/test/withdraw.rs +++ b/apps/contracts/vault/src/test/withdraw.rs @@ -2,7 +2,7 @@ use soroban_sdk::{vec as sorobanvec, String, Vec}; use super::hodl_strategy::StrategyError; use crate::test::{ - create_strategy_params, + create_strategy_params_token0, defindex_vault::{AssetAllocation, Investment}, DeFindexVaultTest, }; @@ -11,12 +11,12 @@ use crate::test::{ fn test_withdraw_from_idle_success() { let test = DeFindexVaultTest::setup(); test.env.mock_all_auths(); - let strategy_params = create_strategy_params(&test); + let strategy_params_token0 = create_strategy_params_token0(&test); let assets: Vec = sorobanvec![ &test.env, AssetAllocation { address: test.token0.address.clone(), - strategies: strategy_params.clone() + strategies: strategy_params_token0.clone() } ]; @@ -64,7 +64,7 @@ fn test_withdraw_from_idle_success() { assert_eq!(vault_balance, amount_to_deposit); // Token balance of hodl strategy should be 0 (all in idle) - let strategy_balance = test.token0.balance(&test.strategy_client.address); + let strategy_balance = test.token0.balance(&test.strategy_client_token0.address); assert_eq!(strategy_balance, 0); // Df balance of user should be equal to deposited amount @@ -90,7 +90,7 @@ fn test_withdraw_from_idle_success() { assert_eq!(vault_balance, amount_to_deposit - amount_to_withdraw); // Token balance of hodl strategy should be 0 (all in idle) - let strategy_balance = test.token0.balance(&test.strategy_client.address); + let strategy_balance = test.token0.balance(&test.strategy_client_token0.address); assert_eq!(strategy_balance, 0); // Df balance of user should be equal to deposited amount - amount_to_withdraw @@ -128,12 +128,12 @@ fn test_withdraw_from_idle_success() { fn test_withdraw_from_strategy_success() { let test = DeFindexVaultTest::setup(); test.env.mock_all_auths(); - let strategy_params = create_strategy_params(&test); + let strategy_params_token0 = create_strategy_params_token0(&test); let assets: Vec = sorobanvec![ &test.env, AssetAllocation { address: test.token0.address.clone(), - strategies: strategy_params.clone() + strategies: strategy_params_token0.clone() } ]; @@ -173,7 +173,7 @@ fn test_withdraw_from_strategy_success() { &test.env, Investment { amount: amount, - strategy: test.strategy_client.address.clone() + strategy: test.strategy_client_token0.address.clone() } ];