Skip to content

Commit

Permalink
Add total staked on address page/ fix wrong digest link format (#14368)
Browse files Browse the repository at this point in the history
## Description 
This PR adds `STAKING` Amount on the explorer page. I moved
`useGetDelegatedStake` to core and added the option to `autoRefetch`
with existing predefined stale time and refresh.

Fix wrong digest link format

<img width="408" alt="Screenshot 2023-10-22 at 4 42 34 PM"
src="https://github.com/MystenLabs/sui/assets/126525197/2f65cb14-e57f-4637-86a4-c3800f93c894">
<img width="1341" alt="Screenshot 2023-10-22 at 4 36 56 PM"
src="https://github.com/MystenLabs/sui/assets/126525197/db4a3aed-c5e5-4e11-b454-d7ac186d4d94">
  • Loading branch information
Jibz-Mysten authored Oct 23, 2023
1 parent 0fbacf4 commit 4265d4b
Show file tree
Hide file tree
Showing 18 changed files with 166 additions and 44 deletions.
21 changes: 21 additions & 0 deletions apps/core/src/hooks/useGetDelegatedStake.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { useSuiClient } from '@mysten/dapp-kit';
import type { DelegatedStake } from '@mysten/sui.js/client';
import { useQuery, type UseQueryOptions } from '@tanstack/react-query';

type UseGetDelegatedStakesOptions = {
address: string;
} & UseQueryOptions<DelegatedStake[], Error>;

export function useGetDelegatedStake(options: UseGetDelegatedStakesOptions) {
const client = useSuiClient();
const { address, ...queryOptions } = options;

return useQuery({
queryKey: ['delegated-stakes', address],
queryFn: () => client.getStakes({ owner: address }),
...queryOptions,
});
}
1 change: 1 addition & 0 deletions apps/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ export * from './utils/kiosk';
export * from './hooks/useElementDimensions';
export * from './hooks/useSuiCoinData';
export * from './hooks/useLocalStorage';
export * from './hooks/useGetDelegatedStake';
1 change: 1 addition & 0 deletions apps/core/tailwind.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ export default {
minWidth: {
10: '2.5rem',
18: '4.5rem',
44: '11rem',
50: '12.5rem',
transactionColumn: '31.875rem',
smallThumbNailsViewContainer: '13.125rem',
Expand Down
2 changes: 2 additions & 0 deletions apps/explorer/src/pages/address-result/AddressResult.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { Divider } from '~/ui/Divider';
import { PageHeader } from '~/ui/PageHeader';
import { LOCAL_STORAGE_SPLIT_PANE_KEYS, SplitPanes } from '~/ui/SplitPanes';
import { TabHeader, TabsList, TabsTrigger } from '~/ui/Tabs';
import { TotalStaked } from './TotalStaked';

const LEFT_RIGHT_PANEL_MIN_SIZE = 30;
const TOP_PANEL_MIN_SIZE = 20;
Expand All @@ -30,6 +31,7 @@ function AddressResultPageHeader({ address, loading }: { address: string; loadin
title={address}
subtitle={domainName}
before={<Domain32 className="h-6 w-6 text-steel-darker sm:h-10 sm:w-10" />}
after={<TotalStaked address={address} />}
/>
);
}
Expand Down
38 changes: 38 additions & 0 deletions apps/explorer/src/pages/address-result/TotalStaked.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { useFormatCoin, useGetDelegatedStake } from '@mysten/core';
import { useMemo } from 'react';
import { SUI_TYPE_ARG } from '@mysten/sui.js/utils';
import { Text, Heading } from '@mysten/ui';
import { Sui } from '@mysten/icons';

export function TotalStaked({ address }: { address: string }) {
const { data: delegatedStake } = useGetDelegatedStake({
address,
});

// Total active stake for all delegations
const totalActivePendingStake = useMemo(() => {
if (!delegatedStake) return 0n;
return delegatedStake.reduce(
(acc, curr) => curr.stakes.reduce((total, { principal }) => total + BigInt(principal), acc),
0n,
);
}, [delegatedStake]);

const [formatted, symbol] = useFormatCoin(totalActivePendingStake, SUI_TYPE_ARG);
return totalActivePendingStake ? (
<div className="flex min-w-44 items-center justify-start gap-3 rounded-xl bg-white/60 px-4 py-3 backdrop-blur-sm">
<Sui className="flex h-8 w-8 items-center justify-center rounded-full bg-sui-primaryBlue2023 py-1.5 text-white" />
<div className="flex flex-col">
<Text variant="pBody/semibold" color="steel-dark" uppercase>
Staking
</Text>
<Heading variant="heading6/semibold" color="hero-darkest" as="div">
{formatted} {symbol}
</Heading>
</div>
</div>
) : null;
}
4 changes: 2 additions & 2 deletions apps/explorer/src/ui/InternalLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

import { isSuiNSName } from '@mysten/core';
import { formatAddress } from '@mysten/sui.js/utils';
import { formatAddress, formatDigest } from '@mysten/sui.js/utils';
import { type ReactNode } from 'react';

import { Link, type LinkProps } from '~/ui/Link';
Expand Down Expand Up @@ -47,5 +47,5 @@ export const AddressLink = createInternalLink('address', 'address', (addressOrNs
return formatAddress(addressOrNs);
});
export const ObjectLink = createInternalLink('object', 'objectId', formatAddress);
export const TransactionLink = createInternalLink('txblock', 'digest', formatAddress);
export const TransactionLink = createInternalLink('txblock', 'digest', formatDigest);
export const ValidatorLink = createInternalLink('validator', 'address', formatAddress);
16 changes: 13 additions & 3 deletions apps/explorer/src/ui/PageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface PageHeaderProps {
type: PageHeaderType;
status?: 'success' | 'failure';
before?: React.ReactNode;
after?: React.ReactNode;
error?: string;
loading?: boolean;
}
Expand All @@ -32,20 +33,28 @@ const TYPE_TO_ICON: Record<PageHeaderType, typeof CallIcon | null> = {
Address: null,
};

export function PageHeader({ title, subtitle, type, before, error, loading }: PageHeaderProps) {
export function PageHeader({
title,
subtitle,
type,
before,
error,
loading,
after,
}: PageHeaderProps) {
const Icon = TYPE_TO_ICON[type];

return (
<div data-testid="pageheader" className="group">
<div className="flex items-center gap-3 sm:gap-5">
<div className="flex w-full items-center gap-3 sm:gap-5">
{before && (
<div className="self-start sm:self-center">
<div className="sm:min-w-16 flex h-10 w-10 min-w-10 items-center justify-center rounded-lg bg-white/60 sm:h-16 sm:w-16 sm:rounded-xl lg:h-18 lg:w-18 lg:min-w-18">
{loading ? <Placeholder rounded="xl" width="100%" height="100%" /> : before}
</div>
</div>
)}
<div>
<div className="flex w-full flex-col items-start justify-between gap-4 md:flex-row md:items-center">
<div>
<div className="mb-1 flex items-center gap-2">
{Icon && <Icon className="text-steel-dark" />}
Expand Down Expand Up @@ -96,6 +105,7 @@ export function PageHeader({ title, subtitle, type, before, error, loading }: Pa
</Banner>
</div>
)}
{after && <div className="sm:self-center md:ml-auto">{after}</div>}
</div>
</div>
</div>
Expand Down
1 change: 1 addition & 0 deletions apps/ui/src/Heading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const headingStyles = cva([], {
'steel-darker': 'text-steel-darker',
hero: 'text-hero',
'hero-dark': 'text-hero-dark',
'hero-darkest': 'text-hero-darkest',
success: 'text-success',
'success-dark': 'text-success-dark',
},
Expand Down
2 changes: 2 additions & 0 deletions apps/wallet/src/shared/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ export const FAQ_LINK = 'https://docs.mystenlabs.com/wallet/faq';
export const NUM_OF_EPOCH_BEFORE_STAKING_REWARDS_REDEEMABLE = 2;
export const NUM_OF_EPOCH_BEFORE_STAKING_REWARDS_STARTS = 1;
export const MIN_NUMBER_SUI_TO_STAKE = 1;
export const DELEGATED_STAKES_QUERY_STALE_TIME = 10_000;
export const DELEGATED_STAKES_QUERY_REFETCH_INTERVAL = 30_000;
14 changes: 11 additions & 3 deletions apps/wallet/src/ui/app/pages/home/tokens/TokenIconLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
// SPDX-License-Identifier: Apache-2.0

import { LargeButton } from '_app/shared/LargeButton';
import { useGetDelegatedStake } from '_app/staking/useGetDelegatedStake';
import { ampli } from '_src/shared/analytics/ampli';
import {
DELEGATED_STAKES_QUERY_REFETCH_INTERVAL,
DELEGATED_STAKES_QUERY_STALE_TIME,
} from '_src/shared/constants';
import { Text } from '_src/ui/app/shared/text';
import { useFormatCoin } from '@mysten/core';
import { useFormatCoin, useGetDelegatedStake } from '@mysten/core';
import { WalletActionStake24 } from '@mysten/icons';
import { SUI_TYPE_ARG } from '@mysten/sui.js/utils';
import { useMemo } from 'react';
Expand All @@ -17,7 +20,11 @@ export function TokenIconLink({
accountAddress: string;
disabled: boolean;
}) {
const { data: delegatedStake, isLoading } = useGetDelegatedStake(accountAddress);
const { data: delegatedStake, isLoading } = useGetDelegatedStake({
address: accountAddress,
staleTime: DELEGATED_STAKES_QUERY_STALE_TIME,
refetchInterval: DELEGATED_STAKES_QUERY_REFETCH_INTERVAL,
});

// Total active stake for all delegations
const totalActivePendingStake = useMemo(() => {
Expand All @@ -44,6 +51,7 @@ export function TokenIconLink({
}}
loading={isLoading || queryResult.isLoading}
before={<WalletActionStake24 />}
data-testId={`stake-button-${formatted}-${symbol}`}
>
<div className="flex flex-col">
<Text variant="pBody" weight="semibold">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@ import LoadingIndicator from '_components/loading/LoadingIndicator';
import { useAppSelector, useCoinsReFetchingConfig } from '_hooks';
import { ampli } from '_src/shared/analytics/ampli';
import { API_ENV } from '_src/shared/api-env';
import { MIN_NUMBER_SUI_TO_STAKE } from '_src/shared/constants';
import {
DELEGATED_STAKES_QUERY_REFETCH_INTERVAL,
DELEGATED_STAKES_QUERY_STALE_TIME,
MIN_NUMBER_SUI_TO_STAKE,
} from '_src/shared/constants';
import FaucetRequestButton from '_src/ui/app/shared/faucet/FaucetRequestButton';
import { useCoinMetadata, useGetValidatorsApy } from '@mysten/core';
import { useCoinMetadata, useGetDelegatedStake, useGetValidatorsApy } from '@mysten/core';
import { useSuiClientQuery } from '@mysten/dapp-kit';
import { ArrowLeft16, StakeAdd16, StakeRemove16 } from '@mysten/icons';
import type { StakeObject } from '@mysten/sui.js/client';
Expand All @@ -26,7 +30,6 @@ import { useActiveAddress } from '../../hooks/useActiveAddress';
import { Heading } from '../../shared/heading';
import { getDelegationDataByStakeId } from '../getDelegationByStakeId';
import { StakeAmount } from '../home/StakeAmount';
import { useGetDelegatedStake } from '../useGetDelegatedStake';

type DelegationDetailCardProps = {
validatorAddress: string;
Expand All @@ -42,7 +45,15 @@ export function DelegationDetailCard({ validatorAddress, stakedId }: DelegationD

const accountAddress = useActiveAddress();

const { data: allDelegation, isLoading, isError } = useGetDelegatedStake(accountAddress || '');
const {
data: allDelegation,
isLoading,
isError,
} = useGetDelegatedStake({
address: accountAddress || '',
staleTime: DELEGATED_STAKES_QUERY_STALE_TIME,
refetchInterval: DELEGATED_STAKES_QUERY_REFETCH_INTERVAL,
});

const apiEnv = useAppSelector(({ app }) => app.apiEnv);
const { staleTime, refetchInterval } = useCoinsReFetchingConfig();
Expand Down
6 changes: 4 additions & 2 deletions apps/wallet/src/ui/app/staking/delegation-detail/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@

import LoadingIndicator from '_components/loading/LoadingIndicator';
import Overlay from '_components/overlay';
import { useGetDelegatedStake } from '@mysten/core';
import { Navigate, useNavigate, useSearchParams } from 'react-router-dom';

import { useActiveAddress } from '../../hooks/useActiveAddress';
import { getDelegationDataByStakeId } from '../getDelegationByStakeId';
import { useGetDelegatedStake } from '../useGetDelegatedStake';
import { ValidatorLogo } from '../validators/ValidatorLogo';
import { DelegationDetailCard } from './DelegationDetailCard';

Expand All @@ -17,7 +17,9 @@ export function DelegationDetail() {
const stakeIdParams = searchParams.get('staked');
const navigate = useNavigate();
const accountAddress = useActiveAddress();
const { data, isLoading } = useGetDelegatedStake(accountAddress || '');
const { data, isLoading } = useGetDelegatedStake({
address: accountAddress || '',
});

if (!validatorAddressParams || !stakeIdParams) {
return <Navigate to={'/stake'} replace={true} />;
Expand Down
17 changes: 12 additions & 5 deletions apps/wallet/src/ui/app/staking/stake/StakingCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ import { parseAmount } from '_helpers';
import { useCoinsReFetchingConfig } from '_hooks';
import { Coin } from '_redux/slices/sui-objects/Coin';
import { ampli } from '_src/shared/analytics/ampli';
import { MIN_NUMBER_SUI_TO_STAKE } from '_src/shared/constants';
import {
DELEGATED_STAKES_QUERY_REFETCH_INTERVAL,
DELEGATED_STAKES_QUERY_STALE_TIME,
MIN_NUMBER_SUI_TO_STAKE,
} from '_src/shared/constants';
import { FEATURES } from '_src/shared/experimentation/features';
import { useFeatureIsOn } from '@growthbook/growthbook-react';
import { useCoinMetadata } from '@mysten/core';
import { useCoinMetadata, useGetDelegatedStake } from '@mysten/core';
import { useSuiClientQuery } from '@mysten/dapp-kit';
import { ArrowLeft16 } from '@mysten/icons';
import type { StakeObject } from '@mysten/sui.js/client';
Expand All @@ -34,7 +38,6 @@ import { useSigner } from '../../hooks/useSigner';
import { QredoActionIgnoredByUser } from '../../QredoSigner';
import { getDelegationDataByStakeId } from '../getDelegationByStakeId';
import { getStakeSuiBySuiId } from '../getStakeSuiBySuiId';
import { useGetDelegatedStake } from '../useGetDelegatedStake';
import StakeForm from './StakeForm';
import { UnStakeForm } from './UnstakeForm';
import { createStakeTransaction, createUnstakeTransaction } from './utils/transaction';
Expand Down Expand Up @@ -62,7 +65,11 @@ function StakingCard() {
const validatorAddress = searchParams.get('address');
const stakeSuiIdParams = searchParams.get('staked');
const unstake = searchParams.get('unstake') === 'true';
const { data: allDelegation, isLoading } = useGetDelegatedStake(accountAddress || '');
const { data: allDelegation, isLoading } = useGetDelegatedStake({
address: accountAddress || '',
staleTime: DELEGATED_STAKES_QUERY_STALE_TIME,
refetchInterval: DELEGATED_STAKES_QUERY_REFETCH_INTERVAL,
});
const effectsOnlySharedTransactions = useFeatureIsOn(
FEATURES.WALLET_EFFECTS_ONLY_SHARED_TRANSACTION as string,
);
Expand Down Expand Up @@ -222,7 +229,7 @@ function StakingCard() {
queryKey: ['system', 'state'],
}),
queryClient.invalidateQueries({
queryKey: ['validator'],
queryKey: ['delegated-stakes'],
}),
]);
resetForm();
Expand Down
23 changes: 20 additions & 3 deletions apps/wallet/src/ui/app/staking/stake/ValidatorFormDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,18 @@
import { Card } from '_app/shared/card';
import Alert from '_components/alert';
import LoadingIndicator from '_components/loading/LoadingIndicator';
import {
DELEGATED_STAKES_QUERY_REFETCH_INTERVAL,
DELEGATED_STAKES_QUERY_STALE_TIME,
} from '_src/shared/constants';
import { Text } from '_src/ui/app/shared/text';
import { IconTooltip } from '_src/ui/app/shared/tooltip';
import { calculateStakeShare, formatPercentageDisplay, useGetValidatorsApy } from '@mysten/core';
import {
calculateStakeShare,
formatPercentageDisplay,
useGetDelegatedStake,
useGetValidatorsApy,
} from '@mysten/core';
import { useSuiClientQuery } from '@mysten/dapp-kit';
import { useMemo } from 'react';
import { useSearchParams } from 'react-router-dom';
Expand All @@ -15,7 +24,6 @@ import { useActiveAddress } from '../../hooks/useActiveAddress';
import { getStakeSuiBySuiId } from '../getStakeSuiBySuiId';
import { getTokenStakeSuiForValidator } from '../getTokenStakeSuiForValidator';
import { StakeAmount } from '../home/StakeAmount';
import { useGetDelegatedStake } from '../useGetDelegatedStake';
import { ValidatorLogo } from '../validators/ValidatorLogo';

type ValidatorFormDetailProps = {
Expand All @@ -34,7 +42,16 @@ export function ValidatorFormDetail({ validatorAddress, unstake }: ValidatorForm
isError: errorValidators,
} = useSuiClientQuery('getLatestSuiSystemState');

const { data: stakeData, isLoading, isError, error } = useGetDelegatedStake(accountAddress || '');
const {
data: stakeData,
isLoading,
isError,
error,
} = useGetDelegatedStake({
address: accountAddress || '',
staleTime: DELEGATED_STAKES_QUERY_STALE_TIME,
refetchInterval: DELEGATED_STAKES_QUERY_REFETCH_INTERVAL,
});

const { data: rollingAverageApys } = useGetValidatorsApy();

Expand Down
16 changes: 0 additions & 16 deletions apps/wallet/src/ui/app/staking/useGetDelegatedStake.tsx

This file was deleted.

Loading

3 comments on commit 4265d4b

@vercel
Copy link

@vercel vercel bot commented on 4265d4b Oct 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 4265d4b Oct 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

mysten-ui – ./apps/ui

mysten-ui.vercel.app
mysten-ui-git-main-mysten-labs.vercel.app
mysten-ui-mysten-labs.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 4265d4b Oct 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.