Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- added: Logbox disable option to env.json
- added: Reverse-resolve recipient addresses to ENS / Unstoppable Domains / ZNS names in the send flow, address modal, and transaction history.
- changed: Prevent sending to the same wallet's own address for EVM assets.

## 4.49.0 (staging)

Expand Down
2 changes: 1 addition & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ export default [
'src/util/crypto.ts',
'src/util/CryptoAmount.ts',
'src/util/cryptoTextUtils.ts',
'src/util/CurrencyInfoHelpers.ts',

'src/util/CurrencyWalletHelpers.ts',

'src/util/exchangeRates.ts',
Expand Down
12 changes: 1 addition & 11 deletions src/components/modals/PendingTxModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import type {
import * as React from 'react'
import type { AirshipBridge } from 'react-native-airship'

import { getSpecialCurrencyInfo } from '../../constants/WalletAndCurrencyConstants'
import { useAsyncEffect } from '../../hooks/useAsyncEffect'
import { useHandler } from '../../hooks/useHandler'
import { lstrings } from '../../locales/strings'
import type { NavigationBase } from '../../types/routerTypes'
import { isEvmWallet } from '../../util/CurrencyInfoHelpers'
import { ButtonsView } from '../buttons/ButtonsView'
import { Airship, showError, showToast } from '../services/AirshipInstance'
import { Paragraph } from '../themed/EdgeText'
Expand All @@ -25,16 +25,6 @@ interface PendingTxModalProps {
navigation: NavigationBase
}

/**
* Checks if a wallet is EVM-based by looking at its WalletConnect v2 chain ID namespace.
* EVM chains use the 'eip155' namespace.
*/
function isEvmWallet(wallet: EdgeCurrencyWallet): boolean {
const { pluginId } = wallet.currencyInfo
const specialInfo = getSpecialCurrencyInfo(pluginId)
return specialInfo.walletConnectV2ChainId?.namespace === 'eip155'
}

const PendingTxModal = (props: PendingTxModalProps): React.ReactElement => {
const { bridge, wallet, navigation, tokenId } = props
const [pendingTransaction, setPendingTransaction] =
Expand Down
12 changes: 1 addition & 11 deletions src/components/scenes/SendScene2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import { useState } from '../../types/reactHooks'
import { useDispatch, useSelector } from '../../types/reactRedux'
import type { EdgeAppSceneProps, NavigationBase } from '../../types/routerTypes'
import type { FioRequest } from '../../types/types'
import { getCurrencyCode } from '../../util/CurrencyInfoHelpers'
import { getCurrencyCode, isEvmWallet } from '../../util/CurrencyInfoHelpers'
import { getWalletName } from '../../util/CurrencyWalletHelpers'
import {
addToFioAddressCache,
Expand Down Expand Up @@ -182,16 +182,6 @@ const MULTI_OUT_DIFF_PERCENT = '0.005'
const PIN_MAX_LENGTH = 4
const INFINITY_STRING = '999999999999999999999999999999999999999'

/**
* Checks if a wallet is EVM-based by looking at its WalletConnect v2 chain ID
* namespace. EVM chains use the 'eip155' namespace.
*/
const isEvmWallet = (wallet: EdgeCurrencyWallet): boolean => {
const { pluginId } = wallet.currencyInfo
const specialInfo = getSpecialCurrencyInfo(pluginId)
return specialInfo.walletConnectV2ChainId?.namespace === 'eip155'
}

const SendComponent: React.FC<Props> = props => {
const { route, navigation } = props
const dispatch = useDispatch()
Expand Down
20 changes: 19 additions & 1 deletion src/components/tiles/AddressTile2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { lstrings } from '../../locales/strings'
import { PaymentProtoError } from '../../types/PaymentProtoError'
import { useSelector } from '../../types/reactRedux'
import type { NavigationBase } from '../../types/routerTypes'
import { getCurrencyCode } from '../../util/CurrencyInfoHelpers'
import { getCurrencyCode, isEvmWallet } from '../../util/CurrencyInfoHelpers'
import { parseDeepLink } from '../../util/DeepLinkParser'
import { checkPubAddress } from '../../util/FioAddressUtils'
import { type NameService, reverseLookupName } from '../../util/nameServices'
Expand Down Expand Up @@ -310,6 +310,24 @@ export const AddressTile2 = React.forwardRef(
return
}

// Prevent sending to the same wallet's own address. EVM wallets use a
// single static address across all EVM chains, so a self-send is
// always a mistake. (Cross-wallet "self transfer" to a *different*
// wallet via `handleSelfTransfer` is unaffected.) Compare
// case-insensitively since EVM addresses are checksummed hex.
if (isEvmWallet(coreWallet)) {
const ownReceiveAddress = await coreWallet.getReceiveAddress({
tokenId: null
})
if (
parsedUri.publicAddress.toLowerCase() ===
ownReceiveAddress.publicAddress.toLowerCase()
) {
showError(lstrings.send_to_self_error_message)
return
}
}

// If we don't already have a resolved name from a forward-typed
// domain, attempt a reverse lookup against the parsed public
// address. The dispatcher caches per (pluginId, address) so this
Expand Down
1 change: 1 addition & 0 deletions src/locales/en_US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ const strings = {
fragment_required: 'Required',
scan_invalid_address_error_title: 'Invalid Address',
scan_invalid_address_error_description: 'Not a valid public address',
send_to_self_error_message: 'You cannot send to the same wallet',
fragment_send_subtitle: 'Send',
fragment_send_myself: 'Myself',
fragment_send_from_label: 'From',
Expand Down
1 change: 1 addition & 0 deletions src/locales/strings/enUS.json
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@
"fragment_required": "Required",
"scan_invalid_address_error_title": "Invalid Address",
"scan_invalid_address_error_description": "Not a valid public address",
"send_to_self_error_message": "You cannot send to the same wallet",
"fragment_send_subtitle": "Send",
"fragment_send_myself": "Myself",
"fragment_send_from_label": "From",
Expand Down
15 changes: 14 additions & 1 deletion src/util/CurrencyInfoHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import type {
} from 'edge-core-js'

import { showError } from '../components/services/AirshipInstance'
import { SPECIAL_CURRENCY_INFO } from '../constants/WalletAndCurrencyConstants'
import {
getSpecialCurrencyInfo,
SPECIAL_CURRENCY_INFO
} from '../constants/WalletAndCurrencyConstants'
import { ENV } from '../env'
import type { EdgeAsset } from '../types/types'
import { asMaybeContractLocation } from './cleaners'
Expand All @@ -24,6 +27,16 @@ export function isKeysOnlyPlugin(pluginId: string): boolean {
return keysOnlyMode || ENV.KEYS_ONLY_PLUGINS[pluginId]
}

/**
* Checks if a wallet is EVM-based by looking at its WalletConnect v2 chain ID
* namespace. EVM chains use the 'eip155' namespace.
*/
export function isEvmWallet(wallet: EdgeCurrencyWallet): boolean {
const { pluginId } = wallet.currencyInfo
const specialInfo = getSpecialCurrencyInfo(pluginId)
return specialInfo.walletConnectV2ChainId?.namespace === 'eip155'
}

export type FindTokenParams =
| {
account: EdgeAccount
Expand Down
Loading