Skip to content

Commit

Permalink
feature(wallet-mobile): new tx review for swap
Browse files Browse the repository at this point in the history
  • Loading branch information
banklesss committed Oct 14, 2024
1 parent b649d33 commit 62319a9
Show file tree
Hide file tree
Showing 39 changed files with 865 additions and 1,788 deletions.
3 changes: 1 addition & 2 deletions apps/wallet-mobile/.storybook/storybook.requires.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions apps/wallet-mobile/src/features/ReviewTx/common/Accordion.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {useTheme} from '@yoroi/theme'
import * as React from 'react'
import {Animated, LayoutAnimation, StyleSheet, Text, TouchableOpacity, View} from 'react-native'
import {Animated, Easing, StyleSheet, Text, TouchableOpacity, View} from 'react-native'

import {Icon} from '../../../components/Icon'

Expand All @@ -10,12 +10,12 @@ export const Accordion = ({label, children}: {label: string; children: React.Rea
const animatedHeight = React.useRef(new Animated.Value(0)).current

const toggleSection = () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
setIsOpen(!isOpen)
Animated.timing(animatedHeight, {
toValue: isOpen ? 0 : 1,
duration: 300,
useNativeDriver: false,
easing: Easing.ease,
}).start()
}

Expand Down
31 changes: 23 additions & 8 deletions apps/wallet-mobile/src/features/ReviewTx/common/CopiableText.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
import {useTheme} from '@yoroi/theme'
import * as React from 'react'
import {StyleSheet, TouchableOpacity, View} from 'react-native'
import {StyleSheet, TouchableOpacity, View, ViewStyle} from 'react-native'

import {useCopy} from '../../../components/Clipboard/ClipboardProvider'
import {Icon} from '../../../components/Icon'

export const CopiableText = ({children, textToCopy}: {children: React.ReactNode; textToCopy: string}) => {
const {styles, colors} = useStyles()
const {copy} = useCopy()
export const CopiableText = ({
children,
style,
textToCopy,
}: {
children: React.ReactNode
style?: ViewStyle
textToCopy: string
}) => {
const {styles} = useStyles()

return (
<View style={styles.text}>
<View style={[styles.text, style]}>
{children}

<TouchableOpacity onPress={() => copy({text: textToCopy})} activeOpacity={0.5}>
<Icon.Copy size={24} color={colors.copy} />
</TouchableOpacity>
<CopyButton textToCopy={textToCopy} />
</View>
)
}

export const CopyButton = ({textToCopy}: {textToCopy: string}) => {
const {colors} = useStyles()
const {copy} = useCopy()
return (
<TouchableOpacity onPress={() => copy({text: textToCopy})} activeOpacity={0.5}>
<Icon.Copy size={24} color={colors.copy} />
</TouchableOpacity>
)
}

const useStyles = () => {
const {atoms, color} = useTheme()
const styles = StyleSheet.create({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ export const ReviewTxProvider = ({
cborChanged: (cbor: ReviewTxState['cbor']) => dispatch({type: ReviewTxActionType.CborChanged, cbor}),
operationsChanged: (operations: ReviewTxState['operations']) =>
dispatch({type: ReviewTxActionType.OperationsChanged, operations}),
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}),
Expand Down Expand Up @@ -55,6 +58,14 @@ const reviewTxReducer = (state: ReviewTxState, action: ReviewTxAction) => {
draft.operations = action.operations
break

case ReviewTxActionType.CustomReceiverTitleChanged:
draft.customReceiverTitle = action.customReceiverTitle
break

case ReviewTxActionType.DetailsChanged:
draft.details = action.details
break

case ReviewTxActionType.OnSuccessChanged:
draft.onSuccess = action.onSuccess
break
Expand Down Expand Up @@ -82,6 +93,14 @@ type ReviewTxAction =
type: ReviewTxActionType.OperationsChanged
operations: ReviewTxState['operations']
}
| {
type: ReviewTxActionType.CustomReceiverTitleChanged
customReceiverTitle: ReviewTxState['customReceiverTitle']
}
| {
type: ReviewTxActionType.DetailsChanged
details: ReviewTxState['details']
}
| {
type: ReviewTxActionType.OnSuccessChanged
onSuccess: ReviewTxState['onSuccess']
Expand All @@ -95,6 +114,8 @@ export type ReviewTxState = {
unsignedTx: YoroiUnsignedTx | null
cbor: string | null
operations: Array<React.ReactNode> | null
customReceiverTitle: React.ReactNode | null
details: {title: string; component: React.ReactNode} | null
onSuccess: ((signedTx: YoroiSignedTx) => void) | null
onError: (() => void) | null
}
Expand All @@ -103,6 +124,8 @@ type ReviewTxActions = {
unsignedTxChanged: (unsignedTx: ReviewTxState['unsignedTx']) => void
cborChanged: (cbor: ReviewTxState['cbor']) => void
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
}
Expand All @@ -111,6 +134,8 @@ const defaultState: ReviewTxState = Object.freeze({
unsignedTx: null,
cbor: null,
operations: null,
customReceiverTitle: null,
details: null,
onSuccess: null,
onError: null,
})
Expand All @@ -124,6 +149,8 @@ const initialReviewTxContext: ReviewTxContext = {
unsignedTxChanged: missingInit,
cborChanged: missingInit,
operationsChanged: missingInit,
customReceiverTitleChanged: missingInit,
detailsChanged: missingInit,
onSuccessChanged: missingInit,
onErrorChanged: missingInit,
}
Expand All @@ -132,6 +159,8 @@ enum ReviewTxActionType {
UnsignedTxChanged = 'unsignedTxChanged',
CborChanged = 'cborChanged',
OperationsChanged = 'operationsChanged',
CustomReceiverTitleChanged = 'customReceiverTitleChanged',
DetailsChanged = 'detailsChanged',
OnSuccessChanged = 'onSuccessChanged',
OnErrorChanged = 'onErrorChanged',
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import {TransactionBody} from '../types'

export const formatMetadata = (unsignedTx: YoroiUnsignedTx, txBody: TransactionBody) => {
const hash = txBody.auxiliary_data_hash ?? null
const metadata = unsignedTx.metadata?.['674'] ?? null
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const metadata = unsignedTx.metadata?.['674']?.['msg' as any] ?? null

return {
hash,
metadata,
metadata: {msg: [JSON.parse(metadata)]},
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// import {CredKind} from '@emurgo/csl-mobile-bridge'
import {CredKind} from '@emurgo/cross-csl-core'
import {isNonNullable} from '@yoroi/common'
import {infoExtractName} from '@yoroi/portfolio'
import {Portfolio} from '@yoroi/types'
Expand All @@ -6,6 +8,7 @@ import {useQuery} from 'react-query'

import {YoroiWallet} from '../../../../yoroi-wallets/cardano/types'
import {deriveRewardAddressFromAddress} from '../../../../yoroi-wallets/cardano/utils'
import {wrappedCsl} from '../../../../yoroi-wallets/cardano/wrappedCsl'
import {formatTokenWithText} from '../../../../yoroi-wallets/utils/format'
import {asQuantity} from '../../../../yoroi-wallets/utils/utils'
import {usePortfolioTokenInfos} from '../../../Portfolio/common/hooks/usePortfolioTokenInfos'
Expand All @@ -14,12 +17,13 @@ import {
FormattedFee,
FormattedInputs,
FormattedOutputs,
FormattedTx,
TransactionBody,
TransactionInputs,
TransactionOutputs,
} from '../types'

export const useFormattedTx = (data: TransactionBody) => {
export const useFormattedTx = (data: TransactionBody): FormattedTx => {
const {wallet} = useSelectedWallet()

const inputs = data?.inputs ?? []
Expand Down Expand Up @@ -93,10 +97,14 @@ const formatInputs = async (
inputs.map(async (input) => {
const receiveUTxO = getUtxoByTxIdAndIndex(wallet, input.transaction_id, input.index)
const address = receiveUTxO?.receiver
const rewardAddress =
address !== undefined ? await deriveRewardAddressFromAddress(address, wallet.networkManager.chainId) : null
const coin = receiveUTxO?.amount != null ? asQuantity(receiveUTxO.amount) : null

const addressKind = address != null ? await getAddressKind(address) : null
const rewardAddress =
address != null && addressKind === CredKind.Key
? await deriveAddress(address, wallet.networkManager.chainId)
: null

const primaryAssets =
coin != null
? [
Expand Down Expand Up @@ -130,6 +138,7 @@ const formatInputs = async (
return {
assets: [...primaryAssets, ...multiAssets].filter(isNonNullable),
address,
addressKind: addressKind ?? null,
rewardAddress,
ownAddress: address != null && isOwnedAddress(wallet, address),
txIndex: input.index,
Expand All @@ -147,9 +156,12 @@ const formatOutputs = async (
return Promise.all(
outputs.map(async (output) => {
const address = output.address
const rewardAddress = await deriveRewardAddressFromAddress(address, wallet.networkManager.chainId)
const coin = asQuantity(output.amount.coin)

const addressKind = await getAddressKind(address)
const rewardAddress =
addressKind === CredKind.Key ? await deriveAddress(address, wallet.networkManager.chainId) : null

const primaryAssets = [
{
tokenInfo: wallet.portfolioPrimaryTokenInfo,
Expand Down Expand Up @@ -183,6 +195,7 @@ const formatOutputs = async (
return {
assets,
address,
addressKind,
rewardAddress,
ownAddress: isOwnedAddress(wallet, address),
}
Expand All @@ -202,6 +215,26 @@ export const formatFee = (wallet: YoroiWallet, data: TransactionBody): Formatted
}
}

const deriveAddress = async (address: string, chainId: number) => {
try {
return await deriveRewardAddressFromAddress(address, chainId)
} catch {
return null
}
}

const getAddressKind = async (addressBech32: string): Promise<CredKind | null> => {
const {csl, release} = wrappedCsl()

try {
const address = await csl.Address.fromBech32(addressBech32)
const addressKind = await (await address.paymentCred())?.kind()
return addressKind ?? null
} finally {
release()
}
}

const getUtxoByTxIdAndIndex = (wallet: YoroiWallet, txId: string, index: number) => {
return wallet.utxos.find((u) => u.tx_hash === txId && u.tx_index === index)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ export const useStrings = () => {
title: intl.formatMessage(messages.title),
utxosTab: intl.formatMessage(messages.utxosTab),
overviewTab: intl.formatMessage(messages.overviewTab),
metadataTab: intl.formatMessage(messages.metadataTab),
metadataHash: intl.formatMessage(messages.metadataHash),
metadataJsonLabel: intl.formatMessage(messages.metadataJsonLabel),
walletLabel: intl.formatMessage(messages.walletLabel),
feeLabel: intl.formatMessage(messages.feeLabel),
myWalletLabel: intl.formatMessage(messages.myWalletLabel),
Expand Down Expand Up @@ -52,6 +55,18 @@ const messages = defineMessages({
id: 'txReview.tabLabel.overview',
defaultMessage: '!!!Overview',
},
metadataTab: {
id: 'txReview.tabLabel.metadataTab',
defaultMessage: '!!!Metadata',
},
metadataHash: {
id: 'txReview.metadata.metadataHash',
defaultMessage: '!!!Metadata hash',
},
metadataJsonLabel: {
id: 'txReview.metadata.metadataJsonLabel',
defaultMessage: '!!!Metadata',
},
walletLabel: {
id: 'txReview.overview.wallet',
defaultMessage: '!!!Wallet',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import {useQuery} from 'react-query'

import {wrappedCsl} from '../../../../yoroi-wallets/cardano/wrappedCsl'
import {YoroiUnsignedTx} from '../../../../yoroi-wallets/types/yoroi'
import {TransactionBody} from '../types'

export const useTxBody = ({cbor, unsignedTx}: {cbor?: string; unsignedTx?: YoroiUnsignedTx}) => {
export const useTxBody = ({cbor, unsignedTx}: {cbor?: string; unsignedTx?: YoroiUnsignedTx}): TransactionBody => {
const query = useQuery(
['useTxBody', cbor, unsignedTx],
async () => {
Expand Down
8 changes: 8 additions & 0 deletions apps/wallet-mobile/src/features/ReviewTx/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
TransactionInputsJSON,
TransactionOutputsJSON,
} from '@emurgo/cardano-serialization-lib-nodejs'
import {CredKind} from '@emurgo/cross-csl-core'
import {Balance, Portfolio} from '@yoroi/types'

export type TransactionBody = TransactionBodyJSON
Expand All @@ -18,6 +19,7 @@ export type FormattedInput = {
isPrimary: boolean
}>
address: string | undefined
addressKind: CredKind | null
rewardAddress: string | null
ownAddress: boolean
txIndex: number
Expand All @@ -35,6 +37,7 @@ export type FormattedOutput = {
isPrimary: boolean
}>
address: string
addressKind: CredKind | null
rewardAddress: string | null
ownAddress: boolean
}
Expand All @@ -54,3 +57,8 @@ export type FormattedTx = {
outputs: FormattedOutputs
fee: FormattedFee
}

export type Metadata = {
json: string | null
hash: string | null
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {useStrings} from '../../../common/hooks/useStrings'

type Props = {
hash: string | null
metadata: string | null
metadata: {msg: Array<string>} | null
}

export const MetadataTab = ({metadata, hash}: Props) => {
Expand Down
Loading

0 comments on commit 62319a9

Please sign in to comment.