[Address Book v2] - Refactor addressBook selectors (#2372)

This commit is contained in:
Fernando 2021-06-07 15:47:16 -03:00 committed by GitHub
parent b9b239e0e3
commit cd9182cf4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 119 additions and 248 deletions

View File

@ -4,20 +4,32 @@ import { getNetworkId } from 'src/config'
import { ADDRESS_BOOK_DEFAULT_NAME, AddressBookEntry } from 'src/logic/addressBook/model/addressBook' import { ADDRESS_BOOK_DEFAULT_NAME, AddressBookEntry } from 'src/logic/addressBook/model/addressBook'
import { AppReduxState } from 'src/store' import { AppReduxState } from 'src/store'
import { Overwrite } from 'src/types/helpers' import { Overwrite } from 'src/types/helpers'
import { checksumAddress } from 'src/utils/checksumAddress'
import { isValidAddress } from 'src/utils/isValidAddress'
const networkId = getNetworkId() const networkId = getNetworkId()
export const addressBookSelector = (state: AppReduxState): AppReduxState['addressBook'] => state['addressBook'] export const addressBookFromQueryParams = (state: AppReduxState): string | undefined => {
return state.router.location?.query?.entryAddress
}
export const addressBookState = (state: AppReduxState): AppReduxState['addressBook'] => state['addressBook']
export const addressBookAddresses = createSelector([addressBookState], (addressBook): string[] => {
return addressBook.map(({ address }) => address)
})
type AddressBookMap = { type AddressBookMap = {
[chainId: number]: {
[address: string]: AddressBookEntry [address: string]: AddressBookEntry
} }
type AddressBookMapByChain = {
[chainId: number]: AddressBookMap
} }
export const addressBookMapSelector = createSelector( export const addressBookAsMap = createSelector(
[addressBookSelector], [addressBookState],
(addressBook): AddressBookMap => { (addressBook): AddressBookMapByChain => {
const addressBookMap = {} const addressBookMap = {}
addressBook.forEach((entry) => { addressBook.forEach((entry) => {
@ -33,33 +45,45 @@ export const addressBookMapSelector = createSelector(
}, },
) )
export const addressBookAddressesListSelector = createSelector([addressBookSelector], (addressBook): string[] => type GetNameParams = Overwrite<Partial<AddressBookEntry>, { address: string }>
addressBook.map(({ address }) => address),
)
type GetNameParams = Overwrite< export const addressBookEntryName = createSelector(
AddressBookEntry,
{ chainId?: AddressBookEntry['chainId']; name?: AddressBookEntry['name'] }
>
type GetNameReturnObject = Overwrite<GetNameParams, { chainId: AddressBookEntry['chainId'] }>
export const getNameFromAddressBookSelector = createSelector(
[ [
addressBookMapSelector, addressBookAsMap,
(_, { address, chainId = networkId }: GetNameParams): GetNameReturnObject => ({ (_, { address, chainId = networkId }: GetNameParams): { address: string; chainId: number } => ({
address, address,
chainId, chainId,
}), }),
], ],
(addressBook, entry) => addressBook?.[entry.chainId]?.[entry.address]?.name ?? ADDRESS_BOOK_DEFAULT_NAME, (addressBook, { address, chainId }) => {
if (isValidAddress(address)) {
return addressBook?.[chainId]?.[checksumAddress(address)]?.name ?? ADDRESS_BOOK_DEFAULT_NAME
}
return ADDRESS_BOOK_DEFAULT_NAME
},
) )
export const addressBookQueryParamsSelector = (state: AppReduxState): string | undefined => { /*********************/
const { location } = state.router /* Connected Network */
/*********************/
if (location?.query) { export const currentNetworkAddressBook = createSelector(
const { entryAddress } = location.query [addressBookState],
return entryAddress (addressBook): AppReduxState['addressBook'] => {
} return addressBook.filter(({ chainId }) => chainId === networkId)
} },
)
export const currentNetworkAddressBookAsMap = createSelector(
[currentNetworkAddressBook],
(addressBook): AddressBookMap => {
const addressBookMap = {}
addressBook.forEach((entry) => {
addressBookMap[entry.address] = entry
})
return addressBookMap
},
)

View File

@ -1,32 +1,4 @@
import { import { isValidAddressBookName } from 'src/logic/addressBook/utils'
checkIfEntryWasDeletedFromAddressBook,
getNameFromAddressBook,
isValidAddressBookName,
} from 'src/logic/addressBook/utils'
import { AddressBookEntry, AddressBookState, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
const getMockAddressBookEntry = (address: string, name: string = 'test'): AddressBookEntry =>
makeAddressBookEntry({
address,
name,
})
describe('getNameFromSafeAddressBook', () => {
const entry1 = getMockAddressBookEntry('123456', 'test1')
const entry2 = getMockAddressBookEntry('78910', 'test2')
const entry3 = getMockAddressBookEntry('4781321', 'test3')
it('It should returns the user name given a safeAddressBook and an user account', () => {
// given
const safeAddressBook = [entry1, entry2, entry3]
const expectedResult = entry2.name
// when
const result = getNameFromAddressBook(safeAddressBook, entry2.address)
// then
expect(result).toBe(expectedResult)
})
})
describe('isValidAddressBookName', () => { describe('isValidAddressBookName', () => {
it('It should return false if given a blacklisted name like UNKNOWN', () => { it('It should return false if given a blacklisted name like UNKNOWN', () => {
@ -78,51 +50,3 @@ describe('isValidAddressBookName', () => {
expect(result).toEqual(expectedResult) expect(result).toEqual(expectedResult)
}) })
}) })
describe('checkIfEntryWasDeletedFromAddressBook', () => {
const mockAdd1 = '0x696fd93D725d84acfFf6c62a1fe8C94E1c9E934A'
const mockAdd2 = '0x2C7aC78b01Be0FC66AD29b684ffAb0C93B381D00'
const mockAdd3 = '0x537BD452c3505FC07bA242E437bD29D4E1DC9126'
const entry1 = getMockAddressBookEntry(mockAdd1, 'test1')
const entry2 = getMockAddressBookEntry(mockAdd2, 'test2')
const entry3 = getMockAddressBookEntry(mockAdd3, 'test3')
it('It should return true if a given entry was deleted from addressBook', () => {
// given
const addressBookEntry = entry1
const addressBook: AddressBookState = [entry2, entry3]
const safeAlreadyLoaded = true
const expectedResult = true
// when
const result = checkIfEntryWasDeletedFromAddressBook(addressBookEntry, addressBook, safeAlreadyLoaded)
// then
expect(result).toEqual(expectedResult)
})
it('It should return false if a given entry was not deleted from addressBook', () => {
// given
const addressBookEntry = entry1
const addressBook: AddressBookState = [entry1, entry2, entry3]
const safeAlreadyLoaded = true
const expectedResult = false
// when
const result = checkIfEntryWasDeletedFromAddressBook(addressBookEntry, addressBook, safeAlreadyLoaded)
// then
expect(result).toEqual(expectedResult)
})
it('It should return false if the safe was not already loaded', () => {
// given
const addressBookEntry = entry1
const addressBook: AddressBookState = [entry1, entry2, entry3]
const safeAlreadyLoaded = false
const expectedResult = false
// when
const result = checkIfEntryWasDeletedFromAddressBook(addressBookEntry, addressBook, safeAlreadyLoaded)
// then
expect(result).toEqual(expectedResult)
})
})

View File

@ -1,5 +1,4 @@
import { mustBeEthereumContractAddress } from 'src/components/forms/validator' import { mustBeEthereumContractAddress } from 'src/components/forms/validator'
import { ETHEREUM_NETWORK } from 'src/config/networks/network.d'
import { AddressBookEntry, AddressBookState } from 'src/logic/addressBook/model/addressBook' import { AddressBookEntry, AddressBookState } from 'src/logic/addressBook/model/addressBook'
import { sameAddress } from 'src/logic/wallets/ethAddresses' import { sameAddress } from 'src/logic/wallets/ethAddresses'
import { AppReduxState } from 'src/store' import { AppReduxState } from 'src/store'
@ -17,22 +16,6 @@ export type OldAddressBookType = {
export const ADDRESS_BOOK_INVALID_NAMES = ['UNKNOWN', 'OWNER #', 'MY WALLET'] export const ADDRESS_BOOK_INVALID_NAMES = ['UNKNOWN', 'OWNER #', 'MY WALLET']
type GetNameFromAddressBookOptions = {
filterOnlyValidName: boolean
}
export const getNameFromAddressBook = (
addressBook: AddressBookState,
userAddress: string,
options?: GetNameFromAddressBookOptions,
): string | null => {
const entry = addressBook.find((addressBookItem) => addressBookItem.address === userAddress)
if (entry) {
return options?.filterOnlyValidName ? getValidAddressBookName(entry.name) : entry.name
}
return null
}
export const isValidAddressBookName = (addressBookName: string): boolean => { export const isValidAddressBookName = (addressBookName: string): boolean => {
const hasInvalidName = ADDRESS_BOOK_INVALID_NAMES.find((invalidName) => const hasInvalidName = ADDRESS_BOOK_INVALID_NAMES.find((invalidName) =>
addressBookName?.toUpperCase().includes(invalidName), addressBookName?.toUpperCase().includes(invalidName),
@ -40,50 +23,6 @@ export const isValidAddressBookName = (addressBookName: string): boolean => {
return !hasInvalidName return !hasInvalidName
} }
// TODO: is this really required?
export const getValidAddressBookName = (addressBookName: string): string | null => {
return isValidAddressBookName(addressBookName) ? addressBookName : null
}
export const formatAddressListToAddressBookNames = (
addressBook: AddressBookState,
addresses: string[],
): AddressBookEntry[] => {
if (!addresses.length) {
return []
}
return addresses.map((address) => {
const ownerName = getNameFromAddressBook(addressBook, address)
return {
address: address,
name: ownerName || '',
chainId: ETHEREUM_NETWORK.UNKNOWN,
}
})
}
/**
* If the safe is not loaded, the owner wasn't deleted
* If the safe is already loaded and the owner has a valid name, will return true if the address is not already on the addressBook
* @param name
* @param address
* @param addressBook
* @param safeAlreadyLoaded
*/
export const checkIfEntryWasDeletedFromAddressBook = (
{ name, address }: AddressBookEntry,
addressBook: AddressBookState,
safeAlreadyLoaded: boolean,
): boolean => {
if (!safeAlreadyLoaded) {
return false
}
const addressShouldBeOnTheAddressBook = !!getValidAddressBookName(name)
const isAlreadyInAddressBook = !!addressBook.find((entry) => sameAddress(entry.address, address))
return addressShouldBeOnTheAddressBook && !isAlreadyInAddressBook
}
/** /**
* Returns a filtered list of AddressBookEntries whose addresses are contracts * Returns a filtered list of AddressBookEntries whose addresses are contracts
* @param {Array<AddressBookEntry>} addressBook * @param {Array<AddressBookEntry>} addressBook

View File

@ -2,9 +2,8 @@ import { List } from 'immutable'
import { matchPath } from 'react-router-dom' import { matchPath } from 'react-router-dom'
import { createSelector } from 'reselect' import { createSelector } from 'reselect'
import { getNetworkId } from 'src/config'
import { AddressBookEntry, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook' import { AddressBookEntry, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
import { addressBookMapSelector } from 'src/logic/addressBook/store/selectors' import { currentNetworkAddressBookAsMap } from 'src/logic/addressBook/store/selectors'
import makeSafe, { SafeRecord, SafeRecordProps } from 'src/logic/safe/store/models/safe' import makeSafe, { SafeRecord, SafeRecordProps } from 'src/logic/safe/store/models/safe'
import { SAFE_REDUCER_ID } from 'src/logic/safe/store/reducer/safe' import { SAFE_REDUCER_ID } from 'src/logic/safe/store/reducer/safe'
import { SAFELIST_ADDRESS } from 'src/routes/routes' import { SAFELIST_ADDRESS } from 'src/routes/routes'
@ -72,23 +71,20 @@ export const currentSafeTotalFiatBalance = createSelector(currentSafe, safeField
/*************************/ /*************************/
/* With AddressBook Data */ /* With AddressBook Data */
/*************************/ /*************************/
const chainId = getNetworkId()
const baseSafeWithName = { ...baseSafe.toJS(), name: '' } const baseSafeWithName = { ...baseSafe.toJS(), name: '' }
export type SafeRecordWithNames = Overwrite<SafeRecordProps, { owners: AddressBookEntry[] }> & { name: string } export type SafeRecordWithNames = Overwrite<SafeRecordProps, { owners: AddressBookEntry[] }> & { name: string }
export const safesWithNamesAsList = createSelector( export const safesWithNamesAsList = createSelector(
[safesAsList, addressBookMapSelector], [safesAsList, currentNetworkAddressBookAsMap],
(safesList, addressBookMap): SafeRecordWithNames[] => { (safesList, addressBookMap): SafeRecordWithNames[] => {
const addressBook = addressBookMap?.[chainId]
return safesList return safesList
.map((safeRecord) => { .map((safeRecord) => {
const safe = safeRecord.toObject() const safe = safeRecord.toObject()
const name = addressBook?.[safe.address]?.name ?? '' const name = addressBookMap?.[safe.address]?.name ?? ''
const owners = safe.owners.map((ownerAddress) => { const owners = safe.owners.map((ownerAddress) => {
return addressBook?.[ownerAddress] ?? makeAddressBookEntry({ address: ownerAddress, name: '' }) return addressBookMap?.[ownerAddress] ?? makeAddressBookEntry({ address: ownerAddress, name: '' })
}) })
return { ...safe, name, owners } return { ...safe, name, owners }

View File

@ -14,10 +14,9 @@ import Hairline from 'src/components/layout/Hairline'
import Paragraph from 'src/components/layout/Paragraph' import Paragraph from 'src/components/layout/Paragraph'
import Row from 'src/components/layout/Row' import Row from 'src/components/layout/Row'
import OpenPaper from 'src/components/Stepper/OpenPaper' import OpenPaper from 'src/components/Stepper/OpenPaper'
import { AddressBookEntry } from 'src/logic/addressBook/model/addressBook' import { AddressBookEntry, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
import { addressBookSelector } from 'src/logic/addressBook/store/selectors' import { currentNetworkAddressBookAsMap } from 'src/logic/addressBook/store/selectors'
import { formatAddressListToAddressBookNames } from 'src/logic/addressBook/utils'
import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts' import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts'
import { FIELD_LOAD_ADDRESS, THRESHOLD } from 'src/routes/load/components/fields' import { FIELD_LOAD_ADDRESS, THRESHOLD } from 'src/routes/load/components/fields'
import { getOwnerAddressBy, getOwnerNameBy } from 'src/routes/open/components/fields' import { getOwnerAddressBy, getOwnerNameBy } from 'src/routes/open/components/fields'
@ -33,12 +32,6 @@ const calculateSafeValues = (owners, threshold, values) => {
return initialValues return initialValues
} }
const useAddressBookForOwnersNames = (ownersList: string[]): AddressBookEntry[] => {
const addressBook = useSelector(addressBookSelector)
return formatAddressListToAddressBookNames(addressBook, ownersList)
}
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
interface OwnerListComponentProps { interface OwnerListComponentProps {
@ -49,8 +42,19 @@ interface OwnerListComponentProps {
const OwnerListComponent = ({ values, updateInitialProps }: OwnerListComponentProps): ReactElement => { const OwnerListComponent = ({ values, updateInitialProps }: OwnerListComponentProps): ReactElement => {
const [owners, setOwners] = useState<string[]>([]) const [owners, setOwners] = useState<string[]>([])
const classes = useStyles() const classes = useStyles()
const addressBookMap = useSelector(currentNetworkAddressBookAsMap)
const [ownersWithName, setOwnersWithName] = useState<AddressBookEntry[]>([])
const ownersWithNames = useAddressBookForOwnersNames(owners) useEffect(() => {
setOwnersWithName(
owners.map((address) =>
makeAddressBookEntry({
address,
name: addressBookMap[address]?.name ?? '',
}),
),
)
}, [addressBookMap, owners, ownersWithName])
useEffect(() => { useEffect(() => {
let isCurrent = true let isCurrent = true
@ -91,7 +95,7 @@ const OwnerListComponent = ({ values, updateInitialProps }: OwnerListComponentPr
</Row> </Row>
<Hairline /> <Hairline />
<Block margin="md" padding="md"> <Block margin="md" padding="md">
{ownersWithNames.map(({ address, name }, index) => { {ownersWithName.map(({ address, name }, index) => {
return ( return (
<Row className={classes.owner} key={address} data-testid="owner-row"> <Row className={classes.owner} key={address} data-testid="owner-row">
<Col className={classes.ownerName} xs={4}> <Col className={classes.ownerName} xs={4}>

View File

@ -17,14 +17,15 @@ import {
getOwnerAddressBy, getOwnerAddressBy,
getOwnerNameBy, getOwnerNameBy,
} from 'src/routes/open/components/fields' } from 'src/routes/open/components/fields'
import { WelcomeLayout } from 'src/routes/welcome/components/index' import { WelcomeLayout } from 'src/routes/welcome/components'
import { history } from 'src/store' import { history } from 'src/store'
import { secondary, sm } from 'src/theme/variables' import { secondary, sm } from 'src/theme/variables'
import { providerNameSelector, userAccountSelector } from 'src/logic/wallets/store/selectors' import { providerNameSelector, userAccountSelector } from 'src/logic/wallets/store/selectors'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import { addressBookSelector } from 'src/logic/addressBook/store/selectors' import { addressBookEntryName } from 'src/logic/addressBook/store/selectors'
import { getNameFromAddressBook } from 'src/logic/addressBook/utils'
import { SafeProps } from 'src/routes/open/container/Open' import { SafeProps } from 'src/routes/open/container/Open'
import { ADDRESS_BOOK_DEFAULT_NAME } from 'src/logic/addressBook/model/addressBook'
import { sameString } from 'src/utils/strings'
const { useEffect } = React const { useEffect } = React
@ -39,12 +40,11 @@ export type InitialValuesForm = {
} }
const useInitialValuesFrom = (userAccount: string, safeProps?: SafeProps): InitialValuesForm => { const useInitialValuesFrom = (userAccount: string, safeProps?: SafeProps): InitialValuesForm => {
const addressBook = useSelector(addressBookSelector) const ownerName = useSelector((state) => addressBookEntryName(state, { address: userAccount }))
const ownerName = getNameFromAddressBook(addressBook, userAccount, { filterOnlyValidName: true })
if (!safeProps) { if (!safeProps) {
return { return {
[getOwnerNameBy(0)]: ownerName || 'My Wallet', [getOwnerNameBy(0)]: sameString(ownerName, ADDRESS_BOOK_DEFAULT_NAME) ? 'My Wallet' : ownerName,
[getOwnerAddressBy(0)]: userAccount, [getOwnerAddressBy(0)]: userAccount,
[FIELD_CONFIRMATIONS]: '1', [FIELD_CONFIRMATIONS]: '1',
[FIELD_CREATION_PROXY_SALT]: Date.now(), [FIELD_CREATION_PROXY_SALT]: Date.now(),

View File

@ -38,8 +38,7 @@ import {
} from 'src/routes/open/components/fields' } from 'src/routes/open/components/fields'
import { getAccountsFrom } from 'src/routes/open/utils/safeDataExtractor' import { getAccountsFrom } from 'src/routes/open/utils/safeDataExtractor'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import { addressBookSelector } from 'src/logic/addressBook/store/selectors' import { currentNetworkAddressBook } from 'src/logic/addressBook/store/selectors'
import { getNameFromAddressBook } from 'src/logic/addressBook/utils'
const { useState } = React const { useState } = React
@ -116,7 +115,7 @@ const SafeOwnersForm = (props): React.ReactElement => {
const classes = useStyles() const classes = useStyles()
const validOwners = getNumOwnersFrom(values) const validOwners = getNumOwnersFrom(values)
const addressBook = useSelector(addressBookSelector) const addressBook = useSelector(currentNetworkAddressBook)
const [numOwners, setNumOwners] = useState(validOwners) const [numOwners, setNumOwners] = useState(validOwners)
const [qrModalOpen, setQrModalOpen] = useState(false) const [qrModalOpen, setQrModalOpen] = useState(false)
@ -206,9 +205,7 @@ const SafeOwnersForm = (props): React.ReactElement => {
<Col className={classes.ownerAddress} xs={7}> <Col className={classes.ownerAddress} xs={7}>
<StyledAddressInput <StyledAddressInput
fieldMutator={(newOwnerAddress) => { fieldMutator={(newOwnerAddress) => {
const newOwnerName = getNameFromAddressBook(addressBook, newOwnerAddress, { const newOwnerName = addressBook[newOwnerAddress]?.name
filterOnlyValidName: true,
})
form.mutators.setValue(addressName, newOwnerAddress) form.mutators.setValue(addressName, newOwnerAddress)
if (newOwnerName) { if (newOwnerName) {
form.mutators.setValue(ownerName, newOwnerName) form.mutators.setValue(ownerName, newOwnerName)

View File

@ -13,7 +13,7 @@ import { composeValidators, required, uniqueAddress, validAddressBookName } from
import Block from 'src/components/layout/Block' import Block from 'src/components/layout/Block'
import Col from 'src/components/layout/Col' import Col from 'src/components/layout/Col'
import Row from 'src/components/layout/Row' import Row from 'src/components/layout/Row'
import { addressBookAddressesListSelector } from 'src/logic/addressBook/store/selectors' import { addressBookAddresses } from 'src/logic/addressBook/store/selectors'
import { AddressBookEntry } from 'src/logic/addressBook/model/addressBook' import { AddressBookEntry } from 'src/logic/addressBook/model/addressBook'
import { Entry } from 'src/routes/safe/components/AddressBook' import { Entry } from 'src/routes/safe/components/AddressBook'
@ -54,7 +54,7 @@ export const CreateEditEntryModal = ({
} }
} }
const storedAddresses = useSelector(addressBookAddressesListSelector) const storedAddresses = useSelector(addressBookAddresses)
const isUniqueAddress = uniqueAddress(storedAddresses) const isUniqueAddress = uniqueAddress(storedAddresses)
return ( return (

View File

@ -9,7 +9,7 @@ import { useDispatch, useSelector } from 'react-redux'
import { sameString } from 'src/utils/strings' import { sameString } from 'src/utils/strings'
import { ADDRESS_BOOK_DEFAULT_NAME } from 'src/logic/addressBook/model/addressBook' import { ADDRESS_BOOK_DEFAULT_NAME } from 'src/logic/addressBook/model/addressBook'
import { getNameFromAddressBookSelector } from 'src/logic/addressBook/store/selectors' import { addressBookEntryName } from 'src/logic/addressBook/store/selectors'
import { SAFELIST_ADDRESS } from 'src/routes/routes' import { SAFELIST_ADDRESS } from 'src/routes/routes'
import { safeAddressFromUrl } from 'src/logic/safe/store/selectors' import { safeAddressFromUrl } from 'src/logic/safe/store/selectors'
import { xs } from 'src/theme/variables' import { xs } from 'src/theme/variables'
@ -52,7 +52,7 @@ export const EllipsisTransactionDetails = ({
const currentSafeAddress = useSelector(safeAddressFromUrl) const currentSafeAddress = useSelector(safeAddressFromUrl)
const isOwnerConnected = useSelector(grantedSelector) const isOwnerConnected = useSelector(grantedSelector)
const recipientName = useSelector((state) => getNameFromAddressBookSelector(state, { address })) const recipientName = useSelector((state) => addressBookEntryName(state, { address }))
// We have to check that the name returned is not UNKNOWN // We have to check that the name returned is not UNKNOWN
const isStoredInAddressBook = !sameString(recipientName, ADDRESS_BOOK_DEFAULT_NAME) const isStoredInAddressBook = !sameString(recipientName, ADDRESS_BOOK_DEFAULT_NAME)

View File

@ -9,8 +9,7 @@ import { enhanceSnackbarForAction, getNotificationsFromTxType } from 'src/logic/
import enqueueSnackbar from 'src/logic/notifications/store/actions/enqueueSnackbar' import enqueueSnackbar from 'src/logic/notifications/store/actions/enqueueSnackbar'
import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions' import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
import { addressBookSelector } from 'src/logic/addressBook/store/selectors' import { addressBookState } from 'src/logic/addressBook/store/selectors'
import { AddressBookState } from 'src/logic/addressBook/model/addressBook'
import { lg, md, background } from 'src/theme/variables' import { lg, md, background } from 'src/theme/variables'
@ -57,7 +56,7 @@ const StyledCSVLink = styled(CSVDownloader)`
export const ExportEntriesModal = ({ isOpen, onClose }: ExportEntriesModalProps): ReactElement => { export const ExportEntriesModal = ({ isOpen, onClose }: ExportEntriesModalProps): ReactElement => {
const dispatch = useDispatch() const dispatch = useDispatch()
const addressBook: AddressBookState = useSelector(addressBookSelector) const addressBook = useSelector(addressBookState)
const [loading, setLoading] = useState<boolean>(true) const [loading, setLoading] = useState<boolean>(true)
const [error, setError] = useState<string | undefined>('') const [error, setError] = useState<string | undefined>('')
const [csvData, setCsvData] = useState<string>('') const [csvData, setCsvData] = useState<string>('')

View File

@ -19,7 +19,7 @@ import Col from 'src/components/layout/Col'
import Row from 'src/components/layout/Row' import Row from 'src/components/layout/Row'
import { AddressBookEntry, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook' import { AddressBookEntry, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
import { addressBookAddOrUpdate, addressBookImport, addressBookRemove } from 'src/logic/addressBook/store/actions' import { addressBookAddOrUpdate, addressBookImport, addressBookRemove } from 'src/logic/addressBook/store/actions'
import { addressBookQueryParamsSelector, addressBookSelector } from 'src/logic/addressBook/store/selectors' import { addressBookFromQueryParams, currentNetworkAddressBook } from 'src/logic/addressBook/store/selectors'
import { isUserAnOwnerOfAnySafe, sameAddress } from 'src/logic/wallets/ethAddresses' import { isUserAnOwnerOfAnySafe, sameAddress } from 'src/logic/wallets/ethAddresses'
import { CreateEditEntryModal } from 'src/routes/safe/components/AddressBook/CreateEditEntryModal' import { CreateEditEntryModal } from 'src/routes/safe/components/AddressBook/CreateEditEntryModal'
import { ExportEntriesModal } from 'src/routes/safe/components/AddressBook/ExportEntriesModal' import { ExportEntriesModal } from 'src/routes/safe/components/AddressBook/ExportEntriesModal'
@ -72,8 +72,8 @@ const AddressBookTable = (): ReactElement => {
const autoColumns = columns.filter(({ custom }) => !custom) const autoColumns = columns.filter(({ custom }) => !custom)
const dispatch = useDispatch() const dispatch = useDispatch()
const safesList = useSelector(safesAsList) const safesList = useSelector(safesAsList)
const entryAddressToEditOrCreateNew = useSelector(addressBookQueryParamsSelector) const entryAddressToEditOrCreateNew = useSelector(addressBookFromQueryParams)
const addressBook = useSelector(addressBookSelector) const addressBook = useSelector(currentNetworkAddressBook)
const granted = useSelector(grantedSelector) const granted = useSelector(grantedSelector)
const [selectedEntry, setSelectedEntry] = useState<Entry>(initialEntryState) const [selectedEntry, setSelectedEntry] = useState<Entry>(initialEntryState)
const [editCreateEntryModalOpen, setEditCreateEntryModalOpen] = useState(false) const [editCreateEntryModalOpen, setEditCreateEntryModalOpen] = useState(false)

View File

@ -8,7 +8,7 @@ import { mustBeEthereumAddress, mustBeEthereumContractAddress } from 'src/compon
import { getNetworkId, isFeatureEnabled } from 'src/config' import { getNetworkId, isFeatureEnabled } from 'src/config'
import { FEATURES } from 'src/config/networks/network.d' import { FEATURES } from 'src/config/networks/network.d'
import { AddressBookEntry } from 'src/logic/addressBook/model/addressBook' import { AddressBookEntry } from 'src/logic/addressBook/model/addressBook'
import { addressBookSelector } from 'src/logic/addressBook/store/selectors' import { currentNetworkAddressBook } from 'src/logic/addressBook/store/selectors'
import { filterContractAddressBookEntries, filterAddressEntries } from 'src/logic/addressBook/utils' import { filterContractAddressBookEntries, filterAddressEntries } from 'src/logic/addressBook/utils'
import { isValidEnsName, isValidCryptoDomainName } from 'src/logic/wallets/ethAddresses' import { isValidEnsName, isValidCryptoDomainName } from 'src/logic/wallets/ethAddresses'
import { getAddressFromDomain } from 'src/logic/wallets/getWeb3' import { getAddressFromDomain } from 'src/logic/wallets/getWeb3'
@ -172,7 +172,7 @@ const BaseAddressBookInput = ({
} }
export const AddressBookInput = (props: AddressBookProps): ReactElement => { export const AddressBookInput = (props: AddressBookProps): ReactElement => {
const addressBookEntries = useSelector(addressBookSelector) const addressBookEntries = useSelector(currentNetworkAddressBook)
const [validationText, setValidationText] = useState<string>('') const [validationText, setValidationText] = useState<string>('')
useEffect(() => { useEffect(() => {
@ -196,7 +196,7 @@ export const ContractsAddressBookInput = ({
setSelectedEntry, setSelectedEntry,
...props ...props
}: AddressBookProps): ReactElement => { }: AddressBookProps): ReactElement => {
const addressBookEntries = useSelector(addressBookSelector) const addressBookEntries = useSelector(currentNetworkAddressBook)
const [filteredEntries, setFilteredEntries] = useState<AddressBookEntry[]>([]) const [filteredEntries, setFilteredEntries] = useState<AddressBookEntry[]>([])
const [validationText, setValidationText] = useState<string>('') const [validationText, setValidationText] = useState<string>('')

View File

@ -27,7 +27,7 @@ import {
getValueFromTxInputs, getValueFromTxInputs,
} from 'src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/utils' } from 'src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/utils'
import { useEstimateTransactionGas, EstimationStatus } from 'src/logic/hooks/useEstimateTransactionGas' import { useEstimateTransactionGas, EstimationStatus } from 'src/logic/hooks/useEstimateTransactionGas'
import { getNameFromAddressBookSelector } from 'src/logic/addressBook/store/selectors' import { addressBookEntryName } from 'src/logic/addressBook/store/selectors'
import { useEstimationStatus } from 'src/logic/hooks/useEstimationStatus' import { useEstimationStatus } from 'src/logic/hooks/useEstimationStatus'
import { ButtonStatus, Modal } from 'src/components/Modal' import { ButtonStatus, Modal } from 'src/components/Modal'
import { TransactionFees } from 'src/components/TransactionsFees' import { TransactionFees } from 'src/components/TransactionsFees'
@ -61,9 +61,7 @@ const ContractInteractionReview = ({ onClose, onPrev, tx }: Props): React.ReactE
const [manualSafeTxGas, setManualSafeTxGas] = useState(0) const [manualSafeTxGas, setManualSafeTxGas] = useState(0)
const [manualGasPrice, setManualGasPrice] = useState<string | undefined>() const [manualGasPrice, setManualGasPrice] = useState<string | undefined>()
const [manualGasLimit, setManualGasLimit] = useState<string | undefined>() const [manualGasLimit, setManualGasLimit] = useState<string | undefined>()
const addressName = useSelector((state) => const addressName = useSelector((state) => addressBookEntryName(state, { address: tx.contractAddress as string }))
getNameFromAddressBookSelector(state, { address: tx.contractAddress as string }),
)
const [txInfo, setTxInfo] = useState<{ const [txInfo, setTxInfo] = useState<{
txRecipient: string txRecipient: string

View File

@ -15,8 +15,7 @@ import Row from 'src/components/layout/Row'
import { ScanQRWrapper } from 'src/components/ScanQRModal/ScanQRWrapper' import { ScanQRWrapper } from 'src/components/ScanQRModal/ScanQRWrapper'
import { Modal } from 'src/components/Modal' import { Modal } from 'src/components/Modal'
import WhenFieldChanges from 'src/components/WhenFieldChanges' import WhenFieldChanges from 'src/components/WhenFieldChanges'
import { addressBookSelector } from 'src/logic/addressBook/store/selectors' import { currentNetworkAddressBook } from 'src/logic/addressBook/store/selectors'
import { getNameFromAddressBook } from 'src/logic/addressBook/utils'
import { nftAssetsSelector, nftTokensSelector } from 'src/logic/collectibles/store/selectors' import { nftAssetsSelector, nftTokensSelector } from 'src/logic/collectibles/store/selectors'
import { Erc721Transfer } from 'src/logic/safe/store/models/types/gateway' import { Erc721Transfer } from 'src/logic/safe/store/models/types/gateway'
import SafeInfo from 'src/routes/safe/components/Balances/SendModal/SafeInfo' import SafeInfo from 'src/routes/safe/components/Balances/SendModal/SafeInfo'
@ -70,7 +69,7 @@ const SendCollectible = ({
const classes = useStyles() const classes = useStyles()
const nftAssets = useSelector(nftAssetsSelector) const nftAssets = useSelector(nftAssetsSelector)
const nftTokens = useSelector(nftTokensSelector) const nftTokens = useSelector(nftTokensSelector)
const addressBook = useSelector(addressBookSelector) const addressBook = useSelector(currentNetworkAddressBook)
const [selectedEntry, setSelectedEntry] = useState<{ address: string; name: string } | null>(() => { const [selectedEntry, setSelectedEntry] = useState<{ address: string; name: string } | null>(() => {
const defaultEntry = { address: recipientAddress || '', name: '' } const defaultEntry = { address: recipientAddress || '', name: '' }
@ -138,7 +137,7 @@ const SendCollectible = ({
if (scannedAddress.startsWith('ethereum:')) { if (scannedAddress.startsWith('ethereum:')) {
scannedAddress = scannedAddress.replace('ethereum:', '') scannedAddress = scannedAddress.replace('ethereum:', '')
} }
const scannedName = addressBook ? getNameFromAddressBook(addressBook, scannedAddress) : '' const scannedName = addressBook[scannedAddress]?.name ?? ''
mutators.setRecipient(scannedAddress) mutators.setRecipient(scannedAddress)
setSelectedEntry({ setSelectedEntry({
name: scannedName ?? '', name: scannedName ?? '',

View File

@ -26,8 +26,7 @@ import Hairline from 'src/components/layout/Hairline'
import Paragraph from 'src/components/layout/Paragraph' import Paragraph from 'src/components/layout/Paragraph'
import Row from 'src/components/layout/Row' import Row from 'src/components/layout/Row'
import { ScanQRWrapper } from 'src/components/ScanQRModal/ScanQRWrapper' import { ScanQRWrapper } from 'src/components/ScanQRModal/ScanQRWrapper'
import { addressBookSelector } from 'src/logic/addressBook/store/selectors' import { currentNetworkAddressBook } from 'src/logic/addressBook/store/selectors'
import { getNameFromAddressBook } from 'src/logic/addressBook/utils'
import { sameAddress } from 'src/logic/wallets/ethAddresses' import { sameAddress } from 'src/logic/wallets/ethAddresses'
import { SpendingLimit } from 'src/logic/safe/store/models/safe' import { SpendingLimit } from 'src/logic/safe/store/models/safe'
import { userAccountSelector } from 'src/logic/wallets/store/selectors' import { userAccountSelector } from 'src/logic/wallets/store/selectors'
@ -98,7 +97,7 @@ const SendFunds = ({
}: SendFundsProps): ReactElement => { }: SendFundsProps): ReactElement => {
const classes = useStyles() const classes = useStyles()
const tokens = useSelector(extendedSafeTokensSelector) const tokens = useSelector(extendedSafeTokensSelector)
const addressBook = useSelector(addressBookSelector) const addressBook = useSelector(currentNetworkAddressBook)
const { nativeCoin } = getNetworkInfo() const { nativeCoin } = getNetworkInfo()
const [selectedEntry, setSelectedEntry] = useState<{ address: string; name: string } | null>(() => { const [selectedEntry, setSelectedEntry] = useState<{ address: string; name: string } | null>(() => {
const defaultEntry = { address: recipientAddress || '', name: '' } const defaultEntry = { address: recipientAddress || '', name: '' }
@ -212,7 +211,7 @@ const SendFunds = ({
if (scannedAddress.startsWith('ethereum:')) { if (scannedAddress.startsWith('ethereum:')) {
scannedAddress = scannedAddress.replace('ethereum:', '') scannedAddress = scannedAddress.replace('ethereum:', '')
} }
const scannedName = addressBook ? getNameFromAddressBook(addressBook, scannedAddress) : '' const scannedName = addressBook[scannedAddress]?.name ?? ''
const addressErrorMessage = mustBeEthereumAddress(scannedAddress) const addressErrorMessage = mustBeEthereumAddress(scannedAddress)
if (!addressErrorMessage) { if (!addressErrorMessage) {
mutators.setRecipient(scannedAddress) mutators.setRecipient(scannedAddress)

View File

@ -8,7 +8,6 @@ import { OnChange } from 'react-final-form-listeners'
import { styles } from './style' import { styles } from './style'
import { getNetworkId } from 'src/config'
import { ScanQRWrapper } from 'src/components/ScanQRModal/ScanQRWrapper' import { ScanQRWrapper } from 'src/components/ScanQRModal/ScanQRWrapper'
import AddressInput from 'src/components/forms/AddressInput' import AddressInput from 'src/components/forms/AddressInput'
import Field from 'src/components/forms/Field' import Field from 'src/components/forms/Field'
@ -26,7 +25,7 @@ import Col from 'src/components/layout/Col'
import Hairline from 'src/components/layout/Hairline' import Hairline from 'src/components/layout/Hairline'
import Paragraph from 'src/components/layout/Paragraph' import Paragraph from 'src/components/layout/Paragraph'
import Row from 'src/components/layout/Row' import Row from 'src/components/layout/Row'
import { addressBookMapSelector } from 'src/logic/addressBook/store/selectors' import { currentNetworkAddressBookAsMap } from 'src/logic/addressBook/store/selectors'
import { currentSafe } from 'src/logic/safe/store/selectors' import { currentSafe } from 'src/logic/safe/store/selectors'
import { isValidAddress } from 'src/utils/isValidAddress' import { isValidAddress } from 'src/utils/isValidAddress'
@ -51,8 +50,6 @@ const formMutators: Record<
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
const chainId = getNetworkId()
type OwnerFormProps = { type OwnerFormProps = {
onClose: () => void onClose: () => void
onSubmit: (values) => void onSubmit: (values) => void
@ -64,7 +61,7 @@ export const OwnerForm = ({ onClose, onSubmit, initialValues }: OwnerFormProps):
const handleSubmit = (values) => { const handleSubmit = (values) => {
onSubmit(values) onSubmit(values)
} }
const addressBookMap = useSelector(addressBookMapSelector) const addressBookMap = useSelector(currentNetworkAddressBookAsMap)
const { address: safeAddress = '', owners = [] } = useSelector(currentSafe) ?? {} const { address: safeAddress = '', owners = [] } = useSelector(currentSafe) ?? {}
const ownerDoesntExist = uniqueAddress(owners) const ownerDoesntExist = uniqueAddress(owners)
const ownerAddressIsNotSafeAddress = addressIsNotCurrentSafe(safeAddress) const ownerAddressIsNotSafeAddress = addressIsNotCurrentSafe(safeAddress)
@ -122,7 +119,7 @@ export const OwnerForm = ({ onClose, onSubmit, initialValues }: OwnerFormProps):
<OnChange name="ownerAddress"> <OnChange name="ownerAddress">
{async (address: string) => { {async (address: string) => {
if (isValidAddress(address)) { if (isValidAddress(address)) {
const { name: ownerName } = addressBookMap[chainId][address] const ownerName = addressBookMap[address]?.name
if (ownerName) { if (ownerName) {
mutators.setOwnerName(ownerName) mutators.setOwnerName(ownerName)
} }

View File

@ -24,12 +24,12 @@ import Row from 'src/components/layout/Row'
import { ScanQRWrapper } from 'src/components/ScanQRModal/ScanQRWrapper' import { ScanQRWrapper } from 'src/components/ScanQRModal/ScanQRWrapper'
import { Modal } from 'src/components/Modal' import { Modal } from 'src/components/Modal'
import { currentSafe } from 'src/logic/safe/store/selectors' import { currentSafe } from 'src/logic/safe/store/selectors'
import { addressBookMapSelector } from 'src/logic/addressBook/store/selectors' import { currentNetworkAddressBookAsMap } from 'src/logic/addressBook/store/selectors'
import { OwnerData } from 'src/routes/safe/components/Settings/ManageOwners/dataFetcher' import { OwnerData } from 'src/routes/safe/components/Settings/ManageOwners/dataFetcher'
import { isValidAddress } from 'src/utils/isValidAddress' import { isValidAddress } from 'src/utils/isValidAddress'
import { useStyles } from './style' import { useStyles } from './style'
import { getExplorerInfo, getNetworkId } from 'src/config' import { getExplorerInfo } from 'src/config'
import { EthHashInfo } from '@gnosis.pm/safe-react-components' import { EthHashInfo } from '@gnosis.pm/safe-react-components'
export const REPLACE_OWNER_NAME_INPUT_TEST_ID = 'replace-owner-name-input' export const REPLACE_OWNER_NAME_INPUT_TEST_ID = 'replace-owner-name-input'
@ -50,8 +50,6 @@ const formMutators: Record<
}, },
} }
const chainId = getNetworkId()
type NewOwnerProps = { type NewOwnerProps = {
ownerAddress: string ownerAddress: string
ownerName: string ownerName: string
@ -70,7 +68,7 @@ export const OwnerForm = ({ onClose, onSubmit, owner, initialValues }: OwnerForm
const handleSubmit = (values: NewOwnerProps) => { const handleSubmit = (values: NewOwnerProps) => {
onSubmit(values) onSubmit(values)
} }
const addressBookMap = useSelector(addressBookMapSelector) const addressBookMap = useSelector(currentNetworkAddressBookAsMap)
const { address: safeAddress = '', owners } = useSelector(currentSafe) ?? {} const { address: safeAddress = '', owners } = useSelector(currentSafe) ?? {}
const ownerDoesntExist = uniqueAddress(owners) const ownerDoesntExist = uniqueAddress(owners)
const ownerAddressIsNotSafeAddress = addressIsNotCurrentSafe(safeAddress) const ownerAddressIsNotSafeAddress = addressIsNotCurrentSafe(safeAddress)
@ -149,7 +147,7 @@ export const OwnerForm = ({ onClose, onSubmit, owner, initialValues }: OwnerForm
<OnChange name="ownerAddress"> <OnChange name="ownerAddress">
{async (address: string) => { {async (address: string) => {
if (isValidAddress(address)) { if (isValidAddress(address)) {
const ownerName = addressBookMap?.[chainId]?.[address]?.name const ownerName = addressBookMap[address]?.name
if (ownerName) { if (ownerName) {
mutators.setOwnerName(ownerName) mutators.setOwnerName(ownerName)
} }

View File

@ -6,8 +6,7 @@ import styled from 'styled-components'
import { ScanQRWrapper } from 'src/components/ScanQRModal/ScanQRWrapper' import { ScanQRWrapper } from 'src/components/ScanQRModal/ScanQRWrapper'
import { getExplorerInfo } from 'src/config' import { getExplorerInfo } from 'src/config'
import { addressBookSelector } from 'src/logic/addressBook/store/selectors' import { currentNetworkAddressBook } from 'src/logic/addressBook/store/selectors'
import { getNameFromAddressBook } from 'src/logic/addressBook/utils'
import { AddressBookInput } from 'src/routes/safe/components/Balances/SendModal/screens/AddressBookInput' import { AddressBookInput } from 'src/routes/safe/components/Balances/SendModal/screens/AddressBookInput'
import { sameString } from 'src/utils/strings' import { sameString } from 'src/utils/strings'
@ -40,13 +39,11 @@ const Beneficiary = (): ReactElement => {
} }
}, [mutators, pristine, selectedEntry]) }, [mutators, pristine, selectedEntry])
const addressBook = useSelector(addressBookSelector) const addressBook = useSelector(currentNetworkAddressBook)
const handleScan = (value, closeQrModal) => { const handleScan = (value, closeQrModal) => {
const scannedAddress = value.startsWith('ethereum:') ? value.replace('ethereum:', '') : value const scannedAddress = value.startsWith('ethereum:') ? value.replace('ethereum:', '') : value
const scannedName = addressBook const scannedName = addressBook[scannedAddress]?.name ?? ''
? getNameFromAddressBook(addressBook, scannedAddress, { filterOnlyValidName: true }) ?? ''
: ''
mutators?.setBeneficiary?.(scannedAddress) mutators?.setBeneficiary?.(scannedAddress)

View File

@ -3,7 +3,7 @@ import React, { ReactElement } from 'react'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import { getExplorerInfo } from 'src/config' import { getExplorerInfo } from 'src/config'
import { getNameFromAddressBookSelector } from 'src/logic/addressBook/store/selectors' import { addressBookEntryName } from 'src/logic/addressBook/store/selectors'
import { ADDRESS_BOOK_DEFAULT_NAME } from 'src/logic/addressBook/model/addressBook' import { ADDRESS_BOOK_DEFAULT_NAME } from 'src/logic/addressBook/model/addressBook'
import { sameString } from 'src/utils/strings' import { sameString } from 'src/utils/strings'
@ -15,7 +15,7 @@ interface AddressInfoProps {
} }
const AddressInfo = ({ address, title }: AddressInfoProps): ReactElement => { const AddressInfo = ({ address, title }: AddressInfoProps): ReactElement => {
const name = useSelector((state) => getNameFromAddressBookSelector(state, { address })) const name = useSelector((state) => addressBookEntryName(state, { address }))
const explorerUrl = getExplorerInfo(address) const explorerUrl = getExplorerInfo(address)
return ( return (

View File

@ -3,12 +3,12 @@ import React, { ReactElement } from 'react'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import { getExplorerInfo } from 'src/config' import { getExplorerInfo } from 'src/config'
import { getNameFromAddressBookSelector } from 'src/logic/addressBook/store/selectors' import { addressBookEntryName } from 'src/logic/addressBook/store/selectors'
import { ADDRESS_BOOK_DEFAULT_NAME } from 'src/logic/addressBook/model/addressBook' import { ADDRESS_BOOK_DEFAULT_NAME } from 'src/logic/addressBook/model/addressBook'
import { sameString } from 'src/utils/strings' import { sameString } from 'src/utils/strings'
export const OwnerRow = ({ address }: { address: string }): ReactElement => { export const OwnerRow = ({ address }: { address: string }): ReactElement => {
const ownerName = useSelector((state) => getNameFromAddressBookSelector(state, { address })) const ownerName = useSelector((state) => addressBookEntryName(state, { address }))
return ( return (
<EthHashInfo <EthHashInfo

View File

@ -2,14 +2,14 @@ import { useSelector } from 'react-redux'
import { sameString } from 'src/utils/strings' import { sameString } from 'src/utils/strings'
import { ADDRESS_BOOK_DEFAULT_NAME } from 'src/logic/addressBook/model/addressBook' import { ADDRESS_BOOK_DEFAULT_NAME } from 'src/logic/addressBook/model/addressBook'
import { getNameFromAddressBookSelector } from 'src/logic/addressBook/store/selectors' import { addressBookEntryName } from 'src/logic/addressBook/store/selectors'
type AddressInfo = { name: string | undefined; image: string | undefined } type AddressInfo = { name: string | undefined; image: string | undefined }
type UseKnownAddressResponse = AddressInfo & { isAddressBook: boolean } type UseKnownAddressResponse = AddressInfo & { isAddressBook: boolean }
export const useKnownAddress = (address: string, addressInfo: AddressInfo): UseKnownAddressResponse => { export const useKnownAddress = (address: string, addressInfo: AddressInfo): UseKnownAddressResponse => {
const recipientName = useSelector((state) => getNameFromAddressBookSelector(state, { address })) const recipientName = useSelector((state) => addressBookEntryName(state, { address }))
// We have to check that the name returned is not UNKNOWN // We have to check that the name returned is not UNKNOWN
const isInAddressBook = !sameString(recipientName, ADDRESS_BOOK_DEFAULT_NAME) const isInAddressBook = !sameString(recipientName, ADDRESS_BOOK_DEFAULT_NAME)