From db70ba2281c1f82ca5b27ed3259fc30ee3f9713f Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Wed, 31 Jan 2024 13:49:07 +0200 Subject: [PATCH 1/7] fix: incorrect balance on alias address #910 Signed-off-by: Eugene Panteleymonchuk --- client/src/app/routes/stardust/AddressState.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/client/src/app/routes/stardust/AddressState.ts b/client/src/app/routes/stardust/AddressState.ts index 19d7ec90b..bd0692a49 100644 --- a/client/src/app/routes/stardust/AddressState.ts +++ b/client/src/app/routes/stardust/AddressState.ts @@ -153,6 +153,7 @@ export const useAddressPageState = (): [IAddressState, React.Dispatch { + const consolidatedAvailableBalance = consolidateOutputBalance(availableBalance, aliasOutput); setState({ addressBasicOutputs, isBasicOutputsLoading, @@ -168,7 +169,7 @@ export const useAddressPageState = (): [IAddressState, React.Dispatch { + let total = currentBalance ? parseInt(currentBalance as string) : 0; + + if (additionalOutput?.type === OutputType.Alias) { + total += parseInt(additionalOutput.amount); + } + + return total; +}; From 0836b1023f8c7a70260a32be7a19984b29a2910a Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Wed, 31 Jan 2024 15:57:59 +0200 Subject: [PATCH 2/7] chore: move check alias output balance to useAddressBalance. Signed-off-by: Eugene Panteleymonchuk --- .../src/app/routes/stardust/AddressState.ts | 18 ++------ client/src/helpers/hooks/useAddressBalance.ts | 27 ++++++++++-- client/src/helpers/hooks/useAliasDetails.ts | 43 +++++++++++-------- 3 files changed, 54 insertions(+), 34 deletions(-) diff --git a/client/src/app/routes/stardust/AddressState.ts b/client/src/app/routes/stardust/AddressState.ts index bd0692a49..5b1d46526 100644 --- a/client/src/app/routes/stardust/AddressState.ts +++ b/client/src/app/routes/stardust/AddressState.ts @@ -118,16 +118,17 @@ export const useAddressPageState = (): [IAddressState, React.Dispatch { - const consolidatedAvailableBalance = consolidateOutputBalance(availableBalance, aliasOutput); setState({ addressBasicOutputs, isBasicOutputsLoading, @@ -169,7 +169,7 @@ export const useAddressPageState = (): [IAddressState, React.Dispatch { - let total = currentBalance ? parseInt(currentBalance as string) : 0; - - if (additionalOutput?.type === OutputType.Alias) { - total += parseInt(additionalOutput.amount); - } - - return total; -}; diff --git a/client/src/helpers/hooks/useAddressBalance.ts b/client/src/helpers/hooks/useAddressBalance.ts index 7d647d72a..7188cabc2 100644 --- a/client/src/helpers/hooks/useAddressBalance.ts +++ b/client/src/helpers/hooks/useAddressBalance.ts @@ -1,16 +1,23 @@ import { useEffect, useState } from "react"; +import { AliasOutput, OutputType } from "@iota/sdk-wasm/web"; import { useIsMounted } from "./useIsMounted"; import { ServiceFactory } from "~factories/serviceFactory"; import { STARDUST } from "~models/config/protocolVersion"; import { StardustApiClient } from "~services/stardust/stardustApiClient"; +import { fetchAliasDetailsOutput } from "~helpers/hooks/useAliasDetails"; /** * Fetch the address balance * @param network The Network in context * @param address The bech32 address + * @param options * @returns The address balance, signature locked balance and a loading bool. */ -export function useAddressBalance(network: string, address: string | null): [number | null, number | null, boolean] { +export function useAddressBalance( + network: string, + address: string | null, + options?: { aliasId?: string | null }, +): [number | null, number | null, boolean] { const isMounted = useIsMounted(); const [apiClient] = useState(ServiceFactory.get(`api-client-${STARDUST}`)); const [balance, setBalance] = useState(null); @@ -23,10 +30,12 @@ export function useAddressBalance(network: string, address: string | null): [num // eslint-disable-next-line no-void void (async () => { const response = await apiClient.addressBalanceChronicle({ network, address }); + const output = options?.aliasId ? await fetchAliasDetailsOutput(network, options.aliasId) : null; if (response?.totalBalance !== undefined && isMounted) { setBalance(response.totalBalance); - setAvailableBalance(response.availableBalance ?? null); + const availableBalance = consolidateOutputBalance(response.totalBalance, output); + setAvailableBalance(availableBalance ?? null); } else if (isMounted) { // Fallback balance from iotajs (node) const addressDetailsWithBalance = await apiClient.addressBalance({ network, address }); @@ -40,7 +49,19 @@ export function useAddressBalance(network: string, address: string | null): [num } else { setIsLoading(false); } - }, [network, address]); + }, [network, address, options?.aliasId]); return [balance, availableBalance, isLoading]; } + +const consolidateOutputBalance = (currentBalance: number | string | null, additionalOutput?: AliasOutput | null) => { + if (!currentBalance) return null; + + let total = currentBalance ? parseInt(currentBalance as string) : 0; + + if (additionalOutput?.type === OutputType.Alias) { + total += parseInt(additionalOutput.amount); + } + + return total; +}; diff --git a/client/src/helpers/hooks/useAliasDetails.ts b/client/src/helpers/hooks/useAliasDetails.ts index 166132134..3ba3ffb04 100644 --- a/client/src/helpers/hooks/useAliasDetails.ts +++ b/client/src/helpers/hooks/useAliasDetails.ts @@ -14,30 +14,20 @@ import { HexHelper } from "../stardust/hexHelper"; */ export function useAliasDetails(network: string, aliasId: string | null): [AliasOutput | null, boolean] { const isMounted = useIsMounted(); - const [apiClient] = useState(ServiceFactory.get(`api-client-${STARDUST}`)); const [aliasOutput, setAliasOutput] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { setIsLoading(true); - if (aliasId) { + if (aliasId && isMounted) { // eslint-disable-next-line no-void void (async () => { - apiClient - .aliasDetails({ - network, - aliasId: HexHelper.addPrefix(aliasId), - }) - .then((response) => { - if (!response?.error && isMounted) { - const output = response.aliasDetails?.output as AliasOutput; - - setAliasOutput(output); - } - }) - .finally(() => { - setIsLoading(false); - }); + try { + const aliasOutput = await fetchAliasDetailsOutput(network, aliasId); + setAliasOutput(aliasOutput); + } finally { + setIsLoading(false); + } })(); } else { setIsLoading(false); @@ -46,3 +36,22 @@ export function useAliasDetails(network: string, aliasId: string | null): [Alias return [aliasOutput, isLoading]; } + +export const fetchAliasDetailsOutput = async (network: string, aliasId: string | null): Promise => { + if (!aliasId) { + return null; + } + + const apiClient = ServiceFactory.get(`api-client-${STARDUST}`); + + const response = await apiClient.aliasDetails({ + network, + aliasId: HexHelper.addPrefix(aliasId), + }); + + if (!response?.error) { + return response.aliasDetails?.output as AliasOutput; + } + + return null; +}; From 59fd086563ba149e6ca6cbb38b7eee9e08a64be2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bego=C3=B1a=20Alvarez?= Date: Wed, 31 Jan 2024 16:38:46 +0100 Subject: [PATCH 3/7] feat: improve address outputs balances --- .../stardust/address/AddressBalance.tsx | 13 ++----- .../src/app/routes/stardust/AddressPage.tsx | 4 +-- .../src/app/routes/stardust/AddressState.ts | 36 ++++++++++++------- client/src/helpers/hooks/useAddressBalance.ts | 26 +++++++++++--- .../helpers/stardust/transactionsHelper.ts | 2 +- 5 files changed, 50 insertions(+), 31 deletions(-) diff --git a/client/src/app/components/stardust/address/AddressBalance.tsx b/client/src/app/components/stardust/address/AddressBalance.tsx index c548a674f..432690d4f 100644 --- a/client/src/app/components/stardust/address/AddressBalance.tsx +++ b/client/src/app/components/stardust/address/AddressBalance.tsx @@ -21,13 +21,13 @@ interface AddressBalanceProps { /** * The storage rent balance. */ - readonly storageRentBalance: number | null; + readonly storageDeposit: number | null; } const CONDITIONAL_BALANCE_INFO = "These funds reside within outputs with additional unlock conditions which might be potentially un-lockable"; -const AddressBalance: React.FC = ({ balance, spendableBalance, storageRentBalance }) => { +const AddressBalance: React.FC = ({ balance, spendableBalance, storageDeposit }) => { const { name: network, tokenInfo } = useContext(NetworkContext); const [formatBalanceFull, setFormatBalanceFull] = useState(false); const [formatConditionalBalanceFull, setFormatConditionalBalanceFull] = useState(false); @@ -101,14 +101,7 @@ const AddressBalance: React.FC = ({ balance, spendableBalan false, conditionalBalance, )} - {buildBalanceView( - "Storage Deposit", - formatStorageBalanceFull, - setFormatStorageBalanceFull, - false, - false, - storageRentBalance, - )} + {buildBalanceView("Storage Deposit", formatStorageBalanceFull, setFormatStorageBalanceFull, false, false, storageDeposit)} ); diff --git a/client/src/app/routes/stardust/AddressPage.tsx b/client/src/app/routes/stardust/AddressPage.tsx index 8972379d8..f5a8deeb5 100644 --- a/client/src/app/routes/stardust/AddressPage.tsx +++ b/client/src/app/routes/stardust/AddressPage.tsx @@ -24,7 +24,7 @@ const AddressPage: React.FC> = ({ bech32AddressDetails, balance, availableBalance, - storageRentBalance, + storageDeposit, isBasicOutputsLoading, isAliasOutputsLoading, isNftOutputsLoading, @@ -88,7 +88,7 @@ const AddressPage: React.FC> = ({ )} diff --git a/client/src/app/routes/stardust/AddressState.ts b/client/src/app/routes/stardust/AddressState.ts index 19d7ec90b..f40cf5099 100644 --- a/client/src/app/routes/stardust/AddressState.ts +++ b/client/src/app/routes/stardust/AddressState.ts @@ -38,7 +38,7 @@ export interface IAddressState { bech32AddressDetails: IBech32AddressDetails | null; balance: number | null; availableBalance: number | null; - storageRentBalance: number | null; + storageDeposit: number | null; addressOutputs: OutputResponse[] | null; addressBasicOutputs: OutputResponse[] | null; isBasicOutputsLoading: boolean; @@ -70,7 +70,7 @@ const initialState = { bech32AddressDetails: null, balance: null, availableBalance: null, - storageRentBalance: null, + storageDeposit: null, addressOutputs: null, addressBasicOutputs: null, isBasicOutputsLoading: true, @@ -121,13 +121,20 @@ export const useAddressPageState = (): [IAddressState, React.Dispatch { - if (addressBasicOutputs && addressAliasOutputs && addressNftOutputs) { - const mergedOutputResponses = [...addressBasicOutputs, ...addressAliasOutputs, ...addressNftOutputs]; - const outputs = mergedOutputResponses.map((or) => or.output); - const storageRentBalanceUpdate = TransactionsHelper.computeStorageRentBalance(outputs, rentStructure); - - setState({ - addressOutputs: mergedOutputResponses, - storageRentBalance: storageRentBalanceUpdate, - }); + const addressOutputs = + [...(addressBasicOutputs ?? []), ...(addressAliasOutputs ?? []), ...(addressNftOutputs ?? [])].filter((o) => o !== null) ?? []; + let outputsComputedInStorageDeposit = addressOutputs?.map((or) => or.output); + const addressOutputItself = nftOutput ?? aliasOutput; + if (addressOutputItself) { + outputsComputedInStorageDeposit = [...outputsComputedInStorageDeposit, addressOutputItself]; } + const storageDeposit = TransactionsHelper.computeStorageDeposit(outputsComputedInStorageDeposit, rentStructure); + + setState({ + addressOutputs, + storageDeposit, + }); if (addressBasicOutputs && !state.participations) { let foundParticipations: IParticipation[] = []; for (const outputResponse of addressBasicOutputs) { diff --git a/client/src/helpers/hooks/useAddressBalance.ts b/client/src/helpers/hooks/useAddressBalance.ts index 7d647d72a..e966285f1 100644 --- a/client/src/helpers/hooks/useAddressBalance.ts +++ b/client/src/helpers/hooks/useAddressBalance.ts @@ -3,14 +3,20 @@ import { useIsMounted } from "./useIsMounted"; import { ServiceFactory } from "~factories/serviceFactory"; import { STARDUST } from "~models/config/protocolVersion"; import { StardustApiClient } from "~services/stardust/stardustApiClient"; +import { AliasOutput, NftOutput } from "@iota/sdk-wasm/web"; /** * Fetch the address balance * @param network The Network in context * @param address The bech32 address + * @param output The output wrapping the address, used to add the output amount to the balance * @returns The address balance, signature locked balance and a loading bool. */ -export function useAddressBalance(network: string, address: string | null): [number | null, number | null, boolean] { +export function useAddressBalance( + network: string, + address: string | null, + output: AliasOutput | NftOutput | null, +): [number | null, number | null, boolean] { const isMounted = useIsMounted(); const [apiClient] = useState(ServiceFactory.get(`api-client-${STARDUST}`)); const [balance, setBalance] = useState(null); @@ -25,14 +31,24 @@ export function useAddressBalance(network: string, address: string | null): [num const response = await apiClient.addressBalanceChronicle({ network, address }); if (response?.totalBalance !== undefined && isMounted) { - setBalance(response.totalBalance); - setAvailableBalance(response.availableBalance ?? null); + let totalBalance = response.totalBalance; + let availableBalance = response.availableBalance ?? 0; + if (output) { + totalBalance = totalBalance + Number(output.amount); + availableBalance = availableBalance + Number(output.amount); + } + setBalance(totalBalance); + setAvailableBalance(availableBalance > 0 ? availableBalance : null); } else if (isMounted) { // Fallback balance from iotajs (node) const addressDetailsWithBalance = await apiClient.addressBalance({ network, address }); if (addressDetailsWithBalance && isMounted) { - setBalance(Number(addressDetailsWithBalance.balance)); + let totalBalance = Number(addressDetailsWithBalance.balance); + if (output) { + totalBalance = totalBalance + Number(output.amount); + } + setBalance(totalBalance); setAvailableBalance(null); } } @@ -40,7 +56,7 @@ export function useAddressBalance(network: string, address: string | null): [num } else { setIsLoading(false); } - }, [network, address]); + }, [network, address, output]); return [balance, availableBalance, isLoading]; } diff --git a/client/src/helpers/stardust/transactionsHelper.ts b/client/src/helpers/stardust/transactionsHelper.ts index 3fd705069..1a04615db 100644 --- a/client/src/helpers/stardust/transactionsHelper.ts +++ b/client/src/helpers/stardust/transactionsHelper.ts @@ -237,7 +237,7 @@ export class TransactionsHelper { return HexHelper.toBigInt256(nftId).eq(bigInt.zero) ? Utils.computeNftId(outputId) : nftId; } - public static computeStorageRentBalance(outputs: Output[], rentStructure: IRent): number { + public static computeStorageDeposit(outputs: Output[], rentStructure: IRent): number { const outputsWithoutSdruc = outputs.filter((output) => { if (output.type === OutputType.Treasury) { return false; From 764e4ec9a75258b5356754ca2a778077f1d7def9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bego=C3=B1a=20Alvarez?= Date: Wed, 31 Jan 2024 16:45:25 +0100 Subject: [PATCH 4/7] revert: client/src/helpers/hooks/useAliasDetails.ts --- client/src/helpers/hooks/useAliasDetails.ts | 43 ++++++++------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/client/src/helpers/hooks/useAliasDetails.ts b/client/src/helpers/hooks/useAliasDetails.ts index 3ba3ffb04..166132134 100644 --- a/client/src/helpers/hooks/useAliasDetails.ts +++ b/client/src/helpers/hooks/useAliasDetails.ts @@ -14,20 +14,30 @@ import { HexHelper } from "../stardust/hexHelper"; */ export function useAliasDetails(network: string, aliasId: string | null): [AliasOutput | null, boolean] { const isMounted = useIsMounted(); + const [apiClient] = useState(ServiceFactory.get(`api-client-${STARDUST}`)); const [aliasOutput, setAliasOutput] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { setIsLoading(true); - if (aliasId && isMounted) { + if (aliasId) { // eslint-disable-next-line no-void void (async () => { - try { - const aliasOutput = await fetchAliasDetailsOutput(network, aliasId); - setAliasOutput(aliasOutput); - } finally { - setIsLoading(false); - } + apiClient + .aliasDetails({ + network, + aliasId: HexHelper.addPrefix(aliasId), + }) + .then((response) => { + if (!response?.error && isMounted) { + const output = response.aliasDetails?.output as AliasOutput; + + setAliasOutput(output); + } + }) + .finally(() => { + setIsLoading(false); + }); })(); } else { setIsLoading(false); @@ -36,22 +46,3 @@ export function useAliasDetails(network: string, aliasId: string | null): [Alias return [aliasOutput, isLoading]; } - -export const fetchAliasDetailsOutput = async (network: string, aliasId: string | null): Promise => { - if (!aliasId) { - return null; - } - - const apiClient = ServiceFactory.get(`api-client-${STARDUST}`); - - const response = await apiClient.aliasDetails({ - network, - aliasId: HexHelper.addPrefix(aliasId), - }); - - if (!response?.error) { - return response.aliasDetails?.output as AliasOutput; - } - - return null; -}; From 032e363782f445862c6839428a8e9a39bd0f2c11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bego=C3=B1a=20Alvarez?= Date: Wed, 31 Jan 2024 16:48:38 +0100 Subject: [PATCH 5/7] fix: add missing reactive condition --- client/src/app/routes/stardust/AddressState.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/app/routes/stardust/AddressState.ts b/client/src/app/routes/stardust/AddressState.ts index 298962c40..ca5621beb 100644 --- a/client/src/app/routes/stardust/AddressState.ts +++ b/client/src/app/routes/stardust/AddressState.ts @@ -241,7 +241,7 @@ export const useAddressPageState = (): [IAddressState, React.Dispatch Date: Wed, 31 Jan 2024 17:00:12 +0100 Subject: [PATCH 6/7] fix: string numbers --- client/src/helpers/hooks/useAddressBalance.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/helpers/hooks/useAddressBalance.ts b/client/src/helpers/hooks/useAddressBalance.ts index ad9bca7e4..4763b36d0 100644 --- a/client/src/helpers/hooks/useAddressBalance.ts +++ b/client/src/helpers/hooks/useAddressBalance.ts @@ -34,8 +34,8 @@ export function useAddressBalance( let totalBalance = response.totalBalance; let availableBalance = response.availableBalance ?? 0; if (output) { - totalBalance = totalBalance + Number(output.amount); - availableBalance = availableBalance + Number(output.amount); + totalBalance = Number(totalBalance) + Number(output.amount); + availableBalance = Number(availableBalance) + Number(output.amount); } setBalance(totalBalance); setAvailableBalance(availableBalance > 0 ? availableBalance : null); @@ -46,7 +46,7 @@ export function useAddressBalance( if (addressDetailsWithBalance && isMounted) { let totalBalance = Number(addressDetailsWithBalance.balance); if (output) { - totalBalance = totalBalance + Number(output.amount); + totalBalance = Number(totalBalance) + Number(output.amount); } setBalance(totalBalance); setAvailableBalance(null); From 8cde08d11ffc9511056cae34f4769f3148e6c3a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bego=C3=B1a=20Alvarez?= Date: Wed, 31 Jan 2024 17:33:27 +0100 Subject: [PATCH 7/7] enhancement: load time --- client/src/app/routes/stardust/AddressState.ts | 6 +----- client/src/helpers/hooks/useAddressBalance.ts | 12 ++++++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/client/src/app/routes/stardust/AddressState.ts b/client/src/app/routes/stardust/AddressState.ts index ca5621beb..17fb0c1ab 100644 --- a/client/src/app/routes/stardust/AddressState.ts +++ b/client/src/app/routes/stardust/AddressState.ts @@ -130,11 +130,7 @@ export const useAddressPageState = (): [IAddressState, React.Dispatch { setIsLoading(true); - if (address) { + const address = addressDetails?.bech32; + const needsOutputToProceed = addressDetails?.type === AddressType.Alias || addressDetails?.type === AddressType.Nft; + const canLoad = address && (!needsOutputToProceed || (needsOutputToProceed && output)); + if (canLoad) { // eslint-disable-next-line no-void void (async () => { const response = await apiClient.addressBalanceChronicle({ network, address }); @@ -56,7 +60,7 @@ export function useAddressBalance( } else { setIsLoading(false); } - }, [network, address, output]); + }, [network, addressDetails, output]); return [balance, availableBalance, isLoading]; }