diff --git a/apps/dapp/src/components/DeployVault/AddNewStrategyButton.tsx b/apps/dapp/src/components/DeployVault/AddNewStrategyButton.tsx index 86ff3b42..e7da6c9f 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,15 +42,36 @@ function AddNewStrategyButton() { const { activeChain } = useSorobanReact() const strategyCallback = useStrategyCallback(); const [open, setOpen] = useState(false) - const [isLoading, setIsLoading] = useState(false) - const [defaultStrategies, setDefaultStrategies] = useState([]) - const [asset, setAsset] = useState({ address: '', strategies: [] }) + 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 }) + 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,76 +83,23 @@ 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) => { - 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 selectedAsset = assets.find((asset) => asset.strategies.some((str) => str.address === strategy.address)) + if (selectedAsset) { + setSelectedAsset(selectedAsset) } } - const addAsset = async () => { - const newAsset: Asset = { - address: asset.address, - strategies: asset.strategies, - symbol: asset.symbol - } - await dispatch(pushAsset(newAsset)) - if (amountInput.enabled && amountInput.amount! > 0) { - await dispatch(pushAmount(amountInput.amount!)) - } - resetForm() - } - - - const handleAmountInput = async (e: any) => { const input = e.target.value const decimalRegex = /^(\d+)?(\.\d{0,7})?$/ @@ -142,6 +110,31 @@ 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 + } + const exists = strategyExists(selectedAsset.strategies[0]!) + if (exists) { + 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 (!exists && amountInput.enabled && amountInput.amount! > 0) { + await dispatch(pushAmount(amountInput.amount!)) + } + resetForm() + } + return ( { setOpen(e.open) }} placement={'center'}> @@ -159,13 +152,12 @@ function AddNewStrategyButton() { {(strategy, index) => ( - {isLoading && } - {!isLoading && str.address === strategy.address)} + 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 +179,7 @@ function AddNewStrategyButton() { @@ -204,7 +196,7 @@ function AddNewStrategyButton() { Close 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) 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 [assets, setAssets] = useState([]); - const [status, setStatus] = useState({ - isSuccess: false, - hasError: false, - network: undefined, - message: undefined, - txHash: undefined - }); + const { getIdleFunds, getInvestedFunds, getTVL, getUserBalance } = useVault() const [deployDisabled, setDeployDisabled] = useState(true); @@ -73,35 +67,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(); @@ -113,7 +78,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({ @@ -144,6 +108,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') @@ -155,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" }) @@ -207,9 +173,16 @@ 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) => { + const parsedAmount = newVault.amounts[index] || 0; + const truncatedAmount = Math.floor(parsedAmount * 1e7) / 1e7; + const convertedAmount = Number(truncatedAmount) * Math.pow(10, 7) + if (newVault.amounts.length === 0) return nativeToScVal(0, { type: "i128" }); + return nativeToScVal(convertedAmount, { 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, @@ -222,7 +195,9 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo salt: bytesn<32>) -> result */ let result: any; - if (amountsScVal.length === 0) { + + + if (newVault.amounts.length === 0) { const createDefindexParams: xdr.ScVal[] = [ emergencyManager.toScVal(), feeReceiver.toScVal(), @@ -242,6 +217,7 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo } catch (e: any) { console.error(e) + dispatch(resetNewVault()); txModal.handleError(e.toString()); return } @@ -269,18 +245,33 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo } catch (e: any) { console.error(e) + dispatch(resetNewVault()); txModal.handleError(e.toString()); return } } 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 }], } - txModal.handleSuccess(result.txHash); + await txModal.handleSuccess(result.txHash); dispatch(pushVault(tempVault)); + dispatch(resetNewVault()); return result; } @@ -305,14 +296,12 @@ export const ConfirmDelpoyModal = ({ isOpen, onClose }: { isOpen: boolean, onClo - {(activeStep == 0 && !status.hasError) && ( - )} + 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}
  • } )}
    diff --git a/apps/dapp/src/components/InteractWithVault/InteractWithVault.tsx b/apps/dapp/src/components/InteractWithVault/InteractWithVault.tsx index 427033be..2fb0431d 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' @@ -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) @@ -36,7 +37,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,38 +53,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) => - 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(parsedNewTVL)) + 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) - 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})?$/; @@ -106,19 +122,20 @@ export const InteractWithVault = () => {

    Vault address:

    -