(Feature) #588 Use addressBook names when creating/loading safe (#1377)

* Fix addressbook types
Restructure addressbook store type

* Add more safe types

* Fix imports

* Removes .toJS() usage

* Fix condition for saving addressBook

* Types & remove send button from addressbook if user not an owner

* Add types for addressBook actions
Remove unused saveAndUpdateAddressBook action

* Refactor addressBook: make it global and removes immutableJS
Removes unused addAddressBook action

* Fix edit and remove entries style when user is not owner

* Adds and updates safe name in addressBook

* Adds checkIfOwnerWasDeletedFromAddressBook
Let the user remove owners users without adding them again each time the safe loads

* Simplify loadAddressBookFromStorage

* Fix compilation errors included in pr #1301

* Uses sameAddress function

* Add migration function for old stored address books

* Replaces shouldAvoidUpdatesNotifications with addAddressBookEntryOptions on addAddressBookEntry

* Unify return on getOwnersWithNameFromAddressBook

* Adds the addressbook names in safe load

* Reword shouldAvoidUpdatesNotifications

* Replaces adbk with addressBook

* Renames adbk to AddressBook

* Types on Open and Layout

* Remove unused actions and selectors

* Replaces initialValuesFrom to a hook and retrieves the ownerName

* Uses addressBook names in safe creation

* Fix owner name on creating safe

* Renames getNameFromAddressBook to getNameFromAddressBookSelector

* Fixs addOrUpdateAddressBookEntry action

* Updates addressbook on safe load

* Revert load update addressbook behaviour

* Renames checkIfOwnerWasDeletedFromAddressBook to checkIfEntryWasDeletedFromAddressBook

* Feedback

* Type review informaiton

* Adds ADD_OR_UPDATE_SAFE action

* Replaces addSafe with addOrUpdateSafe on addSafeHandler

* Exports isValidAddressBookName util function

* Adds isValidAddressBookName test

* Add tests for checkIfEntryWasDeletedFromAddressBook

* Fix saveAddressBook test

* Fix fetchSafeTokens.test.ts

* Add update individually safe props in addOrUpdate

* Fix updating addressbook entries on safe load/create

* Fix always loading safe as LOADED SAFE instead of safe name

* Fix adding owner as UNKNOWN on addressBook when adding new owner

Co-authored-by: Daniel Sanchez <daniel.sanchez@gnosis.pm>
This commit is contained in:
Agustin Pane 2020-09-23 15:14:49 -03:00 committed by GitHub
parent d1348713ad
commit 6f707a632b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 487 additions and 253 deletions

View File

