Skip to content

Commit

Permalink
Fix(staking): display stake statuses & exit & claim "values" (#4249)
Browse files Browse the repository at this point in the history
* fix: wrong stake statuses

* fix: small UI discrepancies with the figma designs

* fix: change to format duration to milliseconds

* fix: incorrect status displayed

* refactor: validator exit

* chore: update @safe-global/safe-gateway-typescript-sdk
  • Loading branch information
compojoom committed Sep 27, 2024
1 parent 5087ae8 commit d769925
Show file tree
Hide file tree
Showing 15 changed files with 109 additions and 103 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"@safe-global/protocol-kit": "^4.1.0",
"@safe-global/safe-apps-sdk": "^9.1.0",
"@safe-global/safe-deployments": "^1.37.8",
"@safe-global/safe-gateway-typescript-sdk": "3.22.3-beta.13",
"@safe-global/safe-gateway-typescript-sdk": "3.22.3-beta.15",
"@safe-global/safe-modules-deployments": "^2.2.1",
"@sentry/react": "^7.91.0",
"@spindl-xyz/attribution-lite": "^1.4.0",
Expand Down
20 changes: 5 additions & 15 deletions src/components/common/Table/DataRow.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { ReactElement, ReactNode } from 'react'
import { Typography } from '@mui/material'
import css from './styles.module.css'
import FieldsGrid from '@/components/tx/FieldsGrid'

type DataRowProps = {
datatestid?: String
Expand All @@ -10,19 +9,10 @@ type DataRowProps = {

export const DataRow = ({ datatestid, title, children }: DataRowProps): ReactElement | null => {
if (children == undefined) return null
return (
<div data-testid={datatestid} className={css.gridRow}>
<div data-testid="tx-row-title" className={css.title}>
{title}
</div>

{typeof children === 'string' ? (
<Typography component="div" data-testid="tx-data-row">
{children}
</Typography>
) : (
children
)}
</div>
return (
<FieldsGrid data-testid={datatestid} title={title}>
{children}
</FieldsGrid>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ exports[`HexEncodedData should not cut the text in case the limit option is high
class="MuiGrid-root MuiGrid-container css-1d1otxt-MuiGrid-root"
>
<div
class="MuiGrid-root MuiGrid-item css-f2z333-MuiGrid-root"
class="MuiGrid-root MuiGrid-item css-658w1k-MuiGrid-root"
data-testid="tx-row-title"
>
<p
class="MuiTypography-root MuiTypography-body1 MuiTypography-noWrap css-s5yue5-MuiTypography-root"
Expand All @@ -16,6 +17,7 @@ exports[`HexEncodedData should not cut the text in case the limit option is high
</div>
<div
class="MuiGrid-root MuiGrid-item MuiGrid-grid-xs-true css-1vd824g-MuiGrid-root"
data-testid="tx-data-row"
>
<div
class="encodedData MuiBox-root css-0"
Expand Down Expand Up @@ -65,7 +67,8 @@ exports[`HexEncodedData should not highlight the data if highlight option is fal
class="MuiGrid-root MuiGrid-container css-1d1otxt-MuiGrid-root"
>
<div
class="MuiGrid-root MuiGrid-item css-f2z333-MuiGrid-root"
class="MuiGrid-root MuiGrid-item css-658w1k-MuiGrid-root"
data-testid="tx-row-title"
>
<p
class="MuiTypography-root MuiTypography-body1 MuiTypography-noWrap css-s5yue5-MuiTypography-root"
Expand All @@ -75,6 +78,7 @@ exports[`HexEncodedData should not highlight the data if highlight option is fal
</div>
<div
class="MuiGrid-root MuiGrid-item MuiGrid-grid-xs-true css-1vd824g-MuiGrid-root"
data-testid="tx-data-row"
>
<div
class="encodedData MuiBox-root css-0"
Expand Down Expand Up @@ -125,7 +129,8 @@ exports[`HexEncodedData should render the default component markup 1`] = `
class="MuiGrid-root MuiGrid-container css-1d1otxt-MuiGrid-root"
>
<div
class="MuiGrid-root MuiGrid-item css-f2z333-MuiGrid-root"
class="MuiGrid-root MuiGrid-item css-658w1k-MuiGrid-root"
data-testid="tx-row-title"
>
<p
class="MuiTypography-root MuiTypography-body1 MuiTypography-noWrap css-s5yue5-MuiTypography-root"
Expand All @@ -135,6 +140,7 @@ exports[`HexEncodedData should render the default component markup 1`] = `
</div>
<div
class="MuiGrid-root MuiGrid-item MuiGrid-grid-xs-true css-1vd824g-MuiGrid-root"
data-testid="tx-data-row"
>
<div
class="encodedData MuiBox-root css-0"
Expand Down
2 changes: 1 addition & 1 deletion src/components/transactions/TxDetails/TxData/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const TxData = ({
}

if (isStakingTxExitInfo(txDetails.txInfo)) {
return <StakingTxExitDetails txData={txDetails.txData} info={txDetails.txInfo} />
return <StakingTxExitDetails info={txDetails.txInfo} />
}

if (isStakingTxWithdrawInfo(txDetails.txInfo)) {
Expand Down
6 changes: 3 additions & 3 deletions src/components/tx/FieldsGrid/index.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { type ReactNode } from 'react'
import { Grid, Typography } from '@mui/material'

const minWidth = { xl: '25%', lg: '100px' }
const minWidth = { xl: '25%', lg: '200px' }
const wrap = { flexWrap: { xl: 'nowrap' } }

const FieldsGrid = ({ title, children }: { title: string | ReactNode; children: ReactNode }) => {
return (
<Grid container alignItems="center" gap={1} sx={wrap}>
<Grid item minWidth={minWidth}>
<Grid item minWidth={minWidth} data-testid="tx-row-title">
<Typography color="primary.light" noWrap>
{title}
</Typography>
</Grid>

<Grid item xs>
<Grid item xs data-testid="tx-data-row">
{children}
</Grid>
</Grid>
Expand Down
11 changes: 4 additions & 7 deletions src/features/stake/components/StakingConfirmationTx/Deposit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ import type { StakingTxDepositInfo } from '@safe-global/safe-gateway-typescript-
import {
ConfirmationViewTypes,
type NativeStakingDepositConfirmationView,
NativeStakingStatus,
} from '@safe-global/safe-gateway-typescript-sdk'
import ConfirmationOrderHeader from '@/components/tx/ConfirmationOrder/ConfirmationOrderHeader'
import { formatDurationFromSeconds, formatVisualAmount } from '@/utils/formatters'
import { formatDurationFromMilliseconds, formatVisualAmount } from '@/utils/formatters'
import { formatCurrency } from '@/utils/formatNumber'
import StakingStatus from '@/features/stake/components/StakingStatus'
import { InfoTooltip } from '@/features/stake/components/InfoTooltip'
Expand All @@ -33,7 +32,7 @@ const StakingConfirmationTxDeposit = ({ order }: StakingOrderConfirmationViewPro
},
{
value: order.annualNrr.toFixed(3) + '%',
label: 'Earn (after fees)',
label: 'Rewards rate (after fees)',
},
]}
/>
Expand Down Expand Up @@ -79,14 +78,12 @@ const StakingConfirmationTxDeposit = ({ order }: StakingOrderConfirmationViewPro
<FieldsGrid title="Validators">{order.numValidators}</FieldsGrid>
)}

{!isOrder && order.status === NativeStakingStatus.VALIDATION_STARTED ? null : (
<FieldsGrid title="Active in">{formatDurationFromSeconds(order.estimatedEntryTime)}</FieldsGrid>
)}
<FieldsGrid title="Activation time">{formatDurationFromMilliseconds(order.estimatedEntryTime)}</FieldsGrid>

<FieldsGrid title="Rewards">Approx. every 5 days after activation</FieldsGrid>

{!isOrder && (
<FieldsGrid title="Status">
<FieldsGrid title="Validator status">
<StakingStatus status={order.status} />
</FieldsGrid>
)}
Expand Down
7 changes: 3 additions & 4 deletions src/features/stake/components/StakingConfirmationTx/Exit.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { Alert, Stack, Typography } from '@mui/material'
import FieldsGrid from '@/components/tx/FieldsGrid'
import type { StakingTxExitInfo } from '@safe-global/safe-gateway-typescript-sdk'
import { formatDurationFromSeconds } from '@/utils/formatters'
import { formatDurationFromMilliseconds } from '@/utils/formatters'
import { type NativeStakingValidatorsExitConfirmationView } from '@safe-global/safe-gateway-typescript-sdk/dist/types/decoded-data'
import ConfirmationOrderHeader from '@/components/tx/ConfirmationOrder/ConfirmationOrderHeader'
import { InfoTooltip } from '@/features/stake/components/InfoTooltip'

type StakingOrderConfirmationViewProps = {
order: NativeStakingValidatorsExitConfirmationView | StakingTxExitInfo
order: NativeStakingValidatorsExitConfirmationView
}

const StakingConfirmationTxExit = ({ order }: StakingOrderConfirmationViewProps) => {
const withdrawIn = formatDurationFromSeconds(order.estimatedExitTime + order.estimatedWithdrawalTime, [
const withdrawIn = formatDurationFromMilliseconds(order.estimatedExitTime + order.estimatedWithdrawalTime, [
'days',
'hours',
])
Expand Down
56 changes: 28 additions & 28 deletions src/features/stake/components/StakingStatus/index.tsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,60 @@
import { NativeStakingExitStatus, NativeStakingStatus } from '@safe-global/safe-gateway-typescript-sdk'
import { NativeStakingStatus } from '@safe-global/safe-gateway-typescript-sdk'
import { SvgIcon } from '@mui/material'
import CheckIcon from '@/public/images/common/circle-check.svg'
import ClockIcon from '@/public/images/common/clock.svg'
import SlashShield from '@/public/images/common/shield-off.svg'
import SignatureIcon from '@/public/images/common/document_signature.svg'
import TxStatusChip, { type TxStatusChipProps } from '@/components/transactions/TxStatusChip'

const ColorIcons: Record<
NativeStakingStatus | NativeStakingExitStatus,
NativeStakingStatus,
| {
color: TxStatusChipProps['color']
icon?: React.ComponentType
text: string
}
| undefined
> = {
[NativeStakingStatus.AWAITING_ENTRY]: {
[NativeStakingStatus.NOT_STAKED]: {
color: 'warning',
icon: SignatureIcon,
text: 'Inactive',
},
[NativeStakingStatus.ACTIVATING]: {
color: 'info',
icon: ClockIcon,
text: 'Activating',
},
[NativeStakingStatus.REQUESTED_EXIT]: {
[NativeStakingStatus.DEPOSIT_IN_PROGRESS]: {
color: 'info',
icon: ClockIcon,
text: 'Requested exit',
text: 'Awaiting entry',
},
[NativeStakingStatus.SIGNATURE_NEEDED]: {
color: 'warning',
icon: SignatureIcon,
text: 'Signature needed',
[NativeStakingStatus.ACTIVE]: {
color: 'success',
icon: CheckIcon,
text: 'Validating',
},
[NativeStakingStatus.AWAITING_EXECUTION]: {
color: 'warning',
[NativeStakingStatus.EXIT_REQUESTED]: {
color: 'info',
icon: ClockIcon,
text: 'Awaiting execution',
text: 'Requested exit',
},
[NativeStakingStatus.VALIDATION_STARTED]: {
color: 'success',
icon: CheckIcon,
text: 'Validation started',
[NativeStakingStatus.EXITING]: {
color: 'info',
icon: ClockIcon,
text: 'Request pending',
},
[NativeStakingStatus.WITHDRAWN]: {
[NativeStakingStatus.EXITED]: {
color: 'success',
icon: CheckIcon,
text: 'Withdrawn',
},
[NativeStakingExitStatus.READY_TO_WITHDRAW]: {
color: 'success',
icon: CheckIcon,
text: 'Ready to withdraw',
},
[NativeStakingExitStatus.REQUEST_PENDING]: {
color: 'info',
icon: ClockIcon,
text: 'Request pending',
[NativeStakingStatus.SLASHED]: {
color: 'warning',
icon: SlashShield,
text: 'Slashed',
},
[NativeStakingStatus.UNKNOWN]: undefined,
}

const capitalizedStatus = (status: string) =>
Expand All @@ -63,7 +63,7 @@ const capitalizedStatus = (status: string) =>
.replace(/_/g, ' ')
.replace(/^\w/g, (l) => l.toUpperCase())

const StakingStatus = ({ status }: { status: NativeStakingStatus | NativeStakingExitStatus }) => {
const StakingStatus = ({ status }: { status: NativeStakingStatus }) => {
const config = ColorIcons[status]

return (
Expand Down
4 changes: 2 additions & 2 deletions src/features/stake/components/StakingTxDepositInfo/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { StakingTxInfo } from '@safe-global/safe-gateway-typescript-sdk'
import type { StakingTxDepositInfo as StakingTxDepositInfoType } from '@safe-global/safe-gateway-typescript-sdk'
import TokenAmount from '@/components/common/TokenAmount'

export const StakingTxDepositInfo = ({ info }: { info: StakingTxInfo }) => {
export const StakingTxDepositInfo = ({ info }: { info: StakingTxDepositInfoType }) => {
return (
<>
<TokenAmount
Expand Down
60 changes: 37 additions & 23 deletions src/features/stake/components/StakingTxExitDetails/index.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,51 @@
import { Box } from '@mui/material'
import type { StakingTxExitInfo, TransactionData } from '@safe-global/safe-gateway-typescript-sdk'
import { NativeStakingExitStatus } from '@safe-global/safe-gateway-typescript-sdk'
import { Box, Link } from '@mui/material'
import type { StakingTxExitInfo } from '@safe-global/safe-gateway-typescript-sdk'
import { NativeStakingStatus } from '@safe-global/safe-gateway-typescript-sdk'
import FieldsGrid from '@/components/tx/FieldsGrid'
import TokenAmount from '@/components/common/TokenAmount'
import StakingStatus from '@/features/stake/components/StakingStatus'
import { formatDurationFromSeconds } from '@/utils/formatters'
import { formatDurationFromMilliseconds } from '@/utils/formatters'
import { BEACON_CHAIN_EXPLORERS } from '@/features/stake/constants'
import useChainId from '@/hooks/useChainId'

const StakingTxExitDetails = ({ info }: { info: StakingTxExitInfo; txData?: TransactionData }) => {
const withdrawIn = formatDurationFromSeconds(info.estimatedExitTime + info.estimatedWithdrawalTime, ['days', 'hours'])
return (
<Box pl={1} pr={5} display="flex" flexDirection="column" gap={1}>
<FieldsGrid title="Receive">
<TokenAmount
value={info.value}
tokenSymbol={info.tokenInfo.symbol}
decimals={info.tokenInfo.decimals}
logoUri={info.tokenInfo.logoUri}
/>
</FieldsGrid>
const StakingTxExitDetails = ({ info }: { info: StakingTxExitInfo }) => {
const withdrawIn = formatDurationFromMilliseconds(info.estimatedExitTime + info.estimatedWithdrawalTime, [
'days',
'hours',
])

return (
<Box pr={5} display="flex" flexDirection="column" gap={1}>
<FieldsGrid title="Exit">
{info.numValidators} Validator{info.numValidators > 1 ? 's' : ''}
{info.validators.map((validator: string, index: number) => {
return (
<>
<BeaconChainLink name={`Validator ${index + 1}`} validator={validator} key={index} />
{index < info.validators.length - 1 && ' | '}
</>
)
})}
</FieldsGrid>
{info.status !== NativeStakingStatus.EXITED && <FieldsGrid title="Est. exit time">Up to {withdrawIn}</FieldsGrid>}

{info.status !== NativeStakingExitStatus.READY_TO_WITHDRAW && (
<FieldsGrid title="Est. exit time">Up to {withdrawIn}</FieldsGrid>
)}

<FieldsGrid title="Exit">
<FieldsGrid title="Validator status">
<StakingStatus status={info.status} />
</FieldsGrid>
</Box>
)
}

export const BeaconChainLink = ({ validator, name }: { validator: string; name: string }) => {
const chainId = useChainId()
return (
<Link
variant="body1"
target="_blank"
href={`${
BEACON_CHAIN_EXPLORERS[chainId as keyof typeof BEACON_CHAIN_EXPLORERS] ?? 'https://beaconcha.in'
}/validator/${validator}`}
>
{name}
</Link>
)
}
export default StakingTxExitDetails
12 changes: 3 additions & 9 deletions src/features/stake/components/StakingTxExitInfo/index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
import type { StakingTxInfo } from '@safe-global/safe-gateway-typescript-sdk'
import TokenAmount from '@/components/common/TokenAmount'
import type { StakingTxExitInfo } from '@safe-global/safe-gateway-typescript-sdk'

const StakingTxExitInfo = ({ info }: { info: StakingTxInfo }) => {
const StakingTxExitInfo = ({ info }: { info: StakingTxExitInfo }) => {
return (
<>
<TokenAmount
value={info.value}
tokenSymbol={info.tokenInfo.symbol}
decimals={info.tokenInfo.decimals}
logoUri={info.tokenInfo.logoUri}
/>
{info.numValidators} Validator{info.numValidators > 1 ? 's' : ''}
</>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const StakingTxWithdrawInfo = ({ info }: { info: StakingTxWithdrawInfo }) => {
return (
<>
<TokenAmount
value={info.rewards}
value={info.value}
tokenSymbol={info.tokenInfo.symbol}
decimals={info.tokenInfo.decimals}
logoUri={info.tokenInfo.logoUri}
Expand Down
Loading

0 comments on commit d769925

Please sign in to comment.