From c22f495695e3ad045594c4868850601b64770480 Mon Sep 17 00:00:00 2001 From: Jagoda Berry Rybacka Date: Fri, 4 Nov 2022 15:24:20 +0100 Subject: [PATCH 1/4] Resolve local names for each network Local names of addresses should be global so they should be the same on every network. --- background/main.ts | 14 ++++ background/redux-slices/accounts.ts | 36 +++++++++- background/services/name/index.ts | 89 ++++++++++++++---------- background/services/preferences/index.ts | 65 ++++++++++------- 4 files changed, 139 insertions(+), 65 deletions(-) diff --git a/background/main.ts b/background/main.ts index 8b2aee2ea7..fc3bb04cd0 100644 --- a/background/main.ts +++ b/background/main.ts @@ -41,6 +41,7 @@ import { deleteAccount, loadAccount, updateAccountBalance, + updateAccountLocalName, updateAccountName, updateENSAvatar, } from "./redux-slices/accounts" @@ -827,6 +828,19 @@ export default class Main extends BaseService { this.store.dispatch(updateAccountName({ ...addressOnNetwork, name })) } ) + this.nameService.emitter.on( + "resolvedLocalName", + async ({ + from: { + addressOnNetwork: { address }, // ignore network + }, + resolved: { + nameOnNetwork: { name }, + }, + }) => { + this.store.dispatch(updateAccountLocalName({ address, name })) + } + ) this.nameService.emitter.on( "resolvedAvatar", async ({ from: { addressOnNetwork }, resolved: { avatar } }) => { diff --git a/background/redux-slices/accounts.ts b/background/redux-slices/accounts.ts index a7d3041f68..ddff9be01d 100644 --- a/background/redux-slices/accounts.ts +++ b/background/redux-slices/accounts.ts @@ -10,7 +10,7 @@ import { import { DomainName, HexString, URI } from "../types" import { normalizeEVMAddress } from "../lib/utils" import { AccountSigner } from "../services/signing" -import { TEST_NETWORK_BY_CHAIN_ID } from "../constants" +import { NETWORK_BY_CHAIN_ID, TEST_NETWORK_BY_CHAIN_ID } from "../constants" /** * The set of available UI account types. These may or may not map 1-to-1 to @@ -309,8 +309,6 @@ const accountSlice = createSlice({ immerState.accountsData.evm[network.chainID] ??= {} const baseAccountData = getOrCreateAccountData( - // TODO Figure out the best way to handle default name assignment - // TODO across networks. immerState, normalizedAddress, network @@ -321,6 +319,37 @@ const accountSlice = createSlice({ ens: { ...baseAccountData.ens, name }, } }, + updateAccountLocalName: ( + immerState, + { + payload: { address, name }, + }: { payload: { address: string; name: DomainName } } + ) => { + const normalizedAddress = normalizeEVMAddress(address) + + Object.keys(immerState.accountsData.evm).forEach((chainID) => { + if ( + immerState.accountsData.evm[chainID]?.[normalizedAddress] === + undefined + ) { + return + } + + immerState.accountsData.evm[chainID] ??= {} + + const network = NETWORK_BY_CHAIN_ID[chainID] + const baseAccountData = getOrCreateAccountData( + immerState, + normalizedAddress, + network + ) + + immerState.accountsData.evm[chainID][normalizedAddress] = { + ...baseAccountData, + ens: { ...baseAccountData.ens, name }, + } + }) + }, updateENSAvatar: ( immerState, { @@ -361,6 +390,7 @@ export const { loadAccount, updateAccountBalance, updateAccountName, + updateAccountLocalName, updateENSAvatar, } = accountSlice.actions diff --git a/background/services/name/index.ts b/background/services/name/index.ts index 4414e1328a..089ac02423 100644 --- a/background/services/name/index.ts +++ b/background/services/name/index.ts @@ -57,6 +57,7 @@ type ResolvedAvatarRecord = { type Events = ServiceLifecycleEvents & { resolvedAddress: ResolvedAddressRecord resolvedName: ResolvedNameRecord + resolvedLocalName: ResolvedNameRecord resolvedAvatar: ResolvedAvatarRecord } @@ -130,7 +131,6 @@ export default class NameService extends BaseService { preferenceService.emitter.on( "addressBookEntryModified", async ({ network, address }) => { - this.clearNameCacheEntry(network.chainID, address) await this.lookUpName({ network, address }) } ) @@ -207,6 +207,52 @@ export default class NameService extends BaseService { ): Promise { const { address, network } = normalizeAddressOnNetwork(addressOnNetwork) + const workingResolvers = this.resolvers.filter((resolver) => + resolver.canAttemptNameResolution({ address, network }) + ) + + // check local address book + const localResolvers = [...workingResolvers].filter( + (resolver) => + resolver.type === "tally-address-book" || + resolver.type === "tally-known-contracts" + ) + + const localResolution = ( + await Promise.allSettled( + localResolvers.map(async (resolver) => ({ + type: resolver.type, + resolved: await resolver.lookUpNameForAddress({ address, network }), + })) + ) + ) + .filter(isFulfilledPromise) + .find(({ value: { resolved } }) => resolved !== undefined)?.value + + if ( + typeof localResolution !== "undefined" && + typeof localResolution.resolved !== "undefined" + ) { + const { type: system, resolved: nameOnNetwork } = localResolution + + const nameRecord = { + from: { addressOnNetwork: { address, network } }, + resolved: { + nameOnNetwork, + // TODO Read this from the name service; for now, this avoids infinite + // TODO resolution loops. + expiresAt: Date.now() + MINIMUM_RECORD_EXPIRY, + }, + system, + } as const + + // emmit local names without a network and update all address networks paird in the redux? + this.emitter.emit("resolvedLocalName", nameRecord) + + return nameRecord + } + + // if there is no loacal name then look deeper if (!this.cachedResolvedNames[network.family][network.chainID]) { this.cachedResolvedNames[network.family][network.chainID] = {} } @@ -224,24 +270,15 @@ export default class NameService extends BaseService { } } - const workingResolvers = this.resolvers.filter((resolver) => - resolver.canAttemptNameResolution({ address, network }) - ) - - const localResolvers = [...workingResolvers].filter( - (resolver) => - resolver.type === "tally-address-book" || - resolver.type === "tally-known-contracts" - ) const remoteResolvers = [...workingResolvers].filter( (resolver) => resolver.type !== "tally-address-book" && resolver.type !== "tally-known-contracts" ) - let firstMatchingResolution = ( + const remoteResolution = ( await Promise.allSettled( - localResolvers.map(async (resolver) => ({ + remoteResolvers.map(async (resolver) => ({ type: resolver.type, resolved: await resolver.lookUpNameForAddress({ address, network }), })) @@ -250,28 +287,14 @@ export default class NameService extends BaseService { .filter(isFulfilledPromise) .find(({ value: { resolved } }) => resolved !== undefined)?.value - if (!firstMatchingResolution) { - firstMatchingResolution = ( - await Promise.allSettled( - remoteResolvers.map(async (resolver) => ({ - type: resolver.type, - resolved: await resolver.lookUpNameForAddress({ address, network }), - })) - ) - ) - .filter(isFulfilledPromise) - .find(({ value: { resolved } }) => resolved !== undefined)?.value - } - if ( - firstMatchingResolution === undefined || - firstMatchingResolution.resolved === undefined + remoteResolution === undefined || + remoteResolution.resolved === undefined ) { return undefined } - const { type: resolverType, resolved: nameOnNetwork } = - firstMatchingResolution + const { type: system, resolved: nameOnNetwork } = remoteResolution const nameRecord = { from: { addressOnNetwork: { address, network } }, @@ -281,7 +304,7 @@ export default class NameService extends BaseService { // TODO resolution loops. expiresAt: Date.now() + MINIMUM_RECORD_EXPIRY, }, - system: resolverType, + system, } as const const cachedNameOnNetwork = cachedResolvedNameRecord?.resolved.nameOnNetwork @@ -297,12 +320,6 @@ export default class NameService extends BaseService { return nameRecord } - clearNameCacheEntry(chainId: string, address: HexString): void { - if (this.cachedResolvedNames.EVM[chainId]?.[address] !== undefined) { - delete this.cachedResolvedNames.EVM[chainId][address] - } - } - async lookUpAvatar( addressOnNetwork: AddressOnNetwork ): Promise { diff --git a/background/services/preferences/index.ts b/background/services/preferences/index.ts index e186c8fa60..de4f522f1f 100644 --- a/background/services/preferences/index.ts +++ b/background/services/preferences/index.ts @@ -12,17 +12,17 @@ import { HexString } from "../../types" import { AccountSignerSettings } from "../../ui" import { AccountSignerWithId } from "../../signing" -type AddressBookEntry = { +type ContractAddressBookEntry = { network: EVMNetwork address: HexString name: string } -type InMemoryAddressBook = AddressBookEntry[] - -const sameAddressBookEntry = (a: AddressOnNetwork, b: AddressOnNetwork) => - normalizeEVMAddress(a.address) === normalizeEVMAddress(b.address) && - sameNetwork(a.network, b.network) +type CustomAddressBookEntry = { + // no network to make custom names global + address: HexString + name: string +} const BUILT_IN_CONTRACTS = [ { @@ -92,7 +92,7 @@ interface Events extends ServiceLifecycleEvents { preferencesChanges: Preferences initializeDefaultWallet: boolean initializeSelectedAccount: AddressOnNetwork - addressBookEntryModified: AddressBookEntry + addressBookEntryModified: AddressOnNetwork & { name: string } updatedSignerSettings: AccountSignerSettings[] } @@ -101,9 +101,9 @@ interface Events extends ServiceLifecycleEvents { * event when preferences change. */ export default class PreferenceService extends BaseService { - private knownContracts: InMemoryAddressBook = BUILT_IN_CONTRACTS + private knownContracts: ContractAddressBookEntry[] = BUILT_IN_CONTRACTS - private addressBook: InMemoryAddressBook = [] + private addressBook: CustomAddressBookEntry[] = [] /* * Create a new PreferenceService. The service isn't initialized until @@ -139,38 +139,51 @@ export default class PreferenceService extends BaseService { // TODO Implement the following 6 methods as something stored in the database and user-manageable. // TODO Track account names in the UI in the address book. - addOrEditNameInAddressBook(newEntry: AddressBookEntry): void { - const correspondingEntryIndex = this.addressBook.findIndex((entry) => - sameAddressBookEntry(newEntry, entry) + addOrEditNameInAddressBook({ + address, + network, + name, + }: { + network: EVMNetwork + address: HexString + name: string + }): void { + const newEntry = { + address: normalizeEVMAddress(address), + name, + } + const correspondingEntryIndex = this.addressBook.findIndex( + (entry) => + normalizeEVMAddress(newEntry.address) === + normalizeEVMAddress(entry.address) ) if (correspondingEntryIndex !== -1) { this.addressBook[correspondingEntryIndex] = newEntry } else { - this.addressBook.push({ - network: newEntry.network, - name: newEntry.name, - address: normalizeEVMAddress(newEntry.address), - }) + this.addressBook.push(newEntry) } - this.emitter.emit("addressBookEntryModified", newEntry) + this.emitter.emit("addressBookEntryModified", { ...newEntry, network }) } lookUpAddressForName({ name, network, }: NameOnNetwork): AddressOnNetwork | undefined { - return this.addressBook.find( - ({ name: entryName, network: entryNetwork }) => - sameNetwork(network, entryNetwork) && name === entryName + const entry = this.addressBook.find( + ({ name: entryName }) => name === entryName ) + return entry ? { address: entry.address, network } : undefined } - lookUpNameForAddress( - addressOnNetwork: AddressOnNetwork - ): NameOnNetwork | undefined { - return this.addressBook.find((addressBookEntry) => - sameAddressBookEntry(addressBookEntry, addressOnNetwork) + lookUpNameForAddress({ + address, + network, + }: AddressOnNetwork): NameOnNetwork | undefined { + const entry = this.addressBook.find( + ({ address: entryAddress }) => + normalizeEVMAddress(entryAddress) === normalizeEVMAddress(address) ) + return entry ? { name: entry.name, network } : undefined } async lookUpAddressForContractName({ From ba4526c74009411531384f990609f887c3984b90 Mon Sep 17 00:00:00 2001 From: Jagoda Berry Rybacka Date: Mon, 7 Nov 2022 17:00:39 +0100 Subject: [PATCH 2/4] Change name form improvements - autoselect input when slideup is opened - fix placement of input error --- ui/components/AccountItem/AccountItemEditName.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/components/AccountItem/AccountItemEditName.tsx b/ui/components/AccountItem/AccountItemEditName.tsx index ac06f0b49b..b5b045ab27 100644 --- a/ui/components/AccountItem/AccountItemEditName.tsx +++ b/ui/components/AccountItem/AccountItemEditName.tsx @@ -83,6 +83,7 @@ export default function AccountItemEditName({ { if (!touched) { @@ -125,8 +126,6 @@ export default function AccountItemEditName({ margin-top: 0px; } .details { - display: flex; - flex-direction: column; line-height: 24px; font-size: 16px; margin-top: 21px; From 9128c9cbe7600e24a707b4fee0208784919dc4a1 Mon Sep 17 00:00:00 2001 From: Jagoda Berry Rybacka Date: Thu, 5 Jan 2023 10:02:45 +0100 Subject: [PATCH 3/4] Fix typos and unnecessary checks --- background/redux-slices/accounts.ts | 2 -- background/services/name/index.ts | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/background/redux-slices/accounts.ts b/background/redux-slices/accounts.ts index 67d27a05fc..fafc54d430 100644 --- a/background/redux-slices/accounts.ts +++ b/background/redux-slices/accounts.ts @@ -373,8 +373,6 @@ const accountSlice = createSlice({ return } - immerState.accountsData.evm[chainID] ??= {} - const network = NETWORK_BY_CHAIN_ID[chainID] const baseAccountData = getOrCreateAccountData( immerState, diff --git a/background/services/name/index.ts b/background/services/name/index.ts index f91881adb4..0dbfc64a91 100644 --- a/background/services/name/index.ts +++ b/background/services/name/index.ts @@ -246,13 +246,13 @@ export default class NameService extends BaseService { system, } as const - // emmit local names without a network and update all address networks paird in the redux? + // Emit local names without a network and update all address-network pairs in Redux this.emitter.emit("resolvedLocalName", nameRecord) return nameRecord } - // if there is no loacal name then look deeper + // If there is no local name then look deeper if (!this.cachedResolvedNames[network.family][network.chainID]) { this.cachedResolvedNames[network.family][network.chainID] = {} } From 9b235e6c5730b1b601baa57f6c4b87a90e04462f Mon Sep 17 00:00:00 2001 From: Jagoda Berry Rybacka Date: Thu, 5 Jan 2023 10:19:41 +0100 Subject: [PATCH 4/4] Remove duplicated autofocus on edit name input --- ui/components/AccountItem/AccountItemEditName.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/components/AccountItem/AccountItemEditName.tsx b/ui/components/AccountItem/AccountItemEditName.tsx index 314b063a92..dc75f8d69d 100644 --- a/ui/components/AccountItem/AccountItemEditName.tsx +++ b/ui/components/AccountItem/AccountItemEditName.tsx @@ -83,7 +83,6 @@ export default function AccountItemEditName({ {