Skip to content

Commit

Permalink
feat(wallet): Backed Up Transactions Warning
Browse files Browse the repository at this point in the history
  • Loading branch information
Douglashdaniel committed Mar 2, 2022
1 parent d03c7da commit 208fc96
Show file tree
Hide file tree
Showing 11 changed files with 343 additions and 22 deletions.
12 changes: 12 additions & 0 deletions components/brave_wallet/browser/brave_wallet_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,18 @@ constexpr webui::LocalizedString kLocalizedStrings[] = {
{"braveWalletConfirmTransactionFrist",
IDS_BRAVE_WALLET_CONFIRM_TRANSACTION_FRIST},
{"braveWalletConfirmTransactions", IDS_BRAVE_WALLET_CONFIRM_TRANSACTIONS},
{"braveWalletBackedUpTransactionsTitle",
IDS_BRAVE_WALLET_BACKED_UP_TRANSACTIONS_TITLE},
{"braveWalletBackedUpTransactionsDescription",
IDS_BRAVE_WALLET_BACKED_UP_TRANSACTIONS_DESCRIPTION},
{"braveWalletBackedUpTransactionsCheckbox",
IDS_BRAVE_WALLET_BACKED_UP_TRANSACTIONS_CHECKBOX},
{"braveWalletBackedUpTransactionsClearButton",
IDS_BRAVE_WALLET_BACKED_UP_TRANSACTIONS_CLEAR_BUTTON},
{"braveWalletBackedUpTransactionsClearingButton",
IDS_BRAVE_WALLET_BACKED_UP_TRANSACTIONS_CLEARING_BUTTON},
{"braveWalletBackedUpTransactionsOKButton",
IDS_BRAVE_WALLET_BACKED_UP_TRANSACTIONS_OK_BUTTON},
{"braveWalletPanelTitle", IDS_BRAVE_WALLET_PANEL_TITLE},
{"braveWalletPanelConnected", IDS_BRAVE_WALLET_PANEL_CONNECTED},
{"braveWalletSitePermissionsAccounts",
Expand Down
1 change: 1 addition & 0 deletions components/brave_wallet_ui/common/async/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,7 @@ handler.on(WalletActions.addSitePermission.getType(), async (store: Store, paylo

handler.on(WalletActions.transactionStatusChanged.getType(), async (store: Store, payload: TransactionStatusChanged) => {
const status = payload.txInfo.txStatus
await store.dispatch(refreshTransactionHistory(payload.txInfo.fromAddress))
if (status === BraveWallet.TransactionStatus.Confirmed || status === BraveWallet.TransactionStatus.Error) {
await refreshBalancesPricesAndHistory(store)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { create } from 'ethereum-blockies'
import {
BraveWallet,
WalletAccountType,
DefaultCurrencies
DefaultCurrencies,
AccountTransactions
} from '../../../constants/types'
import {
UpdateUnapprovedTransactionGasFieldsType,
Expand All @@ -16,15 +17,23 @@ import {
import { reduceAddress } from '../../../utils/reduce-address'
import { reduceNetworkDisplayName } from '../../../utils/network-utils'
import { reduceAccountDisplayName } from '../../../utils/reduce-account-name'
import { sortTransactionByDate } from '../../../utils/tx-utils'
import { mojoTimeDeltaToJSDate, calculatedTimeDiffInMilliseconds } from '../../../utils/datetime-utils'
import Amount from '../../../utils/amount'

// Hooks
import { usePricing, useTransactionParser, useTokenInfo } from '../../../common/hooks'

import { getLocale } from '../../../../common/locale'
import { withPlaceholderIcon } from '../../shared'

import { NavButton, PanelTab, TransactionDetailBox } from '../'
// Components
import { withPlaceholderIcon } from '../../shared'
import {
NavButton,
PanelTab,
TransactionDetailBox,
TransactionsBackedUpWarning
} from '../'
import EditGas, { MaxPriorityPanels } from '../edit-gas'
import EditAllowance from '../edit-allowance'

Expand Down Expand Up @@ -94,6 +103,8 @@ export interface Props {
transactionsQueueLength: number
transactionQueueNumber: number
defaultCurrencies: DefaultCurrencies
transactions: AccountTransactions
selectedAccount: WalletAccountType
onQueueNextTransaction: () => void
onConfirm: () => void
onReject: () => void
Expand All @@ -118,6 +129,8 @@ function ConfirmTransactionPanel (props: Props) {
transactionQueueNumber,
fullTokenList,
defaultCurrencies,
transactions,
selectedAccount,
onQueueNextTransaction,
onConfirm,
onReject,
Expand All @@ -144,11 +157,62 @@ function ConfirmTransactionPanel (props: Props) {
const [currentTokenAllowance, setCurrentTokenAllowance] = React.useState<string>('')
const [isEditingAllowance, setIsEditingAllowance] = React.useState<boolean>(false)
const [showAdvancedTransactionSettings, setShowAdvancedTransactionSettings] = React.useState<boolean>(false)
const [showTransactionsBackedUpWarning, setShowTransactionsBackedUpWarning] = React.useState<boolean>(false)

const { findAssetPrice } = usePricing(transactionSpotPrices)
const parseTransaction = useTransactionParser(selectedNetwork, accounts, transactionSpotPrices, visibleTokens, fullTokenList)
const transactionDetails = parseTransaction(transactionInfo)

const unconfirmedSubmittedTransactions = React.useMemo(() => {
// Gets a the list of transcations for the selected account
const transactionsList =
selectedAccount?.address && transactions[selectedAccount.address]
? sortTransactionByDate(transactions[selectedAccount.address], 'descending')
: []

// Gets a list of all transactions with the status of Submitted
const submittedTransactions = transactionsList.filter((transaction) => transaction.txStatus === BraveWallet.TransactionStatus.Submitted)

// Returns a list of all unconfirmed submited transactions that are over 24 hours old
return submittedTransactions.filter((transaction) =>
Math.floor(
calculatedTimeDiffInMilliseconds(
mojoTimeDeltaToJSDate(transaction.submittedTime)
) / (1000 * 60 * 60)) > 24)
}, [selectedAccount, transactions])

const duplicateNonceTranscations = React.useMemo((): boolean => {
const transactionsList =
selectedAccount?.address && transactions[selectedAccount.address]
? sortTransactionByDate(transactions[selectedAccount.address], 'descending')
: []

if (unconfirmedSubmittedTransactions.length !== 0 && transactionsList.length !== 0) {
const stuckTransactionNonce =
unconfirmedSubmittedTransactions[unconfirmedSubmittedTransactions.length - 1]
.txDataUnion.ethTxData1559?.baseData.nonce ?? ''

const foundTransactions = transactionsList.filter((transaction) =>
transaction.txDataUnion.ethTxData1559?.baseData.nonce === stuckTransactionNonce)

return foundTransactions.length > 1 &&
foundTransactions.filter((transaction) =>
transaction.txStatus === BraveWallet.TransactionStatus.Unapproved
).length === 0
}
return false
}, [unconfirmedSubmittedTransactions, selectedAccount, transactions])

React.useEffect(() => {
if (
(unconfirmedSubmittedTransactions.length !== 0 &&
transactionInfo.txDataUnion.ethTxData1559?.baseData.nonce === '') ||
duplicateNonceTranscations
) {
setShowTransactionsBackedUpWarning(true)
}
}, [unconfirmedSubmittedTransactions])

const {
onFindTokenInfoByContractAddress,
foundTokenInfoByContractAddress
Expand Down Expand Up @@ -261,6 +325,28 @@ function ConfirmTransactionPanel (props: Props) {
)
}, [transactionDetails])

const onContinueTransaction = (clearTransactions: boolean) => {
if (clearTransactions) {
// Finds the nonce of the oldest unconfirmed transaction and
// sets the nonce for the new unapproved transaction.
const newNonce =
unconfirmedSubmittedTransactions[unconfirmedSubmittedTransactions.length - 1]
.txDataUnion.ethTxData1559?.baseData.nonce ?? ''

updateUnapprovedTransactionNonce({ txMetaId: transactionInfo.id, nonce: newNonce })
}
setShowTransactionsBackedUpWarning(false)
}

if (showTransactionsBackedUpWarning) {
return (
<TransactionsBackedUpWarning
onContinue={onContinueTransaction}
duplicateNonceTranscations={duplicateNonceTranscations}
/>
)
}

if (isEditing) {
return (
<EditGas
Expand Down Expand Up @@ -337,7 +423,7 @@ function ConfirmTransactionPanel (props: Props) {
{transactionDetails.isApprovalUnlimited &&
<WarningBox>
<WarningTitleRow>
<WarningIcon/>
<WarningIcon />
<WarningTitle>
{getLocale('braveWalletAllowSpendUnlimitedWarningTitle')}
</WarningTitle>
Expand Down Expand Up @@ -394,7 +480,7 @@ function ConfirmTransactionPanel (props: Props) {
/>

<AdvancedTransactionSettingsButton
onSubmit={ onToggleAdvancedTransactionSettings }
onSubmit={onToggleAdvancedTransactionSettings}
/>
</TabRow>
<MessageBox isDetails={selectedTab === 'details'} isApprove={transactionInfo.txType === BraveWallet.TransactionType.ERC20Approve}>
Expand Down Expand Up @@ -439,7 +525,7 @@ function ConfirmTransactionPanel (props: Props) {
transactionDetails.isApprovalUnlimited
? getLocale('braveWalletTransactionApproveUnlimited')
: new Amount(transactionDetails.valueExact)
.formatAsAsset(undefined, transactionDetails.symbol)
.formatAsAsset(undefined, transactionDetails.symbol)
}
</TransactionTypeText>
<TransactionText />
Expand Down
4 changes: 3 additions & 1 deletion components/brave_wallet_ui/components/extension/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import TransactionsListItem from './transaction-list-item'
import TransactionDetailPanel from './transaction-detail-panel'
import AssetsPanel from './assets-panel'
import EncryptionKeyPanel from './encryption-key-panel'
import TransactionsBackedUpWarning from './transactions-backed-up-warning'
import { NavButton } from './buttons'

export {
Expand Down Expand Up @@ -53,5 +54,6 @@ export {
TransactionsListItem,
TransactionDetailPanel,
AssetsPanel,
EncryptionKeyPanel
EncryptionKeyPanel,
TransactionsBackedUpWarning
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import * as React from 'react'

// Components
import { NavButton } from '../'
import { Checkbox } from 'brave-ui'
import { getLocale } from '../../../../common/locale'

// Styled Components
import {
StyledWrapper,
Title,
Description,
LearnMoreButton,
CheckboxRow,
ClearButton,
ClearButtonText,
LoadIcon,
CheckboxText
} from './style'

export interface Props {
onContinue: (clearTransactions: boolean) => void
duplicateNonceTranscations: boolean
}

function TransactionsBackedUpWarning (props: Props) {
const { onContinue, duplicateNonceTranscations } = props
const [clearTransactions, setClearTransactions] = React.useState<boolean>(false)
const [isClearing, setIsClearing] = React.useState<boolean>(false)

const onCheckClearTransactions = (key: string, selected: boolean) => {
if (key === 'clearTransactions') {
setClearTransactions(selected)
}
}

const onClickContinue = () => {
if (clearTransactions) {
setIsClearing(true)
}
onContinue(clearTransactions)
}

const onClickLearnMore = () => {
chrome.tabs.create({ url: 'https://support.brave.com/hc/en-us/articles/4537540021389' }, () => {
if (chrome.runtime.lastError) {
console.error('tabs.create failed: ' + chrome.runtime.lastError.message)
}
})
}

return (
<StyledWrapper>
<Title>{getLocale('braveWalletBackedUpTransactionsTitle')}</Title>
<Description>{getLocale('braveWalletBackedUpTransactionsDescription')}</Description>
<LearnMoreButton
needsMargin={duplicateNonceTranscations}
onClick={onClickLearnMore}
>
{getLocale('braveWalletWelcomePanelButton')}
</LearnMoreButton>
{!duplicateNonceTranscations &&
<CheckboxRow>
<Checkbox
children={<CheckboxText data-key='clearTransactions'>{getLocale('braveWalletBackedUpTransactionsCheckbox')}</CheckboxText>}
size='small'
value={{ clearTransactions: clearTransactions }}
onChange={onCheckClearTransactions}
/>
</CheckboxRow>
}
{isClearing ? (
<ClearButton>
<ClearButtonText>
{getLocale('braveWalletBackedUpTransactionsClearingButton')}
</ClearButtonText>
<LoadIcon />
</ClearButton>
) : (
<NavButton
disabled={isClearing}
buttonType={
clearTransactions
? 'danger'
: 'primary'
}
text={
duplicateNonceTranscations
? getLocale('braveWalletBackedUpTransactionsOKButton')
: clearTransactions
? getLocale('braveWalletBackedUpTransactionsClearButton')
: getLocale('braveWalletButtonContinue')
}
onSubmit={onClickContinue}
/>
)
}
</StyledWrapper >
)
}

export default TransactionsBackedUpWarning
Loading

0 comments on commit 208fc96

Please sign in to comment.