import BigNumber from 'bignumber.js' import React, { useEffect, useMemo, useState } from 'react' import { useAsyncCallback } from 'react-async-hook' import { useTranslation } from 'react-i18next' import { useDispatch } from 'react-redux' import AppAnalytics from 'src/analytics/AppAnalytics' import { JumpstartEvents } from 'src/analytics/Events' import InLineNotification, { NotificationVariant } from 'src/components/InLineNotification' import { createJumpstartLink } from 'src/firebase/dynamicLinks' import { currentLanguageSelector } from 'src/i18n/selectors' import JumpstartIntro from 'src/jumpstart/JumpstartIntro' import { jumpstartIntroHasBeenSeenSelector, jumpstartSendStatusSelector, } from 'src/jumpstart/selectors' import { depositTransactionFlowStarted } from 'src/jumpstart/slice' import { usePrepareJumpstartTransactions } from 'src/jumpstart/usePrepareJumpstartTransactions' import { convertDollarsToLocalAmount } from 'src/localCurrency/convert' import { getLocalCurrencyCode, usdToLocalCurrencyRateSelector } from 'src/localCurrency/selectors' import { navigate } from 'src/navigator/NavigationService' import { Screens } from 'src/navigator/Screens' import { useSelector } from 'src/redux/hooks' import EnterAmount, { ProceedArgs, SendProceed } from 'src/send/EnterAmount' import { AmountEnteredIn } from 'src/send/types' import { getDynamicConfigParams } from 'src/statsig' import { DynamicConfigs } from 'src/statsig/constants' import { StatsigDynamicConfigs } from 'src/statsig/types' import { tokenAmountInSmallestUnit } from 'src/tokens/saga' import { jumpstartSendTokensSelector } from 'src/tokens/selectors' import { TokenBalance } from 'src/tokens/slice' import Logger from 'src/utils/Logger' import { getSerializablePreparedTransactions } from 'src/viem/preparedTransactionSerialization' import { walletAddressSelector } from 'src/web3/selectors' import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts' const TAG = 'JumpstartEnterAmount' function JumpstartEnterAmount() { const { t } = useTranslation() const dispatch = useDispatch() const [sendAmountExceedsThreshold, setSendAmountExceedsThreshold] = useState(false) const jumpstartSendThreshold = getDynamicConfigParams( DynamicConfigs[StatsigDynamicConfigs.WALLET_JUMPSTART_CONFIG] ).maxAllowedSendAmountUsd const usdToLocalRate = useSelector(usdToLocalCurrencyRateSelector) const maxSendAmountLocalCurrency = convertDollarsToLocalAmount( jumpstartSendThreshold, usdToLocalRate ) const localCurrencyCode = useSelector(getLocalCurrencyCode) const locale = useSelector(currentLanguageSelector) const jumpstartSendStatus = useSelector(jumpstartSendStatusSelector) const walletAddress = useSelector(walletAddressSelector) const introSeen = useSelector(jumpstartIntroHasBeenSeenSelector) const tokens = useSelector(jumpstartSendTokensSelector) const jumpstartLink = useMemo(() => { const privateKey = generatePrivateKey() const publicKey = privateKeyToAccount(privateKey).address return { publicKey, privateKey, } }, []) useEffect(() => { if (jumpstartLink.privateKey) { dispatch(depositTransactionFlowStarted()) } }, [jumpstartLink.privateKey]) const handleProceed = useAsyncCallback( async ({ tokenAmount, token, amountEnteredIn }: ProceedArgs) => { const link = await createJumpstartLink(jumpstartLink.privateKey, token.networkId) return { link, parsedAmount: tokenAmount, token, amountEnteredIn, } }, { onSuccess: ({ link, parsedAmount, token, amountEnteredIn, }: { link: string parsedAmount: BigNumber token: TokenBalance amountEnteredIn: AmountEnteredIn }) => { if (prepareJumpstartTransactions.result?.type !== 'possible') { // should never happen Logger.error( TAG, 'No prepared transactions found when trying to proceed with jumpstart send' ) return } navigate(Screens.JumpstartSendConfirmation, { link, sendAmount: parsedAmount.toString(), tokenId: token.tokenId, serializablePreparedTransactions: getSerializablePreparedTransactions( prepareJumpstartTransactions.result.transactions ), beneficiaryAddress: jumpstartLink.publicKey, }) AppAnalytics.track(JumpstartEvents.jumpstart_send_amount_continue, { localCurrency: localCurrencyCode, localCurrencyExchangeRate: usdToLocalRate, tokenSymbol: token.symbol, tokenAmount: parsedAmount.toString(), amountInUsd: parsedAmount.multipliedBy(token.priceUsd ?? 0).toFixed(2), tokenId: token.tokenId, networkId: token.networkId, amountEnteredIn, }) }, onError: (error) => { Logger.error(TAG, 'Error while generating jumpstart dynamic link', error) }, } ) const prepareJumpstartTransactions = usePrepareJumpstartTransactions() const handleRefreshPreparedTransactions = ( userInputAmount: BigNumber, token: TokenBalance, feeCurrencies: TokenBalance[] ) => { if (!walletAddress) { Logger.error(TAG, 'Wallet address not set. Cannot refresh prepared transactions.') return } const sendTokenAmountInSmallestUnit = tokenAmountInSmallestUnit(userInputAmount, token.decimals) const sendAmountUsd = userInputAmount.multipliedBy(token.priceUsd ?? 0) const sendAmountExceedsMax = sendAmountUsd.isGreaterThan(jumpstartSendThreshold) setSendAmountExceedsThreshold(sendAmountExceedsMax) if (sendAmountExceedsMax) { AppAnalytics.track(JumpstartEvents.jumpstart_send_amount_exceeds_threshold, { amountInUsd: sendAmountUsd.toFixed(2), tokenId: token.tokenId, thresholdUsd: jumpstartSendThreshold, networkId: token.networkId, }) } return prepareJumpstartTransactions.execute({ sendTokenAmountInSmallestUnit: new BigNumber(sendTokenAmountInSmallestUnit), token, walletAddress, feeCurrencies, publicKey: jumpstartLink.publicKey, }) } // add checks for the jumpstartSendStatus here to safeguard against the user // navigating back to this screen to initiate another transaction with the // same link while the previous one is in progress or successful const disableProceed = sendAmountExceedsThreshold || jumpstartSendStatus === 'success' || jumpstartSendStatus === 'loading' if (tokens.length === 0 || !introSeen) { return } return ( {sendAmountExceedsThreshold && ( )} ) } export default JumpstartEnterAmount