diff --git a/packages/@divvi/mobile/src/navigator/Navigator.tsx b/packages/@divvi/mobile/src/navigator/Navigator.tsx index 431217e740e..bf00190f9a7 100644 --- a/packages/@divvi/mobile/src/navigator/Navigator.tsx +++ b/packages/@divvi/mobile/src/navigator/Navigator.tsx @@ -243,6 +243,11 @@ const sendScreens = (Navigator: typeof Stack) => ( component={SendConfirmation} options={sendConfirmationScreenNavOptions as NativeStackNavigationOptions} /> + ( component={SelectCountry} options={SelectCountry.navigationOptions as NativeStackNavigationOptions} /> - ) diff --git a/packages/@divvi/mobile/src/navigator/types.tsx b/packages/@divvi/mobile/src/navigator/types.tsx index d83a707aac5..0c327ca2ed5 100644 --- a/packages/@divvi/mobile/src/navigator/types.tsx +++ b/packages/@divvi/mobile/src/navigator/types.tsx @@ -10,6 +10,7 @@ import { KeylessBackupFlow, KeylessBackupOrigin } from 'src/keylessBackup/types' import { Screens } from 'src/navigator/Screens' import { Nft } from 'src/nfts/types' import { EarnPosition } from 'src/positions/types' +import type { PreparedTransactionsResult } from 'src/public' import { Recipient } from 'src/recipients/recipient' import { QrCode, TransactionDataInput } from 'src/send/types' import { AssetTabType } from 'src/tokens/types' @@ -29,10 +30,18 @@ type NestedNavigatorParams = { : { screen: K; params: ParamList[K] } }[keyof ParamList] +interface SendConfirmationFromExternalParams { + origin: SendOrigin + transactionData: TransactionDataInput + isFromScan: boolean + prepareTransactionsResult?: never +} + interface SendConfirmationParams { origin: SendOrigin transactionData: TransactionDataInput isFromScan: boolean + prepareTransactionsResult: PreparedTransactionsResult } type SendEnterAmountParams = { @@ -249,7 +258,7 @@ export type StackParamList = { } | undefined [Screens.SendConfirmation]: SendConfirmationParams - [Screens.SendConfirmationFromExternal]: SendConfirmationParams + [Screens.SendConfirmationFromExternal]: SendConfirmationFromExternalParams [Screens.SendEnterAmount]: SendEnterAmountParams [Screens.JumpstartEnterAmount]: undefined [Screens.JumpstartSendConfirmation]: { diff --git a/packages/@divvi/mobile/src/send/SendConfirmation.test.tsx b/packages/@divvi/mobile/src/send/SendConfirmation.test.tsx index 43bca6ec783..834ea946467 100644 --- a/packages/@divvi/mobile/src/send/SendConfirmation.test.tsx +++ b/packages/@divvi/mobile/src/send/SendConfirmation.test.tsx @@ -43,6 +43,7 @@ const mockScreenProps = getMockStackScreenProps(Screens.SendConfirmation, { }, origin: SendOrigin.AppSendFlow, isFromScan: false, + prepareTransactionsResult: undefined as never, }) const mockFeeCurrencies = [ diff --git a/packages/@divvi/mobile/src/send/SendConfirmation.tsx b/packages/@divvi/mobile/src/send/SendConfirmation.tsx index 1849790d018..da687f3211e 100644 --- a/packages/@divvi/mobile/src/send/SendConfirmation.tsx +++ b/packages/@divvi/mobile/src/send/SendConfirmation.tsx @@ -63,7 +63,7 @@ export default function SendConfirmation(props: Props) { refreshPreparedTransactions, clearPreparedTransactions, prepareTransactionLoading, - } = usePrepareSendTransactions() + } = usePrepareSendTransactions(props.route.params.prepareTransactionsResult) const fromExternal = props.route.name === Screens.SendConfirmationFromExternal const tokenInfo = useTokenInfo(tokenId) @@ -88,9 +88,15 @@ export default function SendConfirmation(props: Props) { ) useEffect(() => { + // do not refresh if prepared transactions is available in the route params + if (!fromExternal) { + return + } + if (!walletAddress || !tokenInfo) { return // should never happen } + clearPreparedTransactions() const debouncedRefreshTransactions = setTimeout(() => { return refreshPreparedTransactions({ @@ -102,7 +108,7 @@ export default function SendConfirmation(props: Props) { }) }, DEBOUNCE_TIME_MS) return () => clearTimeout(debouncedRefreshTransactions) - }, [tokenInfo, tokenAmount, recipient, walletAddress, feeCurrencies]) + }, [tokenInfo, tokenAmount, recipient, walletAddress, feeCurrencies, fromExternal]) const disableSend = isSending || !prepareTransactionsResult || prepareTransactionsResult.type !== 'possible' diff --git a/packages/@divvi/mobile/src/send/SendEnterAmount.test.tsx b/packages/@divvi/mobile/src/send/SendEnterAmount.test.tsx index 906b45365ab..35506907e59 100644 --- a/packages/@divvi/mobile/src/send/SendEnterAmount.test.tsx +++ b/packages/@divvi/mobile/src/send/SendEnterAmount.test.tsx @@ -129,13 +129,14 @@ describe('SendEnterAmount', () => { }) it('should handle navigating to the next step', async () => { - jest.mocked(usePrepareSendTransactions).mockReturnValue({ + const mockedPrepareTransactions = { prepareTransactionsResult: mockPrepareTransactionsResultPossible, prepareTransactionLoading: false, refreshPreparedTransactions: jest.fn(), clearPreparedTransactions: jest.fn(), prepareTransactionError: undefined, - }) + } + jest.mocked(usePrepareSendTransactions).mockReturnValue(mockedPrepareTransactions) const { getByTestId, getByText } = render( @@ -163,9 +164,10 @@ describe('SendEnterAmount', () => { underlyingTokenSymbol: 'CELO', amountEnteredIn: 'token', }) - expect(navigate).toHaveBeenCalledWith(Screens.SendConfirmation, { + expect(navigate).toHaveBeenLastCalledWith(Screens.SendConfirmation, { origin: params.origin, isFromScan: params.isFromScan, + prepareTransactionsResult: mockedPrepareTransactions.prepareTransactionsResult, transactionData: { tokenId: mockCeloTokenId, recipient: params.recipient, diff --git a/packages/@divvi/mobile/src/send/SendEnterAmount.tsx b/packages/@divvi/mobile/src/send/SendEnterAmount.tsx index 5437e8435cb..ca5911e82b1 100644 --- a/packages/@divvi/mobile/src/send/SendEnterAmount.tsx +++ b/packages/@divvi/mobile/src/send/SendEnterAmount.tsx @@ -50,6 +50,7 @@ function SendEnterAmount({ route }: Props) { navigate(Screens.SendConfirmation, { origin, isFromScan, + prepareTransactionsResult, transactionData: { tokenId: token.tokenId, recipient, @@ -76,13 +77,14 @@ function SendEnterAmount({ route }: Props) { }) } + const prepareTransactions = usePrepareSendTransactions() const { prepareTransactionsResult, refreshPreparedTransactions, clearPreparedTransactions, prepareTransactionError, prepareTransactionLoading, - } = usePrepareSendTransactions() + } = prepareTransactions const walletAddress = useSelector(walletAddressSelector) diff --git a/packages/@divvi/mobile/src/send/usePrepareSendTransactions.ts b/packages/@divvi/mobile/src/send/usePrepareSendTransactions.ts index 5cbd3714ea1..287e8efe9b8 100644 --- a/packages/@divvi/mobile/src/send/usePrepareSendTransactions.ts +++ b/packages/@divvi/mobile/src/send/usePrepareSendTransactions.ts @@ -6,23 +6,25 @@ import Logger from 'src/utils/Logger' import { prepareERC20TransferTransaction, prepareSendNativeAssetTransaction, + type PreparedTransactionsResult, } from 'src/viem/prepareTransactions' const TAG = 'src/send/usePrepareSendTransactions' +type PrepareSendTransactionsCallbackProps = { + amount: BigNumber + token: TokenBalance + recipientAddress: string + walletAddress: string + feeCurrencies: TokenBalance[] +} export async function prepareSendTransactionsCallback({ amount, token, recipientAddress, walletAddress, feeCurrencies, -}: { - amount: BigNumber - token: TokenBalance - recipientAddress: string - walletAddress: string - feeCurrencies: TokenBalance[] -}) { +}: PrepareSendTransactionsCallbackProps) { if (amount.isLessThanOrEqualTo(0)) { return } @@ -54,18 +56,28 @@ export async function prepareSendTransactionsCallback({ /** * Hook to prepare transactions for sending crypto. */ -export function usePrepareSendTransactions() { - const prepareTransactions = useAsyncCallback(prepareSendTransactionsCallback, { - onError: (error) => { - Logger.error(TAG, `prepareTransactionsOutput: ${error}`) +export function usePrepareSendTransactions( + existingPrepareTransactionResult?: PreparedTransactionsResult +) { + const prepareTransactions = useAsyncCallback( + (props: PrepareSendTransactionsCallbackProps) => { + if (existingPrepareTransactionResult) return + return prepareSendTransactionsCallback(props) }, - }) + { + onError: (error) => { + Logger.error(TAG, `prepareTransactionsOutput: ${error}`) + }, + } + ) return { - prepareTransactionsResult: prepareTransactions.result, + prepareTransactionsResult: existingPrepareTransactionResult ?? prepareTransactions.result, refreshPreparedTransactions: prepareTransactions.execute, clearPreparedTransactions: prepareTransactions.reset, prepareTransactionError: prepareTransactions.error, - prepareTransactionLoading: prepareTransactions.loading, + prepareTransactionLoading: existingPrepareTransactionResult + ? false + : prepareTransactions.loading, } } diff --git a/packages/@divvi/mobile/src/send/utils.test.ts b/packages/@divvi/mobile/src/send/utils.test.ts index 3db7c0d0811..be3722ab09a 100644 --- a/packages/@divvi/mobile/src/send/utils.test.ts +++ b/packages/@divvi/mobile/src/send/utils.test.ts @@ -130,7 +130,7 @@ describe('send/utils', () => { ]) .run() expect(navigate).toHaveBeenCalledWith( - Screens.SendConfirmation, + Screens.SendConfirmationFromExternal, expect.objectContaining({ transactionData: { recipient: { address: mockData.address, recipientType: RecipientType.Address }, @@ -165,7 +165,7 @@ describe('send/utils', () => { ]) .run() expect(navigate).toHaveBeenCalledWith( - Screens.SendConfirmation, + Screens.SendConfirmationFromExternal, expect.objectContaining({ transactionData: { recipient: { address: mockData.address, recipientType: RecipientType.Address }, @@ -242,7 +242,7 @@ describe('send/utils', () => { ]) .run() expect(navigate).toHaveBeenCalledWith( - Screens.SendConfirmation, + Screens.SendConfirmationFromExternal, expect.objectContaining({ origin: SendOrigin.AppSendFlow, transactionData: mockTransactionData, @@ -270,7 +270,7 @@ describe('send/utils', () => { ]) .run() expect(navigate).toHaveBeenCalledWith( - Screens.SendConfirmation, + Screens.SendConfirmationFromExternal, expect.objectContaining({ origin: SendOrigin.AppSendFlow, transactionData: mockTransactionData, @@ -295,7 +295,7 @@ describe('send/utils', () => { ]) .run() expect(navigate).toHaveBeenCalledWith( - Screens.SendConfirmation, + Screens.SendConfirmationFromExternal, expect.objectContaining({ origin: SendOrigin.AppSendFlow, transactionData: { diff --git a/packages/@divvi/mobile/src/send/utils.ts b/packages/@divvi/mobile/src/send/utils.ts index d737978677a..bab02e4d023 100644 --- a/packages/@divvi/mobile/src/send/utils.ts +++ b/packages/@divvi/mobile/src/send/utils.ts @@ -93,7 +93,7 @@ export function* handleSendPaymentData({ tokenId: tokenInfo.tokenId, } - navigate(Screens.SendConfirmation, { + navigate(Screens.SendConfirmationFromExternal, { transactionData, isFromScan, origin: SendOrigin.AppSendFlow,