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 42d76f47..53954d92 100644
--- a/apps/dapp/app/page.tsx
+++ b/apps/dapp/app/page.tsx
@@ -1,8 +1,8 @@
"use client";
+import { HStack } from '@chakra-ui/react'
+import { useSorobanReact } from '@soroban-react/core'
import ManageVaults from '@/components/ManageVaults/ManageVaults';
import { TestTokens } from '@/components/TestTokens';
-import { Container } from '@chakra-ui/react';
-import { useSorobanReact } from '@soroban-react/core';
import {
ArcElement,
Chart as ChartJS,
@@ -14,11 +14,8 @@ ChartJS.register(ArcElement);
export default function Home() {
const { address } = useSorobanReact()
return (
-
-
- {address && ()}
-
-
-
+
+
+
);
}
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/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