From 5515b729f0751478f06595bafdbab59249ee12d3 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Mon, 18 Nov 2024 08:17:06 -0300 Subject: [PATCH 01/21] =?UTF-8?q?=E2=9C=A8Implemented=20change=20fee=20rec?= =?UTF-8?q?iever=20in=20edit=20modal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/dapp/app/layout.tsx | 2 +- apps/dapp/app/page.tsx | 10 +- .../components/DeployVault/VaultPreview.tsx | 46 ++-- .../InteractWithVault/EditVault.tsx | 240 ++++++++++++++++++ .../components/ManageVaults/InspectVault.tsx | 13 +- .../components/ManageVaults/ManageVaults.tsx | 18 +- apps/dapp/src/components/ui/button.tsx | 40 +++ apps/dapp/src/contexts/index.ts | 7 +- apps/dapp/src/hooks/useVault.ts | 1 + apps/dapp/src/providers/modal-provider.tsx | 5 + .../src/store/lib/features/walletStore.ts | 12 +- 11 files changed, 356 insertions(+), 38 deletions(-) create mode 100644 apps/dapp/src/components/InteractWithVault/EditVault.tsx create mode 100644 apps/dapp/src/components/ui/button.tsx diff --git a/apps/dapp/app/layout.tsx b/apps/dapp/app/layout.tsx index 7ef5162d..81704878 100644 --- a/apps/dapp/app/layout.tsx +++ b/apps/dapp/app/layout.tsx @@ -6,7 +6,7 @@ export default function RootLayout({ children: ReactNode, }) { return ( - + {children} diff --git a/apps/dapp/app/page.tsx b/apps/dapp/app/page.tsx index 5e0585e9..e011fb93 100644 --- a/apps/dapp/app/page.tsx +++ b/apps/dapp/app/page.tsx @@ -1,5 +1,5 @@ "use client"; -import { Container } from '@chakra-ui/react' +import { Container, HStack } from '@chakra-ui/react' import { useSorobanReact } from '@soroban-react/core' import ManageVaults from '@/components/ManageVaults/ManageVaults'; import { @@ -13,10 +13,8 @@ ChartJS.register(ArcElement); export default function Home() { const { address } = useSorobanReact() return ( - - - - - + + + ); } diff --git a/apps/dapp/src/components/DeployVault/VaultPreview.tsx b/apps/dapp/src/components/DeployVault/VaultPreview.tsx index c0cbea33..d1927875 100644 --- a/apps/dapp/src/components/DeployVault/VaultPreview.tsx +++ b/apps/dapp/src/components/DeployVault/VaultPreview.tsx @@ -155,6 +155,29 @@ interface VaultPreviewProps { setFormControl: (args: FormControlInterface) => any; } + +export const dropdownData = { + strategies: { + title: 'Strategies', + description: 'A strategy is a set of steps to be followed to execute an investment in one or several protocols.', + href: 'https://docs.defindex.io/whitepaper/10-whitepaper/01-introduction#core-concepts' + }, + manager: { + title: 'Manager', + description: 'The Manager can rebalance the Vault, emergency withdraw and invest IDLE funds in strategies.', + href: 'https://docs.defindex.io/whitepaper/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract#management' + }, + emergencyManager: { + title: 'Emergency manager', + description: 'The Emergency Manager has the authority to withdraw assets from the DeFindex in case of an emergency.', + href: 'https://docs.defindex.io/whitepaper/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract#emergency-management' + }, + feeReceiver: { + title: 'Fee receiver', + description: ' 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.', + href: 'https://docs.defindex.io/whitepaper/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract#fee-collection' + } +} export const VaultPreview: React.FC = ({ data, accordionValue, setAccordionValue, formControl, setFormControl }) => { const dispatch = useAppDispatch() @@ -248,28 +271,7 @@ export const VaultPreview: React.FC = ({ data, accordionValue dispatch(setVaultShare(input * 100)) } - const dropdownData = { - strategies: { - title: 'Strategies', - description: 'A strategy is a set of steps to be followed to execute an investment in one or several protocols.', - href: 'https://docs.defindex.io/whitepaper/10-whitepaper/01-introduction#core-concepts' - }, - manager: { - title: 'Manager', - description: 'The Manager can rebalance the Vault, emergency withdraw and invest IDLE funds in strategies.', - href: 'https://docs.defindex.io/whitepaper/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract#management' - }, - emergencyManager: { - title: 'Emergency manager', - description: 'The Emergency Manager has the authority to withdraw assets from the DeFindex in case of an emergency.', - href: 'https://docs.defindex.io/whitepaper/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract#emergency-management' - }, - feeReceiver: { - title: 'Fee receiver', - description: ' 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.', - href: 'https://docs.defindex.io/whitepaper/10-whitepaper/03-the-defindex-approach/02-contracts/01-vault-contract#fee-collection' - } - } + return ( <> setAccordionValue(e.value)}> diff --git a/apps/dapp/src/components/InteractWithVault/EditVault.tsx b/apps/dapp/src/components/InteractWithVault/EditVault.tsx new file mode 100644 index 00000000..2be0c278 --- /dev/null +++ b/apps/dapp/src/components/InteractWithVault/EditVault.tsx @@ -0,0 +1,240 @@ +import React, { useContext, useEffect, useState } from 'react' +import { Address, xdr } from '@stellar/stellar-sdk' +import { useSorobanReact } from '@soroban-react/core' + +import { useAppDispatch, useAppSelector } from '@/store/lib/storeHooks' +import { setVaultFeeReceiver } from '@/store/lib/features/walletStore' +import { setFeeReceiver } from '@/store/lib/features/vaultStore' + +import { ModalContext } from '@/contexts' +import { VaultMethod, useVaultCallback, useVault } from '@/hooks/useVault' +import { isValidAddress } from '@/helpers/address' + +import { DialogBody, DialogContent, DialogHeader } from '../ui/dialog' +import { InputGroup } from '../ui/input-group' +import { + Input, + Text, + Stack, + HStack, + Fieldset, + Link, + IconButton, +} from '@chakra-ui/react' +import { InfoTip } from '../ui/toggle-tip' +import { Tooltip } from '../ui/tooltip' +import { FaRegPaste } from 'react-icons/fa6' +import { dropdownData } from '../DeployVault/VaultPreview' +import { Button } from '../ui/button' + +const CustomInputField = ({ + label, + value, + onChange, + handleClick, + placeholder, + invalid, + description, + href, +}: { + label: string, + value: string, + onChange: (e: any) => void, + handleClick: (address: string) => void, + placeholder: string, + invalid: boolean, + description?: string, + href?: string, +}) => { + const { address } = useSorobanReact() + return ( + + {label} + + {description} + + Learn more. + + + + } /> + + + + handleClick(address!)} + > + + + + } + > + + + + A valid Stellar / Soroban address is required. + + ) +} + +export const EditVaultModal = () => { + const selectedVault = useAppSelector(state => state.wallet.vaults.selectedVault) + const vaultMethod = selectedVault?.method + + const { address } = useSorobanReact(); + const vaultCB = useVaultCallback() + const vault = useVault() + const dispatch = useAppDispatch() + const { transactionStatusModal: statusModal, interactWithVaultModal: interactModal, inspectVaultModal: inspectModal } = useContext(ModalContext) + const [formControl, setFormControl] = useState({ + feeReceiver: { + value: selectedVault?.feeReceiver ?? '', + isValid: false, + needsUpdate: false, + isLoading: false, + } + }) + + + + + const handleFeeReceiverChange = (input: string) => { + const isValid = isValidAddress(input) + while (!isValid) { + setFormControl({ + ...formControl, + feeReceiver: { + ...formControl.feeReceiver, + value: input, + isValid: false, + } + }) + dispatch(setFeeReceiver('')) + return + } + setFormControl({ + ...formControl, + feeReceiver: { + ...formControl.feeReceiver, + value: input, + isValid: true, + } + }) + }; + + useEffect(() => { + setFormControl({ + ...formControl, + feeReceiver: { + isValid: isValidAddress(selectedVault?.feeReceiver ?? ''), + value: selectedVault?.feeReceiver ?? '', + needsUpdate: false, + isLoading: false, + } + }) + }, [selectedVault]) + enum Values { + FEERECIEVER = 'feeReceiver', + MANAGER = 'manager', + EMERGENCYMANAGER = 'emergencyManager' + } + + const updateValue = async (value: Values) => { + if (!address || !selectedVault) return; + let result: any; + if (value === Values.FEERECIEVER) { + setFormControl({ feeReceiver: { ...formControl.feeReceiver, isLoading: true } }) + statusModal.initModal() + console.log('Updating fee receiver') + const caller = new Address(address); + const feeReceiver = new Address(formControl.feeReceiver.value); + const createDefindexParams: xdr.ScVal[] = [ + caller.toScVal(), + feeReceiver.toScVal(), + ]; + try { + result = await vaultCB(VaultMethod.SETFEERECIEVER, selectedVault.address, createDefindexParams, true).then((res) => { + console.log(res) + statusModal.handleSuccess(res.txHash) + dispatch(setVaultFeeReceiver(formControl.feeReceiver.value)) + }) + } catch (error: any) { + console.error('Error:', error) + statusModal.handleError(error.toString()) + } finally { + setFormControl({ feeReceiver: { ...formControl.feeReceiver, isLoading: false } }) + } + + }; + } + + useEffect(() => { + if (!selectedVault?.feeReceiver) return + if (formControl.feeReceiver.value !== selectedVault.feeReceiver && formControl.feeReceiver.isValid) { + setFormControl({ + ...formControl, + feeReceiver: { + ...formControl.feeReceiver, + needsUpdate: true, + } + }) + } else if (formControl.feeReceiver.value === selectedVault.feeReceiver && formControl.feeReceiver.isValid) { + setFormControl({ + ...formControl, + feeReceiver: { + ...formControl.feeReceiver, + needsUpdate: false, + } + }) + } + }, [formControl.feeReceiver.value, formControl.feeReceiver.isValid]) + + if (!selectedVault) return null + return ( + <> + + + Manage {selectedVault.name} + + + + handleFeeReceiverChange(e.target.value)} + handleClick={(address) => setFormControl({ feeReceiver: { ...formControl.feeReceiver, isValid: true, value: address } })} + placeholder='GAFS3TLVM...' + invalid={!formControl.feeReceiver.isValid} + description={dropdownData.feeReceiver.description} + /> + + + {formControl.feeReceiver.needsUpdate && + + } + + + + + ) +} diff --git a/apps/dapp/src/components/ManageVaults/InspectVault.tsx b/apps/dapp/src/components/ManageVaults/InspectVault.tsx index 2678d439..d61e2f48 100644 --- a/apps/dapp/src/components/ManageVaults/InspectVault.tsx +++ b/apps/dapp/src/components/ManageVaults/InspectVault.tsx @@ -11,6 +11,8 @@ import { Button, Grid, GridItem, HStack, Icon, Stack, Text } from "@chakra-ui/re import { DialogBody, DialogContent, DialogFooter, DialogHeader } from "../ui/dialog" import { FaRegEdit } from "react-icons/fa" import { IoClose } from "react-icons/io5" +import { ModalContext } from "@/contexts" +import { useContext } from "react" export const InspectVault = ({ @@ -24,6 +26,7 @@ export const InspectVault = ({ }) => { const selectedVault: VaultData | undefined = useAppSelector(state => state.wallet.vaults.selectedVault) const { address } = useSorobanReact() + const { editVaultModal: editModal } = useContext(ModalContext) if (!selectedVault) return null return ( @@ -34,11 +37,11 @@ export const InspectVault = ({

Inspect {selectedVault?.name ? selectedVault.name : shortenAddress(selectedVault.address)}

{address === selectedVault.manager && - - { handleOpenDeployVault('edit_vault', true, selectedVault) }} css={{ cursor: "pointer" }}> - - - + + { editModal.setIsOpen(true) }} css={{ cursor: "pointer" }}> + + + } diff --git a/apps/dapp/src/components/ManageVaults/ManageVaults.tsx b/apps/dapp/src/components/ManageVaults/ManageVaults.tsx index 9fbe7223..aee2fc75 100644 --- a/apps/dapp/src/components/ManageVaults/ManageVaults.tsx +++ b/apps/dapp/src/components/ManageVaults/ManageVaults.tsx @@ -26,10 +26,17 @@ import { Input, Stack, } from "@chakra-ui/react" +import { EditVaultModal } from "../InteractWithVault/EditVault" export const ManageVaults = () => { const { address, activeChain } = useSorobanReact() - const { inspectVaultModal: inspectModal, deployVaultModal: deployModal, interactWithVaultModal: interactModal, transactionStatusModal: txModal } = useContext(ModalContext) + const { + inspectVaultModal: inspectModal, + deployVaultModal: deployModal, + interactWithVaultModal: interactModal, + transactionStatusModal: txModal, + editVaultModal: editModal + } = useContext(ModalContext) const dispatch = useAppDispatch() const modalContext = useContext(ModalContext) const vaults: VaultData[] = useAppSelector(state => state.wallet.vaults.createdVaults) @@ -155,6 +162,15 @@ export const ManageVaults = () => { onClose={() => { inspectModal.setIsOpen(false) }} /> + { editModal.setIsOpen(e.open) }} + size={'lg'} + placement={'center'} + > + + + { txModal.setIsOpen(e.open) }} diff --git a/apps/dapp/src/components/ui/button.tsx b/apps/dapp/src/components/ui/button.tsx new file mode 100644 index 00000000..21d5f4b5 --- /dev/null +++ b/apps/dapp/src/components/ui/button.tsx @@ -0,0 +1,40 @@ +import type { ButtonProps as ChakraButtonProps } from "@chakra-ui/react" +import { + AbsoluteCenter, + Button as ChakraButton, + Span, + Spinner, +} from "@chakra-ui/react" +import * as React from "react" + +interface ButtonLoadingProps { + loading?: boolean + loadingText?: React.ReactNode +} + +export interface ButtonProps extends ChakraButtonProps, ButtonLoadingProps {} + +export const Button = React.forwardRef( + function Button(props, ref) { + const { loading, disabled, loadingText, children, ...rest } = props + return ( + + {loading && !loadingText ? ( + <> + + + + {children} + + ) : loading && loadingText ? ( + <> + + {loadingText} + + ) : ( + children + )} + + ) + }, +) diff --git a/apps/dapp/src/contexts/index.ts b/apps/dapp/src/contexts/index.ts index 8a96de99..b4d4e28d 100644 --- a/apps/dapp/src/contexts/index.ts +++ b/apps/dapp/src/contexts/index.ts @@ -40,6 +40,7 @@ export type ModalContextType = { deployVaultModal: ToggleModalProps, inspectVaultModal: ToggleModalProps, interactWithVaultModal: ToggleModalProps, + editVaultModal: ToggleModalProps, }; export const ModalContext = React.createContext({ @@ -72,5 +73,9 @@ export const ModalContext = React.createContext({ interactWithVaultModal: { isOpen: false, setIsOpen: () => {}, - } + }, + editVaultModal: { + isOpen: false, + setIsOpen: () => {}, + }, }); \ No newline at end of file diff --git a/apps/dapp/src/hooks/useVault.ts b/apps/dapp/src/hooks/useVault.ts index c5d6a1d6..d9062f72 100644 --- a/apps/dapp/src/hooks/useVault.ts +++ b/apps/dapp/src/hooks/useVault.ts @@ -23,6 +23,7 @@ export enum VaultMethod { GETASSETAMMOUNT = "get_asset_amounts_for_dftokens", GETIDLEFUNDS = "fetch_current_idle_funds", GETINVESTEDFUNDS = "fetch_current_invested_funds", + SETFEERECIEVER = "set_fee_receiver", } const isObject = (val: unknown) => typeof val === 'object' && val !== null && !Array.isArray(val); diff --git a/apps/dapp/src/providers/modal-provider.tsx b/apps/dapp/src/providers/modal-provider.tsx index 8564ab67..669ffa29 100644 --- a/apps/dapp/src/providers/modal-provider.tsx +++ b/apps/dapp/src/providers/modal-provider.tsx @@ -17,6 +17,7 @@ export const ModalProvider = ({ const [isDeployVaultModalOpen, setIsDeployVaultModalOpen] = React.useState(false) const [isInspectVaultModalOpen, setIsInspectVaultModalOpen] = React.useState(false) const [isInteractWithVaultModalOpen, setIsInteractWithVaultModalOpen] = React.useState(false) + const [isEditVaultModalOpen, setIsEditVaultModalOpen] = React.useState(false) const [isTransactionStatusModalOpen, setIsTransactionStatusModalOpen] = React.useState(false) const [transactionStatusModalStep, setTransactionStatusModalStep] = React.useState(0) @@ -101,6 +102,10 @@ export const ModalProvider = ({ isOpen: isInteractWithVaultModalOpen, setIsOpen: setIsInteractWithVaultModalOpen, }, + editVaultModal: { + isOpen: isEditVaultModalOpen, + setIsOpen: setIsEditVaultModalOpen, + }, } return ( diff --git a/apps/dapp/src/store/lib/features/walletStore.ts b/apps/dapp/src/store/lib/features/walletStore.ts index f03bd9be..7c9be113 100644 --- a/apps/dapp/src/store/lib/features/walletStore.ts +++ b/apps/dapp/src/store/lib/features/walletStore.ts @@ -104,7 +104,14 @@ export const walletSlice = createSlice({ vault.userBalance = action.payload.vaule } }) - } + }, + setVaultFeeReceiver: (state, action: PayloadAction) => { + state.vaults.createdVaults.forEach(vault => { + if (vault.address === state.vaults.selectedVault?.address) { + vault.feeReceiver = action.payload + } + }) + }, }, extraReducers(builder) { builder.addCase(fetchDefaultAddresses.pending, (state) => { @@ -131,7 +138,8 @@ export const { setVaults, setVaultTVL, resetSelectedVault, - setVaultUserBalance + setVaultUserBalance, + setVaultFeeReceiver } = walletSlice.actions // Other code such as selectors can use the imported `RootState` type From 88a68f758c9c9ba0c589c2baa341f7ee148f80ac Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Mon, 18 Nov 2024 10:05:55 -0300 Subject: [PATCH 02/21] =?UTF-8?q?=F0=9F=93=9DAdd=20env.example=20&=20updat?= =?UTF-8?q?e=20readme.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/dapp/README.md | 17 ++++++++++++++++- apps/dapp/env.example | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 apps/dapp/env.example diff --git a/apps/dapp/README.md b/apps/dapp/README.md index a98bfa81..fd343629 100644 --- a/apps/dapp/README.md +++ b/apps/dapp/README.md @@ -2,7 +2,17 @@ This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next- ## Getting Started -First, run the development server: +### Create a .env file + +Copy the .env.example file to .env and fill in the values. + +```bash +cp env.example .env +``` + + +## Running the app +Run the development server: ```bash npm run dev @@ -14,6 +24,11 @@ pnpm dev bun dev ``` +## Install dependencies +```bash +yarn install +``` + Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. diff --git a/apps/dapp/env.example b/apps/dapp/env.example new file mode 100644 index 00000000..06c166d9 --- /dev/null +++ b/apps/dapp/env.example @@ -0,0 +1 @@ +NEXT_PUBLIC_TEST_TOKENS_ADMIN= # Test tokens admin address \ No newline at end of file From 853d0c020292fd47362a702920a0093d51ce4eff Mon Sep 17 00:00:00 2001 From: coderipper Date: Mon, 18 Nov 2024 10:18:56 -0300 Subject: [PATCH 03/21] added get_fees to the vault it retruns (u32, u32) --- apps/contracts/vault/src/fee.rs | 2 +- apps/contracts/vault/src/interface.rs | 2 ++ apps/contracts/vault/src/lib.rs | 12 +++++++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/contracts/vault/src/fee.rs b/apps/contracts/vault/src/fee.rs index 8a4dd847..5d5445c0 100644 --- a/apps/contracts/vault/src/fee.rs +++ b/apps/contracts/vault/src/fee.rs @@ -14,7 +14,7 @@ use crate::{ /// Fetches the current fee rate from the factory contract. /// The fee rate is expressed in basis points (BPS). -fn fetch_defindex_fee(e: &Env) -> u32 { +pub fn fetch_defindex_fee(e: &Env) -> u32 { let factory_address = get_factory(e); // Interacts with the factory contract to get the fee rate. e.invoke_contract( diff --git a/apps/contracts/vault/src/interface.rs b/apps/contracts/vault/src/interface.rs index 7dd40f1f..1beeef94 100644 --- a/apps/contracts/vault/src/interface.rs +++ b/apps/contracts/vault/src/interface.rs @@ -198,6 +198,8 @@ 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; + + fn get_fees(e: Env) -> (u32, u32); } pub trait AdminInterfaceTrait { diff --git a/apps/contracts/vault/src/lib.rs b/apps/contracts/vault/src/lib.rs index ece2aec1..6dc5701c 100755 --- a/apps/contracts/vault/src/lib.rs +++ b/apps/contracts/vault/src/lib.rs @@ -24,7 +24,7 @@ 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 fee::{collect_fees, fetch_defindex_fee}; use funds::{fetch_current_idle_funds, fetch_current_invested_funds, fetch_total_managed_funds}; //, fetch_idle_funds_for_asset}; use interface::{AdminInterfaceTrait, VaultManagementTrait, VaultTrait}; use investment::{check_and_execute_investments}; @@ -33,8 +33,7 @@ use models::{ OptionalSwapDetailsExactOut, }; use storage::{ - get_assets, set_asset, set_defindex_protocol_fee_receiver, set_factory, - set_total_assets, set_vault_fee, extend_instance_ttl + extend_instance_ttl, get_assets, get_vault_fee, set_asset, set_defindex_protocol_fee_receiver, set_factory, set_total_assets, set_vault_fee }; use strategies::{ get_asset_allocation_from_address, get_strategy_asset, get_strategy_client, @@ -568,6 +567,13 @@ impl VaultTrait for DeFindexVault { extend_instance_ttl(&e); calculate_asset_amounts_for_dftokens(&e, df_tokens) } + + fn get_fees(e: Env) -> (u32, u32) { + extend_instance_ttl(&e); + let defindex_protocol_fee = fetch_defindex_fee(&e); + let vault_fee = get_vault_fee(&e); + (defindex_protocol_fee, vault_fee) + } } #[contractimpl] From 96e45faf4a7ffb89c891cb8dba55b40ae1b180e4 Mon Sep 17 00:00:00 2001 From: coderipper Date: Mon, 18 Nov 2024 10:24:06 -0300 Subject: [PATCH 04/21] published addresses, new factory deployed --- public/testnet.contracts.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/testnet.contracts.json b/public/testnet.contracts.json index c00c6f35..a184b171 100644 --- a/public/testnet.contracts.json +++ b/public/testnet.contracts.json @@ -1,11 +1,11 @@ { "ids": { "hodl_strategy": "CAEYFO6TY5MMPHSX6CMUKSDHPFVLJKV4TFPHE4ZXAEER2NEIB5GSVBVG", - "defindex_factory": "CBHW3ETUDAZ4FKKEEASONLALSZAYR6IMBA2THXKRPGRAVRG7UQLZFEKP" + "defindex_factory": "CARG5QZELODA44D3NB4LIEIJSEMWLJSKA6CNOT4FFGPRYUKC7JLDDZ6L" }, "hashes": { "hodl_strategy": "fcdb4a3c11525a1f32611951741bca5bc4196f58fd1633af37d5b35d30fdf5b0", - "defindex_vault": "861b1b2734e65edb70cc632889d00ce0f07946110e3f957a76714ce9674c73ce", + "defindex_vault": "468b456399610600ae2718188e16052aabb7488493f7260d16b21c8f7dbf1001", "defindex_factory": "d6522e73d98e7826782e0b8df6c15410d1b1be95cca985b3e6f1c88a27a11a92" } } \ No newline at end of file From 8f66b084642e4a7a2f5951a4df423ac5b9c9f9e3 Mon Sep 17 00:00:00 2001 From: coderipper Date: Mon, 18 Nov 2024 15:30:04 -0300 Subject: [PATCH 05/21] fixed version and added core toml --- apps/contracts/Cargo.lock | 16 ++--- apps/contracts/Cargo.toml | 3 +- apps/contracts/strategies/core/Cargo.toml | 3 + apps/contracts/strategies/core/README.md | 77 +++++++++++++++++++++++ 4 files changed, 90 insertions(+), 9 deletions(-) create mode 100644 apps/contracts/strategies/core/README.md diff --git a/apps/contracts/Cargo.lock b/apps/contracts/Cargo.lock index 64415e34..234d602b 100644 --- a/apps/contracts/Cargo.lock +++ b/apps/contracts/Cargo.lock @@ -94,7 +94,7 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "blend_strategy" -version = "1.0.0" +version = "0.1.0" dependencies = [ "defindex-strategy-core", "soroban-sdk", @@ -281,21 +281,21 @@ dependencies = [ [[package]] name = "defindex-factory" -version = "1.0.0" +version = "0.1.0" dependencies = [ "soroban-sdk", ] [[package]] name = "defindex-strategy-core" -version = "1.0.0" +version = "0.1.0" dependencies = [ "soroban-sdk", ] [[package]] name = "defindex-vault" -version = "1.0.0" +version = "0.1.0" dependencies = [ "defindex-strategy-core", "soroban-sdk", @@ -449,7 +449,7 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "fixed_apr_strategy" -version = "1.0.0" +version = "0.1.0" dependencies = [ "defindex-strategy-core", "soroban-sdk", @@ -540,7 +540,7 @@ dependencies = [ [[package]] name = "hodl_strategy" -version = "1.0.0" +version = "0.1.0" dependencies = [ "defindex-strategy-core", "soroban-sdk", @@ -1200,7 +1200,7 @@ dependencies = [ [[package]] name = "soroswap_strategy" -version = "1.0.0" +version = "0.1.0" dependencies = [ "defindex-strategy-core", "soroban-sdk", @@ -1519,7 +1519,7 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "xycloans_adapter" -version = "1.0.0" +version = "0.1.0" dependencies = [ "defindex-strategy-core", "soroban-sdk", diff --git a/apps/contracts/Cargo.toml b/apps/contracts/Cargo.toml index 9dee20c8..79b3c9e0 100644 --- a/apps/contracts/Cargo.toml +++ b/apps/contracts/Cargo.toml @@ -6,10 +6,11 @@ exclude = [ resolver = "2" [workspace.package] -version = "1.0.0" +version = "0.1.0" edition = "2021" license = "GPL-3.0" repository = "https://github.com/paltalabs/defindex" +homepage = "https://defindex.io" [workspace.dependencies] soroban-sdk = "21.7.6" diff --git a/apps/contracts/strategies/core/Cargo.toml b/apps/contracts/strategies/core/Cargo.toml index 7949f80b..97cc36d3 100644 --- a/apps/contracts/strategies/core/Cargo.toml +++ b/apps/contracts/strategies/core/Cargo.toml @@ -6,6 +6,9 @@ license = { workspace = true } edition = { workspace = true } publish = false repository = { workspace = true } +homepage = { workspace = true } +keywords = ["soroban", "defindex", "strategy", "core", "stellar"] +categories = ["cryptography::cryptocurrencies", "no-std", "development-tools"] [dependencies] soroban-sdk = { workspace = true } diff --git a/apps/contracts/strategies/core/README.md b/apps/contracts/strategies/core/README.md new file mode 100644 index 00000000..0f59f1b9 --- /dev/null +++ b/apps/contracts/strategies/core/README.md @@ -0,0 +1,77 @@ +# DeFindex Strategy Core + +The defindex-strategy-core package is a foundational library designed to facilitate the development of strategies for DeFindex. It provides reusable abstractions and utilities that streamline the creation, management, and integration of strategies into the DeFindex ecosystem. + +### Features + +- **Reusable Events**: Predefined events to log actions such as deposits, withdrawals, and harvests. +- **Custom Errors**: A unified error handling system to ensure consistent and informative feedback across strategies. +- **Core Abstractions**: Base traits and utilities to define and implement strategies with minimal boilerplate. + +### Structure + +This package includes the following modules: +1. **Error**: Provides custom error types to handle various edge cases and ensure smooth execution. +2. **Event**: Includes predefined events for logging and monitoring strategy activity. +3. **Core Traits**: Defines the DeFindexStrategyTrait, which serves as the contract for developing new strategies. + +### Installation + +Add the defindex-strategy-core package to your Cargo.toml dependencies: + +```toml +[dependencies] +defindex-strategy-core = "0.1.0" +``` + +### Usage + +Here is a simple example of how to use this package to build a custom strategy: + +1. Import the Core Library +```rust +use defindex_strategy_core::{DeFindexStrategyTrait, StrategyError, event}; +``` + +2. Implement the Strategy Trait + +Define your custom strategy by implementing the DeFindexStrategyTrait: +```rust +#[contract] +struct MyCustomStrategy; + +#[contractimpl] +impl DeFindexStrategyTrait for MyCustomStrategy { + fn initialize(e: Env, asset: Address, init_args: Vec) -> Result<(), StrategyError> { + // Initialization logic + Ok(()) + } + + fn deposit(e: Env, amount: i128, from: Address) -> Result<(), StrategyError> { + // Deposit logic + Ok(()) + } + + fn withdraw(e: Env, amount: i128, from: Address) -> Result { + // Withdrawal logic + Ok(amount) + } + + fn balance(e: Env, from: Address) -> Result { + // Balance check logic + Ok(0) + } + + fn harvest(e: Env, from: Address) -> Result<(), StrategyError> { + // Harvest logic + Ok(()) + } +} +``` + +3. Emit Events + +Use the event module to log actions: +```rust +event::emit_deposit(&e, String::from("MyCustomStrategy"), amount, from.clone()); +``` \ No newline at end of file From 742aa4f45fe84a3b26303e24532a5ddbd083150a Mon Sep 17 00:00:00 2001 From: coderipper Date: Mon, 18 Nov 2024 15:30:58 -0300 Subject: [PATCH 06/21] publish tru --- apps/contracts/strategies/core/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/contracts/strategies/core/Cargo.toml b/apps/contracts/strategies/core/Cargo.toml index 97cc36d3..c597e1b6 100644 --- a/apps/contracts/strategies/core/Cargo.toml +++ b/apps/contracts/strategies/core/Cargo.toml @@ -4,7 +4,7 @@ version = { workspace = true } authors = ["coderipper "] license = { workspace = true } edition = { workspace = true } -publish = false +publish = true repository = { workspace = true } homepage = { workspace = true } keywords = ["soroban", "defindex", "strategy", "core", "stellar"] From 4c589625993b1290fb1ad4d3eeb6bd6a10abcde5 Mon Sep 17 00:00:00 2001 From: coderipper Date: Mon, 18 Nov 2024 15:34:01 -0300 Subject: [PATCH 07/21] added description --- apps/contracts/strategies/core/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/contracts/strategies/core/Cargo.toml b/apps/contracts/strategies/core/Cargo.toml index c597e1b6..a32161a9 100644 --- a/apps/contracts/strategies/core/Cargo.toml +++ b/apps/contracts/strategies/core/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "defindex-strategy-core" +description = "A foundational library for developing and integrating strategies into the DeFindex ecosystem, providing reusable abstractions, events, and custom error handling." version = { workspace = true } authors = ["coderipper "] license = { workspace = true } From fe508d894db6f87ef0de6c3d14dd2a9b9c2109bf Mon Sep 17 00:00:00 2001 From: coderipper Date: Mon, 18 Nov 2024 15:38:32 -0300 Subject: [PATCH 08/21] updated scf rtacker --- scf-tracker.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scf-tracker.md b/scf-tracker.md index f10d6fc0..0366cf32 100644 --- a/scf-tracker.md +++ b/scf-tracker.md @@ -34,7 +34,7 @@ - **Result:** - ✅ Code available on [GitHub](https://github.com/paltalabs/defindex/tree/main/apps/contracts/strategies) - - 🛠️ Adapter Struct published at crates.io + - ✅ Adapter Struct published at crates.io - 🛠️ SEP proposal --- From fc9b2198e14781f0e86b87ff0b6f1b50af5ea7e8 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Mon, 18 Nov 2024 20:25:56 -0300 Subject: [PATCH 09/21] =?UTF-8?q?=F0=9F=A9=B9Fix=20resetNewVault?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/DeployVault/ConfirmDelpoyModal.tsx | 4 +++- apps/dapp/src/store/lib/features/vaultStore.ts | 11 ++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/apps/dapp/src/components/DeployVault/ConfirmDelpoyModal.tsx b/apps/dapp/src/components/DeployVault/ConfirmDelpoyModal.tsx index eec5324c..616acc00 100644 --- a/apps/dapp/src/components/DeployVault/ConfirmDelpoyModal.tsx +++ b/apps/dapp/src/components/DeployVault/ConfirmDelpoyModal.tsx @@ -18,6 +18,7 @@ import { ModalContext, TransactionStatusModalStatus } from "@/contexts"; import { AccordionItems, FormControlInterface, VaultPreview } from "./VaultPreview"; import { DialogBody, DialogCloseTrigger, DialogFooter, DialogHeader, DialogTitle } from "../ui/dialog"; import { Button } from "@chakra-ui/react" +import { resetNewVault } from "@/store/lib/features/vaultStore"; interface Status { isSuccess: boolean, @@ -279,8 +280,9 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo ...newVault, address: parsedResult } - txModal.handleSuccess(result.txHash); + await txModal.handleSuccess(result.txHash); dispatch(pushVault(tempVault)); + dispatch(resetNewVault()); return result; } diff --git a/apps/dapp/src/store/lib/features/vaultStore.ts b/apps/dapp/src/store/lib/features/vaultStore.ts index c09fb6f8..7400031f 100644 --- a/apps/dapp/src/store/lib/features/vaultStore.ts +++ b/apps/dapp/src/store/lib/features/vaultStore.ts @@ -97,7 +97,16 @@ export const newVaultSlice = createSlice({ state.TVL = action.payload.TVL; }), resetNewVault: ((state) => { - state = initialState; + state.address = ""; + state.emergencyManager = ""; + state.feeReceiver = ""; + state.manager = ""; + state.name = ""; + state.symbol = ""; + state.vaultShare = 0; + state.assets = []; + state.amounts = []; + state.TVL = 0; }), } }) From 5c83021f3a305e688a6310fee0d6d31d4e9cac86 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Mon, 18 Nov 2024 20:29:18 -0300 Subject: [PATCH 10/21] =?UTF-8?q?=F0=9F=90=9BFix=20asset=20symbol=20assign?= =?UTF-8?q?ment=20in=20useVault=20hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/dapp/src/hooks/useVault.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/dapp/src/hooks/useVault.ts b/apps/dapp/src/hooks/useVault.ts index c5d6a1d6..9a363003 100644 --- a/apps/dapp/src/hooks/useVault.ts +++ b/apps/dapp/src/hooks/useVault.ts @@ -80,7 +80,8 @@ export const useVault = (vaultAddress?: string | undefined) => { ]); for (let asset of assets){ const symbol = await getTokenSymbol(asset.address, sorobanContext); - if(symbol === 'native') asset.symbol = 'XLM'; + if(symbol === 'native') asset.symbol = 'XLM' + else asset.symbol = symbol } getInvestedFunds(vaultAddress); const newData: VaultData = { From 27d9b14049b32fa1147e75bcd5f71a19b37a7264 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Mon, 18 Nov 2024 21:26:36 -0300 Subject: [PATCH 11/21] =?UTF-8?q?=F0=9F=A9=B9Remove=20unnecesary=20loading?= =?UTF-8?q?=20on=20select=20strategy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DeployVault/AddNewStrategyButton.tsx | 112 ++++++++---------- 1 file changed, 47 insertions(+), 65 deletions(-) diff --git a/apps/dapp/src/components/DeployVault/AddNewStrategyButton.tsx b/apps/dapp/src/components/DeployVault/AddNewStrategyButton.tsx index 86ff3b42..72496720 100644 --- a/apps/dapp/src/components/DeployVault/AddNewStrategyButton.tsx +++ b/apps/dapp/src/components/DeployVault/AddNewStrategyButton.tsx @@ -44,13 +44,34 @@ function AddNewStrategyButton() { const [open, setOpen] = useState(false) const [isLoading, setIsLoading] = useState(false) const [defaultStrategies, setDefaultStrategies] = useState([]) - const [asset, setAsset] = useState({ address: '', strategies: [] }) + const [selectedAsset, setSelectedAsset] = useState({ address: '', strategies: [], symbol: '' }) + const [assets, setAssets] = useState([]) const [amountInput, setAmountInput] = useState({ amount: 0, enabled: false }) + const resetForm = () => { + setSelectedAsset({ address: '', strategies: [], symbol: '' }) + setAmountInput({ amount: 0, enabled: false }) + setOpen(false) + } + + const getSymbol = async (address: string) => { + const symbol = await getTokenSymbol(address, sorobanContext) + if (!symbol) return ''; + return symbol === 'native' ? 'XLM' : symbol + } + useEffect(() => { const fetchStrategies = async () => { const tempStrategies = await getDefaultStrategies(activeChain?.name?.toLowerCase() || 'testnet') - for (const strategy of tempStrategies) { + setDefaultStrategies(tempStrategies) + } + fetchStrategies(); + }, [activeChain?.networkPassphrase]) + + useEffect(() => { + const fetchStrategies = async () => { + const rawDefaultStrategies = await getDefaultStrategies(activeChain?.name?.toLowerCase() || 'testnet') + const defaultStrategiesWithAssets = await Promise.all(rawDefaultStrategies.map(async (strategy) => { const assetAddress = await strategyCallback( strategy.address, StrategyMethod.ASSET, @@ -62,66 +83,39 @@ function AddNewStrategyButton() { return asset; }) const assetSymbol = await getSymbol(assetAddress) - setAsset({ ...asset, address: assetAddress, symbol: assetSymbol! }) + const asset = { address: assetAddress, strategies: [strategy], symbol: assetSymbol! } + return asset } - setDefaultStrategies(tempStrategies) + )) + setAssets(defaultStrategiesWithAssets) } fetchStrategies(); }, [activeChain?.networkPassphrase]) - const resetForm = () => { - setAsset({ address: '', strategies: [] }) - setAmountInput({ amount: 0, enabled: false }) - setOpen(false) - } - - const getSymbol = async (address: string) => { - const symbol = await getTokenSymbol(address, sorobanContext) - if (!symbol) return ''; - return symbol === 'native' ? 'XLM' : symbol + const handleSelectStrategy = (value: boolean, strategy: Strategy) => { + const selectedAsset = assets.find((asset) => asset.strategies.some((str) => str.address === strategy.address)) + if (selectedAsset) { + setSelectedAsset(selectedAsset) + } } - const handleSelectStrategy = (value: boolean, strategy: Strategy) => { - setIsLoading(true) - switch (value) { - case true: - const fetchAssets = async () => { - try { - const asset = await strategyCallback( - strategy.address, - StrategyMethod.ASSET, - undefined, - false - ).then((result) => { - const resultScval = result as xdr.ScVal; - const asset = scValToNative(resultScval); - return asset; - }); - const symbol = await getSymbol(asset); - const newAsset = { address: asset, symbol: symbol!, strategies: [strategy] } - console.log(newAsset) - setAsset({ address: asset, symbol: symbol!, strategies: [strategy] }) - } catch (error) { - console.error(error); - } finally { - setIsLoading(false) - } - }; - fetchAssets(); - break - case false: - setAsset({ ...asset, strategies: asset.strategies.filter(str => str.address !== strategy.address) }) - setIsLoading(false) - break + const handleAmountInput = async (e: any) => { + const input = e.target.value + const decimalRegex = /^(\d+)?(\.\d{0,7})?$/ + if (!decimalRegex.test(input)) return + if (input.startsWith('.')) { + setAmountInput({ amount: 0 + input, enabled: true }); + return } + setAmountInput({ amount: input, enabled: true }); } const addAsset = async () => { const newAsset: Asset = { - address: asset.address, - strategies: asset.strategies, - symbol: asset.symbol + address: selectedAsset.address, + strategies: selectedAsset.strategies, + symbol: selectedAsset.symbol } await dispatch(pushAsset(newAsset)) if (amountInput.enabled && amountInput.amount! > 0) { @@ -130,18 +124,6 @@ function AddNewStrategyButton() { resetForm() } - - - const handleAmountInput = async (e: any) => { - const input = e.target.value - const decimalRegex = /^(\d+)?(\.\d{0,7})?$/ - if (!decimalRegex.test(input)) return - if (input.startsWith('.')) { - setAmountInput({ amount: 0 + input, enabled: true }); - return - } - setAmountInput({ amount: input, enabled: true }); - } return ( { setOpen(e.open) }} placement={'center'}> @@ -161,11 +143,11 @@ function AddNewStrategyButton() { {isLoading && } {!isLoading && str.address === strategy.address)} + checked={selectedAsset.strategies.some((str) => str.address === strategy.address)} onCheckedChange={(e) => handleSelectStrategy(!!e.checked, strategy)} label={strategy.name} />} - {asset.strategies.some((str) => str.address === strategy.address) && + {selectedAsset.strategies.some((str) => str.address === strategy.address) && Initial deposit: @@ -187,7 +169,7 @@ function AddNewStrategyButton() { @@ -204,7 +186,7 @@ function AddNewStrategyButton() { Close Date: Mon, 18 Nov 2024 21:39:28 -0300 Subject: [PATCH 12/21] =?UTF-8?q?=F0=9F=A9=B9Fix=20vault=20tvl=20after=20c?= =?UTF-8?q?reate=20&=20deposit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/InteractWithVault/InteractWithVault.tsx | 2 +- apps/dapp/src/components/ManageVaults/AllVaults.tsx | 6 +++++- apps/dapp/src/store/lib/features/walletStore.ts | 8 ++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/dapp/src/components/InteractWithVault/InteractWithVault.tsx b/apps/dapp/src/components/InteractWithVault/InteractWithVault.tsx index 427033be..cb15e024 100644 --- a/apps/dapp/src/components/InteractWithVault/InteractWithVault.tsx +++ b/apps/dapp/src/components/InteractWithVault/InteractWithVault.tsx @@ -71,7 +71,7 @@ export const InteractWithVault = () => { ).finally(async () => { const newTVL = await vault.getTVL(selectedVault?.address!) const parsedNewTVL = Number(newTVL) / 10 ** 7 - dispatch(setVaultTVL(parsedNewTVL)) + dispatch(setVaultTVL({ value: parsedNewTVL, address: selectedVault?.address! })) }); } catch (error: any) { diff --git a/apps/dapp/src/components/ManageVaults/AllVaults.tsx b/apps/dapp/src/components/ManageVaults/AllVaults.tsx index 2e63d4b1..6e2d2590 100644 --- a/apps/dapp/src/components/ManageVaults/AllVaults.tsx +++ b/apps/dapp/src/components/ManageVaults/AllVaults.tsx @@ -6,7 +6,7 @@ import { shortenAddress } from '@/helpers/address' import { useVault } from '@/hooks/useVault' import { FactoryMethod, useFactoryCallback } from '@/hooks/useFactory' -import { setIsVaultsLoading, setVaults, setVaultUserBalance } from '@/store/lib/features/walletStore' +import { setIsVaultsLoading, setVaults, setVaultTVL, setVaultUserBalance } from '@/store/lib/features/walletStore' import { useAppDispatch, useAppSelector } from '@/store/lib/storeHooks' import { VaultData } from '@/store/lib/types' @@ -94,6 +94,10 @@ export const AllVaults = ({ if (userBalance) { dispatch(setVaultUserBalance({ address: v.address, vaule: userBalance })) } + const TVL = await vault.getTVL(v.address) + if (TVL) { + dispatch(setVaultTVL({ value: TVL, address: v.address })) + } }) } }, [createdVaults]) diff --git a/apps/dapp/src/store/lib/features/walletStore.ts b/apps/dapp/src/store/lib/features/walletStore.ts index f03bd9be..307a911b 100644 --- a/apps/dapp/src/store/lib/features/walletStore.ts +++ b/apps/dapp/src/store/lib/features/walletStore.ts @@ -88,10 +88,10 @@ export const walletSlice = createSlice({ setVaults: (state, action: PayloadAction) => { state.vaults.createdVaults = action.payload }, - setVaultTVL: (state, action: PayloadAction) => { + setVaultTVL: (state, action: PayloadAction<{address:string, value: number}>) => { state.vaults.createdVaults.forEach(vault => { - if (vault.address === state.vaults.selectedVault?.address) { - vault.TVL = action.payload + if (vault.address === action.payload.address) { + vault.TVL = action.payload.value } }) }, @@ -104,7 +104,7 @@ export const walletSlice = createSlice({ vault.userBalance = action.payload.vaule } }) - } + }, }, extraReducers(builder) { builder.addCase(fetchDefaultAddresses.pending, (state) => { From 98ca74d1dd32254adaa04dffa013add43f6d5434 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Mon, 18 Nov 2024 22:01:03 -0300 Subject: [PATCH 13/21] =?UTF-8?q?=F0=9F=90=9BFix=20user=20balance=20retrie?= =?UTF-8?q?val=20logic=20in=20AllVaults=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/ManageVaults/AllVaults.tsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/dapp/src/components/ManageVaults/AllVaults.tsx b/apps/dapp/src/components/ManageVaults/AllVaults.tsx index 6e2d2590..68274157 100644 --- a/apps/dapp/src/components/ManageVaults/AllVaults.tsx +++ b/apps/dapp/src/components/ManageVaults/AllVaults.tsx @@ -85,15 +85,11 @@ export const AllVaults = ({ useEffect(() => { getDefindexVaults() - }, [activeChain?.networkPassphrase, address]) + }, [activeChain?.networkPassphrase]) useEffect(() => { if (address) { createdVaults.forEach(async (v: VaultData) => { - const userBalance = await vault.getUserBalance(v.address, address) - if (userBalance) { - dispatch(setVaultUserBalance({ address: v.address, vaule: userBalance })) - } const TVL = await vault.getTVL(v.address) if (TVL) { dispatch(setVaultTVL({ value: TVL, address: v.address })) @@ -102,6 +98,17 @@ export const AllVaults = ({ } }, [createdVaults]) + useEffect(() => { + if (address) { + createdVaults.forEach(async (v: VaultData) => { + const userBalance = await vault.getUserBalance(v.address, address) + if (userBalance) { + dispatch(setVaultUserBalance({ address: v.address, vaule: userBalance })) + } + }) + } + }, [createdVaults, address]) + return ( {!isMobile ? ( From 3b8d48358d3191b80d904deca9b82279385f3db8 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Mon, 18 Nov 2024 22:27:06 -0300 Subject: [PATCH 14/21] =?UTF-8?q?=F0=9F=A9=B9Refactor=20ConfirmDeployModal?= =?UTF-8?q?=20and=20useVault=20hook=20by=20removing=20unused=20code=20and?= =?UTF-8?q?=20console=20logs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DeployVault/ConfirmDelpoyModal.tsx | 42 +------------------ apps/dapp/src/hooks/useVault.ts | 2 - 2 files changed, 1 insertion(+), 43 deletions(-) diff --git a/apps/dapp/src/components/DeployVault/ConfirmDelpoyModal.tsx b/apps/dapp/src/components/DeployVault/ConfirmDelpoyModal.tsx index 616acc00..f26ffd8e 100644 --- a/apps/dapp/src/components/DeployVault/ConfirmDelpoyModal.tsx +++ b/apps/dapp/src/components/DeployVault/ConfirmDelpoyModal.tsx @@ -41,7 +41,6 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo const { activeChain, address } = sorobanContext; const factory = useFactoryCallback(); const newVault: NewVaultState = useAppSelector(state => state.newVault); - //const strategies: Strategy[] = newVault.strategies; const indexName = useAppSelector(state => state.newVault.name) const indexSymbol = useAppSelector(state => state.newVault.symbol) const indexShare = useAppSelector(state => state.newVault.vaultShare) @@ -50,14 +49,6 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo const feeReceiverString = useAppSelector(state => state.newVault.feeReceiver) const { transactionStatusModal: txModal } = useContext(ModalContext); const dispatch = useAppDispatch(); - const [assets, setAssets] = useState([]); - const [status, setStatus] = useState({ - isSuccess: false, - hasError: false, - network: undefined, - message: undefined, - txHash: undefined - }); const [deployDisabled, setDeployDisabled] = useState(true); @@ -74,35 +65,6 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo } }, [managerString, emergencyManagerString, feeReceiverString]) - - - - /* useEffect(() => { - const newChartData: ChartData[] = strategies.map((strategy: Strategy, index: number) => { - return { - id: index, - label: strategy.name, - address: strategy.address, - value: strategy.share, - } - }); - const total = newChartData.reduce((acc: number, curr: ChartData) => acc + curr.value, 0) - if (total == 100) { - setChartData(newChartData); - return; - } else { - newChartData.push({ - id: newChartData.length, - label: 'Unassigned', - value: 100 - newChartData.reduce((acc: number, curr: ChartData) => acc + curr.value, 0), - address: undefined, - color: '#e0e0e0' - }) - setChartData(newChartData); - return; - } - }, [strategies]); */ - const autoCloseModal = async () => { await new Promise(resolve => setTimeout(resolve, 30000)) txModal.resetModal(); @@ -307,14 +269,12 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo - {(activeStep == 0 && !status.hasError) && ( - )} + diff --git a/apps/dapp/src/hooks/useVault.ts b/apps/dapp/src/hooks/useVault.ts index 9a363003..aab1737c 100644 --- a/apps/dapp/src/hooks/useVault.ts +++ b/apps/dapp/src/hooks/useVault.ts @@ -182,7 +182,6 @@ export const useVault = (vaultAddress?: string | undefined) => { assets.forEach((asset)=>{ idleFunds.push({address: asset, amount: Number(rawIdleFunds[asset]) / 10 ** 7}) }) - console.log(idleFunds); return idleFunds; } catch (error) { console.error(error); @@ -196,7 +195,6 @@ export const useVault = (vaultAddress?: string | undefined) => { assets.forEach((asset)=>{ investedFunds.push({address: asset, amount: Number(rawInvestedFunds[asset]) / 10 ** 7}) }) - console.log(investedFunds); return investedFunds; } catch (error) { console.error(error); From 26c188f919b47d213332233d5a4e7542d23e856d Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Mon, 18 Nov 2024 23:12:20 -0300 Subject: [PATCH 15/21] =?UTF-8?q?=F0=9F=A9=B9Refactor=20AddNewStrategyButt?= =?UTF-8?q?on=20and=20ConfirmDeployModal=20to=20improve=20state=20manageme?= =?UTF-8?q?nt=20and=20add=20setAmountByAddress=20action?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DeployVault/AddNewStrategyButton.tsx | 23 +++++++++++-------- .../DeployVault/ConfirmDelpoyModal.tsx | 13 ++++++++--- .../dapp/src/store/lib/features/vaultStore.ts | 9 +++++++- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/apps/dapp/src/components/DeployVault/AddNewStrategyButton.tsx b/apps/dapp/src/components/DeployVault/AddNewStrategyButton.tsx index 72496720..feef64df 100644 --- a/apps/dapp/src/components/DeployVault/AddNewStrategyButton.tsx +++ b/apps/dapp/src/components/DeployVault/AddNewStrategyButton.tsx @@ -9,8 +9,8 @@ import { } from '@/components/ui/dialog' import { getTokenSymbol } from '@/helpers/getTokenInfo' import { StrategyMethod, useStrategyCallback } from '@/hooks/useStrategy' -import { getDefaultStrategies, pushAmount, pushAsset } from '@/store/lib/features/vaultStore' -import { useAppDispatch } from '@/store/lib/storeHooks' +import { getDefaultStrategies, pushAmount, pushAsset, setAmountByAddress } from '@/store/lib/features/vaultStore' +import { useAppDispatch, useAppSelector } from '@/store/lib/storeHooks' import { Asset, Strategy } from '@/store/lib/types' import { Button, @@ -42,8 +42,8 @@ function AddNewStrategyButton() { const { activeChain } = useSorobanReact() const strategyCallback = useStrategyCallback(); const [open, setOpen] = useState(false) - const [isLoading, setIsLoading] = useState(false) - const [defaultStrategies, setDefaultStrategies] = useState([]) + const newVault = useAppSelector((state) => state.newVault) + const [defaultStrategies, setDefaultStrategies] = useState([]) const [selectedAsset, setSelectedAsset] = useState({ address: '', strategies: [], symbol: '' }) const [assets, setAssets] = useState([]) const [amountInput, setAmountInput] = useState({ amount: 0, enabled: false }) @@ -110,13 +110,19 @@ function AddNewStrategyButton() { } setAmountInput({ amount: input, enabled: true }); } - + const strategyExists = (strategy: Strategy) => { + const exists = newVault.assets.some((asset) => asset.strategies.some((str) => str.address === strategy.address)) + return exists + } const addAsset = async () => { const newAsset: Asset = { address: selectedAsset.address, strategies: selectedAsset.strategies, symbol: selectedAsset.symbol } + if (strategyExists(selectedAsset.strategies[0]!)) { + await dispatch(setAmountByAddress({ address: selectedAsset.address, amount: amountInput.amount })) + } await dispatch(pushAsset(newAsset)) if (amountInput.enabled && amountInput.amount! > 0) { await dispatch(pushAmount(amountInput.amount!)) @@ -141,12 +147,11 @@ function AddNewStrategyButton() { {(strategy, index) => ( - {isLoading && } - {!isLoading && str.address === strategy.address)} + str.address === strategy.address)} onCheckedChange={(e) => handleSelectStrategy(!!e.checked, strategy)} label={strategy.name} - />} + /> {selectedAsset.strategies.some((str) => str.address === strategy.address) && diff --git a/apps/dapp/src/components/DeployVault/ConfirmDelpoyModal.tsx b/apps/dapp/src/components/DeployVault/ConfirmDelpoyModal.tsx index f26ffd8e..b2930cca 100644 --- a/apps/dapp/src/components/DeployVault/ConfirmDelpoyModal.tsx +++ b/apps/dapp/src/components/DeployVault/ConfirmDelpoyModal.tsx @@ -76,7 +76,6 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo autoCloseModal(); } }, [txModal.status]) - const activeStep: number = 0; const [buttonText, setButtonText] = useState('') const [accordionValue, setAccordionValue] = useState([AccordionItems.STRATEGY_DETAILS]) const [formControl, setFormControl] = useState({ @@ -107,6 +106,7 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo } }, [managerString, emergencyManagerString, feeReceiverString, indexShare]) + const deployDefindex = async () => { if (managerString === '' || emergencyManagerString === '') { console.log('please fill manager config') @@ -170,9 +170,13 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo ]); }); const assetParamsScValVec = xdr.ScVal.scvVec(assetParamsScVal); - const amountsScVal = newVault.amounts.map((amount) => { - return nativeToScVal((amount * Math.pow(10, 7)), { type: "i128" }); + const amountsScVal = newVault.assets.map((asset, index) => { + if (newVault.amounts.length === 0) return nativeToScVal(0, { type: "i128" }); + return nativeToScVal((newVault.amounts[index]!) * 100, { type: "i128" }); }); + /* const amountsScVal = newVault.amounts.map((amount) => { + return nativeToScVal((amount * Math.pow(10, 7)), { type: "i128" }); + }); */ const amountsScValVec = xdr.ScVal.scvVec(amountsScVal); /* fn create_defindex_vault( emergency_manager: address, @@ -185,6 +189,7 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo salt: bytesn<32>) -> result */ let result: any; + if (amountsScVal.length === 0) { const createDefindexParams: xdr.ScVal[] = [ emergencyManager.toScVal(), @@ -205,6 +210,7 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo } catch (e: any) { console.error(e) + dispatch(resetNewVault()); txModal.handleError(e.toString()); return } @@ -232,6 +238,7 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo } catch (e: any) { console.error(e) + dispatch(resetNewVault()); txModal.handleError(e.toString()); return } diff --git a/apps/dapp/src/store/lib/features/vaultStore.ts b/apps/dapp/src/store/lib/features/vaultStore.ts index 7400031f..889aa76c 100644 --- a/apps/dapp/src/store/lib/features/vaultStore.ts +++ b/apps/dapp/src/store/lib/features/vaultStore.ts @@ -85,6 +85,12 @@ export const newVaultSlice = createSlice({ pushAmount: ((state, action: PayloadAction) => { state.amounts?.push(action.payload); }), + setAmountByAddress: ((state, action: PayloadAction<{address:string, amount:number}>) => { + const index = state.assets.findIndex(asset => asset.address === action.payload.address); + if(index !== -1) { + state.amounts[index] = action.payload.amount; + } + }), removeAmountByIndex: ((state, action: PayloadAction) => { state.amounts?.splice(action.payload, 1); }), @@ -124,7 +130,8 @@ export const { removeAmountByIndex, resetAssets, openEditVault, - resetNewVault + resetNewVault, + setAmountByAddress } = newVaultSlice.actions // Other code such as selectors can use the imported `RootState` type From cb98c0cb77c0eb7ab196846637762f4dba891e88 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Tue, 19 Nov 2024 09:09:50 -0300 Subject: [PATCH 16/21] =?UTF-8?q?=F0=9F=90=9BFix=20amount=20handling=20in?= =?UTF-8?q?=20AddNewStrategyButton=20and=20update=20initial=20deposit=20di?= =?UTF-8?q?splay=20in=20DeployVault?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/dapp/src/components/DeployVault/AddNewStrategyButton.tsx | 4 ++++ apps/dapp/src/components/DeployVault/DeployVault.tsx | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/dapp/src/components/DeployVault/AddNewStrategyButton.tsx b/apps/dapp/src/components/DeployVault/AddNewStrategyButton.tsx index feef64df..313f43ce 100644 --- a/apps/dapp/src/components/DeployVault/AddNewStrategyButton.tsx +++ b/apps/dapp/src/components/DeployVault/AddNewStrategyButton.tsx @@ -121,7 +121,11 @@ function AddNewStrategyButton() { symbol: selectedAsset.symbol } if (strategyExists(selectedAsset.strategies[0]!)) { + if (amountInput.enabled && amountInput.amount! > 0) { await dispatch(setAmountByAddress({ address: selectedAsset.address, amount: amountInput.amount })) + } else if (amountInput.enabled == false || amountInput.amount! == 0) { + await dispatch(setAmountByAddress({ address: selectedAsset.address, amount: 0 })) + } } await dispatch(pushAsset(newAsset)) if (amountInput.enabled && amountInput.amount! > 0) { diff --git a/apps/dapp/src/components/DeployVault/DeployVault.tsx b/apps/dapp/src/components/DeployVault/DeployVault.tsx index 987f13aa..7432b96d 100644 --- a/apps/dapp/src/components/DeployVault/DeployVault.tsx +++ b/apps/dapp/src/components/DeployVault/DeployVault.tsx @@ -103,7 +103,7 @@ export const DeployVault = () => { Strategy Address: {shortenAddress(strategy.address)} - {amounts[j] &&
  • Initial deposit: ${amounts[j]} {asset.symbol}
  • } + {(amounts[j]! > 0) &&
  • Initial deposit: ${amounts[j]} {asset.symbol}
  • }
    )} From 2e94614a65a535c45532ec0c38b3ae6f72813b24 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Tue, 19 Nov 2024 20:58:19 -0300 Subject: [PATCH 17/21] =?UTF-8?q?=F0=9F=A9=B9Refactor=20ConfirmDeployModal?= =?UTF-8?q?=20and=20InteractWithVault=20components=20to=20enhance=20state?= =?UTF-8?q?=20management=20and=20integrate=20vault=20data=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DeployVault/ConfirmDelpoyModal.tsx | 28 +++++++++++++++---- .../InteractWithVault/InteractWithVault.tsx | 8 ++++-- .../components/ManageVaults/InspectVault.tsx | 5 ++-- apps/dapp/src/providers/modal-provider.tsx | 13 ++++----- .../src/store/lib/features/walletStore.ts | 25 ++++++++++++++++- 5 files changed, 60 insertions(+), 19 deletions(-) diff --git a/apps/dapp/src/components/DeployVault/ConfirmDelpoyModal.tsx b/apps/dapp/src/components/DeployVault/ConfirmDelpoyModal.tsx index b2930cca..6293b532 100644 --- a/apps/dapp/src/components/DeployVault/ConfirmDelpoyModal.tsx +++ b/apps/dapp/src/components/DeployVault/ConfirmDelpoyModal.tsx @@ -10,7 +10,7 @@ import { randomBytes } from "crypto"; import { useAppDispatch, useAppSelector } from "@/store/lib/storeHooks" import { pushVault } from '@/store/lib/features/walletStore' -import { Asset, NewVaultState } from "@/store/lib/types"; +import { Asset, NewVaultState, VaultData } from "@/store/lib/types"; import { useFactoryCallback, FactoryMethod } from '@/hooks/useFactory' import { ModalContext, TransactionStatusModalStatus } from "@/contexts"; @@ -19,6 +19,7 @@ import { AccordionItems, FormControlInterface, VaultPreview } from "./VaultPrevi import { DialogBody, DialogCloseTrigger, DialogFooter, DialogHeader, DialogTitle } from "../ui/dialog"; import { Button } from "@chakra-ui/react" import { resetNewVault } from "@/store/lib/features/vaultStore"; +import { useVault } from "@/hooks/useVault"; interface Status { isSuccess: boolean, @@ -47,8 +48,9 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo const managerString = useAppSelector(state => state.newVault.manager) const emergencyManagerString = useAppSelector(state => state.newVault.emergencyManager) const feeReceiverString = useAppSelector(state => state.newVault.feeReceiver) - const { transactionStatusModal: txModal } = useContext(ModalContext); + const { transactionStatusModal: txModal, deployVaultModal: deployModal } = useContext(ModalContext); const dispatch = useAppDispatch(); + const { getIdleFunds, getInvestedFunds, getTVL, getUserBalance } = useVault() const [deployDisabled, setDeployDisabled] = useState(true); @@ -118,6 +120,7 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo setAccordionValue([AccordionItems.FEES_CONFIGS]) return } + deployModal.setIsOpen(false) txModal.initModal(); const vaultName = nativeToScVal(indexName, { type: "string" }) @@ -190,7 +193,8 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo */ let result: any; - if (amountsScVal.length === 0) { + + if (newVault.amounts.length === 0) { const createDefindexParams: xdr.ScVal[] = [ emergencyManager.toScVal(), feeReceiver.toScVal(), @@ -245,10 +249,24 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo } const parsedResult: string = scValToNative(result.returnValue); if (parsedResult.length !== 56) throw new Error('Invalid result') - const tempVault: any = { + const idleFunds = newVault.assets.map((asset, index) => { + return { + address: asset.address, + amount: newVault.amounts[index] || 0 + } + }) + const tempVault: VaultData = { ...newVault, - address: parsedResult + address: parsedResult, + emergencyManager: emergencyManagerString, + feeReceiver: feeReceiverString, + manager: managerString, + TVL: 0, + totalSupply: 0, + idleFunds: idleFunds, + investedFunds: [{ address: '', amount: 0 }], } + console.log(tempVault) await txModal.handleSuccess(result.txHash); dispatch(pushVault(tempVault)); dispatch(resetNewVault()); diff --git a/apps/dapp/src/components/InteractWithVault/InteractWithVault.tsx b/apps/dapp/src/components/InteractWithVault/InteractWithVault.tsx index cb15e024..b160c4a9 100644 --- a/apps/dapp/src/components/InteractWithVault/InteractWithVault.tsx +++ b/apps/dapp/src/components/InteractWithVault/InteractWithVault.tsx @@ -66,8 +66,11 @@ export const InteractWithVault = () => { selectedVault?.address!, params, true, - ).then((res) => - statusModal.handleSuccess(res.txHash) + ).then((res) => { + interactModal.setIsOpen(false), + inspectModal.setIsOpen(false), + statusModal.handleSuccess(res.txHash) + } ).finally(async () => { const newTVL = await vault.getTVL(selectedVault?.address!) const parsedNewTVL = Number(newTVL) / 10 ** 7 @@ -76,6 +79,7 @@ export const InteractWithVault = () => { } catch (error: any) { console.error('Error:', error) + interactModal.setIsOpen(false) statusModal.handleError(error.toString()) } } diff --git a/apps/dapp/src/components/ManageVaults/InspectVault.tsx b/apps/dapp/src/components/ManageVaults/InspectVault.tsx index 2678d439..81ff6d08 100644 --- a/apps/dapp/src/components/ManageVaults/InspectVault.tsx +++ b/apps/dapp/src/components/ManageVaults/InspectVault.tsx @@ -2,7 +2,7 @@ import { useSorobanReact } from "@soroban-react/core" import { shortenAddress } from "@/helpers/address" -import { VaultMethod } from "@/hooks/useVault" +import { useVault, VaultMethod } from "@/hooks/useVault" import { useAppSelector } from "@/store/lib/storeHooks" import { Asset, AssetAmmount, VaultData } from "@/store/lib/types" @@ -12,7 +12,6 @@ import { DialogBody, DialogContent, DialogFooter, DialogHeader } from "../ui/dia import { FaRegEdit } from "react-icons/fa" import { IoClose } from "react-icons/io5" - export const InspectVault = ({ handleOpenDeployVault, handleOpenInteract, @@ -24,8 +23,8 @@ export const InspectVault = ({ }) => { const selectedVault: VaultData | undefined = useAppSelector(state => state.wallet.vaults.selectedVault) const { address } = useSorobanReact() - if (!selectedVault) return null + if (!selectedVault) return null return ( diff --git a/apps/dapp/src/providers/modal-provider.tsx b/apps/dapp/src/providers/modal-provider.tsx index 8564ab67..3b4beeaf 100644 --- a/apps/dapp/src/providers/modal-provider.tsx +++ b/apps/dapp/src/providers/modal-provider.tsx @@ -26,10 +26,6 @@ export const ModalProvider = ({ const [txHash, setTxHash] = React.useState('') const handleResetModal = () => { - console.log('resetting modal') - setIsDeployVaultModalOpen(false) - setIsInspectVaultModalOpen(false) - setIsInteractWithVaultModalOpen(false) setIsTransactionStatusModalOpen(false) setTransactionStatusModalStep(0) setTransactionStatusModalStatus(TransactionStatusModalStatus.PENDING) @@ -38,23 +34,23 @@ export const ModalProvider = ({ setTxHash('') } + const handleFirstStep = setTimeout(() => setTransactionStatusModalStep(1), 3000) useEffect(() => { - const handleFirstStep = setTimeout(() => setTransactionStatusModalStep(1), 3000) if (isTransactionStatusModalOpen && transactionStatusModalStep === 0 && transactionStatusModalStatus === TransactionStatusModalStatus.PENDING) { - console.log('modal is open') handleFirstStep - } else { + } else if (transactionStatusModalStatus !== TransactionStatusModalStatus.PENDING) { clearTimeout(handleFirstStep) + setTransactionStatusModalStep(2) } }, [isTransactionStatusModalOpen, transactionStatusModalStep, transactionStatusModalStatus]) const handleInitModal = () => { - console.log('init modal') handleResetModal() setIsTransactionStatusModalOpen(true) } const handleError = (error: string) => { + clearTimeout(handleFirstStep) setTransactionStatusModalError(error) setTransactionStatusModalStatus(TransactionStatusModalStatus.ERROR) setTransactionStatusModalStep(2) @@ -62,6 +58,7 @@ export const ModalProvider = ({ } const handleSuccess = (txHash: string) => { + clearTimeout(handleFirstStep) setTxHash(txHash) setTransactionStatusModalStatus(TransactionStatusModalStatus.SUCCESS) setTransactionStatusModalStep(2) diff --git a/apps/dapp/src/store/lib/features/walletStore.ts b/apps/dapp/src/store/lib/features/walletStore.ts index 307a911b..47b7bf83 100644 --- a/apps/dapp/src/store/lib/features/walletStore.ts +++ b/apps/dapp/src/store/lib/features/walletStore.ts @@ -5,6 +5,7 @@ import { ChainMetadata } from '@soroban-react/types' import vaults from '@/constants/constants.json' import { Networks } from '@stellar/stellar-sdk' import { SelectedVault, VaultData, WalletState } from '../types' +import { VaultMethod } from '@/hooks/useVault' const getDefaultVaults = async (network: string) => { const filteredVaults = vaults.filter(vault => { @@ -105,6 +106,27 @@ export const walletSlice = createSlice({ } }) }, + updateVaultData: (state, action: PayloadAction>) => { + state.vaults.createdVaults.forEach(vault => { + if (vault.address === action.payload.address) { + Object.assign(vault, action.payload) + } + }) + state.vaults.selectedVault = { + address: action.payload.address ?? state.vaults.selectedVault?.address ?? '', + name: action.payload.name ?? state.vaults.selectedVault?.name ?? '', + manager: action.payload.manager ?? state.vaults.selectedVault?.manager ?? '', + emergencyManager: action.payload.emergencyManager ?? state.vaults.selectedVault?.emergencyManager ?? '', + feeReceiver: action.payload.feeReceiver ?? state.vaults.selectedVault?.feeReceiver ?? '', + TVL: action.payload.TVL ?? state.vaults.selectedVault?.TVL ?? 0, + userBalance: action.payload.userBalance ?? state.vaults.selectedVault?.userBalance ?? 0, + assets: action.payload.assets ?? state.vaults.selectedVault?.assets ?? [], + idleFunds: action.payload.idleFunds ?? state.vaults.selectedVault?.idleFunds ?? [], + investedFunds: action.payload.investedFunds ?? state.vaults.selectedVault?.investedFunds ?? [], + totalSupply: action.payload.totalSupply ?? state.vaults.selectedVault?.totalSupply ?? 0, + method: state.vaults.selectedVault?.method || VaultMethod.DEPOSIT, + } + }, }, extraReducers(builder) { builder.addCase(fetchDefaultAddresses.pending, (state) => { @@ -131,7 +153,8 @@ export const { setVaults, setVaultTVL, resetSelectedVault, - setVaultUserBalance + setVaultUserBalance, + updateVaultData } = walletSlice.actions // Other code such as selectors can use the imported `RootState` type From b513ec59a538742e02d5faa49496094b3698310f Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Wed, 20 Nov 2024 09:27:58 -0300 Subject: [PATCH 18/21] =?UTF-8?q?=F0=9F=A9=B9Refactor=20InteractWithVault?= =?UTF-8?q?=20component=20to=20enhance=20user=20balance=20handling=20and?= =?UTF-8?q?=20update=20vault=20data=20management?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InteractWithVault/InteractWithVault.tsx | 43 ++++++++++++------- .../src/store/lib/features/walletStore.ts | 14 ------ 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/apps/dapp/src/components/InteractWithVault/InteractWithVault.tsx b/apps/dapp/src/components/InteractWithVault/InteractWithVault.tsx index b160c4a9..04176201 100644 --- a/apps/dapp/src/components/InteractWithVault/InteractWithVault.tsx +++ b/apps/dapp/src/components/InteractWithVault/InteractWithVault.tsx @@ -3,8 +3,8 @@ import { Address, nativeToScVal, xdr } from '@stellar/stellar-sdk' import { useSorobanReact } from '@soroban-react/core' import { useAppDispatch, useAppSelector } from '@/store/lib/storeHooks' -import { setVaultTVL } from '@/store/lib/features/walletStore' -import { Strategy } from '@/store/lib/types' +import { setVaultTVL, setVaultUserBalance, updateVaultData } from '@/store/lib/features/walletStore' +import { Strategy, VaultData } from '@/store/lib/types' import { VaultMethod, useVaultCallback, useVault } from '@/hooks/useVault' import { ModalContext } from '@/contexts' @@ -36,7 +36,7 @@ export const InteractWithVault = () => { const { transactionStatusModal: statusModal, interactWithVaultModal: interactModal, inspectVaultModal: inspectModal } = useContext(ModalContext) const vaultOperation = async () => { - if (!address || !vaultMethod) return; + if (!address || !vaultMethod || !selectedVault.address) return; if (!amount) throw new Error('Amount is required'); const parsedAmount = parseFloat(amount.toString()) const convertedAmount = parsedAmount * Math.pow(10, 7) @@ -52,42 +52,53 @@ export const InteractWithVault = () => { }; if (vaultMethod === VaultMethod.WITHDRAW) { const withdrawAmount = ((amount * selectedVault.totalSupply) / selectedVault.TVL) - const convertedWithdrawAmount = withdrawAmount * Math.pow(10, 7) + const truncatedWithdrawAmount = Math.floor(withdrawAmount * 1e7) / 1e7; + const convertedWithdrawAmount = Number(truncatedWithdrawAmount) * Math.pow(10, 7) const withdrawParams: xdr.ScVal[] = [ - nativeToScVal(convertedWithdrawAmount, { type: "i128" }), + nativeToScVal(Math.ceil(convertedWithdrawAmount), { type: "i128" }), new Address(address).toScVal(), ] params = withdrawParams }; - console.log('Vault method:', vaultMethod) try { const result = await vaultCB( vaultMethod!, selectedVault?.address!, params, true, - ).then((res) => { - interactModal.setIsOpen(false), - inspectModal.setIsOpen(false), - statusModal.handleSuccess(res.txHash) + ).then(async (res) => { + await statusModal.handleSuccess(res.txHash) } ).finally(async () => { + const newBalance = await vault.getUserBalance(selectedVault.address, address) + const newIdleFunds = await vault.getIdleFunds(selectedVault.address!) + const newInvestedFunds = await vault.getInvestedFunds(selectedVault.address) const newTVL = await vault.getTVL(selectedVault?.address!) - const parsedNewTVL = Number(newTVL) / 10 ** 7 - dispatch(setVaultTVL({ value: parsedNewTVL, address: selectedVault?.address! })) + const newVaultData: Partial = { + address: selectedVault.address, + userBalance: newBalance || 0, + idleFunds: newIdleFunds, + investedFunds: newInvestedFunds, + TVL: newTVL + } + dispatch(updateVaultData(newVaultData)) }); } catch (error: any) { console.error('Error:', error) - interactModal.setIsOpen(false) - statusModal.handleError(error.toString()) + await statusModal.handleError(error.toString()) + } finally { + set_amount(0) + await setTimeout(() => { + interactModal.setIsOpen(false) + inspectModal.setIsOpen(false) + }, 3000) } } const setAmount = (input: any) => { if (input < 0 || !selectedVault) return; if (vaultMethod === VaultMethod.WITHDRAW) { - console.log(input, selectedVault?.userBalance) if (input > selectedVault.userBalance!) return; } const decimalRegex = /^(\d+)?(\.\d{0,7})?$/; @@ -122,7 +133,7 @@ export const InteractWithVault = () => {

    Total value locked: ${selectedVault?.TVL} {selectedVault.assets[0]?.symbol}

    -

    User balance in vault: ${selectedVault?.userBalance} {selectedVault.assets[0]?.symbol}

    +

    User balance in vault: ${`${selectedVault.userBalance ?? 0}`} {selectedVault.assets[0]?.symbol}

    {vaultMethod != VaultMethod.EMERGENCY_WITHDRAW && diff --git a/apps/dapp/src/store/lib/features/walletStore.ts b/apps/dapp/src/store/lib/features/walletStore.ts index 47b7bf83..07e7db2c 100644 --- a/apps/dapp/src/store/lib/features/walletStore.ts +++ b/apps/dapp/src/store/lib/features/walletStore.ts @@ -112,20 +112,6 @@ export const walletSlice = createSlice({ Object.assign(vault, action.payload) } }) - state.vaults.selectedVault = { - address: action.payload.address ?? state.vaults.selectedVault?.address ?? '', - name: action.payload.name ?? state.vaults.selectedVault?.name ?? '', - manager: action.payload.manager ?? state.vaults.selectedVault?.manager ?? '', - emergencyManager: action.payload.emergencyManager ?? state.vaults.selectedVault?.emergencyManager ?? '', - feeReceiver: action.payload.feeReceiver ?? state.vaults.selectedVault?.feeReceiver ?? '', - TVL: action.payload.TVL ?? state.vaults.selectedVault?.TVL ?? 0, - userBalance: action.payload.userBalance ?? state.vaults.selectedVault?.userBalance ?? 0, - assets: action.payload.assets ?? state.vaults.selectedVault?.assets ?? [], - idleFunds: action.payload.idleFunds ?? state.vaults.selectedVault?.idleFunds ?? [], - investedFunds: action.payload.investedFunds ?? state.vaults.selectedVault?.investedFunds ?? [], - totalSupply: action.payload.totalSupply ?? state.vaults.selectedVault?.totalSupply ?? 0, - method: state.vaults.selectedVault?.method || VaultMethod.DEPOSIT, - } }, }, extraReducers(builder) { From 788794f13b22f3ad7f3f8b8004f53f458085a7f9 Mon Sep 17 00:00:00 2001 From: Matias Poblete <86752543+MattPoblete@users.noreply.github.com> Date: Wed, 20 Nov 2024 09:36:28 -0300 Subject: [PATCH 19/21] =?UTF-8?q?=E2=9C=A8Add=20clipboard=20functionality?= =?UTF-8?q?=20for=20vault=20addresses?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InteractWithVault/InteractWithVault.tsx | 16 ++++---- .../components/ManageVaults/InspectVault.tsx | 10 ++++- apps/dapp/src/components/ui/clipboard.tsx | 39 ++++++++++--------- 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/apps/dapp/src/components/InteractWithVault/InteractWithVault.tsx b/apps/dapp/src/components/InteractWithVault/InteractWithVault.tsx index 04176201..2fb0431d 100644 --- a/apps/dapp/src/components/InteractWithVault/InteractWithVault.tsx +++ b/apps/dapp/src/components/InteractWithVault/InteractWithVault.tsx @@ -23,6 +23,7 @@ import { NativeSelectField, HStack, } from '@chakra-ui/react' +import { ClipboardIconButton, ClipboardRoot } from '../ui/clipboard' export const InteractWithVault = () => { const [amount, set_amount] = useState(0) @@ -121,13 +122,14 @@ export const InteractWithVault = () => {

    Vault address:

    -