Skip to content

Commit

Permalink
feature(wallet-mobile): new tx review for stake delegation (#3696)
Browse files Browse the repository at this point in the history
  • Loading branch information
banklesss authored Oct 21, 2024
1 parent 1b18d92 commit eb9c3bf
Show file tree
Hide file tree
Showing 19 changed files with 495 additions and 570 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const TokenItem = ({
style={[styles.sentTokenItem, !isPrimaryToken && styles.notPrimarySentTokenItem]}
disabled={isPrimaryToken}
>
<Text style={[styles.tokenSentItemText, !isPrimaryToken && styles.notPrimarySentTokenItemText]}>-{label}</Text>
<Text style={[styles.tokenSentItemText, !isPrimaryToken && styles.notPrimarySentTokenItemText]}>{label}</Text>
</TouchableOpacity>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ export const useFormattedTx = (data: TransactionBody): FormattedTx => {
const formattedOutputs = useFormattedOutputs(wallet, outputs, portfolioTokenInfos)
const formattedFee = formatFee(wallet, data)

return {inputs: formattedInputs, outputs: formattedOutputs, fee: formattedFee}
return {
inputs: formattedInputs,
outputs: formattedOutputs,
fee: formattedFee,
certificates: data.certs ?? null,
}
}

export const useFormattedInputs = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ export const useStrings = () => {
description: intl.formatMessage(messages.description),
details: intl.formatMessage(messages.details),
tokenDetailsTitle: intl.formatMessage(messages.tokenDetailsTitle),
registerStakingKey: intl.formatMessage(messages.registerStakingKey),
selectAbstain: intl.formatMessage(messages.selectAbstain),
selectNoConfidence: intl.formatMessage(messages.selectNoConfidence),
delegateVotingToDRep: intl.formatMessage(messages.delegateVotingToDRep),
delegateStake: intl.formatMessage(messages.delegateStake),
}
}

Expand Down Expand Up @@ -151,4 +156,24 @@ const messages = defineMessages({
id: 'txReview.tokenDetails.title',
defaultMessage: '!!!Asset Details',
},
registerStakingKey: {
id: 'txReview.operations.registerStakingKey',
defaultMessage: '!!!Register staking key deposit',
},
selectAbstain: {
id: 'txReview.operations.selectAbstain',
defaultMessage: '!!!Select abstain',
},
selectNoConfidence: {
id: 'txReview.operations.selectNoConfidence',
defaultMessage: '!!!Select no confidence',
},
delegateVotingToDRep: {
id: 'txReview.operations.delegateVotingToDRep',
defaultMessage: '!!!Delegate voting to',
},
delegateStake: {
id: 'txReview.operations.delegateStake',
defaultMessage: '!!!Stake entire wallet balance to',
},
})
140 changes: 140 additions & 0 deletions apps/wallet-mobile/src/features/ReviewTx/common/operations.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import {PoolInfoApi} from '@emurgo/yoroi-lib'
import {useBech32DRepID} from '@yoroi/staking'
import {useTheme} from '@yoroi/theme'
import * as React from 'react'
import {Linking, StyleSheet, Text, View} from 'react-native'
import {TouchableOpacity} from 'react-native-gesture-handler'
import {useQuery} from 'react-query'

import {Space} from '../../../components/Space/Space'
import {getPoolBech32Id} from '../../../yoroi-wallets/cardano/delegationUtils'
import {formatTokenWithText} from '../../../yoroi-wallets/utils/format'
import {asQuantity} from '../../../yoroi-wallets/utils/utils'
import {useSelectedNetwork} from '../../WalletManager/common/hooks/useSelectedNetwork'
import {useSelectedWallet} from '../../WalletManager/common/hooks/useSelectedWallet'
import {useStrings} from './hooks/useStrings'

export const RegisterStakingKeyOperation = () => {
const {styles} = useStyles()
const strings = useStrings()
const {wallet} = useSelectedWallet()

return (
<View style={styles.operation}>
<Text style={styles.operationLabel}>{strings.registerStakingKey}</Text>

<Space width="lg" />

<Text style={styles.operationValue}>
{formatTokenWithText(asQuantity(wallet.protocolParams.keyDeposit), wallet.portfolioPrimaryTokenInfo)}
</Text>
</View>
)
}
export const DelegateStakeOperation = ({poolId}: {poolId: string}) => {
const {styles} = useStyles()
const strings = useStrings()
const poolInfo = usePoolInfo({poolId})
const {networkManager} = useSelectedNetwork()

const poolInfoText = poolInfo != null ? `[${poolInfo.ticker}] ${poolInfo.name}` : poolId

return (
<View style={styles.operation}>
<Text style={styles.operationLabel}>{strings.delegateStake}</Text>

<Space width="lg" />

<TouchableOpacity
activeOpacity={0.5}
onPress={() => Linking.openURL(networkManager.explorers.cardanoscan.pool(poolId))}
>
<Text style={styles.operationLink}>{poolInfoText}</Text>
</TouchableOpacity>
</View>
)
}

export const usePoolInfo = ({poolId}: {poolId: string}) => {
const {networkManager} = useSelectedNetwork()
const poolInfoApi = React.useMemo(
() => new PoolInfoApi(networkManager.legacyApiBaseUrl),
[networkManager.legacyApiBaseUrl],
)
const poolInfo = useQuery({
queryKey: ['usePoolInfoStakeOperation', poolId],
queryFn: async () => {
const poolBech32Id = await getPoolBech32Id(poolId)
return poolInfoApi.getSingleExplorerPoolInfo(poolBech32Id)
},
})

return poolInfo?.data ?? null
}

export const AbstainOperation = () => {
const {styles} = useStyles()
const strings = useStrings()

return (
<View style={styles.operation}>
<Text style={styles.operationLabel}>{strings.selectAbstain}</Text>
</View>
)
}

export const NoConfidenceOperation = () => {
const {styles} = useStyles()
const strings = useStrings()

return (
<View style={styles.operation}>
<Text style={styles.operationLabel}>{strings.selectNoConfidence}</Text>
</View>
)
}

export const DelegateVotingToDrepOperation = ({drepID}: {drepID: string}) => {
const {styles} = useStyles()
const strings = useStrings()

const {data: bech32DrepId} = useBech32DRepID(drepID)

return (
<View style={styles.operation}>
<Text style={styles.operationLabel}>{strings.delegateVotingToDRep}</Text>

<Space width="lg" />

<Text style={styles.operationValue}>{bech32DrepId ?? drepID}</Text>
</View>
)
}

const useStyles = () => {
const {color, atoms} = useTheme()

const styles = StyleSheet.create({
operation: {
...atoms.flex_row,
...atoms.justify_between,
...atoms.align_start,
},
operationLabel: {
...atoms.body_2_md_regular,
color: color.text_gray_low,
},
operationValue: {
...atoms.flex_1,
...atoms.text_right,
...atoms.body_2_md_regular,
color: color.text_gray_medium,
},
operationLink: {
...atoms.body_2_md_regular,
color: color.text_primary_medium,
},
})

return {styles} as const
}
4 changes: 4 additions & 0 deletions apps/wallet-mobile/src/features/ReviewTx/common/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
CertificatesJSON,
TransactionBodyJSON,
TransactionInputsJSON,
TransactionOutputsJSON,
Expand Down Expand Up @@ -56,9 +57,12 @@ export type FormattedTx = {
inputs: FormattedInputs
outputs: FormattedOutputs
fee: FormattedFee
certificates: Certificates
}

export type FormattedMetadata = {
hash: string | null
metadata: {msg: Array<unknown>} | null
}

export type Certificates = CertificatesJSON | null
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,29 @@ import {
governanceApiMaker,
governanceManagerMaker,
GovernanceProvider,
useBech32DRepID,
useGovernance,
useStakingKeyState,
useUpdateLatestGovernanceAction,
} from '@yoroi/staking'
import {useTheme} from '@yoroi/theme'
import * as React from 'react'
import {StyleSheet, Text, View} from 'react-native'

import {Space} from '../../../../components/Space/Space'
import {governaceAfterBlock} from '../../../../kernel/config'
import {useWalletNavigation} from '../../../../kernel/navigation'
import {YoroiWallet} from '../../../../yoroi-wallets/cardano/types'
import {useStakingKey} from '../../../../yoroi-wallets/hooks'
import {YoroiUnsignedTx} from '../../../../yoroi-wallets/types/yoroi'
import {formatTokenWithText} from '../../../../yoroi-wallets/utils/format'
import {asQuantity} from '../../../../yoroi-wallets/utils/utils'
import {CardanoMobile} from '../../../../yoroi-wallets/wallets'
import {
AbstainOperation,
DelegateVotingToDrepOperation,
NoConfidenceOperation,
RegisterStakingKeyOperation,
} from '../../../ReviewTx/common/operations'
import {useReviewTx} from '../../../ReviewTx/common/ReviewTxProvider'
import {useBestBlock} from '../../../WalletManager/common/hooks/useBestBlock'
import {useSelectedWallet} from '../../../WalletManager/common/hooks/useSelectedWallet'
import {GovernanceVote} from '../types'
import {useNavigateTo} from './navigation'
import {useStrings} from './strings'

export const useIsParticipatingInGovernance = (wallet: YoroiWallet) => {
const stakingKeyHash = useStakingKey(wallet)
Expand Down Expand Up @@ -100,7 +99,7 @@ export const useGovernanceActions = () => {
}) => {
let operations = [
<GovernanceProvider key="0" manager={manager}>
<DelegateOperation drepID={drepID} />
<DelegateVotingToDrepOperation drepID={drepID} />
</GovernanceProvider>,
]

Expand Down Expand Up @@ -177,84 +176,3 @@ export const useGovernanceActions = () => {

return {handleDelegateAction, handleAbstainAction, handleNoConfidenceAction} as const
}

const RegisterStakingKeyOperation = () => {
const {styles} = useStyles()
const strings = useStrings()
const {wallet} = useSelectedWallet()

return (
<View style={styles.operation}>
<Text style={styles.operationLabel}>{strings.registerStakingKey}</Text>

<Space width="lg" />

<Text style={styles.operationValue}>
{formatTokenWithText(asQuantity(wallet.protocolParams.keyDeposit), wallet.portfolioPrimaryTokenInfo)}
</Text>
</View>
)
}

const AbstainOperation = () => {
const {styles} = useStyles()
const strings = useStrings()

return (
<View style={styles.operation}>
<Text style={styles.operationLabel}>{strings.selectAbstain}</Text>
</View>
)
}

const NoConfidenceOperation = () => {
const {styles} = useStyles()
const strings = useStrings()

return (
<View style={styles.operation}>
<Text style={styles.operationLabel}>{strings.selectNoConfidence}</Text>
</View>
)
}

const DelegateOperation = ({drepID}: {drepID: string}) => {
const {styles} = useStyles()
const strings = useStrings()

const {data: bech32DrepId} = useBech32DRepID(drepID)

return (
<View style={styles.operation}>
<Text style={styles.operationLabel}>{strings.delegateVotingToDRep}</Text>

<Space width="lg" />

<Text style={styles.operationValue}>{bech32DrepId ?? drepID}</Text>
</View>
)
}

const useStyles = () => {
const {color, atoms} = useTheme()

const styles = StyleSheet.create({
operation: {
...atoms.flex_row,
...atoms.justify_between,
...atoms.align_start,
},
operationLabel: {
...atoms.body_2_md_regular,
color: color.text_gray_low,
},
operationValue: {
...atoms.flex_1,
...atoms.text_right,
...atoms.body_2_md_regular,
color: color.text_gray_medium,
},
})

return {styles} as const
}
7 changes: 6 additions & 1 deletion apps/wallet-mobile/src/kernel/i18n/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -1246,5 +1246,10 @@
"txReview.tokenDetails.overViewTab.symbol.label": "Symbol",
"txReview.tokenDetails.overViewTab.description.label": "Description",
"txReview.tokenDetails.overViewTab.details.label": "Details on",
"txReview.tokenDetails.title": "Asset Details"
"txReview.tokenDetails.title": "Asset Details",
"txReview.operations.registerStakingKey": "Register staking key deposit",
"txReview.operations.selectAbstain": "Select abstain",
"txReview.operations.selectNoConfidence": "Select no confidence",
"txReview.operations.delegateVotingToDRep": "Delegate voting to",
"txReview.operations.delegateStake": "Stake entire wallet balance to"
}
9 changes: 0 additions & 9 deletions apps/wallet-mobile/src/kernel/navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {Dimensions, Platform, TouchableOpacity, TouchableOpacityProps, View} fro

import {Icon} from '../components/Icon'
import {Routes as StakingGovernanceRoutes} from '../features/Staking/Governance/common/navigation'
import {YoroiUnsignedTx} from '../yoroi-wallets/types/yoroi'

// prettier-ignore
export const useUnsafeParams = <Params, >() => {
Expand Down Expand Up @@ -190,10 +189,6 @@ export type SwapTokenRouteseNavigation = StackNavigationProp<SwapTokenRoutes>

export type StakingCenterRoutes = {
'staking-center-main': undefined
'delegation-confirmation': {
poolId: string
yoroiUnsignedTx: YoroiUnsignedTx
}
'delegation-failed-tx': undefined
}

Expand Down Expand Up @@ -273,10 +268,6 @@ export type BrowserRoutes = {
export type DashboardRoutes = {
'staking-dashboard-main': undefined
'staking-center': NavigatorScreenParams<StakingCenterRoutes>
'delegation-confirmation': {
poolId: string
yoroiUnsignedTx: YoroiUnsignedTx
}
'delegation-failed-tx': undefined
}

Expand Down
Loading

0 comments on commit eb9c3bf

Please sign in to comment.