Skip to content

Commit

Permalink
feature(wallet-mobile): new tx review for stake delegation
Browse files Browse the repository at this point in the history
  • Loading branch information
banklesss committed Oct 18, 2024
1 parent 1c18498 commit ce59642
Show file tree
Hide file tree
Showing 20 changed files with 501 additions and 580 deletions.
16 changes: 6 additions & 10 deletions apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -837,11 +837,10 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = yoroi/yoroi.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 610;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = F8NVT2G2L4;
DEVELOPMENT_TEAM = F8NVT2G2L4;
ENABLE_BITCODE = NO;
ENVFILE = "$(PODS_ROOT)/../../.env";
INFOPLIST_FILE = yoroi/Info.plist;
Expand All @@ -864,7 +863,6 @@
PRODUCT_BUNDLE_IDENTIFIER = com.emurgo.yoroi;
PRODUCT_NAME = yoroi;
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.emurgo.yoroi";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
Expand All @@ -882,11 +880,10 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = yoroi/yoroi.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 610;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = F8NVT2G2L4;
DEVELOPMENT_TEAM = F8NVT2G2L4;
ENVFILE = "$(PODS_ROOT)/../../.env.production";
INFOPLIST_FILE = yoroi/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Yoroi;
Expand All @@ -908,7 +905,6 @@
PRODUCT_BUNDLE_IDENTIFIER = com.emurgo.yoroi;
PRODUCT_NAME = yoroi;
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.emurgo.yoroi";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
Expand Down
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
}
Loading

0 comments on commit ce59642

Please sign in to comment.