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

feature(wallet-mobile): new tx review success and error screens #3712

Merged
merged 1 commit into from
Oct 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
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
Loading