Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix(staking): display stake statuses & exit & claim "values" #4249

Merged
merged 6 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading