From 8c36af18aa560ded111224d0d0752c9a6b47de64 Mon Sep 17 00:00:00 2001 From: storywithoutend Date: Fri, 29 Mar 2024 12:32:25 +0800 Subject: [PATCH 1/2] fix offchain dns nametype + [root] validation --- .../ExpirySection/hooks/useExpiryDetails.ts | 1 + src/hooks/nameType/getNameType.ts | 4 + src/hooks/nameType/useNameType.test.ts | 10 ++ .../ownership/useRoles/utils/getRoles.ts | 4 + src/hooks/useBasicName.ts | 1 + src/hooks/useValidate.ts | 2 +- .../input/SendName/utils/checkCanSend.ts | 1 + .../SyncManager/utils/checkCanSyncManager.ts | 1 + src/utils/registrationStatus.ts | 4 + test/mock/makeMockUseBasicName.ts | 65 ++++++++++++ test/mock/makeMockUseExpiryData.ts | 3 +- test/mock/makeMockUseNameType.ts | 99 +++++++++++++++++++ test/mock/makeMockUseOwnerData.ts | 9 ++ test/mock/makeMockUsePriceData.ts | 3 +- test/mock/makeMockUseValidate.ts | 55 +++++++++++ 15 files changed, 259 insertions(+), 3 deletions(-) create mode 100644 test/mock/makeMockUseNameType.ts diff --git a/src/components/pages/profile/[name]/tabs/OwnershipTab/sections/ExpirySection/hooks/useExpiryDetails.ts b/src/components/pages/profile/[name]/tabs/OwnershipTab/sections/ExpirySection/hooks/useExpiryDetails.ts index 4bde1eb79..5ed917192 100644 --- a/src/components/pages/profile/[name]/tabs/OwnershipTab/sections/ExpirySection/hooks/useExpiryDetails.ts +++ b/src/components/pages/profile/[name]/tabs/OwnershipTab/sections/ExpirySection/hooks/useExpiryDetails.ts @@ -141,6 +141,7 @@ export const useExpiryDetails = ({ name, details }: Input, options: Options = {} 'dns-wrapped-2ld', 'dns-emancipated-2ld', 'dns-locked-2ld', + 'dns-offchain-2ld', 'dns-unwrapped-subname', 'dns-wrapped-subname', 'dns-emancipated-subname', diff --git a/src/hooks/nameType/getNameType.ts b/src/hooks/nameType/getNameType.ts index 2c0bbde28..c0021c2cc 100644 --- a/src/hooks/nameType/getNameType.ts +++ b/src/hooks/nameType/getNameType.ts @@ -22,6 +22,7 @@ export type NameType = | 'eth-pcc-expired-subname' | 'dns-unwrapped-2ld' | 'dns-wrapped-2ld' + | 'dns-offchain-2ld' | 'dns-emancipated-2ld' // * | 'dns-locked-2ld' // * | 'dns-unwrapped-subname' @@ -86,6 +87,9 @@ export const getNameType = ({ return `${_tldType}-${_wrapLevel}-2ld` as const }, ) + .with(['dns', 'unwrapped', '2ld', 'imported'], () => + ownerData ? ('dns-unwrapped-2ld' as const) : ('dns-offchain-2ld' as const), + ) .with(['dns', P._, '2ld', P._], ([, _wrapLevel]) => `dns-${_wrapLevel}-2ld` as const) .with([P._, P._, 'subname', P._], ([_tldType, _wrapLevel]) => pccExpired diff --git a/src/hooks/nameType/useNameType.test.ts b/src/hooks/nameType/useNameType.test.ts index e14dedce1..839301ced 100644 --- a/src/hooks/nameType/useNameType.test.ts +++ b/src/hooks/nameType/useNameType.test.ts @@ -5,6 +5,7 @@ import { describe, expect, it, vi } from 'vitest' import { useNameType } from './useNameType' import { makeMockUseContractAddress } from '../../../test/mock/makeMockUseContractAddress' import { makeMockUseBasicName } from '../../../test/mock/makeMockUseBasicName' +import { mockUseNameTypeConfig, mockUseNameTypes } from '../../../test/mock/makeMockUseNameType' const mockBasicData = vi.fn() vi.mock('@app/hooks/useBasicName', () => ({ @@ -165,4 +166,13 @@ describe('useNameType', () => { expect(result.current.data).toEqual('eth-locked-2ld:grace-period') }) }) + + describe('mocks', () => { + it.each(mockUseNameTypes)('should return the correct data for: %s', async (type) => { + const config = mockUseNameTypeConfig[type] + mockBasicData.mockReturnValue(makeMockUseBasicName(config.basicNameType)) + const { result } = renderHook(() => useNameType(config.name)) + expect(result.current.data).toEqual(type) + }) + }) }) diff --git a/src/hooks/ownership/useRoles/utils/getRoles.ts b/src/hooks/ownership/useRoles/utils/getRoles.ts index 4fcadab54..c5dae70fd 100644 --- a/src/hooks/ownership/useRoles/utils/getRoles.ts +++ b/src/hooks/ownership/useRoles/utils/getRoles.ts @@ -74,6 +74,10 @@ export const getRoles = ({ { address: owner, role: 'manager' as const }, { address: ethAddress, role: 'eth-record' as const }, ]) + .with('dns-offchain-2ld', () => [ + { address: dnsOwner, role: 'dns-owner' as const }, + { address: ethAddress, role: 'eth-record' as const }, + ]) .with(P.union(P.nullish, 'tld', 'root'), () => []) .exhaustive() } diff --git a/src/hooks/useBasicName.ts b/src/hooks/useBasicName.ts index 10c7a1cf5..b6a7ef0f8 100644 --- a/src/hooks/useBasicName.ts +++ b/src/hooks/useBasicName.ts @@ -129,6 +129,7 @@ export const useBasicName = ({ const registrationStatus = !publicCallsLoading ? getRegistrationStatus({ + name: normalisedName, timestamp: registrationStatusTimestamp, validation, ownerData, diff --git a/src/hooks/useValidate.ts b/src/hooks/useValidate.ts index bfe292fd3..9d1722546 100644 --- a/src/hooks/useValidate.ts +++ b/src/hooks/useValidate.ts @@ -35,7 +35,7 @@ export const validate = (input: string) => { ...parsedInput, name: outputName, beautifiedName: tryBeautify(outputName), - isNonASCII, + isNonASCII: input !== '[root]' && isNonASCII, labelCount: parsedInput.labelDataArray.length, } } diff --git a/src/transaction-flow/input/SendName/utils/checkCanSend.ts b/src/transaction-flow/input/SendName/utils/checkCanSend.ts index d8a22cfe9..6f79765ee 100644 --- a/src/transaction-flow/input/SendName/utils/checkCanSend.ts +++ b/src/transaction-flow/input/SendName/utils/checkCanSend.ts @@ -34,6 +34,7 @@ export const senderRole = (nameType: ReturnType['data']) => P.union( 'dns-unwrapped-2ld', 'dns-wrapped-2ld', + 'dns-offchain-2ld', 'eth-emancipated-2ld:grace-period', 'eth-locked-2ld:grace-period', 'eth-unwrapped-2ld:grace-period', diff --git a/src/transaction-flow/input/SyncManager/utils/checkCanSyncManager.ts b/src/transaction-flow/input/SyncManager/utils/checkCanSyncManager.ts index 713d44482..589c612bb 100644 --- a/src/transaction-flow/input/SyncManager/utils/checkCanSyncManager.ts +++ b/src/transaction-flow/input/SyncManager/utils/checkCanSyncManager.ts @@ -41,6 +41,7 @@ export const checkCanSyncManager = ({ 'eth-pcc-expired-subname', 'dns-locked-2ld', 'dns-emancipated-2ld', + 'dns-offchain-2ld', 'dns-unwrapped-subname', 'dns-wrapped-subname', 'dns-emancipated-subname', diff --git a/src/utils/registrationStatus.ts b/src/utils/registrationStatus.ts index 13e16fdd9..4a7d73e38 100644 --- a/src/utils/registrationStatus.ts +++ b/src/utils/registrationStatus.ts @@ -23,6 +23,7 @@ export type RegistrationStatus = | 'unsupportedTLD' export const getRegistrationStatus = ({ + name, timestamp, validation: { isETH, is2LD, isShort, type }, ownerData, @@ -32,6 +33,7 @@ export const getRegistrationStatus = ({ addrData, supportedTLD, }: { + name: string timestamp: number validation: Partial> ownerData?: GetOwnerReturnType @@ -41,6 +43,8 @@ export const getRegistrationStatus = ({ addrData?: GetAddressRecordReturnType supportedTLD?: boolean | null }): RegistrationStatus => { + if (name === '[root]') return 'owned' + if (isETH && is2LD && isShort) { return 'short' } diff --git a/test/mock/makeMockUseBasicName.ts b/test/mock/makeMockUseBasicName.ts index b1598d36f..c636ed308 100644 --- a/test/mock/makeMockUseBasicName.ts +++ b/test/mock/makeMockUseBasicName.ts @@ -20,6 +20,12 @@ type MockUseBasicNameConfig = { } export const mockUseBasicNameConfig = { + root: { + useValidateType: 'root', + useOwnerType: 'root', + useExpiryType: 'root', + usePriceType: 'root', + } as MockUseBasicNameConfig, eth: { useValidateType: 'eth', useOwnerType: 'eth', @@ -179,6 +185,10 @@ export const mockUseBasicNameConfig = { useOwnerType: 'namewrapper:unowned', useWrapperDataType: 'locked:unowned', } as MockUseBasicNameConfig, + 'eth-pcc-expired-subname': { + useValidateType: 'valid-subname', + useOwnerType: 'registry:pcc-expired', + } as MockUseBasicNameConfig, // DNS dns: { useValidateType: 'dns', @@ -209,6 +219,17 @@ export const mockUseBasicNameConfig = { useValidateType: 'valid-2ld:dns', useAddrRecordType: 'owned', } as MockUseBasicNameConfig, + 'dns-unwrapped-subname': { + useValidateType: 'valid-subname:dns', + useOwnerType: 'registry', + useAddrRecordType: 'owned', + } as MockUseBasicNameConfig, + 'dns-wrapped-subname': { + useValidateType: 'valid-subname:dns', + useOwnerType: 'namewrapper', + useWrapperDataType: 'wrapped', + useAddrRecordType: 'owned', + } as MockUseBasicNameConfig, } as const export type MockUseBasicNameType = keyof typeof mockUseBasicNameConfig @@ -236,6 +257,17 @@ export const makeMockUseBasicName = (type: MockUseBasicNameType) => { gracePeriodEndDate, } return match(type) + .with('root', () => ({ + ...BaseBasicName, + normalisedName: '[root]', + truncatedName: '[root]', + canBeWrapped: false, + pccExpired: false, + registrationStatus: 'owned' as const, + isCachedData: false, + isLoading: false, + isWrapped: false, + })) .with('eth', () => ({ ...BaseBasicName, normalisedName: 'eth', @@ -373,6 +405,17 @@ export const makeMockUseBasicName = (type: MockUseBasicNameType) => { isCachedData: false, }), ) + .with('eth-pcc-expired-subname', () => ({ + ...BaseBasicName, + normalisedName: 'subname.name.eth', + truncatedName: 'subname.name.eth', + registrationStatus: 'owned' as const, + isWrapped: false, + pccExpired: true, + canBeWrapped: true, + isLoading: false, + isCachedData: false, + })) .with('dns', () => ({ ...BaseBasicName, normalisedName: 'com', @@ -414,5 +457,27 @@ export const makeMockUseBasicName = (type: MockUseBasicNameType) => { isLoading: false, isCachedData: false, })) + .with('dns-unwrapped-subname', () => ({ + ...BaseBasicName, + normalisedName: 'subname.name.com', + truncatedName: 'subname.name.com', + registrationStatus: 'owned' as const, + isWrapped: false, + pccExpired: false, + canBeWrapped: false, + isLoading: false, + isCachedData: false, + })) + .with('dns-wrapped-subname', () => ({ + ...BaseBasicName, + normalisedName: 'subname.name.com', + truncatedName: 'subname.name.com', + registrationStatus: 'owned' as const, + isWrapped: true, + pccExpired: false, + canBeWrapped: false, + isLoading: false, + isCachedData: false, + })) .exhaustive() } diff --git a/test/mock/makeMockUseExpiryData.ts b/test/mock/makeMockUseExpiryData.ts index e5b2293f4..ef1cd552f 100644 --- a/test/mock/makeMockUseExpiryData.ts +++ b/test/mock/makeMockUseExpiryData.ts @@ -2,11 +2,12 @@ import { match, P } from 'ts-pattern' import { GetExpiryReturnType } from '@ensdomains/ensjs/public' -const mockUseExpiryTypes = ['eth', 'active', 'grace-period'] as const +const mockUseExpiryTypes = ['root', 'eth', 'active', 'grace-period'] as const export type MockUseExpiryType = (typeof mockUseExpiryTypes)[number] | undefined export const makeMockUseExpiryData = (type: MockUseExpiryType): GetExpiryReturnType | undefined => match(type) + .with('root', () => undefined) .with('eth', () => ({ expiry: { date: new Date('+275760-09-13T00:00:00.000Z'), diff --git a/test/mock/makeMockUseNameType.ts b/test/mock/makeMockUseNameType.ts new file mode 100644 index 000000000..7ef1798b1 --- /dev/null +++ b/test/mock/makeMockUseNameType.ts @@ -0,0 +1,99 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { NameType } from '@app/hooks/nameType/getNameType' + +import { MockUseBasicNameType } from './makeMockUseBasicName' + +export type MockUseNameTypeType = Exclude< + NameType, + | 'dns-emancipated-2ld' + | 'dns-locked-2ld' + | 'dns-emancipated-subname' + | 'dns-locked-subname' + | 'dns-pcc-expired-subname' +> + +type MockUseNameTypeConfig = { + name: string + basicNameType: MockUseBasicNameType +} + +export const mockUseNameTypeConfig: { [key in MockUseNameTypeType]: MockUseNameTypeConfig } = { + root: { + name: '[root]', + basicNameType: 'root', + }, + tld: { + name: 'eth', + basicNameType: 'eth', + }, + 'eth-unwrapped-2ld': { + name: 'name.eth', + basicNameType: 'eth-unwrapped-2ld', + }, + 'eth-unwrapped-2ld:grace-period': { + name: 'name.eth', + basicNameType: 'eth-unwrapped-2ld:grace-period', + }, + 'eth-emancipated-2ld': { + name: 'name.eth', + basicNameType: 'eth-emancipated-2ld', + }, + 'eth-emancipated-2ld:grace-period': { + name: 'name.eth', + basicNameType: 'eth-emancipated-2ld:grace-period', + }, + 'eth-locked-2ld': { + name: 'name.eth', + basicNameType: 'eth-locked-2ld', + }, + 'eth-locked-2ld:grace-period': { + name: 'name.eth', + basicNameType: 'eth-locked-2ld:grace-period', + }, + 'eth-unwrapped-subname': { + name: 'subname.name.eth', + basicNameType: 'eth-unwrapped-subname', + }, + 'eth-wrapped-subname': { + name: 'subname.name.eth', + basicNameType: 'eth-wrapped-subname', + }, + 'eth-emancipated-subname': { + name: 'subname.name.eth', + basicNameType: 'eth-emancipated-subname', + }, + 'eth-locked-subname': { + name: 'subname.name.eth', + basicNameType: 'eth-locked-subname', + }, + 'eth-pcc-expired-subname': { + name: 'subname.name.eth', + basicNameType: 'eth-pcc-expired-subname', + }, + 'dns-unwrapped-2ld': { + name: 'name.com', + basicNameType: 'dns-unwrapped-2ld', + }, + 'dns-wrapped-2ld': { + name: 'name.com', + basicNameType: 'dns-wrapped-2ld', + }, + 'dns-offchain-2ld': { + name: 'name.com', + basicNameType: 'dns-offchain-2ld', + }, + 'dns-unwrapped-subname': { + name: 'subname.name.com', + basicNameType: 'dns-unwrapped-subname', + }, + 'dns-wrapped-subname': { + name: 'subname.name.com', + basicNameType: 'dns-wrapped-subname', + }, +} + +export const mockUseNameTypes = Object.keys(mockUseNameTypeConfig) as MockUseNameTypeType[] + +export const makeMockUseNameTypeData = (type: MockUseNameTypeType) => { + return type +} diff --git a/test/mock/makeMockUseOwnerData.ts b/test/mock/makeMockUseOwnerData.ts index 4bd189b83..0b9ff7ce4 100644 --- a/test/mock/makeMockUseOwnerData.ts +++ b/test/mock/makeMockUseOwnerData.ts @@ -7,6 +7,7 @@ import { createAccounts } from '../../playwright/fixtures/accounts' import { makeMockUseContractAddress } from './makeMockUseContractAddress' const mockUseOwnerTypes = [ + 'root', 'eth', 'registrar', 'registrar:owner', @@ -24,7 +25,9 @@ const mockUseOwnerTypes = [ 'namewrapper:grace-period', 'registry', 'registry:unowned', + 'registry:pcc-expired', 'dns', + 'dns:offchain', ] as const export type MockUseOwnerType = (typeof mockUseOwnerTypes)[number] | undefined @@ -34,6 +37,7 @@ const user2Address = createAccounts().getAddress('user2') as Address export const makeMockUseOwnerData = (type: MockUseOwnerType): GetOwnerReturnType | undefined => match(type) + .with('root', () => undefined) .with('eth', () => ({ owner: '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0' as Address, ownershipLevel: 'registry' as const, @@ -94,9 +98,14 @@ export const makeMockUseOwnerData = (type: MockUseOwnerType): GetOwnerReturnType owner: _type.endsWith('unowned') ? user2Address : userAddress, ownershipLevel: 'registry' as const, })) + .with('registry:pcc-expired', () => ({ + owner: makeMockUseContractAddress({ contract: 'ensNameWrapper' }), + ownershipLevel: 'registry' as const, + })) .with('dns', () => ({ owner: '0xB32cB5677a7C971689228EC835800432B339bA2B' as Address, ownershipLevel: 'registry' as const, })) + .with('dns:offchain', () => null) .with(P.nullish, () => null) .exhaustive() diff --git a/test/mock/makeMockUsePriceData.ts b/test/mock/makeMockUsePriceData.ts index f2d11cf8a..ff65874ab 100644 --- a/test/mock/makeMockUsePriceData.ts +++ b/test/mock/makeMockUsePriceData.ts @@ -2,11 +2,12 @@ import { match, P } from 'ts-pattern' import { GetPriceReturnType } from '@ensdomains/ensjs/public' -const mockUsePriceTypes = ['tld', 'base', 'premium'] as const +const mockUsePriceTypes = ['root', 'tld', 'base', 'premium'] as const export type MockUsePriceType = (typeof mockUsePriceTypes)[number] | undefined export const makeMockUsePriceData = (type: MockUsePriceType): GetPriceReturnType | undefined => match(type) + .with('root', () => undefined) .with('tld', () => undefined) .with('base', () => ({ base: 3203936997786453n, premium: 0n })) .with('premium', () => ({ diff --git a/test/mock/makeMockUseValidate.ts b/test/mock/makeMockUseValidate.ts index f8ae2f3d1..57cf5d768 100644 --- a/test/mock/makeMockUseValidate.ts +++ b/test/mock/makeMockUseValidate.ts @@ -4,18 +4,39 @@ import { match } from 'ts-pattern' import { ValidationResult } from '@app/hooks/useValidate' export const mockUseValidateConfig = { + root: { input: '[root]' }, eth: { input: 'eth' }, dns: { input: 'com' }, 'valid-2ld': { input: 'name.eth' }, 'valid-2ld:dns': { input: 'name.com' }, 'invalid-2ld': { input: 'name❤️.eth' }, 'valid-subname': { input: 'subname.name.eth' }, + 'valid-subname:dns': { input: 'subname.name.com' }, } as const export type MockUseValidateType = keyof typeof mockUseValidateConfig export const mockUseValidateTypes = Object.keys(mockUseValidateConfig) as MockUseValidateType[] export const makeMockUseValidate = (type: MockUseValidateType): ValidationResult => { return match(type) + .with('root', () => ({ + type: 'label' as const, + isShort: true, + isValid: true, + is2LD: false, + isETH: false, + labelDataArray: [ + { + error: new Error('disallowed character: "["‎ {5B}'), + input: [91, 114, 111, 111, 116, 93], + offset: 0, + output: undefined, + }, + ], + name: '[root]', + beautifiedName: '[root]', + isNonASCII: false, + labelCount: 1, + })) .with('eth', () => ({ type: 'label' as const, isShort: false, @@ -175,5 +196,39 @@ export const makeMockUseValidate = (type: MockUseValidateType): ValidationResult isNonASCII: false, labelCount: 3, })) + .with('valid-subname:dns', () => ({ + type: 'name' as const, + name: 'subname.name.com', + isShort: false, + isValid: true, + is2LD: false, + isETH: false, + labelDataArray: [ + { + input: [115, 117, 98, 110, 97, 109, 101], + offset: 0, + tokens: [[115, +117, +98, +110, +97, +109, +101]], + type: 'ASCII', + output: [115, +117, +98, +110, +97, +109, +101], + }, + { + input: [110, 97, 109, 101], + offset: 8, + tokens: [[110, 97, 109, 101]], + type: 'ASCII', + output: [110, 97, 109, 101], + }, + { + input: [99, 111, 109], + offset: 13, + tokens: [[99, 111, 109]], + type: 'ASCII', + output: [99, 111, 109], + }, + ], + beautifiedName: 'subname.name.com', + isNonASCII: false, + labelCount: 3, + })) .exhaustive() } From e4ae8a274e9f0600daf448a4617cf460fd65bfb2 Mon Sep 17 00:00:00 2001 From: storywithoutend Date: Fri, 29 Mar 2024 12:57:11 +0800 Subject: [PATCH 2/2] fix build error --- src/components/@molecules/SearchInput/SearchInput.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/@molecules/SearchInput/SearchInput.tsx b/src/components/@molecules/SearchInput/SearchInput.tsx index 933c1a5f2..50378ae03 100644 --- a/src/components/@molecules/SearchInput/SearchInput.tsx +++ b/src/components/@molecules/SearchInput/SearchInput.tsx @@ -383,6 +383,7 @@ export const SearchInput = ({ ) if (ownerData) { const registrationStatus = getRegistrationStatus({ + name: selectedItem.value, timestamp: Date.now(), validation: currentValidation, ownerData,