From 77a72d8e8577270c217b88bc30653bd70ed72c6b Mon Sep 17 00:00:00 2001 From: Branko Bosnic Date: Mon, 26 Feb 2024 16:19:22 +0100 Subject: [PATCH] Add hook to fetch delegation outputs --- .../nova/hooks/useAccountAddressState.ts | 13 ++++++ .../nova/hooks/useAddressDelegationOutputs.ts | 43 +++++++++++++++++++ .../nova/hooks/useAnchorAddressState.ts | 12 ++++++ .../nova/hooks/useEd25519AddressState.ts | 22 +++++++++- .../useImplicitAccountCreationAddressState.ts | 22 +++++++++- .../helpers/nova/hooks/useNftAddressState.ts | 13 ++++++ client/src/services/nova/novaApiClient.ts | 12 ++++++ 7 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 client/src/helpers/nova/hooks/useAddressDelegationOutputs.ts diff --git a/client/src/helpers/nova/hooks/useAccountAddressState.ts b/client/src/helpers/nova/hooks/useAccountAddressState.ts index d25c56bab..92374b46d 100644 --- a/client/src/helpers/nova/hooks/useAccountAddressState.ts +++ b/client/src/helpers/nova/hooks/useAccountAddressState.ts @@ -21,6 +21,7 @@ import { useAccountControlledFoundries } from "./useAccountControlledFoundries"; import { useAccountCongestion } from "./useAccountCongestion"; import { useAddressNftOutputs } from "~/helpers/nova/hooks/useAddressNftOutputs"; import { useAccountValidatorDetails } from "./useAccountValidatorDetails"; +import { useAddressDelegationOutputs } from "./useAddressDelegationOutputs"; export interface IAccountAddressState { addressDetails: IAddressDetails | null; @@ -32,12 +33,14 @@ export interface IAccountAddressState { validatorDetails: ValidatorResponse | null; addressBasicOutputs: OutputResponse[] | null; addressNftOutputs: OutputResponse[] | null; + addressDelegationOutputs: OutputResponse[] | null; foundries: string[] | null; congestion: CongestionResponse | null; isAccountDetailsLoading: boolean; isAssociatedOutputsLoading: boolean; isBasicOutputsLoading: boolean; isNftOutputsLoading: boolean; + isDelegationOutputsLoading: boolean; isFoundriesLoading: boolean; isCongestionLoading: boolean; isValidatorDetailsLoading: boolean; @@ -53,12 +56,14 @@ const initialState = { validatorDetails: null, addressBasicOutputs: null, addressNftOutputs: null, + addressDelegationOutputs: null, foundries: null, congestion: null, isAccountDetailsLoading: true, isAssociatedOutputsLoading: false, isBasicOutputsLoading: false, isNftOutputsLoading: false, + isDelegationOutputsLoading: false, isFoundriesLoading: false, isCongestionLoading: false, isValidatorDetailsLoading: false, @@ -85,6 +90,10 @@ export const useAccountAddressState = (address: AccountAddress): [IAccountAddres const { totalBalance, availableBalance } = useAddressBalance(network, state.addressDetails, accountOutput); const [addressBasicOutputs, isBasicOutputsLoading] = useAddressBasicOutputs(network, state.addressDetails?.bech32 ?? null); const [addressNftOutputs, isNftOutputsLoading] = useAddressNftOutputs(network, state.addressDetails?.bech32 ?? null); + const [addressDelegationOutputs, isDelegationOutputsLoading] = useAddressDelegationOutputs( + network, + state.addressDetails?.bech32 ?? null, + ); const [foundries, isFoundriesLoading] = useAccountControlledFoundries(network, state.addressDetails); const { congestion, isLoading: isCongestionLoading } = useAccountCongestion(network, state.addressDetails?.hex ?? null); const { validatorDetails, isLoading: isValidatorDetailsLoading } = useAccountValidatorDetails( @@ -115,8 +124,10 @@ export const useAccountAddressState = (address: AccountAddress): [IAccountAddres validatorDetails, addressBasicOutputs, addressNftOutputs, + addressDelegationOutputs, isBasicOutputsLoading, isNftOutputsLoading, + isDelegationOutputsLoading, isFoundriesLoading, isCongestionLoading, isValidatorDetailsLoading, @@ -152,11 +163,13 @@ export const useAccountAddressState = (address: AccountAddress): [IAccountAddres availableBalance, addressBasicOutputs, addressNftOutputs, + addressDelegationOutputs, congestion, validatorDetails, isAccountDetailsLoading, isBasicOutputsLoading, isNftOutputsLoading, + isDelegationOutputsLoading, isCongestionLoading, isValidatorDetailsLoading, ]); diff --git a/client/src/helpers/nova/hooks/useAddressDelegationOutputs.ts b/client/src/helpers/nova/hooks/useAddressDelegationOutputs.ts new file mode 100644 index 000000000..ebb48112d --- /dev/null +++ b/client/src/helpers/nova/hooks/useAddressDelegationOutputs.ts @@ -0,0 +1,43 @@ +import { OutputResponse } from "@iota/sdk-wasm-nova/web"; +import { useEffect, useState } from "react"; +import { useIsMounted } from "~helpers/hooks/useIsMounted"; +import { ServiceFactory } from "~factories/serviceFactory"; +import { NOVA } from "~models/config/protocolVersion"; +import { NovaApiClient } from "~/services/nova/novaApiClient"; + +/** + * Fetch Address delegation UTXOs + * @param network The Network in context + * @param addressBech32 The address in bech32 format + * @returns The output responses and loading bool. + */ +export function useAddressDelegationOutputs(network: string, addressBech32: string | null): [OutputResponse[] | null, boolean] { + const isMounted = useIsMounted(); + const [apiClient] = useState(ServiceFactory.get(`api-client-${NOVA}`)); + const [outputs, setOutputs] = useState(null); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + setIsLoading(true); + setOutputs(null); + if (addressBech32) { + // eslint-disable-next-line no-void + void (async () => { + apiClient + .delegationOutputsDetails({ network, address: addressBech32 }) + .then((response) => { + if (!response?.error && response.outputs && isMounted) { + setOutputs(response.outputs); + } + }) + .finally(() => { + setIsLoading(false); + }); + })(); + } else { + setIsLoading(false); + } + }, [network, addressBech32]); + + return [outputs, isLoading]; +} diff --git a/client/src/helpers/nova/hooks/useAnchorAddressState.ts b/client/src/helpers/nova/hooks/useAnchorAddressState.ts index 352dc1223..04f22327f 100644 --- a/client/src/helpers/nova/hooks/useAnchorAddressState.ts +++ b/client/src/helpers/nova/hooks/useAnchorAddressState.ts @@ -17,8 +17,10 @@ export interface IAnchorAddressState { totalBalance: number | null; addressBasicOutputs: OutputResponse[] | null; addressNftOutputs: OutputResponse[] | null; + addressDelegationOutputs: OutputResponse[] | null; isBasicOutputsLoading: boolean; isNftOutputsLoading: boolean; + isDelegationOutputsLoading: boolean; isAnchorDetailsLoading: boolean; isAssociatedOutputsLoading: boolean; } @@ -30,8 +32,10 @@ const initialState = { availableBalance: null, addressBasicOutputs: null, addressNftOutputs: null, + addressDelegationOutputs: null, isBasicOutputsLoading: false, isNftOutputsLoading: false, + isDelegationOutputsLoading: false, isAnchorDetailsLoading: true, isAssociatedOutputsLoading: false, }; @@ -56,6 +60,10 @@ export const useAnchorAddressState = (address: AnchorAddress): [IAnchorAddressSt const { totalBalance, availableBalance } = useAddressBalance(network, state.addressDetails, anchorOutput); const [addressBasicOutputs, isBasicOutputsLoading] = useAddressBasicOutputs(network, state.addressDetails?.bech32 ?? null); const [addressNftOutputs, isNftOutputsLoading] = useAddressNftOutputs(network, state.addressDetails?.bech32 ?? null); + const [addressDelegationOutputs, isDelegationOutputsLoading] = useAddressDelegationOutputs( + network, + state.addressDetails?.bech32 ?? null, + ); useEffect(() => { const locationState = location.state as IAddressPageLocationProps; @@ -76,8 +84,10 @@ export const useAnchorAddressState = (address: AnchorAddress): [IAnchorAddressSt availableBalance, addressBasicOutputs, addressNftOutputs, + addressDelegationOutputs, isBasicOutputsLoading, isNftOutputsLoading, + isDelegationOutputsLoading, isAnchorDetailsLoading, }); }, [ @@ -86,8 +96,10 @@ export const useAnchorAddressState = (address: AnchorAddress): [IAnchorAddressSt availableBalance, addressBasicOutputs, addressNftOutputs, + addressDelegationOutputs, isBasicOutputsLoading, isNftOutputsLoading, + isDelegationOutputsLoading, isAnchorDetailsLoading, ]); diff --git a/client/src/helpers/nova/hooks/useEd25519AddressState.ts b/client/src/helpers/nova/hooks/useEd25519AddressState.ts index 0d9e79066..eb280e4ad 100644 --- a/client/src/helpers/nova/hooks/useEd25519AddressState.ts +++ b/client/src/helpers/nova/hooks/useEd25519AddressState.ts @@ -7,6 +7,7 @@ import { AddressHelper } from "~/helpers/nova/addressHelper"; import { useAddressBalance } from "./useAddressBalance"; import { useAddressBasicOutputs } from "~/helpers/nova/hooks/useAddressBasicOutputs"; import { useAddressNftOutputs } from "~/helpers/nova/hooks/useAddressNftOutputs"; +import { useAddressDelegationOutputs } from "./useAddressDelegationOutputs"; export interface IEd25519AddressState { addressDetails: IAddressDetails | null; @@ -14,8 +15,10 @@ export interface IEd25519AddressState { availableBalance: number | null; addressBasicOutputs: OutputResponse[] | null; addressNftOutputs: OutputResponse[] | null; + addressDelegationOutputs: OutputResponse[] | null; isBasicOutputsLoading: boolean; isNftOutputsLoading: boolean; + isDelegationOutputsLoading: boolean; isAssociatedOutputsLoading: boolean; } @@ -25,8 +28,10 @@ const initialState = { availableBalance: null, addressBasicOutputs: null, addressNftOutputs: null, + addressDelegationOutputs: null, isBasicOutputsLoading: false, isNftOutputsLoading: false, + isDelegationOutputsLoading: false, isAssociatedOutputsLoading: false, }; @@ -48,6 +53,10 @@ export const useEd25519AddressState = (address: Ed25519Address): [IEd25519Addres const { totalBalance, availableBalance } = useAddressBalance(network, state.addressDetails, null); const [addressBasicOutputs, isBasicOutputsLoading] = useAddressBasicOutputs(network, state.addressDetails?.bech32 ?? null); const [addressNftOutputs, isNftOutputsLoading] = useAddressNftOutputs(network, state.addressDetails?.bech32 ?? null); + const [addressDelegationOutputs, isDelegationOutputsLoading] = useAddressDelegationOutputs( + network, + state.addressDetails?.bech32 ?? null, + ); useEffect(() => { const locationState = location.state as IAddressPageLocationProps; @@ -66,10 +75,21 @@ export const useEd25519AddressState = (address: Ed25519Address): [IEd25519Addres availableBalance, addressBasicOutputs, addressNftOutputs, + addressDelegationOutputs, isBasicOutputsLoading, isNftOutputsLoading, + isDelegationOutputsLoading, }); - }, [totalBalance, availableBalance, addressBasicOutputs, addressNftOutputs, isBasicOutputsLoading, isNftOutputsLoading]); + }, [ + totalBalance, + availableBalance, + addressBasicOutputs, + addressNftOutputs, + addressDelegationOutputs, + isBasicOutputsLoading, + isNftOutputsLoading, + isDelegationOutputsLoading, + ]); return [state, setState]; }; diff --git a/client/src/helpers/nova/hooks/useImplicitAccountCreationAddressState.ts b/client/src/helpers/nova/hooks/useImplicitAccountCreationAddressState.ts index ea28ac024..9e6daa39e 100644 --- a/client/src/helpers/nova/hooks/useImplicitAccountCreationAddressState.ts +++ b/client/src/helpers/nova/hooks/useImplicitAccountCreationAddressState.ts @@ -8,6 +8,7 @@ import { AddressHelper } from "~/helpers/nova/addressHelper"; import { useAddressBalance } from "./useAddressBalance"; import { useAddressBasicOutputs } from "~/helpers/nova/hooks/useAddressBasicOutputs"; import { useAddressNftOutputs } from "~/helpers/nova/hooks/useAddressNftOutputs"; +import { useAddressDelegationOutputs } from "./useAddressDelegationOutputs"; export interface IImplicitAccountCreationAddressState { addressDetails: IAddressDetails | null; @@ -15,8 +16,10 @@ export interface IImplicitAccountCreationAddressState { availableBalance: number | null; addressBasicOutputs: OutputResponse[] | null; addressNftOutputs: OutputResponse[] | null; + addressDelegationOutputs: OutputResponse[] | null; isBasicOutputsLoading: boolean; isNftOutputsLoading: boolean; + isDelegationOutputsLoading: boolean; isAssociatedOutputsLoading: boolean; } @@ -26,8 +29,10 @@ const initialState = { availableBalance: null, addressBasicOutputs: null, addressNftOutputs: null, + addressDelegationOutputs: null, isBasicOutputsLoading: false, isNftOutputsLoading: false, + isDelegationOutputsLoading: false, isAssociatedOutputsLoading: false, }; @@ -52,6 +57,10 @@ export const useImplicitAccountCreationAddressState = ( const { totalBalance, availableBalance } = useAddressBalance(network, state.addressDetails, null); const [addressBasicOutputs, isBasicOutputsLoading] = useAddressBasicOutputs(network, state.addressDetails?.bech32 ?? null); const [addressNftOutputs, isNftOutputsLoading] = useAddressNftOutputs(network, state.addressDetails?.bech32 ?? null); + const [addressDelegationOutputs, isDelegationOutputsLoading] = useAddressDelegationOutputs( + network, + state.addressDetails?.bech32 ?? null, + ); useEffect(() => { const locationState = location.state as IAddressPageLocationProps; @@ -71,10 +80,21 @@ export const useImplicitAccountCreationAddressState = ( availableBalance, addressBasicOutputs, addressNftOutputs, + addressDelegationOutputs, isBasicOutputsLoading, isNftOutputsLoading, + isDelegationOutputsLoading, }); - }, [totalBalance, availableBalance, addressBasicOutputs, addressNftOutputs, isBasicOutputsLoading, isBasicOutputsLoading]); + }, [ + totalBalance, + availableBalance, + addressBasicOutputs, + addressNftOutputs, + addressDelegationOutputs, + isBasicOutputsLoading, + isNftOutputsLoading, + isDelegationOutputsLoading, + ]); return [state, setState]; }; diff --git a/client/src/helpers/nova/hooks/useNftAddressState.ts b/client/src/helpers/nova/hooks/useNftAddressState.ts index 0852ecdd0..1da862b5b 100644 --- a/client/src/helpers/nova/hooks/useNftAddressState.ts +++ b/client/src/helpers/nova/hooks/useNftAddressState.ts @@ -9,6 +9,7 @@ import { AddressHelper } from "~/helpers/nova/addressHelper"; import { useAddressBalance } from "./useAddressBalance"; import { useAddressBasicOutputs } from "~/helpers/nova/hooks/useAddressBasicOutputs"; import { useAddressNftOutputs } from "~/helpers/nova/hooks/useAddressNftOutputs"; +import { useAddressDelegationOutputs } from "./useAddressDelegationOutputs"; export interface INftAddressState { addressDetails: IAddressDetails | null; @@ -17,8 +18,10 @@ export interface INftAddressState { availableBalance: number | null; addressBasicOutputs: OutputResponse[] | null; addressNftOutputs: OutputResponse[] | null; + addressDelegationOutputs: OutputResponse[] | null; isBasicOutputsLoading: boolean; isNftOutputsLoading: boolean; + isDelegationOutputsLoading: boolean; isNftDetailsLoading: boolean; isAssociatedOutputsLoading: boolean; } @@ -31,8 +34,10 @@ const initialState = { availableBalance: null, addressBasicOutputs: null, addressNftOutputs: null, + addressDelegationOutputs: null, isBasicOutputsLoading: false, isNftOutputsLoading: false, + isDelegationOutputsLoading: false, isAssociatedOutputsLoading: false, }; @@ -56,6 +61,10 @@ export const useNftAddressState = (address: NftAddress): [INftAddressState, Reac const { totalBalance, availableBalance } = useAddressBalance(network, state.addressDetails, nftOutput); const [addressBasicOutputs, isBasicOutputsLoading] = useAddressBasicOutputs(network, state.addressDetails?.bech32 ?? null); const [addressNftOutputs, isNftOutputsLoading] = useAddressNftOutputs(network, state.addressDetails?.bech32 ?? null); + const [addressDelegationOutputs, isDelegationOutputsLoading] = useAddressDelegationOutputs( + network, + state.addressDetails?.bech32 ?? null, + ); useEffect(() => { const locationState = location.state as IAddressPageLocationProps; @@ -77,8 +86,10 @@ export const useNftAddressState = (address: NftAddress): [INftAddressState, Reac isNftDetailsLoading, addressBasicOutputs, addressNftOutputs, + addressDelegationOutputs, isBasicOutputsLoading, isNftOutputsLoading, + isDelegationOutputsLoading, }); }, [ nftOutput, @@ -87,8 +98,10 @@ export const useNftAddressState = (address: NftAddress): [INftAddressState, Reac isNftDetailsLoading, addressBasicOutputs, addressNftOutputs, + addressDelegationOutputs, isBasicOutputsLoading, isNftOutputsLoading, + isDelegationOutputsLoading, ]); return [state, setState]; diff --git a/client/src/services/nova/novaApiClient.ts b/client/src/services/nova/novaApiClient.ts index fb098467f..4c54b49c6 100644 --- a/client/src/services/nova/novaApiClient.ts +++ b/client/src/services/nova/novaApiClient.ts @@ -157,6 +157,18 @@ export class NovaApiClient extends ApiClient { return this.callApi(`nova/address/outputs/nft/${request.network}/${request.address}`, "get"); } + /** + * Get the delegation outputs details of an address. + * @param request The Address Delegation outputs request. + * @returns The Address outputs response + */ + public async delegationOutputsDetails(request: IAddressDetailsRequest): Promise { + return this.callApi( + `nova/address/outputs/delegation/${request.network}/${request.address}`, + "get", + ); + } + /** * Get the associated outputs. * @param request The request to send.