From a40e050ea080782b3724462172b171e078af4a5c Mon Sep 17 00:00:00 2001 From: Anirudha Bose Date: Wed, 15 Dec 2021 18:40:50 +0530 Subject: [PATCH] feat(wallet): add EIP-55 checksum address checks in Send widget --- .../browser/brave_wallet_constants.h | 4 ++ .../brave_wallet_ui/common/async/lib.ts | 5 +++ .../brave_wallet_ui/common/hooks/send.ts | 39 +++++++++++++++++-- .../components/buy-send-swap/send/index.tsx | 3 ++ .../swap-input-component/index.tsx | 7 ++++ .../buy-send-swap/tabs/send-tab.tsx | 3 ++ components/brave_wallet_ui/constants/types.ts | 4 ++ components/brave_wallet_ui/page/container.tsx | 4 ++ .../brave_wallet_ui/panel/container.tsx | 4 ++ components/brave_wallet_ui/stories/locale.ts | 2 + .../stories/screens/buy-send-swap.tsx | 3 ++ .../stories/wallet-concept.tsx | 1 + .../stories/wallet-extension-panels.tsx | 1 + components/resources/wallet_strings.grdp | 2 + 14 files changed, 78 insertions(+), 4 deletions(-) diff --git a/components/brave_wallet/browser/brave_wallet_constants.h b/components/brave_wallet/browser/brave_wallet_constants.h index 4c230c039db8..5da949bcfa36 100644 --- a/components/brave_wallet/browser/brave_wallet_constants.h +++ b/components/brave_wallet/browser/brave_wallet_constants.h @@ -486,6 +486,10 @@ constexpr webui::LocalizedString kLocalizedStrings[] = { {"braveWalletSameAddressError", IDS_BRAVE_WALLET_SAME_ADDRESS_ERROR}, {"braveWalletContractAddressError", IDS_BRAVE_WALLET_CONTRACT_ADDRESS_ERROR}, + {"braveWalletAddressMissingChecksumInfoWarning", + IDS_BRAVE_WALLET_ADDRESS_MISSING_CHECKSUM_INFO_WARNING}, + {"braveWalletNotValidChecksumAddressError", + IDS_BRAVE_WALLET_NOT_VALID_CHECKSUM_ADDRESS_ERROR}, {"braveWalletQueueOf", IDS_BRAVE_WALLET_QUEUE_OF}, {"braveWalletQueueNext", IDS_BRAVE_WALLET_QUEUE_NEXT}, {"braveWalletQueueFirst", IDS_BRAVE_WALLET_QUEUE_FIRST}, diff --git a/components/brave_wallet_ui/common/async/lib.ts b/components/brave_wallet_ui/common/async/lib.ts index 8a1c5928dd17..93c54cca1166 100644 --- a/components/brave_wallet_ui/common/async/lib.ts +++ b/components/brave_wallet_ui/common/async/lib.ts @@ -75,6 +75,11 @@ export const getBalance = (address: string): Promise => { }) } +export async function getChecksumEthAddress (value: string) { + const { keyringController } = getAPIProxy() + return (await keyringController.getChecksumEthAddress(value)) +} + export async function isStrongPassword (value: string) { const apiProxy = getAPIProxy() return (await apiProxy.keyringController.isStrongPassword(value)).result diff --git a/components/brave_wallet_ui/common/hooks/send.ts b/components/brave_wallet_ui/common/hooks/send.ts index 397a84cd8352..f859ad0db884 100644 --- a/components/brave_wallet_ui/common/hooks/send.ts +++ b/components/brave_wallet_ui/common/hooks/send.ts @@ -12,7 +12,8 @@ import { ER20TransferParams, SendTransactionParams, ERC721TransferFromParams, - ERCToken + ERCToken, + GetChecksumEthAddressReturnInfo } from '../../constants/types' import { isValidAddress } from '../../utils/address-utils' import { getLocale } from '../../../common/locale' @@ -21,6 +22,7 @@ import { toWeiHex } from '../../utils/format-balances' export default function useSend ( findENSAddress: (address: string) => Promise, findUnstoppableDomainAddress: (address: string) => Promise, + getChecksumEthAddress: (address: string) => Promise, sendAssetOptions: AccountAssetOptionType[], selectedAccount: WalletAccountType, sendERC20Transfer: SimpleActionCreator, @@ -32,6 +34,7 @@ export default function useSend ( const [toAddressOrUrl, setToAddressOrUrl] = React.useState('') const [toAddress, setToAddress] = React.useState('') const [addressError, setAddressError] = React.useState('') + const [addressWarning, setAddressWarning] = React.useState('') const [sendAmount, setSendAmount] = React.useState('') React.useEffect(() => { @@ -77,6 +80,7 @@ export default function useSend ( findENSAddress(toAddressOrUrl).then((value: GetEthAddrReturnInfo) => { if (value.success) { setAddressError('') + setAddressWarning('') setToAddress(value.address) return } @@ -91,6 +95,7 @@ export default function useSend ( findUnstoppableDomainAddress(toAddressOrUrl).then((value: GetEthAddrReturnInfo) => { if (value.success) { setAddressError('') + setAddressWarning('') setToAddress(value.address) return } @@ -102,6 +107,7 @@ export default function useSend ( // If value is the same as the selectedAccounts Wallet Address if (valueToLowerCase === selectedAccount?.address?.toLowerCase()) { setToAddress(toAddressOrUrl) + setAddressWarning('') setAddressError(getLocale('braveWalletSameAddressError')) return } @@ -109,6 +115,7 @@ export default function useSend ( // If value is a Tokens Contract Address if (fullTokenList.some(token => token.contractAddress.toLowerCase() === valueToLowerCase)) { setToAddress(toAddressOrUrl) + setAddressWarning('') setAddressError(getLocale('braveWalletContractAddressError')) return } @@ -116,20 +123,43 @@ export default function useSend ( // If value starts with 0x, will check if it's a valid address if (valueToLowerCase.startsWith('0x')) { setToAddress(toAddressOrUrl) - isValidAddress(toAddressOrUrl, 20) - ? setAddressError('') - : setAddressError(getLocale('braveWalletNotValidEthAddress')) + if (!isValidAddress(toAddressOrUrl, 20)) { + setAddressWarning('') + setAddressError(getLocale('braveWalletNotValidEthAddress')) + return + } + + getChecksumEthAddress(toAddressOrUrl).then((value: GetChecksumEthAddressReturnInfo) => { + const { checksumAddress } = value + if (checksumAddress === toAddressOrUrl) { + setAddressWarning('') + setAddressError('') + return + } + + if ([toAddressOrUrl.toLowerCase(), toAddressOrUrl.toUpperCase()].includes(toAddressOrUrl)) { + setAddressError('') + setAddressWarning(getLocale('braveWalletAddressMissingChecksumInfoWarning')) + return + } + + setAddressWarning('') + setAddressError(getLocale('braveWalletNotValidChecksumAddressError')) + }).catch(e => console.log(e)) + return } // Resets State if (toAddressOrUrl === '') { setAddressError('') + setAddressWarning('') setToAddress('') return } // Fallback error state + setAddressWarning('') setAddressError(getLocale('braveWalletNotValidAddress')) }, [toAddressOrUrl, selectedAccount]) @@ -168,6 +198,7 @@ export default function useSend ( toAddress, sendAmount, addressError, + addressWarning, selectedSendAsset } } diff --git a/components/brave_wallet_ui/components/buy-send-swap/send/index.tsx b/components/brave_wallet_ui/components/buy-send-swap/send/index.tsx index 7cf8fe6fe33d..16c3f764b53b 100644 --- a/components/brave_wallet_ui/components/buy-send-swap/send/index.tsx +++ b/components/brave_wallet_ui/components/buy-send-swap/send/index.tsx @@ -20,6 +20,7 @@ export interface Props { toAddressOrUrl: string toAddress: string addressError: string + addressWarning: string onSubmit: () => void onInputChange: (value: string, name: string) => void onChangeSendView: (view: BuySendSwapViewTypes, option?: ToOrFromType) => void @@ -34,6 +35,7 @@ function Send (props: Props) { toAddressOrUrl, toAddress, addressError, + addressWarning, onInputChange, onSelectPresetAmount, onSubmit, @@ -74,6 +76,7 @@ function Send (props: Props) { onInputChange={onInputChange} toAddressOrUrl={toAddressOrUrl} addressError={addressError} + addressWarning={addressWarning} toAddress={toAddress} inputName='address' onPaste={onPasteFromClipboard} diff --git a/components/brave_wallet_ui/components/buy-send-swap/swap-input-component/index.tsx b/components/brave_wallet_ui/components/buy-send-swap/swap-input-component/index.tsx index c02c973e7b83..bbd59a32e554 100644 --- a/components/brave_wallet_ui/components/buy-send-swap/swap-input-component/index.tsx +++ b/components/brave_wallet_ui/components/buy-send-swap/swap-input-component/index.tsx @@ -58,6 +58,7 @@ export interface Props { selectedAsset?: AccountAssetOptionType selectedAssetInputAmount?: string addressError?: string + addressWarning?: string toAddressOrUrl?: string toAddress?: string inputName?: string @@ -87,6 +88,7 @@ function SwapInputComponent (props: Props) { selectedAssetInputAmount, inputName, addressError, + addressWarning, toAddressOrUrl, toAddress, orderType, @@ -348,6 +350,11 @@ function SwapInputComponent (props: Props) { {componentType === 'toAddress' && addressError && {addressError} } + + {componentType === 'toAddress' && addressWarning && + {addressWarning} + } + {componentType === 'toAddress' && toAddress !== toAddressOrUrl && !addressError && {reduceAddress(toAddress ?? '')} } diff --git a/components/brave_wallet_ui/components/buy-send-swap/tabs/send-tab.tsx b/components/brave_wallet_ui/components/buy-send-swap/tabs/send-tab.tsx index 7645970a1475..49f85563f9c9 100644 --- a/components/brave_wallet_ui/components/buy-send-swap/tabs/send-tab.tsx +++ b/components/brave_wallet_ui/components/buy-send-swap/tabs/send-tab.tsx @@ -24,6 +24,7 @@ export interface Props { toAddress: string showHeader?: boolean addressError: string + addressWarning: string onSubmit: () => void onSelectNetwork: (network: EthereumChain) => void onSelectAccount: (account: UserAccountType) => void @@ -50,6 +51,7 @@ function SendTab (props: Props) { showHeader, assetOptions, addressError, + addressWarning, onSubmit, onSelectNetwork, onSelectAccount, @@ -115,6 +117,7 @@ function SendTab (props: Props) { toAddressOrUrl={toAddressOrUrl} toAddress={toAddress} addressError={addressError} + addressWarning={addressWarning} onChangeSendView={onChangeSendView} onInputChange={onInputChange} onSelectPresetAmount={onSelectPresetAmount} diff --git a/components/brave_wallet_ui/constants/types.ts b/components/brave_wallet_ui/constants/types.ts index 69f114c66792..363ee243e84e 100644 --- a/components/brave_wallet_ui/constants/types.ts +++ b/components/brave_wallet_ui/constants/types.ts @@ -388,6 +388,10 @@ export interface GetIsStrongPassswordReturnInfo { result: boolean } +export interface GetChecksumEthAddressReturnInfo { + checksumAddress: string +} + export interface RecoveryObject { value: string id: number diff --git a/components/brave_wallet_ui/page/container.tsx b/components/brave_wallet_ui/page/container.tsx index 5d8bf219e096..7a60ea2bd270 100644 --- a/components/brave_wallet_ui/page/container.tsx +++ b/components/brave_wallet_ui/page/container.tsx @@ -52,6 +52,7 @@ import { findENSAddress, findUnstoppableDomainAddress, getBalance, + getChecksumEthAddress, getERC20Allowance, onConnectHardwareWallet, isStrongPassword @@ -172,10 +173,12 @@ function Container (props: Props) { toAddressOrUrl, toAddress, addressError, + addressWarning, selectedSendAsset } = useSend( findENSAddress, findUnstoppableDomainAddress, + getChecksumEthAddress, sendAssetOptions, selectedAccount, props.walletActions.sendERC20Transfer, @@ -662,6 +665,7 @@ function Container (props: Props) { fromAssetBalance={fromAssetBalance} toAmount={toAmount} addressError={addressError} + addressWarning={addressWarning} toAssetBalance={toAssetBalance} orderExpiration={orderExpiration} slippageTolerance={slippageTolerance} diff --git a/components/brave_wallet_ui/panel/container.tsx b/components/brave_wallet_ui/panel/container.tsx index 252cc64b5de8..cf4452e4050f 100644 --- a/components/brave_wallet_ui/panel/container.tsx +++ b/components/brave_wallet_ui/panel/container.tsx @@ -61,6 +61,7 @@ import { GetNetworkInfo } from '../utils/network-utils' import { findENSAddress, findUnstoppableDomainAddress, + getChecksumEthAddress, getERC20Allowance } from '../common/async/lib' import { isHardwareAccount } from '../utils/address-utils' @@ -182,10 +183,12 @@ function Container (props: Props) { toAddressOrUrl, toAddress, addressError, + addressWarning, selectedSendAsset } = useSend( findENSAddress, findUnstoppableDomainAddress, + getChecksumEthAddress, sendAssetOptions, selectedAccount, props.walletActions.sendERC20Transfer, @@ -766,6 +769,7 @@ function Container (props: Props) { selectedAssetAmount={sendAmount} selectedAssetBalance={sendAssetBalance} addressError={addressError} + addressWarning={addressWarning} toAddressOrUrl={toAddressOrUrl} toAddress={toAddress} /> diff --git a/components/brave_wallet_ui/stories/locale.ts b/components/brave_wallet_ui/stories/locale.ts index 5acdf8801377..c291e30fde96 100644 --- a/components/brave_wallet_ui/stories/locale.ts +++ b/components/brave_wallet_ui/stories/locale.ts @@ -391,6 +391,8 @@ provideStrings({ braveWalletNotDomain: 'Domain is not registered', braveWalletSameAddressError: 'The receiving address is your own address', braveWalletContractAddressError: 'The receiving address is a tokens contract address', + braveWalletAddressMissingChecksumInfoWarning: 'Missing checksum information', + braveWalletNotValidChecksumAddressError: 'Invalid checksum information', // Transaction Queue Strings braveWalletQueueOf: 'of', diff --git a/components/brave_wallet_ui/stories/screens/buy-send-swap.tsx b/components/brave_wallet_ui/stories/screens/buy-send-swap.tsx index 0b593f47d621..d2493a3fa865 100644 --- a/components/brave_wallet_ui/stories/screens/buy-send-swap.tsx +++ b/components/brave_wallet_ui/stories/screens/buy-send-swap.tsx @@ -44,6 +44,7 @@ export interface Props { toAddressOrUrl: string toAddress: string addressError: string + addressWarning: string buyAssetOptions: AccountAssetOptionType[] sendAssetOptions: AccountAssetOptionType[] swapAssetOptions: AccountAssetOptionType[] @@ -95,6 +96,7 @@ function BuySendSwap (props: Props) { fromAmount, toAmount, addressError, + addressWarning, selectedSendAsset, sendAssetBalance, fromAssetBalance, @@ -213,6 +215,7 @@ function BuySendSwap (props: Props) { {selectedTab === 'send' && { onSelectPresetAmount={onSelectPresetAmount} onSubmit={onSubmitSend} addressError='' + addressWarning='' selectedAsset={selectedAsset} selectedAssetAmount={fromAmount} selectedAssetBalance={selectedAccount.balance.toString()} diff --git a/components/resources/wallet_strings.grdp b/components/resources/wallet_strings.grdp index d94e7895dc78..6c819983931f 100644 --- a/components/resources/wallet_strings.grdp +++ b/components/resources/wallet_strings.grdp @@ -324,6 +324,8 @@ Domain doug.wallet$1 is not registered The receiving address is your own address The receiving address is a token's contract address + Missing checksum information + Invalid checksum information of next first