Files

165 lines
5.3 KiB
TypeScript

import { STAKE_STATE, parseStakeState } from '@/components/StakedNode/state';
import { parseStakes } from '@/hooks/parseStakes';
import { BACKEND, BLOCK_TIME_MS, PREFERENCE, SESSION_NODE } from '@/lib/constants';
import { NEXT_PUBLIC_TESTNET } from '@/lib/env';
import logger from '@/lib/logger';
import { getStakedNodes } from '@/lib/queries/getStakedNodes';
import { useStakingBackendQueryWithParams } from '@/lib/staking-api-client';
import { useNodesWithConfirmations } from '@/lib/volatile-storage';
import { bigIntToNumber } from '@session/util-crypto/maths';
import { areHexesEqual } from '@session/util-crypto/string';
import { safeTrySyncWithFallback } from '@session/util-js/try';
import { useBlockNumber } from '@session/wallet/hooks/useBlockNumber';
import { useWallet } from '@session/wallet/hooks/useWallet';
import { useMemo } from 'react';
import { usePreferences } from 'usepref';
import type { Address } from 'viem';
import { arbitrum, arbitrumSepolia } from 'viem/chains';
/**
* Hook to get the stakes and related data for the connected wallet.
* @param overrideAddress - override address, this overrides the connected address
* @param overrideRefetchIntervalMs - override refetch interval
* @returns The stakes and related data for the connected wallet.
*/
export function useStakes(overrideAddress?: Address, overrideRefetchIntervalMs?: number) {
const { address: connectedAddress } = useWallet();
const address = overrideAddress ?? connectedAddress;
const { getItem } = usePreferences();
const enabled = !!address;
const autoRefresh = !getItem<boolean>(PREFERENCE.DISABLE_BACKEND_AUTO_REFRESH);
const { data: arbBlock } = useBlockNumber({
chainId: NEXT_PUBLIC_TESTNET ? arbitrumSepolia.id : arbitrum.id,
query: {
gcTime: overrideRefetchIntervalMs ?? BACKEND.NODE_TARGET_UPDATE_INTERVAL_SECONDS * 1000,
},
});
const { data, isLoading, isFetching, refetch, isError, status } =
useStakingBackendQueryWithParams(
getStakedNodes,
{
address: address!,
},
{
enabled,
refetchInterval: autoRefresh
? (overrideRefetchIntervalMs ?? BACKEND.NODE_TARGET_UPDATE_INTERVAL_SECONDS * 1000)
: undefined,
}
);
const {
nodes: { nodesConfirmingRegistration },
} = useNodesWithConfirmations();
const {
stakes,
vesting,
hiddenContractsWithStakes,
awaitingOperatorContracts,
networkContractIds,
visibleContracts,
networkBlsKeys,
joiningContracts,
network,
blockHeight,
networkTime,
} = useMemo(() => {
const [networkError, network] = safeTrySyncWithFallback(() => data?.network ?? null, null);
if (networkError) logger.error(networkError);
const [blockHeightError, blockHeight] = safeTrySyncWithFallback(
() => network?.block_height ?? 0,
0
);
if (blockHeightError) logger.error(blockHeightError);
const [networkTimeError, networkTime] = safeTrySyncWithFallback(
() => network?.block_timestamp ?? 0,
0
);
if (networkTimeError) logger.error(networkTimeError);
const [stakesError, stakes] = safeTrySyncWithFallback(() => data?.stakes ?? [], []);
if (stakesError) logger.error(stakesError);
const [addedBlsKeysSetsError, addedBlsKeys] = safeTrySyncWithFallback(
() => data?.added_bls_keys ?? {},
{}
);
if (addedBlsKeysSetsError) logger.error(addedBlsKeysSetsError);
const [vestingErr, vesting] = safeTrySyncWithFallback(() => data?.vesting ?? [], []);
if (vestingErr) logger.error(vestingErr);
const [contractsErr, contracts] = safeTrySyncWithFallback(() => data?.contracts ?? [], []);
if (contractsErr) logger.error(contractsErr);
// Minimum time in blocks that a node can go from "joining" to "exited"
const nodeMinLifespanArbBlocks =
(arbBlock ? bigIntToNumber(arbBlock, 0) : 0) -
SESSION_NODE.INITIAL_DOWNTIME_CREDITS_MS / BLOCK_TIME_MS.ARBITRUM;
return {
...parseStakes({
contracts,
address,
blockHeight,
addedBlsKeys,
nodeMinLifespanArbBlocks,
stakes,
vesting,
}),
network,
networkTime,
};
}, [data, address, arbBlock]);
const notFoundJoiningNodes = useMemo(
() =>
nodesConfirmingRegistration.filter((node) => {
return (
areHexesEqual(node.confirmationOwner, address) &&
!joiningContracts.some(
({ pubkey_bls, service_node_pubkey }) =>
pubkey_bls === node.pubkeyBls ||
areHexesEqual(service_node_pubkey, node.pubkeyEd25519)
) &&
!stakes.some((stake) => {
const state = parseStakeState(stake, blockHeight);
if (state === STAKE_STATE.DEREGISTERED) {
return false;
}
return (
stake.pubkey_bls === node.pubkeyBls || stake.pubkey_ed25519 === node.pubkeyEd25519
);
})
);
}),
[nodesConfirmingRegistration, stakes, joiningContracts, blockHeight, address]
);
return {
stakes,
visibleContracts,
joiningContracts,
notFoundJoiningNodes,
awaitingOperatorContracts,
vesting,
hiddenContractsWithStakes,
networkBlsKeys,
networkContractIds,
network,
blockHeight,
networkTime,
refetch,
isLoading,
isFetching,
isError,
status,
enabled,
};
}