diff --git a/src/logic/safe/utils/mocks/remoteConfig.json b/src/logic/safe/utils/mocks/remoteConfig.json index 9d7108985e..03b973ddbc 100644 --- a/src/logic/safe/utils/mocks/remoteConfig.json +++ b/src/logic/safe/utils/mocks/remoteConfig.json @@ -64,59 +64,6 @@ "SPENDING_LIMIT" ] }, - { - "transactionService": "https://safe-transaction.xdai.gnosis.io", - "chainId": "100", - "chainName": "Gnosis Chain", - "shortName": "gno", - "l2": true, - "description": "Gnosis Chain", - "rpcUri": { - "authentication": "NO_AUTHENTICATION", - "value": "https://rpc.xdaichain.com/oe-only/" - }, - "safeAppsRpcUri": { - "authentication": "NO_AUTHENTICATION", - "value": "https://rpc.xdaichain.com/oe-only/" - }, - "publicRpcUri": { - "authentication": "NO_AUTHENTICATION", - "value": "https://rpc.xdaichain.com/oe-only/" - }, - "blockExplorerUriTemplate": { - "address": "https://blockscout.com/xdai/mainnet/address/{{address}}/transactions", - "txHash": "https://blockscout.com/xdai/mainnet/tx/{{txHash}}/", - "api": "https://blockscout.com/poa/xdai/api?module={{module}}&action={{action}}&address={{address}}&apiKey={{apiKey}}" - }, - "nativeCurrency": { - "name": "xDai", - "symbol": "XDAI", - "decimals": 18, - "logoUri": "https://safe-transaction-assets.staging.gnosisdev.com/chains/100/currency_logo.png" - }, - "theme": { - "textColor": "#ffffff", - "backgroundColor": "#48A9A6" - }, - "gasPrice": [ - { - "type": "FIXED", - "weiValue": "4000000000" - } - ], - "disabledWallets": [ - "metamask", - "walletConnect" - ], - "features": [ - "CONTRACT_INTERACTION", - "EIP1559", - "ERC721", - "SAFE_APPS", - "SAFE_TX_GAS_OPTIONAL", - "SPENDING_LIMIT" - ] - }, { "transactionService": "https://safe-transaction-polygon.staging.gnosisdev.com", "chainId": "137", @@ -174,6 +121,7 @@ ], "features": [ "CONTRACT_INTERACTION", + "EIP1559", "ERC721", "SAFE_APPS", "SAFE_TX_GAS_OPTIONAL" diff --git a/src/logic/wallets/getWeb3.ts b/src/logic/wallets/getWeb3.ts index 50655ad6b9..e4d6c8eea9 100644 --- a/src/logic/wallets/getWeb3.ts +++ b/src/logic/wallets/getWeb3.ts @@ -113,6 +113,8 @@ export const reverseENSLookup = async (address: string): Promise => { return verifiedAddress === address ? name : '' } +export const removeTld = (name: string): string => name.replace(/\.[^.]+$/, '') + export const getContentFromENS = (name: string): Promise => web3.eth.ens.getContenthash(name) export const isTxPendingError = (err: Error): boolean => { diff --git a/src/routes/CreateSafePage/CreateSafePage.test.tsx b/src/routes/CreateSafePage/CreateSafePage.test.tsx index 84c3c410df..6331ff0b43 100644 --- a/src/routes/CreateSafePage/CreateSafePage.test.tsx +++ b/src/routes/CreateSafePage/CreateSafePage.test.tsx @@ -20,6 +20,7 @@ const estimateGasForDeployingSafeSpy = jest.spyOn(safeContracts, 'estimateGasFor const calculateGasPriceSpy = jest.spyOn(ethTransactions, 'calculateGasPrice') const getENSAddressSpy = jest.spyOn(getWeb3ReadOnly().eth.ens, 'getAddress') +jest.spyOn(getWeb3ReadOnly().eth.ens, 'getResolver') jest.mock('src/logic/contracts/safeContracts', () => { // Require the original module to not be mocked... diff --git a/src/routes/CreateSafePage/CreateSafePage.tsx b/src/routes/CreateSafePage/CreateSafePage.tsx index 1dc2264751..2c822a8220 100644 --- a/src/routes/CreateSafePage/CreateSafePage.tsx +++ b/src/routes/CreateSafePage/CreateSafePage.tsx @@ -1,4 +1,4 @@ -import { ReactElement, useState, useEffect } from 'react' +import { ReactElement, useEffect, useState } from 'react' import IconButton from '@material-ui/core/IconButton' import ChevronLeft from '@material-ui/icons/ChevronLeft' import styled from 'styled-components' @@ -12,7 +12,7 @@ import Block from 'src/components/layout/Block' import Row from 'src/components/layout/Row' import Heading from 'src/components/layout/Heading' import { history } from 'src/routes/routes' -import { sm, secondary } from 'src/theme/variables' +import { secondary, sm } from 'src/theme/variables' import StepperForm, { StepFormElement } from 'src/components/StepperForm/StepperForm' import NameNewSafeStep, { nameNewSafeStepLabel } from './steps/NameNewSafeStep' import { @@ -22,6 +22,7 @@ import { FIELD_MAX_OWNER_NUMBER, FIELD_NEW_SAFE_PROXY_SALT, FIELD_NEW_SAFE_THRESHOLD, + FIELD_SAFE_OWNER_ENS_LIST, FIELD_SAFE_OWNERS_LIST, SAFE_PENDING_CREATION_STORAGE_KEY, } from './fields/createSafeFields' @@ -143,7 +144,7 @@ function getInitialValues(userAddress, addressBook, location, suggestedSafeName) // we set the owner names const ownersNamesFromUrl = Array.isArray(ownernames) ? ownernames : [ownernames] - const userAddressName = [addressBook[userAddress]?.name || 'My Wallet'] + const userAddressName = [addressBook[userAddress]?.name || ''] const ownerNames = isOwnersPresentInTheUrl ? ownersNamesFromUrl : userAddressName const thresholdFromURl = Number(threshold) @@ -158,6 +159,7 @@ function getInitialValues(userAddress, addressBook, location, suggestedSafeName) nameFieldName: `owner-name-${index}`, addressFieldName: `owner-address-${index}`, })), + [FIELD_SAFE_OWNER_ENS_LIST]: {}, // we set owners address values as owner-address-${index} format in the form state ...owners.reduce( (ownerAddressFields, ownerAddress, index) => ({ diff --git a/src/routes/CreateSafePage/components/SafeCreationProcess.tsx b/src/routes/CreateSafePage/components/SafeCreationProcess.tsx index 67a21b114e..8e83e9ff9a 100644 --- a/src/routes/CreateSafePage/components/SafeCreationProcess.tsx +++ b/src/routes/CreateSafePage/components/SafeCreationProcess.tsx @@ -23,6 +23,7 @@ import { FIELD_CREATE_CUSTOM_SAFE_NAME, FIELD_NEW_SAFE_PROXY_SALT, FIELD_NEW_SAFE_GAS_PRICE, + FIELD_SAFE_OWNER_ENS_LIST, } from '../fields/createSafeFields' import { getSafeInfo } from 'src/logic/safe/utils/safeInformation' import { buildSafe } from 'src/logic/safe/store/actions/fetchSafe' @@ -172,13 +173,14 @@ function SafeCreationProcess(): ReactElement { const owners = createSafeFormValues[FIELD_SAFE_OWNERS_LIST] // we update the address book with the owners and the new safe - const ownersAddressBookEntry = owners.map(({ nameFieldName, addressFieldName }) => - makeAddressBookEntry({ + const ownersAddressBookEntry = owners.map(({ nameFieldName, addressFieldName }) => { + const ownerAddress = createSafeFormValues[addressFieldName] + return makeAddressBookEntry({ address: createSafeFormValues[addressFieldName], - name: createSafeFormValues[nameFieldName], + name: createSafeFormValues[nameFieldName] || createSafeFormValues[FIELD_SAFE_OWNER_ENS_LIST][ownerAddress], chainId, - }), - ) + }) + }) const safeAddressBookEntry = makeAddressBookEntry({ address: newSafeAddress, name: safeName, chainId }) await dispatch(addressBookSafeLoad([...ownersAddressBookEntry, safeAddressBookEntry])) diff --git a/src/routes/CreateSafePage/fields/createSafeFields.tsx b/src/routes/CreateSafePage/fields/createSafeFields.tsx index a12b01103c..c7157b983d 100644 --- a/src/routes/CreateSafePage/fields/createSafeFields.tsx +++ b/src/routes/CreateSafePage/fields/createSafeFields.tsx @@ -1,6 +1,7 @@ export const FIELD_CREATE_CUSTOM_SAFE_NAME = 'customSafeName' export const FIELD_CREATE_SUGGESTED_SAFE_NAME = 'suggestedSafeName' export const FIELD_SAFE_OWNERS_LIST = 'owners' +export const FIELD_SAFE_OWNER_ENS_LIST = 'safeOwnerENSList' export const FIELD_NEW_SAFE_THRESHOLD = 'newSafeThreshold' export const FIELD_MAX_OWNER_NUMBER = 'maxOwnerNumber' export const FIELD_NEW_SAFE_PROXY_SALT = 'safeCreationSalt' @@ -18,6 +19,7 @@ export type CreateSafeFormValues = { [FIELD_CREATE_CUSTOM_SAFE_NAME]?: string [FIELD_NEW_SAFE_THRESHOLD]: number [FIELD_SAFE_OWNERS_LIST]: Array + [FIELD_SAFE_OWNER_ENS_LIST]: Record [FIELD_MAX_OWNER_NUMBER]: number [FIELD_NEW_SAFE_PROXY_SALT]: number [FIELD_NEW_SAFE_GAS_LIMIT]: number diff --git a/src/routes/CreateSafePage/steps/NameNewSafeStep.tsx b/src/routes/CreateSafePage/steps/NameNewSafeStep.tsx index f57c5bcf0f..00067c073b 100644 --- a/src/routes/CreateSafePage/steps/NameNewSafeStep.tsx +++ b/src/routes/CreateSafePage/steps/NameNewSafeStep.tsx @@ -1,4 +1,4 @@ -import { ReactElement, useEffect } from 'react' +import { ReactElement, useEffect, useState } from 'react' import { useSelector } from 'react-redux' import { useForm } from 'react-final-form' import styled from 'styled-components' @@ -10,13 +10,20 @@ import Paragraph from 'src/components/layout/Paragraph' import Field from 'src/components/forms/Field' import TextField from 'src/components/forms/TextField' import { providerNameSelector } from 'src/logic/wallets/store/selectors' -import { FIELD_CREATE_CUSTOM_SAFE_NAME, FIELD_CREATE_SUGGESTED_SAFE_NAME } from '../fields/createSafeFields' +import { + FIELD_CREATE_CUSTOM_SAFE_NAME, + FIELD_CREATE_SUGGESTED_SAFE_NAME, + FIELD_SAFE_OWNER_ENS_LIST, + FIELD_SAFE_OWNERS_LIST, +} from '../fields/createSafeFields' import { useStepper } from 'src/components/Stepper/stepperContext' import NetworkLabel from 'src/components/NetworkLabel/NetworkLabel' +import { removeTld, reverseENSLookup } from 'src/logic/wallets/getWeb3' export const nameNewSafeStepLabel = 'Name' function NameNewSafeStep(): ReactElement { + const [ownersWithENSName, setOwnersWithENSName] = useState>({}) const provider = useSelector(providerNameSelector) const { setCurrentStep } = useStepper() @@ -28,9 +35,42 @@ function NameNewSafeStep(): ReactElement { }, [provider, setCurrentStep]) const createNewSafeForm = useForm() - const formValues = createNewSafeForm.getState().values + useEffect(() => { + const getInitialOwnerENSNames = async () => { + const formValues = createNewSafeForm.getState().values + const owners = formValues[FIELD_SAFE_OWNERS_LIST] + const ownersWithENSName = await Promise.all( + owners.map(async ({ addressFieldName }) => { + const address = formValues[addressFieldName] + const ensName = await reverseENSLookup(address) + const ensDomain = removeTld(ensName) + return { + address, + name: ensDomain, + } + }), + ) + + const ownersWithENSNameRecord = ownersWithENSName.reduce>((acc, { address, name }) => { + return { + ...acc, + [address]: name, + } + }, {}) + + setOwnersWithENSName(ownersWithENSNameRecord) + } + getInitialOwnerENSNames() + }, [createNewSafeForm]) + + useEffect(() => { + if (ownersWithENSName) { + createNewSafeForm.change(FIELD_SAFE_OWNER_ENS_LIST, ownersWithENSName) + } + }, [ownersWithENSName, createNewSafeForm]) + return ( diff --git a/src/routes/CreateSafePage/steps/OwnersAndConfirmationsNewSafeStep.tsx b/src/routes/CreateSafePage/steps/OwnersAndConfirmationsNewSafeStep.tsx index 20fed60665..ed47105989 100644 --- a/src/routes/CreateSafePage/steps/OwnersAndConfirmationsNewSafeStep.tsx +++ b/src/routes/CreateSafePage/steps/OwnersAndConfirmationsNewSafeStep.tsx @@ -29,10 +29,16 @@ import { required, THRESHOLD_ERROR, } from 'src/components/forms/validator' -import { FIELD_MAX_OWNER_NUMBER, FIELD_NEW_SAFE_THRESHOLD, FIELD_SAFE_OWNERS_LIST } from '../fields/createSafeFields' +import { + FIELD_MAX_OWNER_NUMBER, + FIELD_NEW_SAFE_THRESHOLD, + FIELD_SAFE_OWNER_ENS_LIST, + FIELD_SAFE_OWNERS_LIST, +} from '../fields/createSafeFields' import { ScanQRWrapper } from 'src/components/ScanQRModal/ScanQRWrapper' import { currentNetworkAddressBookAsMap } from 'src/logic/addressBook/store/selectors' import NetworkLabel from 'src/components/NetworkLabel/NetworkLabel' +import { removeTld, reverseENSLookup } from 'src/logic/wallets/getWeb3' export const ownersAndConfirmationsNewSafeStepLabel = 'Owners and Confirmations' @@ -53,6 +59,7 @@ function OwnersAndConfirmationsNewSafeStep(): ReactElement { const formErrors = createSafeForm.getState().errors || {} const owners = createSafeFormValues[FIELD_SAFE_OWNERS_LIST] + const ownersWithENSName = createSafeFormValues[FIELD_SAFE_OWNER_ENS_LIST] const threshold = createSafeFormValues[FIELD_NEW_SAFE_THRESHOLD] const maxOwnerNumber = createSafeFormValues[FIELD_MAX_OWNER_NUMBER] @@ -68,16 +75,27 @@ function OwnersAndConfirmationsNewSafeStep(): ReactElement { function onClickRemoveOwner({ addressFieldName }) { const ownersUpdated = owners.filter((owner) => owner.addressFieldName !== addressFieldName) - createSafeForm.change(FIELD_SAFE_OWNERS_LIST, ownersUpdated) + createSafeForm.change(addressFieldName, undefined) - const hasToUpdateThreshold = threshold > ownersUpdated.length + const updatedMaxOwnerNumbers = maxOwnerNumber - 1 + createSafeForm.change(FIELD_MAX_OWNER_NUMBER, updatedMaxOwnerNumbers) + const hasToUpdateThreshold = threshold > ownersUpdated.length if (hasToUpdateThreshold) { createSafeForm.change(FIELD_NEW_SAFE_THRESHOLD, threshold - 1) } } + const getENSName = async (address: string): Promise => { + const ensName = await reverseENSLookup(address) + const ensDomain = removeTld(ensName) + const newOwnersWithENSName: Record = Object.assign(ownersWithENSName, { + [address]: ensDomain, + }) + createSafeForm.change(FIELD_SAFE_OWNER_ENS_LIST, newOwnersWithENSName) + } + return ( <> @@ -113,9 +131,12 @@ function OwnersAndConfirmationsNewSafeStep(): ReactElement { {owners.map(({ nameFieldName, addressFieldName }) => { const hasOwnerAddressError = formErrors[addressFieldName] + const ownerAddress = createSafeFormValues[addressFieldName] const showDeleteIcon = addressFieldName !== 'owner-address-0' // we hide de delete icon for the first owner + const ownerName = ownersWithENSName[ownerAddress] || 'Owner Name' - const handleScan = (address: string, closeQrModal: () => void): void => { + const handleScan = async (address: string, closeQrModal: () => void): Promise => { + await getENSName(address) createSafeForm.change(addressFieldName, address) closeQrModal() } @@ -126,7 +147,7 @@ function OwnersAndConfirmationsNewSafeStep(): ReactElement { { + fieldMutator={async (address) => { + await getENSName(address) createSafeForm.change(addressFieldName, address) const addressName = addressBook[address]?.name if (addressName) { diff --git a/src/routes/CreateSafePage/steps/ReviewNewSafeStep.tsx b/src/routes/CreateSafePage/steps/ReviewNewSafeStep.tsx index b54f7c2a0f..0a988c7525 100644 --- a/src/routes/CreateSafePage/steps/ReviewNewSafeStep.tsx +++ b/src/routes/CreateSafePage/steps/ReviewNewSafeStep.tsx @@ -17,6 +17,7 @@ import { FIELD_NEW_SAFE_GAS_PRICE, FIELD_NEW_SAFE_PROXY_SALT, FIELD_NEW_SAFE_THRESHOLD, + FIELD_SAFE_OWNER_ENS_LIST, FIELD_SAFE_OWNERS_LIST, } from '../fields/createSafeFields' import { getExplorerInfo, getNativeCurrency } from 'src/config' @@ -46,6 +47,7 @@ function ReviewNewSafeStep(): ReactElement | null { const safeName = createSafeFormValues[FIELD_CREATE_CUSTOM_SAFE_NAME] || defaultSafeValue const threshold = createSafeFormValues[FIELD_NEW_SAFE_THRESHOLD] const owners = createSafeFormValues[FIELD_SAFE_OWNERS_LIST] + const ownersWithENSName = createSafeFormValues[FIELD_SAFE_OWNER_ENS_LIST] const numberOfOwners = owners.length const safeCreationSalt = createSafeFormValues[FIELD_NEW_SAFE_PROXY_SALT] const ownerAddresses = owners.map(({ addressFieldName }) => createSafeFormValues[addressFieldName]) @@ -110,8 +112,8 @@ function ReviewNewSafeStep(): ReactElement | null { {owners.map(({ nameFieldName, addressFieldName }) => { - const ownerName = createSafeFormValues[nameFieldName] const ownerAddress = createSafeFormValues[addressFieldName] + const ownerName = createSafeFormValues[nameFieldName] || ownersWithENSName[ownerAddress] return ( diff --git a/src/routes/LoadSafePage/LoadSafePage.test.tsx b/src/routes/LoadSafePage/LoadSafePage.test.tsx index dd078572a7..1d78026e5f 100644 --- a/src/routes/LoadSafePage/LoadSafePage.test.tsx +++ b/src/routes/LoadSafePage/LoadSafePage.test.tsx @@ -9,6 +9,7 @@ import * as safeVersion from 'src/logic/safe/utils/safeVersion' import { GATEWAY_URL } from 'src/utils/constants' const getENSAddressSpy = jest.spyOn(getWeb3ReadOnly().eth.ens, 'getAddress') +jest.spyOn(getWeb3ReadOnly().eth.ens, 'getResolver') jest.spyOn(safeVersion, 'getSafeVersionInfo').mockImplementation(async () => ({ current: '1.3.0', diff --git a/src/routes/LoadSafePage/LoadSafePage.tsx b/src/routes/LoadSafePage/LoadSafePage.tsx index 7c150f0bd6..40e55034cd 100644 --- a/src/routes/LoadSafePage/LoadSafePage.tsx +++ b/src/routes/LoadSafePage/LoadSafePage.tsx @@ -30,13 +30,14 @@ import { FIELD_LOAD_IS_LOADING_SAFE_ADDRESS, FIELD_LOAD_SAFE_ADDRESS, FIELD_LOAD_SUGGESTED_SAFE_NAME, + FIELD_SAFE_OWNER_ENS_LIST, FIELD_SAFE_OWNER_LIST, LoadSafeFormValues, } from './fields/loadFields' import { extractPrefixedSafeAddress, generateSafeRoute, LOAD_SPECIFIC_SAFE_ROUTE, SAFE_ROUTES } from '../routes' import { getShortName } from 'src/config' import { currentNetworkAddressBookAsMap } from 'src/logic/addressBook/store/selectors' -import { getLoadSafeName } from './fields/utils' +import { getLoadSafeName, getOwnerName } from './fields/utils' import { currentChainId } from 'src/logic/config/store/selectors' function Load(): ReactElement { @@ -54,6 +55,7 @@ function Load(): ReactElement { [FIELD_LOAD_SAFE_ADDRESS]: safeAddress, [FIELD_LOAD_IS_LOADING_SAFE_ADDRESS]: false, [FIELD_SAFE_OWNER_LIST]: [], + [FIELD_SAFE_OWNER_ENS_LIST]: {}, } setInitialFormValues(initialValues) }, [safeAddress, safeRandomName]) @@ -63,11 +65,10 @@ function Load(): ReactElement { const ownerEntries = ownerList .map((owner) => { - const ownerFieldName = `owner-address-${owner.address}` - const ownerNameValue = values[ownerFieldName] + const ownerName = getOwnerName(values, owner.address) return { ...owner, - name: ownerNameValue, + name: ownerName, } }) .filter((owner) => !!owner.name) diff --git a/src/routes/LoadSafePage/fields/loadFields.tsx b/src/routes/LoadSafePage/fields/loadFields.tsx index a3bc058541..b8fdae41aa 100644 --- a/src/routes/LoadSafePage/fields/loadFields.tsx +++ b/src/routes/LoadSafePage/fields/loadFields.tsx @@ -3,6 +3,7 @@ export const FIELD_LOAD_SUGGESTED_SAFE_NAME = 'suggestedSafeName' export const FIELD_LOAD_SAFE_ADDRESS = 'safeAddress' export const FIELD_LOAD_IS_LOADING_SAFE_ADDRESS = 'isLoadingSafeAddress' export const FIELD_SAFE_OWNER_LIST = 'safeOwnerList' +export const FIELD_SAFE_OWNER_ENS_LIST = 'safeOwnerENSList' export const FIELD_SAFE_THRESHOLD = 'safeThreshold' export type OwnerFieldListItem = { @@ -16,5 +17,6 @@ export type LoadSafeFormValues = { [FIELD_LOAD_SAFE_ADDRESS]?: string [FIELD_LOAD_IS_LOADING_SAFE_ADDRESS]: boolean [FIELD_SAFE_OWNER_LIST]: Array + [FIELD_SAFE_OWNER_ENS_LIST]: Record [FIELD_SAFE_THRESHOLD]?: number } diff --git a/src/routes/LoadSafePage/fields/utils.ts b/src/routes/LoadSafePage/fields/utils.ts index a1cc98e465..0799ab9240 100644 --- a/src/routes/LoadSafePage/fields/utils.ts +++ b/src/routes/LoadSafePage/fields/utils.ts @@ -4,6 +4,7 @@ import { FIELD_LOAD_CUSTOM_SAFE_NAME, FIELD_LOAD_SAFE_ADDRESS, FIELD_LOAD_SUGGESTED_SAFE_NAME, + FIELD_SAFE_OWNER_ENS_LIST, LoadSafeFormValues, } from './loadFields' @@ -17,3 +18,8 @@ export function getLoadSafeName(formValues: LoadSafeFormValues, addressBook: Add formValues[FIELD_LOAD_SUGGESTED_SAFE_NAME] ) } + +export function getOwnerName(formValues: LoadSafeFormValues, address: string): string { + const ownerFieldName = `owner-address-${address}` + return formValues[ownerFieldName] || formValues[FIELD_SAFE_OWNER_ENS_LIST][address] +} diff --git a/src/routes/LoadSafePage/steps/LoadSafeAddressStep.tsx b/src/routes/LoadSafePage/steps/LoadSafeAddressStep.tsx index 76766ca716..ceb088fb35 100644 --- a/src/routes/LoadSafePage/steps/LoadSafeAddressStep.tsx +++ b/src/routes/LoadSafePage/steps/LoadSafeAddressStep.tsx @@ -22,6 +22,7 @@ import { FIELD_LOAD_CUSTOM_SAFE_NAME, FIELD_LOAD_IS_LOADING_SAFE_ADDRESS, FIELD_LOAD_SAFE_ADDRESS, + FIELD_SAFE_OWNER_ENS_LIST, FIELD_SAFE_OWNER_LIST, FIELD_SAFE_THRESHOLD, LoadSafeFormValues, @@ -29,11 +30,13 @@ import { import NetworkLabel from 'src/components/NetworkLabel/NetworkLabel' import { getLoadSafeName } from '../fields/utils' import { currentChainId } from 'src/logic/config/store/selectors' +import { removeTld, reverseENSLookup } from 'src/logic/wallets/getWeb3' export const loadSafeAddressStepLabel = 'Name and address' function LoadSafeAddressStep(): ReactElement { const [ownersWithName, setOwnersWithName] = useState([]) + const [ownersWithENSName, setOwnersWithENSName] = useState>({}) const [threshold, setThreshold] = useState() const [isValidSafeAddress, setIsValidSafeAddress] = useState(false) const [isSafeInfoLoading, setIsSafeInfoLoading] = useState(false) @@ -64,10 +67,27 @@ function LoadSafeAddressStep(): ReactElement { try { const { owners, threshold } = await getSafeInfo(safeAddress) setIsSafeInfoLoading(false) - const ownersWithName = owners.map(({ value: address }) => - makeAddressBookEntry(addressBook[address] || { address, name: '', chainId }), + const ownersWithName = owners.map(({ value: address }) => { + return makeAddressBookEntry(addressBook[address] || { address, name: '', chainId }) + }) + + const ownersWithENSName = await Promise.all( + owners.map(async ({ value: address }) => { + const ensName = await reverseENSLookup(address) + const ensDomain = removeTld(ensName) + return makeAddressBookEntry({ address, name: ensDomain, chainId }) + }), ) + + const ownersWithENSNameRecord = ownersWithENSName.reduce>((acc, { address, name }) => { + return { + ...acc, + [address]: name, + } + }, {}) + setOwnersWithName(ownersWithName) + setOwnersWithENSName(ownersWithENSNameRecord) setThreshold(threshold) setIsValidSafeAddress(true) } catch (error) { @@ -97,6 +117,12 @@ function LoadSafeAddressStep(): ReactElement { } }, [ownersWithName, loadSafeForm]) + useEffect(() => { + if (ownersWithENSName) { + loadSafeForm.change(FIELD_SAFE_OWNER_ENS_LIST, ownersWithENSName) + } + }, [ownersWithENSName, loadSafeForm]) + const handleScan = (value: string, closeQrModal: () => void): void => { loadSafeForm.change(FIELD_LOAD_SAFE_ADDRESS, value) closeQrModal() diff --git a/src/routes/LoadSafePage/steps/LoadSafeOwnersStep.tsx b/src/routes/LoadSafePage/steps/LoadSafeOwnersStep.tsx index 47881136db..8d6bfd0cba 100644 --- a/src/routes/LoadSafePage/steps/LoadSafeOwnersStep.tsx +++ b/src/routes/LoadSafePage/steps/LoadSafeOwnersStep.tsx @@ -14,20 +14,22 @@ import PrefixedEthHashInfo from 'src/components/PrefixedEthHashInfo' import { disabled, extraSmallFontSize, lg, md, sm } from 'src/theme/variables' import { minMaxLength } from 'src/components/forms/validator' import { getExplorerInfo } from 'src/config' -import { FIELD_SAFE_OWNER_LIST } from '../fields/loadFields' +import { FIELD_SAFE_OWNER_ENS_LIST, FIELD_SAFE_OWNER_LIST, LoadSafeFormValues } from '../fields/loadFields' import NetworkLabel from 'src/components/NetworkLabel/NetworkLabel' export const loadSafeOwnersStepLabel = 'Owners' function LoadSafeOwnersStep(): ReactElement { const loadSafeForm = useForm() - const ownersWithName = loadSafeForm.getState().values[FIELD_SAFE_OWNER_LIST] + + const formValues = loadSafeForm.getState().values as LoadSafeFormValues + const ownerList = formValues[FIELD_SAFE_OWNER_LIST] return ( <> - This Safe on has {ownersWithName.length} owners. Optional: Provide a name for each owner. + This Safe on has {ownerList.length} owners. Optional: Provide a name for each owner. @@ -38,8 +40,10 @@ function LoadSafeOwnersStep(): ReactElement { - {ownersWithName.map(({ address, name }, index) => { + {ownerList.map(({ address, name }, index) => { const ownerFieldName = `owner-address-${address}` + const ownerName = formValues[FIELD_SAFE_OWNER_ENS_LIST][address] || 'Owner Name' + return ( @@ -47,7 +51,7 @@ function LoadSafeOwnersStep(): ReactElement { component={TextField} initialValue={name} name={ownerFieldName} - placeholder="Owner Name" + placeholder={ownerName} text="Owner Name" type="text" validate={minMaxLength(0, 50)} diff --git a/src/routes/LoadSafePage/steps/ReviewLoadStep.tsx b/src/routes/LoadSafePage/steps/ReviewLoadStep.tsx index 457fdbfc70..b962d059e0 100644 --- a/src/routes/LoadSafePage/steps/ReviewLoadStep.tsx +++ b/src/routes/LoadSafePage/steps/ReviewLoadStep.tsx @@ -19,7 +19,7 @@ import { FIELD_SAFE_THRESHOLD, LoadSafeFormValues, } from '../fields/loadFields' -import { getLoadSafeName } from '../fields/utils' +import { getLoadSafeName, getOwnerName } from '../fields/utils' import NetworkLabel from 'src/components/NetworkLabel/NetworkLabel' import { currentNetworkAddressBookAsMap } from 'src/logic/addressBook/store/selectors' @@ -37,11 +37,10 @@ function ReviewLoadStep(): ReactElement { const ownerList = formValues[FIELD_SAFE_OWNER_LIST] const ownerListWithNames = ownerList.map((owner) => { - const ownerFieldName = `owner-address-${owner.address}` - const ownerNameValue = formValues[ownerFieldName] + const ownerName = getOwnerName(formValues, owner.address) return { ...owner, - name: ownerNameValue, + name: ownerName, } })