@ -11,8 +11,8 @@ import { getValidAddressBookName } from 'src/logic/addressBook/utils'
export const ADDRESS_BOOK_REDUCER_ID = 'addressBook'
export const buildAddressBook = (storedAdbk: AddressBookState): AddressBookState => {
return storedAdbk.map((addressBookEntry) => {
export const buildAddressBook = (storedAddressBook: AddressBookState): AddressBookState => {
return storedAddressBook.map((addressBookEntry) => {
const { address, name } = addressBookEntry
return makeAddressBookEntry({ address: checksumAddress(address), name })
})
@ -51,14 +51,16 @@ export default handleActions(
return state
},
[ADD_OR_UPDATE_ENTRY]: (state, action) => {
const { entry, entryAddress } = action.payload
const { entry } = action.payload
// Only updates entries with valid names
const validName = getValidAddressBookName(entry.name)
if (!validName) {
return state
}
const entryIndex = state.findIndex((oldEntry) => oldEntry.address === entryAddress)
const entryIndex = state.findIndex((oldEntry) => oldEntry.address === entry.address)
if (entryIndex >= 0) {
state[entryIndex] = entry
} else {

View File

@ -8,7 +8,7 @@ import { AddressBookState } from 'src/logic/addressBook/model/addressBook'
export const addressBookSelector = (state: AppReduxState): AddressBookState => state[ADDRESS_BOOK_REDUCER_ID]
export const getNameFromAddressBook = createSelector(
export const getNameFromAddressBookSelector = createSelector(
addressBookSelector,
(_, address) => address,
(addressBook, address) => {

View File

@ -1,9 +1,11 @@
import { List } from 'immutable'
import {
checkIfEntryWasDeletedFromAddressBook,
getAddressBookFromStorage,
getAddressesListFromAddressBook,
getNameFromAddressBook,
getOwnersWithNameFromAddressBook,
isValidAddressBookName,
migrateOldAddressBook,
OldAddressBookEntry,
OldAddressBookType,
@ -85,6 +87,7 @@ describe('getOwnersWithNameFromAddressBook', () => {
})
})
jest.mock('src/utils/storage/index')
describe('saveAddressBook', () => {
const mockAdd1 = '0x696fd93D725d84acfFf6c62a1fe8C94E1c9E934A'
const mockAdd2 = '0x2C7aC78b01Be0FC66AD29b684ffAb0C93B381D00'
@ -92,19 +95,27 @@ describe('saveAddressBook', () => {
const entry1 = getMockAddressBookEntry(mockAdd1, 'test1')
const entry2 = getMockAddressBookEntry(mockAdd2, 'test2')
const entry3 = getMockAddressBookEntry(mockAdd3, 'test3')
afterAll(() => {
jest.unmock('src/utils/storage/index')
})
it('It should save a given addressBook to the localStorage', async () => {
// given
const addressBook: AddressBookState = [entry1, entry2, entry3]
// when
// @ts-ignore
await saveAddressBook(addressBook)
const storedAdBk = await getAddressBookFromStorage()
const storageUtils = require('src/utils/storage/index')
const spy = storageUtils.loadFromStorage.mockImplementationOnce(() => JSON.stringify(addressBook))
const storedAddressBook = await getAddressBookFromStorage()
// @ts-ignore
let result = buildAddressBook(storedAdBk)
let result = buildAddressBook(storedAddressBook)
// then
expect(result).toStrictEqual(addressBook)
expect(spy).toHaveBeenCalled()
})
})
@ -136,18 +147,19 @@ describe('migrateOldAddressBook', () => {
})
})
jest.mock('src/utils/storage/index')
describe('getAddressBookFromStorage', () => {
const safeAddress1 = '0x696fd93D725d84acfFf6c62a1fe8C94E1c9E934A'
const safeAddress2 = '0x2C7aC78b01Be0FC66AD29b684ffAb0C93B381D00'
const mockAdd1 = '0x9163c2F4452E3399CB60AAf737231Af87548DA91'
const mockAdd2 = '0xC4e446Da9C3D37385C86488294C6758c4e25dbD8'
beforeAll(() => {
jest.mock('src/utils/storage/index')
})
afterAll(() => {
jest.unmock('src/utils/storage/index')
})
it('It should return null if no addressBook in storage', async () => {
// given
const expectedResult = null
const storageUtils = require('src/utils/storage/index')
const spy = storageUtils.loadFromStorage.mockImplementationOnce(() => null)
@ -199,3 +211,102 @@ describe('getAddressBookFromStorage', () => {
expect(spy).toHaveBeenCalled()
})
})
describe('isValidAddressBookName', () => {
it('It should return false if given a blacklisted name like UNKNOWN', () => {
// given
const addressNameInput = 'UNKNOWN'
const expectedResult = false
// when
const result = isValidAddressBookName(addressNameInput)
// then
expect(result).toStrictEqual(expectedResult)
})
it('It should return false if given a blacklisted name like MY WALLET', () => {
// given
const addressNameInput = 'MY WALLET'
const expectedResult = false
// when
const result = isValidAddressBookName(addressNameInput)
// then
expect(result).toStrictEqual(expectedResult)
})
it('It should return false if given a blacklisted name like OWNER #', () => {
// given
const addressNameInput = 'OWNER #'
const expectedResult = false
// when
const result = isValidAddressBookName(addressNameInput)
// then
expect(result).toStrictEqual(expectedResult)
})
it('It should return true if the given address name is valid', () => {
// given
const addressNameInput = 'User'
const expectedResult = true
// when
const result = isValidAddressBookName(addressNameInput)
// then
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,6 +1,6 @@
import { List } from 'immutable'
import { loadFromStorage, saveToStorage } from 'src/utils/storage'
import { AddressBookState, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
import { AddressBookEntry, AddressBookState, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
import { SafeOwner } from 'src/logic/safe/store/models/safe'
import { sameAddress } from 'src/logic/wallets/ethAddresses'
@ -16,6 +16,8 @@ export type OldAddressBookType = {
[safeAddress: string]: [OldAddressBookEntry]
}
const ADDRESSBOOK_INVALID_NAMES = ['UNKNOWN', 'OWNER #', 'MY WALLET']
export const migrateOldAddressBook = (oldAddressBook: OldAddressBookType): AddressBookState => {
const values: AddressBookState = []
const adbkValues = Object.values(oldAddressBook)
@ -56,19 +58,31 @@ export const saveAddressBook = async (addressBook: AddressBookState): Promise<vo
export const getAddressesListFromAddressBook = (addressBook: AddressBookState): string[] =>
addressBook.map((entry) => entry.address)
export const getNameFromAddressBook = (addressBook: AddressBookState, userAddress: string): string | null => {
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 entry.name
return options?.filterOnlyValidName ? getValidAddressBookName(entry.name) : entry.name
}
return null
}
export const getValidAddressBookName = (addressbookName: string): string | null => {
const INVALID_NAMES = ['UNKNOWN', 'OWNER #', 'MY WALLET']
const isInvalid = INVALID_NAMES.find((invalidName) => addressbookName.toUpperCase().includes(invalidName))
if (isInvalid) return null
return addressbookName
export const isValidAddressBookName = (addressBookName: string): boolean => {
const hasInvalidName = ADDRESSBOOK_INVALID_NAMES.find((invalidName) =>
addressBookName.toUpperCase().includes(invalidName),
)
return !hasInvalidName
}
export const getValidAddressBookName = (addressBookName: string): string | null => {
return isValidAddressBookName(addressBookName) ? addressBookName : null
}
export const getOwnersWithNameFromAddressBook = (
@ -86,3 +100,41 @@ export const getOwnersWithNameFromAddressBook = (
}
})
}
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 || '',
}
})
}
/**
* If the safe is not loaded, the owner wasn't not 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
}

View File

@ -48,6 +48,8 @@ describe('fetchTokenCurrenciesBalances', () => {
// then
expect(result).toStrictEqual(expectedResult)
expect(axios.get).toHaveBeenCalled()
expect(axios.get).toBeCalledWith(`${apiUrl}safes/${safeAddress}/balances/usd/`, { params: { limit: 3000 } })
expect(axios.get).toBeCalledWith(`${apiUrl}safes/${safeAddress}/balances/usd/?exclude_spam=true`, {
params: { limit: 3000 },
})
})
})

View File

@ -22,13 +22,13 @@ export const useLoadSafe = (safeAddress?: string): void => {
return dispatch(fetchSafeTokens(safeAddress))
})
.then(() => {
dispatch(loadAddressBookFromStorage())
dispatch(fetchSafeCreationTx(safeAddress))
dispatch(fetchTransactions(safeAddress))
return dispatch(addViewedSafe(safeAddress))
})
}
}
dispatch(loadAddressBookFromStorage())
fetchData()
}, [dispatch, safeAddress])

View File

@ -0,0 +1,9 @@
import { createAction } from 'redux-actions'
import { SafeRecordProps } from '../models/safe'
export const ADD_OR_UPDATE_SAFE = 'ADD_OR_UPDATE_SAFE'
export const addOrUpdateSafe = createAction(ADD_OR_UPDATE_SAFE, (safe: SafeRecordProps) => ({
safe,
}))

View File

@ -119,6 +119,7 @@ export const checkAndUpdateSafe = (safeAdd: string) => async (dispatch: Dispatch
dispatch(
updateSafe({
address: safeAddress,
name: localSafe?.name,
modules: buildModulesLinkedList(modules?.array, modules?.next),
nonce: Number(remoteNonce),
threshold: Number(remoteThreshold),

View File

@ -13,16 +13,19 @@ import { SET_DEFAULT_SAFE } from 'src/logic/safe/store/actions/setDefaultSafe'
import { UPDATE_SAFE } from 'src/logic/safe/store/actions/updateSafe'
import { getActiveTokensAddressesForAllSafes, safesMapSelector } from 'src/logic/safe/store/selectors'
import { checksumAddress } from 'src/utils/checksumAddress'
import { AddressBookEntry, AddressBookState, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
import { makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
import { addOrUpdateAddressBookEntry } from 'src/logic/addressBook/store/actions/addOrUpdateAddressBookEntry'
import { getValidAddressBookName } from 'src/logic/addressBook/utils'
import { checkIfEntryWasDeletedFromAddressBook, isValidAddressBookName } from 'src/logic/addressBook/utils'
import { addressBookSelector } from 'src/logic/addressBook/store/selectors'
import { sameAddress } from 'src/logic/wallets/ethAddresses'
import { updateAddressBookEntry } from 'src/logic/addressBook/store/actions/updateAddressBookEntry'
import { ADD_OR_UPDATE_SAFE } from 'src/logic/safe/store/actions/addOrUpdateSafe'
const watchedActions = [
ADD_SAFE,
UPDATE_SAFE,
REMOVE_SAFE,
ADD_OR_UPDATE_SAFE,
ADD_SAFE_OWNER,
REMOVE_SAFE_OWNER,
REPLACE_SAFE_OWNER,
@ -46,30 +49,6 @@ const recalculateActiveTokens = (state) => {
saveActiveTokens(activeTokens)
}
/**
* If the owner has a valid name that means that should be on the addressBook
* if the owner is not currently on the addressBook, that means the user deleted it
* or that it's a new safe with valid names, so we also check if it's a new safe or an already loaded one
* @param name
* @param address
* @param addressBook
* @param safeAlreadyLoaded -> true if the safe was loaded from the localStorage
*/
// TODO TEST
const checkIfOwnerWasDeletedFromAddressBook = (
{ name, address }: AddressBookEntry,
addressBook: AddressBookState,
safeAlreadyLoaded: boolean,
) => {
if (!safeAlreadyLoaded) {
return false
}
const addressShouldBeOnTheAddressBook = !!getValidAddressBookName(name)
const isAlreadyInAddressBook = !!addressBook.find((entry) => sameAddress(entry.address, address))
return addressShouldBeOnTheAddressBook && !isAlreadyInAddressBook
}
const safeStorageMware = (store) => (next) => async (action) => {
const handledAction = next(action)
@ -87,22 +66,30 @@ const safeStorageMware = (store) => (next) => async (action) => {
}
case ADD_SAFE: {
const { safe, loadedFromStorage } = action.payload
const safeAlreadyLoaded =
loadedFromStorage || safes.find((safeIterator) => sameAddress(safeIterator.address, safe.address))
safe.owners.forEach((owner) => {
const checksumEntry = makeAddressBookEntry({ address: checksumAddress(owner.address), name: owner.name })
const ownerWasAlreadyInAddressBook = checkIfOwnerWasDeletedFromAddressBook(
const ownerWasAlreadyInAddressBook = checkIfEntryWasDeletedFromAddressBook(
checksumEntry,
addressBook,
loadedFromStorage,
safeAlreadyLoaded,
)
if (!ownerWasAlreadyInAddressBook) {
dispatch(addAddressBookEntry(checksumEntry, { notifyEntryUpdate: false }))
}
const addressAlreadyExists = addressBook.find((entry) => sameAddress(entry.address, checksumEntry.address))
if (isValidAddressBookName(checksumEntry.name) && addressAlreadyExists) {
dispatch(updateAddressBookEntry(checksumEntry))
}
})
const safeWasAlreadyInAddressBook = checkIfOwnerWasDeletedFromAddressBook(
const safeWasAlreadyInAddressBook = checkIfEntryWasDeletedFromAddressBook(
{ address: safe.address, name: safe.name },
addressBook,
loadedFromStorage,
safeAlreadyLoaded,
)
if (!safeWasAlreadyInAddressBook) {
@ -114,13 +101,23 @@ const safeStorageMware = (store) => (next) => async (action) => {
}
break
}
case ADD_OR_UPDATE_SAFE: {
const { safe } = action.payload
safe.owners.forEach((owner) => {
const checksumEntry = makeAddressBookEntry({ address: checksumAddress(owner.address), name: owner.name })
if (isValidAddressBookName(checksumEntry.name)) {
dispatch(addOrUpdateAddressBookEntry(checksumEntry))
}
})
break
}
case UPDATE_SAFE: {
const { activeTokens, name, address } = action.payload
if (activeTokens) {
recalculateActiveTokens(state)
}
if (name) {
dispatch(addOrUpdateAddressBookEntry(makeAddressBookEntry({ name, address })), { notifyEntryUpdate: false })
dispatch(addOrUpdateAddressBookEntry(makeAddressBookEntry({ name, address })))
}
break
}

View File

@ -15,6 +15,7 @@ import { makeOwner } from 'src/logic/safe/store/models/owner'
import makeSafe, { SafeRecordProps } from 'src/logic/safe/store/models/safe'
import { checksumAddress } from 'src/utils/checksumAddress'
import { SafeReducerMap } from 'src/routes/safe/store/reducer/types/safe'
import { ADD_OR_UPDATE_SAFE } from 'src/logic/safe/store/actions/addOrUpdateSafe'
export const SAFE_REDUCER_ID = 'safes'
export const DEFAULT_SAFE_INITIAL_STATE = 'NOT_ASKED'
@ -42,6 +43,32 @@ export const buildSafe = (storedSafe: SafeRecordProps): SafeRecordProps => {
}
}
const updateSafeProps = (prevSafe, safe) => {
return prevSafe.withMutations((record) => {
// Every property is updated individually to overcome the issue with nested data being overwritten
const safeProperties = Object.keys(safe)
// We check each safe property sent in action.payload
safeProperties.forEach((key) => {
if (safe[key] && typeof safe[key] === 'object') {
if (safe[key].length) {
// If type is array we update the array
record.update(key, () => safe[key])
} else if (safe[key].size) {
// If type is Immutable List we replace current List
// If type is Object we do a merge
List.isList(safe[key])
? record.update(key, (current) => current.set(safe[key]))
: record.update(key, (current) => current.merge(safe[key]))
}
} else {
// By default we overwrite the value. This is for strings, numbers and unset values
record.set(key, safe[key])
}
})
})
}
export default handleActions(
{
[UPDATE_SAFE]: (state: SafeReducerMap, action) => {
@ -50,32 +77,8 @@ export default handleActions(
return state.updateIn(
['safes', safeAddress],
makeSafe({ name: 'LOADED SAFE', address: safeAddress }),
(prevSafe) => {
return prevSafe.withMutations((record) => {
// Every property is updated individually to overcome the issue with nested data being overwritten
const safeProperties = Object.keys(safe)
// We check each safe property sent in action.payload
safeProperties.forEach((key) => {
if (safe[key] && typeof safe[key] === 'object') {
if (safe[key].length) {
// If type is array we update the array
record.update(key, () => safe[key])
} else if (safe[key].size) {
// If type is Immutable List we replace current List
// If type is Object we do a merge
List.isList(safe[key])
? record.update(key, (current) => current.set(safe[key]))
: record.update(key, (current) => current.merge(safe[key]))
}
} else {
// By default we overwrite the value. This is for strings, numbers and unset values
record.set(key, safe[key])
}
})
})
},
makeSafe({ name: safe?.name || 'LOADED SAFE', address: safeAddress }),
(prevSafe) => updateSafeProps(prevSafe, safe),
)
},
[ACTIVATE_TOKEN_FOR_ALL_SAFES]: (state: SafeReducerMap, action) => {
@ -106,6 +109,19 @@ export default handleActions(
return state.setIn(['safes', safe.address], makeSafe(safe))
},
[ADD_OR_UPDATE_SAFE]: (state: SafeReducerMap, action) => {
const { safe } = action.payload
if (!state.hasIn(['safes', safe.address])) {
return state.setIn(['safes', safe.address], makeSafe(safe))
}
return state.updateIn(
['safes', safe.address],
makeSafe({ name: 'LOADED SAFE', address: safe.address }),
(prevSafe) => updateSafeProps(prevSafe, safe),
)
},
[REMOVE_SAFE]: (state: SafeReducerMap, action) => {
const safeAddress = action.payload

View File

@ -1,5 +1,5 @@
import TableContainer from '@material-ui/core/TableContainer'
import { withStyles } from '@material-ui/core/styles'
import { makeStyles } from '@material-ui/core/styles'
import React, { useEffect, useState } from 'react'
import CopyBtn from 'src/components/CopyBtn'
@ -17,57 +17,13 @@ import Row from 'src/components/layout/Row'
import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts'
import { FIELD_LOAD_ADDRESS, THRESHOLD } from 'src/routes/load/components/fields'
import { getOwnerAddressBy, getOwnerNameBy } from 'src/routes/open/components/fields'
import { border, disabled, extraSmallFontSize, lg, md, screenSm, sm } from 'src/theme/variables'
const styles = () => ({
details: {
padding: lg,
borderRight: `solid 1px ${border}`,
height: '100%',
},
owners: {
display: 'flex',
justifyContent: 'flex-start',
},
ownerName: {
marginBottom: '15px',
minWidth: '100%',
[`@media (min-width: ${screenSm}px)`]: {
marginBottom: '0',
minWidth: '0',
},
},
ownerAddresses: {
alignItems: 'center',
marginLeft: `${sm}`,
},
address: {
paddingLeft: '6px',
marginRight: sm,
},
open: {
paddingLeft: sm,
width: 'auto',
'&:hover': {
cursor: 'pointer',
},
},
title: {
padding: `${md} ${lg}`,
},
owner: {
padding: `0 ${lg}`,
marginBottom: '12px',
},
header: {
padding: `${sm} ${lg}`,
color: disabled,
fontSize: extraSmallFontSize,
},
name: {
marginRight: `${sm}`,
},
})
import { useSelector } from 'react-redux'
import { addressBookSelector } from 'src/logic/addressBook/store/selectors'
import { formatAddressListToAddressBookNames } from 'src/logic/addressBook/utils'
import { AddressBookEntry } from 'src/logic/addressBook/model/addressBook'
import { styles } from './styles'
const calculateSafeValues = (owners, threshold, values) => {
const initialValues = { ...values }
@ -78,9 +34,20 @@ const calculateSafeValues = (owners, threshold, values) => {
return initialValues
}
const useAddressBookForOwnersNames = (ownersList: string[]): AddressBookEntry[] => {
const addressBook = useSelector(addressBookSelector)
return formatAddressListToAddressBookNames(addressBook, ownersList)
}
const useStyles = makeStyles(styles)
const OwnerListComponent = (props) => {
const [owners, setOwners] = useState<string[]>([])
const { classes, updateInitialProps, values } = props
const classes = useStyles()
const { updateInitialProps, values } = props
const ownersWithNames = useAddressBookForOwnersNames(owners)
useEffect(() => {
let isCurrent = true
@ -121,47 +88,48 @@ const OwnerListComponent = (props) => {
</Row>
<Hairline />
<Block margin="md" padding="md">
{owners.map((address, index) => (
<Row className={classes.owner} key={address} data-testid="owner-row">
<Col className={classes.ownerName} xs={4}>
<Field
className={classes.name}
component={TextField}
initialValue={`Owner #${index + 1}`}
name={getOwnerNameBy(index)}
placeholder="Owner Name*"
text="Owner Name"
type="text"
validate={composeValidators(required, minMaxLength(1, 50))}
testId={`load-safe-owner-name-${index}`}
/>
</Col>
<Col xs={8}>
<Row className={classes.ownerAddresses}>
<Identicon address={address} diameter={32} />
<Paragraph className={classes.address} color="disabled" noMargin size="md">
{address}
</Paragraph>
<CopyBtn content={address} />
<EtherscanBtn type="address" value={address} />
</Row>
</Col>
</Row>
))}
{ownersWithNames.map(({ address, name }, index) => {
const ownerName = name || `Owner #${index + 1}`
return (
<Row className={classes.owner} key={address} data-testid="owner-row">
<Col className={classes.ownerName} xs={4}>
<Field
className={classes.name}
component={TextField}
initialValue={ownerName}
name={getOwnerNameBy(index)}
placeholder="Owner Name*"
text="Owner Name"
type="text"
validate={composeValidators(required, minMaxLength(1, 50))}
testId={`load-safe-owner-name-${index}`}
/>
</Col>
<Col xs={8}>
<Row className={classes.ownerAddresses}>
<Identicon address={address} diameter={32} />
<Paragraph className={classes.address} color="disabled" noMargin size="md">
{address}
</Paragraph>
<CopyBtn content={address} />
<EtherscanBtn type="address" value={address} />
</Row>
</Col>
</Row>
)
})}
</Block>
</TableContainer>
</>
)
}
const OwnerListPage = withStyles(styles as any)(OwnerListComponent)
const OwnerList = ({ updateInitialProps }, network) =>
function LoadSafeOwnerList(controls, { values }): React.ReactElement {
return (
<>
<OpenPaper controls={controls} padding={false}>
<OwnerListPage network={network} updateInitialProps={updateInitialProps} values={values} />
<OwnerListComponent network={network} updateInitialProps={updateInitialProps} values={values} />
</OpenPaper>
</>
)

View File

@ -0,0 +1,52 @@
import { border, disabled, extraSmallFontSize, lg, md, screenSm, sm } from 'src/theme/variables'
import { createStyles } from '@material-ui/core'
export const styles = createStyles({
details: {
padding: lg,
borderRight: `solid 1px ${border}`,
height: '100%',
},
owners: {
display: 'flex',
justifyContent: 'flex-start',
},
ownerName: {
marginBottom: '15px',
minWidth: '100%',
[`@media (min-width: ${screenSm}px)`]: {
marginBottom: '0',
minWidth: '0',
},
},
ownerAddresses: {
alignItems: 'center',
marginLeft: `${sm}`,
},
address: {
paddingLeft: '6px',
marginRight: sm,
},
open: {
paddingLeft: sm,
width: 'auto',
'&:hover': {
cursor: 'pointer',
},
},
title: {
padding: `${md} ${lg}`,
},
owner: {
padding: `0 ${lg}`,
marginBottom: '12px',
},
header: {
padding: `${sm} ${lg}`,
color: disabled,
fontSize: extraSmallFontSize,
},
name: {
marginRight: `${sm}`,
},
})

View File

@ -14,8 +14,8 @@ import { history } from 'src/store'
import { SafeOwner, SafeRecordProps } from 'src/logic/safe/store/models/safe'
import { List } from 'immutable'
import { checksumAddress } from 'src/utils/checksumAddress'
import { addSafe } from 'src/logic/safe/store/actions/addSafe'
import { networkSelector, providerNameSelector, userAccountSelector } from 'src/logic/wallets/store/selectors'
import { addOrUpdateSafe } from 'src/logic/safe/store/actions/addOrUpdateSafe'
export const loadSafe = async (
safeName: string,
@ -46,8 +46,8 @@ const Load = (): React.ReactElement => {
const network = useSelector(networkSelector)
const userAddress = useSelector(userAccountSelector)
const addSafeHandler = (safe: SafeRecordProps) => {
dispatch(addSafe(safe))
const addSafeHandler = async (safe: SafeRecordProps) => {
await dispatch(addOrUpdateSafe(safe))
}
const onLoadSafeSubmit = async (values: LoadFormValues) => {
let safeAddress = values[FIELD_LOAD_ADDRESS]

View File

@ -19,22 +19,43 @@ import {
import Welcome from 'src/routes/welcome/components/Layout'
import { history } from 'src/store'
import { secondary, sm } from 'src/theme/variables'
import { networkSelector, providerNameSelector, userAccountSelector } from 'src/logic/wallets/store/selectors'
import { useSelector } from 'react-redux'
import { addressBookSelector } from 'src/logic/addressBook/store/selectors'
import { getNameFromAddressBook } from 'src/logic/addressBook/utils'
const { useEffect } = React
const getSteps = () => ['Name', 'Owners and confirmations', 'Review']
const initialValuesFrom = (userAccount, safeProps) => {
type SafeProps = {
name: string
ownerAddresses: any
ownerNames: string
threshold: string
}
type InitialValuesForm = {
owner0Address?: string
owner0Name?: string
confirmations: string
safeName?: string
}
const useInitialValuesFrom = (userAccount: string, safeProps?: SafeProps): InitialValuesForm => {
const addressBook = useSelector(addressBookSelector)
const ownerName = getNameFromAddressBook(addressBook, userAccount, { filterOnlyValidName: true })
if (!safeProps) {
return {
[getOwnerNameBy(0)]: 'My Wallet',
[getOwnerNameBy(0)]: ownerName || 'My Wallet',
[getOwnerAddressBy(0)]: userAccount,
[FIELD_CONFIRMATIONS]: '1',
}
}
let obj = {}
const { name, ownerAddresses, ownerNames, threshold } = safeProps
// eslint-disable-next-line no-restricted-syntax
for (const [index, value] of ownerAddresses.entries()) {
const safeName = ownerNames[index] ? ownerNames[index] : 'My Wallet'
obj = {
@ -66,8 +87,17 @@ const formMutators = {
},
}
const Layout = (props) => {
const { network, onCallSafeContractSubmit, provider, safeProps, userAccount } = props
type LayoutProps = {
onCallSafeContractSubmit: (formValues: unknown) => void
safeProps?: SafeProps
}
const Layout = (props: LayoutProps): React.ReactElement => {
const { onCallSafeContractSubmit, safeProps } = props
const provider = useSelector(providerNameSelector)
const network = useSelector(networkSelector)
const userAccount = useSelector(userAccountSelector)
useEffect(() => {
if (provider) {
@ -77,7 +107,7 @@ const Layout = (props) => {
const steps = getSteps()
const initialValues = initialValuesFrom(userAccount, safeProps)
const initialValues = useInitialValuesFrom(userAccount, safeProps)
return (
<>

View File

@ -1,5 +1,5 @@
import TableContainer from '@material-ui/core/TableContainer'
import { withStyles } from '@material-ui/core/styles'
import { createStyles, makeStyles } from '@material-ui/core/styles'
import classNames from 'classnames'
import * as React from 'react'
@ -22,7 +22,7 @@ import { background, border, lg, screenSm, sm } from 'src/theme/variables'
const { useEffect, useState } = React
const styles = () => ({
const styles = createStyles({
root: {
minHeight: '300px',
[`@media (min-width: ${screenSm}px)`]: {
@ -84,7 +84,15 @@ const styles = () => ({
},
})
const ReviewComponent = ({ classes, userAccount, values }: any) => {
const useStyles = makeStyles(styles)
type ReviewComponentProps = {
userAccount: string
values: any
}
const ReviewComponent = ({ userAccount, values }: ReviewComponentProps) => {
const classes = useStyles()
const [gasCosts, setGasCosts] = useState('< 0.001')
const names = getNamesFrom(values)
const addresses = getAccountsFrom(values)
@ -198,12 +206,11 @@ const ReviewComponent = ({ classes, userAccount, values }: any) => {
)
}
const ReviewPage = withStyles(styles as any)(ReviewComponent)
const Review = () => (controls, { values }) => (
// eslint-disable-next-line react/display-name
const Review = () => (controls, props): React.ReactElement => (
<>
<OpenPaper controls={controls} padding={false}>
<ReviewPage values={values} />
<ReviewComponent {...props} />
</OpenPaper>
</>
)

View File

@ -1,10 +1,8 @@
import InputAdornment from '@material-ui/core/InputAdornment'
import MenuItem from '@material-ui/core/MenuItem'
import { withStyles } from '@material-ui/core/styles'
import { makeStyles } from '@material-ui/core/styles'
import CheckCircle from '@material-ui/icons/CheckCircle'
import * as React from 'react'
import { withRouter } from 'react-router-dom'
import { styles } from './style'
import { getAddressValidator } from './validators'
@ -38,6 +36,9 @@ import {
getOwnerNameBy,
} from 'src/routes/open/components/fields'
import { getAccountsFrom } from 'src/routes/open/utils/safeDataExtractor'
import { useSelector } from 'react-redux'
import { addressBookSelector } from 'src/logic/addressBook/store/selectors'
import { getNameFromAddressBook } from 'src/logic/addressBook/utils'
const { useState } = React
@ -63,10 +64,14 @@ export const calculateValuesAfterRemoving = (index, notRemovedOwners, values) =>
return initialValues
}
const SafeOwners = (props) => {
const { classes, errors, form, otherAccounts, values } = props
const useStyles = makeStyles(styles)
const SafeOwnersForm = (props): React.ReactElement => {
const { errors, form, otherAccounts, values } = props
const classes = useStyles()
const validOwners = getNumOwnersFrom(values)
const addressBook = useSelector(addressBookSelector)
const [numOwners, setNumOwners] = useState(validOwners)
const [qrModalOpen, setQrModalOpen] = useState(false)
@ -125,6 +130,7 @@ const SafeOwners = (props) => {
<Block margin="md" padding="md">
{[...Array(Number(numOwners))].map((x, index) => {
const addressName = getOwnerAddressBy(index)
const ownerName = getOwnerNameBy(index)
return (
<Row className={classes.owner} key={`owner${index}`} data-testid={`create-safe-owner-row`}>
@ -132,7 +138,7 @@ const SafeOwners = (props) => {
<Field
className={classes.name}
component={TextField}
name={getOwnerNameBy(index)}
name={ownerName}
placeholder="Owner Name*"
text="Owner Name"
type="text"
@ -142,8 +148,14 @@ const SafeOwners = (props) => {
</Col>
<Col className={classes.ownerAddress} xs={6}>
<AddressInput
fieldMutator={(val) => {
form.mutators.setValue(addressName, val)
fieldMutator={(newOwnerAddress) => {
const newOwnerName = getNameFromAddressBook(addressBook, newOwnerAddress, {
filterOnlyValidName: true,
})
form.mutators.setValue(addressName, newOwnerAddress)
if (newOwnerName) {
form.mutators.setValue(ownerName, newOwnerName)
}
}}
// eslint-disable-next-line
// @ts-ignore
@ -224,8 +236,6 @@ const SafeOwners = (props) => {
)
}
const SafeOwnersForm = withStyles(styles as any)(withRouter(SafeOwners))
const SafeOwnersPage = ({ updateInitialProps }) =>
function OpenSafeOwnersPage(controls, { errors, form, values }) {
return (

View File

@ -1,6 +1,7 @@
import { disabled, extraSmallFontSize, lg, md, screenSm, sm } from 'src/theme/variables'
import { createStyles } from '@material-ui/core'
export const styles = () => ({
export const styles = createStyles({
root: {
display: 'flex',
},

View File

@ -2,15 +2,9 @@ import { Loader } from '@gnosis.pm/safe-react-components'
import queryString from 'query-string'
import React, { useEffect, useState } from 'react'
import ReactGA from 'react-ga'
import { connect } from 'react-redux'
import { withRouter, RouteComponentProps } from 'react-router-dom'
import Opening from '../../opening'
import Layout from '../components/Layout'
import actions from './actions'
import selector from './selector'
import { useDispatch, useSelector } from 'react-redux'
import Opening from 'src/routes/opening'
import Layout from 'src/routes/open/components/Layout'
import Page from 'src/components/layout/Page'
import { getSafeDeploymentTransaction } from 'src/logic/contracts/safeContracts'
import { checkReceiptStatus } from 'src/logic/wallets/ethTransactions'
@ -25,6 +19,9 @@ import { SAFELIST_ADDRESS, WELCOME_ADDRESS } from 'src/routes/routes'
import { buildSafe } from 'src/logic/safe/store/actions/fetchSafe'
import { history } from 'src/store'
import { loadFromStorage, removeFromStorage, saveToStorage } from 'src/utils/storage'
import { userAccountSelector } from 'src/logic/wallets/store/selectors'
import { SafeRecordProps } from 'src/logic/safe/store/models/safe'
import { addOrUpdateSafe } from 'src/logic/safe/store/actions/addOrUpdateSafe'
const SAFE_PENDING_CREATION_STORAGE_KEY = 'SAFE_PENDING_CREATION_STORAGE_KEY'
@ -39,13 +36,15 @@ const validateQueryParams = (ownerAddresses, ownerNames, threshold, safeName) =>
if (Number.isNaN(Number(threshold))) {
return false
}
if (threshold > ownerAddresses.length) {
return false
}
return true
return threshold <= ownerAddresses.length
}
export const getSafeProps = async (safeAddress, safeName, ownersNames, ownerAddresses) => {
export const getSafeProps = async (
safeAddress: string,
safeName: string,
ownersNames: string[],
ownerAddresses: string[],
): Promise<SafeRecordProps> => {
const safeProps = await buildSafe(safeAddress, safeName)
const owners = getOwnersFrom(ownersNames, ownerAddresses)
safeProps.owners = owners
@ -81,19 +80,14 @@ export const createSafe = (values, userAccount) => {
return promiEvent
}
interface OwnProps extends RouteComponentProps {
userAccount: string
network: string
provider: string
addSafe: any
}
const Open = ({ addSafe, network, provider, userAccount }: OwnProps): React.ReactElement => {
const Open = (): React.ReactElement => {
const [loading, setLoading] = useState(false)
const [showProgress, setShowProgress] = useState(false)
const [creationTxPromise, setCreationTxPromise] = useState()
const [safeCreationPendingInfo, setSafeCreationPendingInfo] = useState<any>()
const [safePropsFromUrl, setSafePropsFromUrl] = useState()
const userAccount = useSelector(userAccountSelector)
const dispatch = useDispatch()
useEffect(() => {
// #122: Allow to migrate an old Multisig by passing the parameters to the URL.
@ -141,14 +135,15 @@ const Open = ({ addSafe, network, provider, userAccount }: OwnProps): React.Reac
setShowProgress(true)
}
const onSafeCreated = async (safeAddress) => {
const onSafeCreated = async (safeAddress): Promise<void> => {
const pendingCreation = await loadFromStorage<{ txHash: string }>(SAFE_PENDING_CREATION_STORAGE_KEY)
const name = getSafeNameFrom(pendingCreation)
const ownersNames = getNamesFrom(pendingCreation)
const ownerAddresses = getAccountsFrom(pendingCreation)
const safeProps = await getSafeProps(safeAddress, name, ownersNames, ownerAddresses)
addSafe(safeProps)
await dispatch(addOrUpdateSafe(safeProps))
ReactGA.event({
category: 'User',
@ -194,21 +189,14 @@ const Open = ({ addSafe, network, provider, userAccount }: OwnProps): React.Reac
creationTxHash={safeCreationPendingInfo?.txHash}
onCancel={onCancel}
onRetry={onRetry}
onSuccess={onSafeCreated as any}
provider={provider}
onSuccess={onSafeCreated}
submittedPromise={creationTxPromise}
/>
) : (
<Layout
network={network}
onCallSafeContractSubmit={createSafeProxy}
provider={provider}
safeProps={safePropsFromUrl}
userAccount={userAccount}
/>
<Layout onCallSafeContractSubmit={createSafeProxy} safeProps={safePropsFromUrl} />
)}
</Page>
)
}
export default connect(selector, actions)(withRouter(Open))
export default Open

View File

@ -1,5 +0,0 @@
import addSafe from 'src/logic/safe/store/actions/addSafe'
export default {
addSafe,
}

View File

@ -1,9 +0,0 @@
import { createStructuredSelector } from 'reselect'
import { networkSelector, providerNameSelector, userAccountSelector } from 'src/logic/wallets/store/selectors'
export default createStructuredSelector({
provider: providerNameSelector,
network: networkSelector,
userAccount: userAccountSelector,
})

View File

@ -2,7 +2,7 @@ import { Loader, Stepper } from '@gnosis.pm/safe-react-components'
import React, { useEffect, useState } from 'react'
import styled from 'styled-components'
import { ErrorFooter } from './components/Footer'
import { ErrorFooter } from 'src/routes/opening/components/Footer'
import { isConfirmationStep, steps } from './steps'
import Button from 'src/components/layout/Button'
@ -13,6 +13,8 @@ import { initContracts } from 'src/logic/contracts/safeContracts'
import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions'
import { getWeb3 } from 'src/logic/wallets/getWeb3'
import { background, connected } from 'src/theme/variables'
import { providerNameSelector } from 'src/logic/wallets/store/selectors'
import { useSelector } from 'react-redux'
const loaderDotsSvg = require('./assets/loader-dots.svg')
const successSvg = require('./assets/success.svg')
@ -102,16 +104,17 @@ const BackButton = styled(Button)`
// onCancel: () => void
// }
const SafeDeployment = ({ creationTxHash, onCancel, onRetry, onSuccess, provider, submittedPromise }: any) => {
const SafeDeployment = ({ creationTxHash, onCancel, onRetry, onSuccess, submittedPromise }): React.ReactElement => {
const [loading, setLoading] = useState(true)
const [stepIndex, setStepIndex] = useState(0)
const [safeCreationTxHash, setSafeCreationTxHash] = useState('')
const [createdSafeAddress, setCreatedSafeAddress] = useState()
const [createdSafeAddress, setCreatedSafeAddress] = useState('')
const [error, setError] = useState(false)
const [intervalStarted, setIntervalStarted] = useState(false)
const [waitingSafeDeployed, setWaitingSafeDeployed] = useState(false)
const [continueButtonDisabled, setContinueButtonDisabled] = useState(false)
const provider = useSelector(providerNameSelector)
const confirmationStep = isConfirmationStep(stepIndex)

View File

@ -16,6 +16,7 @@ import createTransaction from 'src/logic/safe/store/actions/createTransaction'
import { safeOwnersSelector, safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
import { checksumAddress } from 'src/utils/checksumAddress'
import { makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
const styles = () => ({
biggerModalWindow: {
@ -92,7 +93,7 @@ const AddOwner = ({ classes, closeSnackbar, enqueueSnackbar, isOpen, onClose })
try {
await sendAddOwner(values, safeAddress, owners, enqueueSnackbar, closeSnackbar, dispatch)
dispatch(
addOrUpdateAddressBookEntry(values.ownerAddress, { name: values.ownerName, address: values.ownerAddress }),
addOrUpdateAddressBookEntry(makeAddressBookEntry({ name: values.ownerName, address: values.ownerAddress })),
)
} catch (error) {
console.error('Error while removing an owner', error)

View File

@ -14,6 +14,7 @@ import createTransaction from 'src/logic/safe/store/actions/createTransaction'
import replaceSafeOwner from 'src/logic/safe/store/actions/replaceSafeOwner'
import { safeParamAddressFromStateSelector, safeThresholdSelector } from 'src/logic/safe/store/selectors'
import { checksumAddress } from 'src/utils/checksumAddress'
import { makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
const styles = () => ({
biggerModalWindow: {
@ -96,10 +97,7 @@ const ReplaceOwner = ({ classes, closeSnackbar, enqueueSnackbar, isOpen, onClose
await sendReplaceOwner(values, safeAddress, ownerAddress, enqueueSnackbar, closeSnackbar, threshold, dispatch)
dispatch(
// Needs the `address` field because we need to provide the minimum required values to ADD a new entry
// The reducer will update all the addressBooks stored, so we cannot decide what to do beforehand,
// thus, we pass the minimum required fields (name and address)
addOrUpdateAddressBookEntry(values.ownerAddress, { name: values.ownerName, address: values.ownerAddress }),
addOrUpdateAddressBookEntry(makeAddressBookEntry({ address: values.ownerAddress, name: values.ownerName })),
)
} catch (error) {
console.error('Error while removing an owner', error)

View File

@ -5,7 +5,7 @@ import { useSelector } from 'react-redux'
import EtherscanLink from 'src/components/EtherscanLink'
import Block from 'src/components/layout/Block'
import Bold from 'src/components/layout/Bold'
import { getNameFromAddressBook } from 'src/logic/addressBook/store/selectors'
import { getNameFromAddressBookSelector } from 'src/logic/addressBook/store/selectors'
import OwnerAddressTableCell from 'src/routes/safe/components/Settings/ManageOwners/OwnerAddressTableCell'
import { getIncomingTxAmount } from 'src/routes/safe/components/Transactions/TxsTable/columns'
import { lg, md } from 'src/theme/variables'
@ -35,7 +35,7 @@ const TransferDescription = ({ from, txFromName, value = '' }) => (
const IncomingTxDescription = ({ tx }) => {
const classes = useStyles()
const txFromName = useSelector((state) => getNameFromAddressBook(state, tx.from))
const txFromName = useSelector((state) => getNameFromAddressBookSelector(state, tx.from))
return (
<Block className={classes.txDataContainer}>
<TransferDescription from={tx.from} txFromName={txFromName} value={getIncomingTxAmount(tx, false)} />

View File

@ -16,7 +16,7 @@ import { styles } from './style'
import Block from 'src/components/layout/Block'
import Button from 'src/components/layout/Button'
import Img from 'src/components/layout/Img'
import { getNameFromAddressBook } from 'src/logic/addressBook/store/selectors'
import { getNameFromAddressBookSelector } from 'src/logic/addressBook/store/selectors'
import { OwnersWithoutConfirmations } from './index'
export const CONFIRM_TX_BTN_TEST_ID = 'confirm-btn'
@ -64,7 +64,7 @@ const OwnerComponent = (props: OwnerComponentProps): React.ReactElement => {
showExecuteRejectBtn,
confirmed,
} = props
const nameInAdbk = useSelector((state) => getNameFromAddressBook(state, owner))
const nameInAdbk = useSelector((state) => getNameFromAddressBookSelector(state, owner))
const classes = useStyles()
const [imgCircle, setImgCircle] = React.useState(ConfirmSmallGreyCircle)

View File

@ -15,7 +15,7 @@ import Bold from 'src/components/layout/Bold'
import { humanReadableValue } from 'src/logic/tokens/utils/humanReadableValue'
import Collapse from 'src/components/Collapse'
import { useSelector } from 'react-redux'
import { getNameFromAddressBook } from 'src/logic/addressBook/store/selectors'
import { getNameFromAddressBookSelector } from 'src/logic/addressBook/store/selectors'
import Paragraph from 'src/components/layout/Paragraph'
import LinkWithRef from 'src/components/layout/Link'
import { shortVersionOf } from 'src/logic/wallets/ethAddresses'
@ -176,7 +176,7 @@ interface GenericCustomDataProps {
const GenericCustomData = ({ amount = '0', data, recipient, storedTx }: GenericCustomDataProps): React.ReactElement => {
const classes = useStyles()
const recipientName = useSelector((state) => getNameFromAddressBook(state, recipient))
const recipientName = useSelector((state) => getNameFromAddressBookSelector(state, recipient))
return (
<Block>

View File

@ -1,7 +1,7 @@
import { useSelector } from 'react-redux'
import React from 'react'
import { getNameFromAddressBook } from 'src/logic/addressBook/store/selectors'
import { getNameFromAddressBookSelector } from 'src/logic/addressBook/store/selectors'
import Block from 'src/components/layout/Block'
import Bold from 'src/components/layout/Bold'
import OwnerAddressTableCell from 'src/routes/safe/components/Settings/ManageOwners/OwnerAddressTableCell'
@ -21,7 +21,7 @@ interface RemovedOwnerProps {
}
const RemovedOwner = ({ removedOwner }: RemovedOwnerProps): React.ReactElement => {
const ownerChangedName = useSelector((state) => getNameFromAddressBook(state, removedOwner))
const ownerChangedName = useSelector((state) => getNameFromAddressBookSelector(state, removedOwner))
return (
<Block data-testid={TRANSACTIONS_DESC_REMOVE_OWNER_TEST_ID}>
@ -40,7 +40,7 @@ interface AddedOwnerProps {
}
const AddedOwner = ({ addedOwner }: AddedOwnerProps): React.ReactElement => {
const ownerChangedName = useSelector((state) => getNameFromAddressBook(state, addedOwner))
const ownerChangedName = useSelector((state) => getNameFromAddressBookSelector(state, addedOwner))
return (
<Block data-testid={TRANSACTIONS_DESC_ADD_OWNER_TEST_ID}>

View File

@ -2,7 +2,7 @@ import React from 'react'
import { useSelector } from 'react-redux'
import { TRANSACTIONS_DESC_SEND_TEST_ID } from './index'
import { getNameFromAddressBook } from 'src/logic/addressBook/store/selectors'
import { getNameFromAddressBookSelector } from 'src/logic/addressBook/store/selectors'
import Block from 'src/components/layout/Block'
import Bold from 'src/components/layout/Bold'
import OwnerAddressTableCell from 'src/routes/safe/components/Settings/ManageOwners/OwnerAddressTableCell'
@ -14,7 +14,7 @@ interface TransferDescriptionProps {
}
const TransferDescription = ({ amount = '', recipient }: TransferDescriptionProps): React.ReactElement => {
const recipientName = useSelector((state) => getNameFromAddressBook(state, recipient))
const recipientName = useSelector((state) => getNameFromAddressBookSelector(state, recipient))
return (
<Block data-testid={TRANSACTIONS_DESC_SEND_TEST_ID}>
<Bold>Send {amount} to:</Bold>