Skip to content

Commit

Permalink
feature(wallet-mobile): new tx review success and error screens (#3712)
Browse files Browse the repository at this point in the history
  • Loading branch information
banklesss authored Oct 27, 2024
1 parent bc9338d commit c14e545
Show file tree
Hide file tree
Showing 75 changed files with 979 additions and 2,293 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import React, {useState} from 'react'
import {ErrorBoundary} from 'react-error-boundary'
import {ActivityIndicator, ScrollView, StyleSheet, View} from 'react-native'

import {LedgerTransportSwitch} from '../../features/Swap/useCases/ConfirmTxScreen/LedgerTransportSwitch'
import {useSelectedWallet} from '../../features/WalletManager/common/hooks/useSelectedWallet'
import {useWalletManager} from '../../features/WalletManager/context/WalletManagerProvider'
import {LedgerConnect} from '../../legacy/HW'
import {useSignTxWithHW, useSubmitTx} from '../../yoroi-wallets/hooks'
import {withBLE, withUSB} from '../../yoroi-wallets/hw/hwWallet'
import {YoroiSignedTx, YoroiUnsignedTx} from '../../yoroi-wallets/types/yoroi'
import {delay} from '../../yoroi-wallets/utils/timeUtils'
import {LedgerTransportSwitch} from '../LedgerTransportSwitch/LedgerTransportSwitch'
import {ModalError} from '../ModalError/ModalError'
import {Text} from '../Text'
import {useStrings} from './strings'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import {useTheme} from '@yoroi/theme'
import React from 'react'
import {Alert, ScrollView, StyleSheet, View} from 'react-native'

import {Button, ButtonType} from '../../../../components/Button/Button'
import {Spacer} from '../../../../components/Spacer/Spacer'
import {Text} from '../../../../components/Text'
import {useIsUsbSupported} from '../../../../legacy/HW'
import {HARDWARE_WALLETS, useLedgerPermissions} from '../../../../yoroi-wallets/hw/hw'
import {useStrings} from '../../common/strings'
import {useStrings} from '../../features/Swap/common/strings'
import {useIsUsbSupported} from '../../legacy/HW'
import {HARDWARE_WALLETS, useLedgerPermissions} from '../../yoroi-wallets/hw/hw'
import {Button, ButtonType} from '../Button/Button'
import {Spacer} from '../Spacer/Spacer'
import {Text} from '../Text'

type Props = {
onSelectUSB: () => void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import {HW} from '@yoroi/types'
import React, {useCallback, useState} from 'react'
import {ActivityIndicator, ScrollView, StyleSheet, View} from 'react-native'

import {LedgerTransportSwitch} from '../../../components/LedgerTransportSwitch/LedgerTransportSwitch'
import {useModal} from '../../../components/Modal/ModalContext'
import {Text} from '../../../components/Text'
import {LedgerConnect} from '../../../legacy/HW'
import {withBLE, withUSB} from '../../../yoroi-wallets/hw/hwWallet'
import {LedgerTransportSwitch} from '../../Swap/useCases/ConfirmTxScreen/LedgerTransportSwitch'
import {useSelectedWallet} from '../../WalletManager/common/hooks/useSelectedWallet'
import {useWalletManager} from '../../WalletManager/context/WalletManagerProvider'
import {useStrings} from './useStrings'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const ConfirmPin = () => {
const navigateTo = useNavigateTo()
const [currentActivePin, setCurrentActivePin] = React.useState(1)
const {wallet, meta} = useSelectedWallet()
const {onCIP36SupportChangeChanged, unsignedTxChanged, onSuccessChanged} = useReviewTx()
const {unsignedTxChanged} = useReviewTx()
const {navigateToTxReview} = useWalletNavigation()

const {generateVotingKeys, isLoading} = useGenerateVotingKeys({
Expand All @@ -40,13 +40,13 @@ export const ConfirmPin = () => {
})

unsignedTxChanged(votingRegTx.votingRegTx)
onCIP36SupportChangeChanged(async (supportsCIP36: boolean) => {
votingRegTx = await wallet.createVotingRegTx({catalystKeyHex, supportsCIP36, addressMode: meta.addressMode})
unsignedTxChanged(votingRegTx.votingRegTx)
navigateToTxReview({
onCIP36SupportChange: async (supportsCIP36: boolean) => {
votingRegTx = await wallet.createVotingRegTx({catalystKeyHex, supportsCIP36, addressMode: meta.addressMode})
unsignedTxChanged(votingRegTx.votingRegTx)
},
onSuccess: navigateTo.qrCode,
})
onSuccessChanged(navigateTo.qrCode)

navigateToTxReview()
},
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {Boundary} from '../../components/Boundary/Boundary'
import {defaultStackNavigationOptions, ReviewTxRoutes} from '../../kernel/navigation'
import {useStrings} from './common/hooks/useStrings'
import {ReviewTxScreen} from './useCases/ReviewTxScreen/ReviewTxScreen'
import {FailedTxScreen} from './useCases/ShowFailedTxScreen/FailedTxScreen'
import {SubmittedTxScreen} from './useCases/ShowSubmittedTxScreen/SubmittedTxScreen'

export const Stack = createStackNavigator<ReviewTxRoutes>()

Expand All @@ -26,6 +28,10 @@ export const ReviewTxNavigator = () => {
</Boundary>
)}
</Stack.Screen>

<Stack.Screen name="review-tx-submitted-tx" component={SubmittedTxScreen} options={{headerShown: false}} />

<Stack.Screen name="review-tx-failed-tx" component={FailedTxScreen} options={{headerShown: false}} />
</Stack.Navigator>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {castDraft, produce} from 'immer'
import _ from 'lodash'
import React from 'react'

import {YoroiSignedTx, YoroiUnsignedTx} from '../../../yoroi-wallets/types/yoroi'
import {YoroiUnsignedTx} from '../../../yoroi-wallets/types/yoroi'

export const useReviewTx = () => React.useContext(ReviewTxContext)

Expand All @@ -27,13 +27,6 @@ export const ReviewTxProvider = ({
customReceiverTitleChanged: (customReceiverTitle: ReviewTxState['customReceiverTitle']) =>
dispatch({type: ReviewTxActionType.CustomReceiverTitleChanged, customReceiverTitle}),
detailsChanged: (details: ReviewTxState['details']) => dispatch({type: ReviewTxActionType.DetailsChanged, details}),
onSuccessChanged: (onSuccess: ReviewTxState['onSuccess']) =>
dispatch({type: ReviewTxActionType.OnSuccessChanged, onSuccess}),
onErrorChanged: (onError: ReviewTxState['onError']) => dispatch({type: ReviewTxActionType.OnErrorChanged, onError}),
onNotSupportedCIP1694Changed: (onNotSupportedCIP1694: ReviewTxState['onNotSupportedCIP1694']) =>
dispatch({type: ReviewTxActionType.OnNotSupportedCIP1694Changed, onNotSupportedCIP1694}),
onCIP36SupportChangeChanged: (onCIP36SupportChange: ReviewTxState['onCIP36SupportChange']) =>
dispatch({type: ReviewTxActionType.OnCIP36SupportChangeChanged, onCIP36SupportChange}),
reset: () => dispatch({type: ReviewTxActionType.Reset}),
}).current

Expand Down Expand Up @@ -71,32 +64,12 @@ const reviewTxReducer = (state: ReviewTxState, action: ReviewTxAction) => {
draft.details = action.details
break

case ReviewTxActionType.OnSuccessChanged:
draft.onSuccess = action.onSuccess
break

case ReviewTxActionType.OnErrorChanged:
draft.onError = action.onError
break

case ReviewTxActionType.OnNotSupportedCIP1694Changed:
draft.onNotSupportedCIP1694 = action.onNotSupportedCIP1694
break

case ReviewTxActionType.OnCIP36SupportChangeChanged:
draft.onCIP36SupportChange = action.onCIP36SupportChange
break

case ReviewTxActionType.Reset:
draft.unsignedTx = castDraft(defaultState.unsignedTx)
draft.cbor = defaultState.cbor
draft.operations = defaultState.operations
draft.customReceiverTitle = defaultState.customReceiverTitle
draft.details = defaultState.details
draft.onSuccess = defaultState.onSuccess
draft.onError = defaultState.onError
draft.onNotSupportedCIP1694 = defaultState.onNotSupportedCIP1694
draft.onCIP36SupportChange = defaultState.onCIP36SupportChange
break

default:
Expand Down Expand Up @@ -126,22 +99,6 @@ type ReviewTxAction =
type: ReviewTxActionType.DetailsChanged
details: ReviewTxState['details']
}
| {
type: ReviewTxActionType.OnSuccessChanged
onSuccess: ReviewTxState['onSuccess']
}
| {
type: ReviewTxActionType.OnErrorChanged
onError: ReviewTxState['onError']
}
| {
type: ReviewTxActionType.OnNotSupportedCIP1694Changed
onNotSupportedCIP1694: ReviewTxState['onNotSupportedCIP1694']
}
| {
type: ReviewTxActionType.OnCIP36SupportChangeChanged
onCIP36SupportChange: ReviewTxState['onCIP36SupportChange']
}
| {
type: ReviewTxActionType.Reset
}
Expand All @@ -152,10 +109,6 @@ export type ReviewTxState = {
operations: Array<React.ReactNode> | null
customReceiverTitle: React.ReactNode | null
details: {title: string; component: React.ReactNode} | null
onSuccess: ((signedTx: YoroiSignedTx) => void) | null
onError: (() => void) | null
onNotSupportedCIP1694: (() => void) | null
onCIP36SupportChange: ((isCIP36Supported: boolean) => void) | null
}

type ReviewTxActions = {
Expand All @@ -164,10 +117,6 @@ type ReviewTxActions = {
operationsChanged: (operations: ReviewTxState['operations']) => void
customReceiverTitleChanged: (customReceiverTitle: ReviewTxState['customReceiverTitle']) => void
detailsChanged: (details: ReviewTxState['details']) => void
onSuccessChanged: (onSuccess: ReviewTxState['onSuccess']) => void
onErrorChanged: (onError: ReviewTxState['onError']) => void
onNotSupportedCIP1694Changed: (onNotSupportedCIP1694: ReviewTxState['onNotSupportedCIP1694']) => void
onCIP36SupportChangeChanged: (onCIP36SupportChange: ReviewTxState['onCIP36SupportChange']) => void
reset: () => void
}

Expand All @@ -177,10 +126,6 @@ const defaultState: ReviewTxState = Object.freeze({
operations: null,
customReceiverTitle: null,
details: null,
onSuccess: null,
onError: null,
onNotSupportedCIP1694: null,
onCIP36SupportChange: null,
})

function missingInit() {
Expand All @@ -194,10 +139,6 @@ const initialReviewTxContext: ReviewTxContext = {
operationsChanged: missingInit,
customReceiverTitleChanged: missingInit,
detailsChanged: missingInit,
onSuccessChanged: missingInit,
onErrorChanged: missingInit,
onNotSupportedCIP1694Changed: missingInit,
onCIP36SupportChangeChanged: missingInit,
reset: missingInit,
}

Expand All @@ -207,10 +148,6 @@ enum ReviewTxActionType {
OperationsChanged = 'operationsChanged',
CustomReceiverTitleChanged = 'customReceiverTitleChanged',
DetailsChanged = 'detailsChanged',
OnSuccessChanged = 'onSuccessChanged',
OnErrorChanged = 'onErrorChanged',
OnNotSupportedCIP1694Changed = 'onNotSupportedCIP1694Changed',
OnCIP36SupportChangeChanged = 'onCIP36SupportChangeChanged',
Reset = 'reset',
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {NavigationProp, useNavigation} from '@react-navigation/native'
import * as React from 'react'

import {ReviewTxRoutes} from '../../../../kernel/navigation'

export const useNavigateTo = () => {
const navigation = useNavigation<NavigationProp<ReviewTxRoutes>>()

return React.useRef({
showSubmittedTxScreen: () => navigation.navigate('review-tx-submitted-tx'),
showFailedTxScreen: () => navigation.navigate('review-tx-failed-tx'),
} as const).current
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {ConfirmTxWithSpendingPasswordModal} from '../../../../components/Confirm
import {useModal} from '../../../../components/Modal/ModalContext'
import {YoroiSignedTx, YoroiUnsignedTx} from '../../../../yoroi-wallets/types/yoroi'
import {useSelectedWallet} from '../../../WalletManager/common/hooks/useSelectedWallet'
import {useReviewTx} from '../ReviewTxProvider'
import {useNavigateTo} from './useNavigateTo'
import {useStrings} from './useStrings'

// TODO: make it compatible with CBOR signing
Expand All @@ -29,15 +29,15 @@ export const useOnConfirm = ({
const {meta} = useSelectedWallet()
const {openModal, closeModal} = useModal()
const strings = useStrings()
const {reset} = useReviewTx()
const navigateTo = useNavigateTo()

const handleOnSuccess = (signedTx: YoroiSignedTx) => {
onSuccess?.(signedTx)
reset()
navigateTo.showSubmittedTxScreen()
}
const handleOnError = () => {
onError?.()
reset()
navigateTo.showFailedTxScreen()
}

const onConfirm = () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import {TransactionWitnessSet} from '@emurgo/cross-csl-core'
import {WalletMeta} from '@yoroi/types/lib/typescript/wallet/meta'
import * as React from 'react'

import {useModal} from '../../../../components/Modal/ModalContext'
import {cip30ExtensionMaker} from '../../../../yoroi-wallets/cardano/cip30/cip30'
import {YoroiWallet} from '../../../../yoroi-wallets/cardano/types'
import {wrappedCsl} from '../../../../yoroi-wallets/cardano/wrappedCsl'
import {useStrings} from '../../../Discover/common/useStrings'
import {ConfirmRawTxWithOs} from '../../../Swap/common/ConfirmRawTx/ConfirmRawTxWithOs'
import {ConfirmRawTxWithPassword} from '../../../Swap/common/ConfirmRawTx/ConfirmRawTxWithPassword'
import {useSelectedWallet} from '../../../WalletManager/common/hooks/useSelectedWallet'

export const useSignTx = () => {
const {openModal, closeModal} = useModal()
const {meta, wallet} = useSelectedWallet()
const strings = useStrings()
const modalHeight = 350

return React.useCallback(
(cbor: string) => {
const handleOnConfirm = async (rootKey: string) => {
console.log('signTx-123')
const witnesses = await signTx(Buffer.from(cbor).toString('hex'), rootKey, wallet, meta)

if (!witnesses) throw new Error('kdkdkdk')
await submitTx(cbor, witnesses, wallet)
closeModal()
return
}

if (meta.isHW) {
throw new Error('Not implemented yet')
}

if (meta.isEasyConfirmationEnabled) {
openModal(strings.confirmTx, <ConfirmRawTxWithOs onConfirm={handleOnConfirm} />, modalHeight)
return
}

openModal(strings.confirmTx, <ConfirmRawTxWithPassword onConfirm={handleOnConfirm} />, modalHeight)
},
[meta, openModal, strings.confirmTx, wallet, closeModal],
)
}

export const signTx = async (cbor: string, rootKey: string, wallet: YoroiWallet, meta: WalletMeta) => {
const cip30 = cip30ExtensionMaker(wallet, meta)
try {
return cip30.signTx(rootKey, cbor, false)
} catch {
console.log('smksksksksk')
}
}

export const submitTx = async (cbor: string, witnesses: TransactionWitnessSet, wallet: YoroiWallet) => {
const {csl, release} = wrappedCsl()

try {
const tx = await csl.Transaction.fromHex(cbor)
const txBody = await tx.body()
const signedTx = await csl.Transaction.new(txBody, witnesses, undefined)
const signedTxBytes = await signedTx.toBytes()

await wallet.submitTransaction(Buffer.from(signedTxBytes).toString('base64'))
} finally {
release()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ export const useStrings = () => {
deregisterStakingKey: intl.formatMessage(messages.deregisterStakingKey),
rewardsWithdrawalLabel: intl.formatMessage(messages.rewardsWithdrawalLabel),
rewardsWithdrawalText: intl.formatMessage(messages.rewardsWithdrawalText),
submittedTxTitle: intl.formatMessage(messages.submittedTxTitle),
submittedTxText: intl.formatMessage(messages.submittedTxText),
submittedTxButton: intl.formatMessage(messages.submittedTxButton),
failedTxTitle: intl.formatMessage(messages.failedTxTitle),
failedTxText: intl.formatMessage(messages.failedTxText),
failedTxButton: intl.formatMessage(messages.failedTxButton),
}
}

Expand Down Expand Up @@ -191,4 +197,28 @@ const messages = defineMessages({
id: 'txReview.operations.delegateStake',
defaultMessage: '!!!Stake entire wallet balance to',
},
submittedTxTitle: {
id: 'txReview.submittedTxTitle',
defaultMessage: '!!!Transaction submitted',
},
submittedTxText: {
id: 'txReview.submittedTxText',
defaultMessage: '!!!Check this transaction in the list of wallet transactions',
},
submittedTxButton: {
id: 'txReview.submittedTxButton',
defaultMessage: '!!!Go to transactions',
},
failedTxTitle: {
id: 'txReview.failedTxTitle',
defaultMessage: '!!!Transaction failed',
},
failedTxText: {
id: 'txReview.failedTxText',
defaultMessage: '!!!Your transaction has not been processed properly due to technical issues',
},
failedTxButton: {
id: 'txReview.failedTxButton',
defaultMessage: '!!!Go to transactions',
},
})
Loading

0 comments on commit c14e545

Please sign in to comment.