diff --git a/packages/mask/src/components/DataSource/useNextID.ts b/packages/mask/src/components/DataSource/useNextID.ts index c68873a46e22..039da241104d 100644 --- a/packages/mask/src/components/DataSource/useNextID.ts +++ b/packages/mask/src/components/DataSource/useNextID.ts @@ -31,9 +31,9 @@ const verifyPersona = (personaIdentifier?: PersonaIdentifier, username?: string) }) } -export const useNextIDBoundByPlatform = (platform: NextIDPlatform, identity?: string) => { +export const useNextIDBoundByPlatform = (platform?: NextIDPlatform, identity?: string) => { const res = useAsyncRetry(() => { - if (!identity) return Promise.resolve([]) + if (!platform || !identity) return Promise.resolve([]) return NextIDProof.queryExistedBindingByPlatform(platform, identity) }, [platform, identity]) useEffect(() => MaskMessages.events.ownProofChanged.on(res.retry), [res.retry]) diff --git a/packages/mask/src/components/InjectedComponents/ProfileTabContent.tsx b/packages/mask/src/components/InjectedComponents/ProfileTabContent.tsx index 077d37edc208..4f7f3a6f7fb6 100644 --- a/packages/mask/src/components/InjectedComponents/ProfileTabContent.tsx +++ b/packages/mask/src/components/InjectedComponents/ProfileTabContent.tsx @@ -1,18 +1,17 @@ +import { useEffect, useMemo, useState } from 'react' +import { useUpdateEffect } from 'react-use' +import { first } from 'lodash-unified' import { createInjectHooksRenderer, PluginId, useActivatedPluginsSNSAdaptor, usePluginI18NField, } from '@masknet/plugin-infra/content-script' -import { useAddressNames, useAvailablePlugins } from '@masknet/plugin-infra/web3' +import { useSocialAddressListAll, useAvailablePlugins } from '@masknet/plugin-infra/web3' import { ConcealableTabs } from '@masknet/shared' import { EMPTY_LIST, NextIDPlatform } from '@masknet/shared-base' import { makeStyles, useStylesExtends } from '@masknet/theme' -import { NetworkPluginID } from '@masknet/web3-shared-base' import { Box, CircularProgress, Typography } from '@mui/material' -import { first } from 'lodash-unified' -import { useEffect, useMemo, useState } from 'react' -import { useUpdateEffect } from 'react-use' import { activatedSocialNetworkUI } from '../../social-network' import { isTwitter } from '../../social-network-adaptor/twitter.com/base' import { MaskMessages, useI18N } from '../../utils' @@ -21,11 +20,12 @@ import { useCurrentVisitingIdentity, useLastRecognizedIdentity } from '../DataSo import { useNextIDBoundByPlatform } from '../DataSource/useNextID' import { usePersonaConnectStatus } from '../DataSource/usePersonaConnectStatus' -function getTabContent(tabId: string) { +const platform = activatedSocialNetworkUI.configuration.nextIDConfig?.platform as NextIDPlatform | undefined + +function getTabContent(tabId?: string) { return createInjectHooksRenderer(useActivatedPluginsSNSAdaptor.visibility.useAnyMode, (x) => { const tab = x.ProfileTabs?.find((x) => x.ID === tabId) - if (!tab) return - return tab.UI?.TabContent + return tab?.UI?.TabContent }) } @@ -43,19 +43,19 @@ export function ProfileTabContent(props: ProfileTabContentProps) { const classes = useStylesExtends(useStyles(), props) const { t } = useI18N() + const translate = usePluginI18NField() + const [hidden, setHidden] = useState(true) const [selectedTab, setSelectedTab] = useState() const currentIdentity = useLastRecognizedIdentity() const identity = useCurrentVisitingIdentity() const { currentConnectedPersona } = usePersonaConnectStatus() - const platform = activatedSocialNetworkUI.configuration.nextIDConfig?.platform as NextIDPlatform - const { value: addressNames = EMPTY_LIST, loading: loadingAddressNames } = useAddressNames( - NetworkPluginID.PLUGIN_EVM, - identity, - ) + + const { value: socialAddressList = EMPTY_LIST, loading: loadingSocialAddressList } = + useSocialAddressListAll(identity) const { value: personaList = EMPTY_LIST, loading: loadingPersonaList } = useNextIDBoundByPlatform( - platform as NextIDPlatform, + platform, identity.identifier?.userId, ) @@ -64,14 +64,14 @@ export function ProfileTabContent(props: ProfileTabContentProps) { personaList.findIndex((persona) => persona?.persona === currentConnectedPersona?.identifier.publicKeyAsHex) === -1 - const translate = usePluginI18NField() const activatedPlugins = useActivatedPluginsSNSAdaptor('any') const availablePlugins = useAvailablePlugins(activatedPlugins) const displayPlugins = useMemo(() => { return availablePlugins .flatMap((x) => x.ProfileTabs?.map((y) => ({ ...y, pluginID: x.ID })) ?? []) - .filter((z) => z.Utils?.shouldDisplay?.(identity, addressNames) ?? true) - }, [availablePlugins, identity, addressNames]) + .filter((z) => z.Utils?.shouldDisplay?.(identity, socialAddressList) ?? true) + }, [availablePlugins]) + const tabs = useMemo(() => { return displayPlugins .sort((a, z) => { @@ -121,17 +121,31 @@ export function ProfileTabContent(props: ProfileTabContentProps) { }) }, [identity.identifier?.userId]) - const ContentComponent = useMemo(() => { - const tabId = + const content = useMemo(() => { + const Component = getTabContent( isTwitter(activatedSocialNetworkUI) && currentAccountNotConnectPersona ? displayPlugins?.find((tab) => tab?.pluginID === PluginId.NextID)?.ID - : selectedTabId - return getTabContent(tabId ?? '') - }, [selectedTabId, identity.identifier?.userId, currentAccountNotConnectPersona]) + : selectedTabId, + ) + const Utils = displayPlugins.find((x) => x.ID === selectedTabId)?.Utils + + return ( + x.persona)} + socialAddressList={socialAddressList.filter((x) => Utils?.filter?.(x) ?? true).sort(Utils?.sorter)} + /> + ) + }, [ + selectedTabId, + displayPlugins.map((x) => x.ID).join(), + identity.identifier?.userId, + currentAccountNotConnectPersona, + ]) if (hidden) return null - if (loadingAddressNames || loadingPersonaList) + if (!identity.identifier?.userId || loadingSocialAddressList || loadingPersonaList) return (
)}
-
- persona.persona)} - /> -
+
{content}
) } diff --git a/packages/mask/src/extension/options-page/DashboardComponents/CollectibleList/index.tsx b/packages/mask/src/extension/options-page/DashboardComponents/CollectibleList/index.tsx index c866f0fcd77b..246ce005405c 100644 --- a/packages/mask/src/extension/options-page/DashboardComponents/CollectibleList/index.tsx +++ b/packages/mask/src/extension/options-page/DashboardComponents/CollectibleList/index.tsx @@ -1,5 +1,5 @@ import { createContext } from 'react' -import type { IdentityAddress, NonFungibleAsset } from '@masknet/web3-shared-base' +import type { SocialAddress, NonFungibleAsset, NetworkPluginID } from '@masknet/web3-shared-base' import type { ChainId, NonFungibleAssetProvider, SchemaType, Wallet } from '@masknet/web3-shared-evm' import { Button, styled, Typography } from '@mui/material' import { makeStyles } from '@masknet/theme' @@ -228,7 +228,7 @@ export function CollectionList({ addressName, onSelectAddress, }: { - addressName: IdentityAddress + addressName: SocialAddress onSelectAddress: (event: React.MouseEvent) => void }) { return null diff --git a/packages/mask/src/plugins/Avatar/Services/bind.ts b/packages/mask/src/plugins/Avatar/Services/bind.ts index ecdb02f6074d..b080cb51dde9 100644 --- a/packages/mask/src/plugins/Avatar/Services/bind.ts +++ b/packages/mask/src/plugins/Avatar/Services/bind.ts @@ -7,8 +7,12 @@ import { getGunData } from '@masknet/gun-utils' const READ_GUN_TIMEOUT = 15 * 1000 -const NFTAvatarDB = (network: string) => KeyValue.createJSON_Storage(NFT_AVATAR_DB_NAME + '_' + network) -const NFTAvatarDBStorage = (network: string) => KeyValue.createJSON_Storage(NFT_AVATAR_DB_NAME_STORAGE + '_' + network) +const NFTAvatarDB = (network: string) => + KeyValue.createJSON_Storage<{ networkPluginId: string; chainId: number; address: string }>( + NFT_AVATAR_DB_NAME + '_' + network, + ) +const NFTAvatarDBStorage = (network: string) => + KeyValue.createJSON_Storage>(NFT_AVATAR_DB_NAME_STORAGE + '_' + network) const cache = new Map, number]>() @@ -41,11 +45,9 @@ function getCacheKey( async function _getUserAddress(userId: string, network: string, networkPluginId?: NetworkPluginID, chainId?: number) { try { - const result = await NFTAvatarDB(network).get<{ networkPluginId: string; chainId: number; address: string }>( - userId, - ) + const result = await NFTAvatarDB(network).get(userId) if (!result || !result?.address) { - const result = await NFTAvatarDBStorage(network).get>(userId) + const result = await NFTAvatarDBStorage(network).get(userId) const address = result?.[getKey(networkPluginId, chainId)] if (address) return address return getUserAddressFromGUN(userId) diff --git a/packages/mask/src/plugins/Avatar/Services/kv.ts b/packages/mask/src/plugins/Avatar/Services/kv.ts new file mode 100644 index 000000000000..7d42cfc7144e --- /dev/null +++ b/packages/mask/src/plugins/Avatar/Services/kv.ts @@ -0,0 +1,40 @@ +import type { EnhanceableSite } from '@masknet/shared-base' +import { KeyValue } from '@masknet/web3-providers' +import { isSameAddress, NetworkPluginID } from '@masknet/web3-shared-base' +import { NFT_AVATAR_DB_NAME } from '../constants' + +type AddressStorageV1 = { address: string; networkId: NetworkPluginID } +type AddressStorageV2 = Record + +// The DB contains data that an SNS identity handle maps to a Web3 address. +const createAddressDB = (site: EnhanceableSite) => + KeyValue.createJSON_Storage(`${NFT_AVATAR_DB_NAME}_${site}`) + +export async function getAddress(site: EnhanceableSite, userId: string, pluginID: NetworkPluginID): Promise { + if (userId === '$unknown') return '' + + const storage = await createAddressDB(site).get(userId) + const storageV1 = storage as AddressStorageV1 + const storageV2 = storage as AddressStorageV2 + + if (storageV2[pluginID]) return storageV2[pluginID] + + // V1 only supports EVM + if (storageV1.address && pluginID === NetworkPluginID.PLUGIN_EVM) return storageV1.address + + return '' +} + +export async function setAddress(site: EnhanceableSite, userId: string, pluginID: NetworkPluginID, address: string) { + if (userId === '$unknown') return + + const storage = await createAddressDB(site).get(userId) + const storageV2 = storage as AddressStorageV2 + + if (!isSameAddress(address, storageV2[pluginID])) { + await createAddressDB(site).set(userId, { + ...(storageV2 as AddressStorageV2), + [pluginID]: address, + }) + } +} diff --git a/packages/mask/src/plugins/Collectible/SNSAdaptor/NFTPage.tsx b/packages/mask/src/plugins/Collectible/SNSAdaptor/NFTPage.tsx index e463784558fd..77df9cc8cd95 100644 --- a/packages/mask/src/plugins/Collectible/SNSAdaptor/NFTPage.tsx +++ b/packages/mask/src/plugins/Collectible/SNSAdaptor/NFTPage.tsx @@ -1,10 +1,11 @@ import { useState } from 'react' +import { first, uniqBy } from 'lodash-unified' +import { ReversedAddress } from '@masknet/shared' import { getMaskColor, makeStyles, ShadowRootMenu } from '@masknet/theme' import { MenuItem } from '@mui/material' +import type { SocialAddress, NetworkPluginID, SocialIdentity } from '@masknet/web3-shared-base' import { CollectionList } from '../../../extension/options-page/DashboardComponents/CollectibleList' -import { first, uniqBy } from 'lodash-unified' -import { ReversedAddress } from '@masknet/shared' -import type { IdentityAddress } from '@masknet/web3-shared-base' +import { EMPTY_LIST } from '@masknet/shared-base' const useStyles = makeStyles()((theme) => ({ root: { @@ -43,18 +44,18 @@ const useStyles = makeStyles()((theme) => ({ })) export interface NFTPageProps { - addressNames?: IdentityAddress[] + identity?: SocialIdentity + socialAddressList?: Array> } -export function NFTPage(props: NFTPageProps) { - const { addressNames } = props +export function NFTPage({ socialAddressList }: NFTPageProps) { const { classes } = useStyles() const [anchorEl, setAnchorEl] = useState(null) - const [selectedAddress, setSelectedAddress] = useState(first(addressNames)) + const [selectedAddress, setSelectedAddress] = useState(first(socialAddressList)) const onOpen = (event: React.MouseEvent) => setAnchorEl(event.currentTarget) const onClose = () => setAnchorEl(null) - const onSelect = (option: IdentityAddress) => { + const onSelect = (option: SocialAddress) => { setSelectedAddress(option) onClose() } @@ -68,7 +69,7 @@ export function NFTPage(props: NFTPageProps) { onClose={onClose} anchorEl={anchorEl} PaperProps={{ style: { maxHeight: 192 } }}> - {uniqBy(addressNames ?? [], (x) => x.address.toLowerCase()).map((x) => { + {uniqBy(socialAddressList ?? EMPTY_LIST, (x) => x.address.toLowerCase()).map((x) => { return ( onSelect(x)}> @@ -76,7 +77,7 @@ export function NFTPage(props: NFTPageProps) { ) })} - + ) } diff --git a/packages/mask/src/plugins/Collectible/SNSAdaptor/index.tsx b/packages/mask/src/plugins/Collectible/SNSAdaptor/index.tsx index d0c8ff0679f6..10e65a45e22f 100644 --- a/packages/mask/src/plugins/Collectible/SNSAdaptor/index.tsx +++ b/packages/mask/src/plugins/Collectible/SNSAdaptor/index.tsx @@ -1,4 +1,6 @@ import { uniq } from 'lodash-unified' +import { Trans } from 'react-i18next' +import { CollectiblesIcon } from '@masknet/icons' import { type Plugin, usePostInfoDetails, usePluginWrapper } from '@masknet/plugin-infra/content-script' import { PostInspector } from './PostInspector' import { base } from '../base' @@ -6,9 +8,7 @@ import { checkUrl, getAssetInfoFromURL, getRelevantUrl } from '../utils' import { PLUGIN_ID } from '../constants' import { extractTextFromTypedMessage } from '@masknet/typed-message' import { NFTPage } from './NFTPage' -import { Trans } from 'react-i18next' -import { CollectiblesIcon } from '@masknet/icons' -import { IdentityAddress, IdentityAddressType } from '@masknet/web3-shared-base' +import { SocialAddressType } from '@masknet/web3-shared-base' const sns: Plugin.SNSAdaptor.Definition = { ...base, @@ -34,36 +34,36 @@ const sns: Plugin.SNSAdaptor.Definition = { label: 'NFTs', priority: 1, UI: { - TabContent: ({ addressNames = [] }) => , + TabContent: NFTPage, }, Utils: { - addressNameSorter: (a, z) => { - if (a.type === IdentityAddressType.ENS) return -1 - if (z.type === IdentityAddressType.ENS) return 1 + shouldDisplay: (identity, socialAddressList) => { + return !!socialAddressList?.length + }, + sorter: (a, z) => { + if (a.type === SocialAddressType.ENS) return -1 + if (z.type === SocialAddressType.ENS) return 1 - if (a.type === IdentityAddressType.UNS) return -1 - if (z.type === IdentityAddressType.UNS) return 1 + if (a.type === SocialAddressType.UNS) return -1 + if (z.type === SocialAddressType.UNS) return 1 - if (a.type === IdentityAddressType.DNS) return -1 - if (z.type === IdentityAddressType.DNS) return 1 + if (a.type === SocialAddressType.DNS) return -1 + if (z.type === SocialAddressType.DNS) return 1 - if (a.type === IdentityAddressType.RSS3) return -1 - if (z.type === IdentityAddressType.RSS3) return 1 + if (a.type === SocialAddressType.RSS3) return -1 + if (z.type === SocialAddressType.RSS3) return 1 - if (a.type === IdentityAddressType.ADDRESS) return -1 - if (z.type === IdentityAddressType.ADDRESS) return 1 + if (a.type === SocialAddressType.ADDRESS) return -1 + if (z.type === SocialAddressType.ADDRESS) return 1 - if (a.type === IdentityAddressType.GUN) return -1 - if (z.type === IdentityAddressType.GUN) return 1 + if (a.type === SocialAddressType.GUN) return -1 + if (z.type === SocialAddressType.GUN) return 1 - if (a.type === IdentityAddressType.THE_GRAPH) return -1 - if (z.type === IdentityAddressType.THE_GRAPH) return 1 + if (a.type === SocialAddressType.THE_GRAPH) return -1 + if (z.type === SocialAddressType.THE_GRAPH) return 1 return 0 }, - shouldDisplay: (identity, addressNames) => { - return !!addressNames?.length - }, }, }, ], diff --git a/packages/mask/src/plugins/NextID/components/NextIdPage.tsx b/packages/mask/src/plugins/NextID/components/NextIdPage.tsx index cd4f64d501cf..106fbdff3e75 100644 --- a/packages/mask/src/plugins/NextID/components/NextIdPage.tsx +++ b/packages/mask/src/plugins/NextID/components/NextIdPage.tsx @@ -85,7 +85,6 @@ export function NextIdPage({ personaList }: NextIdPageProps) { const { value: isAccountVerified, loading: loadingVerifyInfo } = useAsync(async () => { if (!currentPersona?.identifier.publicKeyAsHex) return - if (!currentPersona.identifier) return if (!visitingPersonaIdentifier.identifier) return return NextIDProof.queryIsBound( currentPersona.identifier.publicKeyAsHex, @@ -99,7 +98,7 @@ export function NextIdPage({ personaList }: NextIdPageProps) { loading, retry: retryQueryBinding, } = useAsyncRetry(async () => { - if (!currentPersona) return + if (!currentPersona?.identifier.publicKeyAsHex) return return NextIDProof.queryExistedBindingByPersona(currentPersona.identifier.publicKeyAsHex) }, [currentPersona, isOwn]) @@ -183,7 +182,7 @@ export function NextIdPage({ personaList }: NextIdPageProps) { open={openBindDialog} onClose={() => toggleBindDialog(false)} persona={currentPersona} - bounds={bindings?.proofs ?? []} + bounds={bindings?.proofs ?? EMPTY_LIST} onBound={retryQueryBinding} /> )} @@ -193,7 +192,7 @@ export function NextIdPage({ personaList }: NextIdPageProps) { onClose={() => setUnBindAddress(undefined)} persona={currentPersona} onUnBound={retryQueryBinding} - bounds={bindings?.proofs ?? []} + bounds={bindings?.proofs ?? EMPTY_LIST} /> )} @@ -233,7 +232,7 @@ export function NextIdPage({ personaList }: NextIdPageProps) { open={openBindDialog} onClose={() => toggleBindDialog(false)} persona={currentPersona} - bounds={bindings?.proofs ?? []} + bounds={bindings?.proofs ?? EMPTY_LIST} onBound={retryQueryBinding} /> )} diff --git a/packages/mask/src/plugins/Pets/Services/storage.ts b/packages/mask/src/plugins/Pets/Services/storage.ts index 1a8c04cbca43..bc974d6a12e4 100644 --- a/packages/mask/src/plugins/Pets/Services/storage.ts +++ b/packages/mask/src/plugins/Pets/Services/storage.ts @@ -2,7 +2,7 @@ import { KeyValue } from '@masknet/web3-providers' import { PetsPluginID } from '../constants' import type { User } from '../types' -const storage = KeyValue.createJSON_Storage(PetsPluginID) +const storage = KeyValue.createJSON_Storage(PetsPluginID) export async function setUserAddress(user: User) { await storage.set(user.userId, user.address) diff --git a/packages/plugin-infra/package.json b/packages/plugin-infra/package.json index c1c2c2addc6b..a71f23e9283a 100644 --- a/packages/plugin-infra/package.json +++ b/packages/plugin-infra/package.json @@ -82,6 +82,7 @@ "async-call-rpc": "^6.0.2", "bignumber.js": "^9.0.2", "ethereum-blockies": "^0.1.1", + "lru-cache": "^7.10.1", "react-use": "^17.3.2", "unstated-next": "^1.1.0", "urlcat": "^2.0.4", @@ -91,6 +92,9 @@ "web3-core-helpers": "1.7.3", "web3-utils": "1.7.3" }, + "devDependencies": { + "@types/lru-cache": "^7.10.10" + }, "peerDependencies": { "react-i18next": "^11.15.3" } diff --git a/packages/plugin-infra/src/types.ts b/packages/plugin-infra/src/types.ts index 335f7bddb168..2ba46f77517c 100644 --- a/packages/plugin-infra/src/types.ts +++ b/packages/plugin-infra/src/types.ts @@ -1,22 +1,23 @@ /* eslint @dimensiondev/unicode/specific-set: ["error", { "only": "code" }] */ import type React from 'react' import type { Option, Result } from 'ts-results' +import type { Subscription } from 'use-subscription' import type { JsonRpcPayload, JsonRpcResponse } from 'web3-core-helpers' import type { TypedMessage } from '@masknet/typed-message' import type { ScopedStorage, ProfileIdentifier, PersonaIdentifier, PopupRoutes } from '@masknet/shared-base' import type { ChainDescriptor, - IdentityAddress, + SocialAddress, NetworkDescriptor, ProviderDescriptor, SocialIdentity, Wallet, Web3EnableRequirement, + NetworkPluginID, } from '@masknet/web3-shared-base' import type { ChainId, SchemaType, Transaction } from '@masknet/web3-shared-evm' import type { Emitter } from '@servie/events' import type { Web3Plugin } from './web3-types' -import type { Subscription } from 'use-subscription' export declare namespace Plugin { /** @@ -538,6 +539,7 @@ export namespace Plugin.SNSAdaptor { * The name of the tab */ label: I18NStringField | string + /** * Used to order the sliders */ @@ -549,20 +551,23 @@ export namespace Plugin.SNSAdaptor { */ TabContent: InjectUI<{ identity?: SocialIdentity - addressNames?: IdentityAddress[] personaList?: string[] + socialAddressList?: Array> }> } Utils?: { /** * If it returns false, this tab will not be displayed. */ - shouldDisplay?(identity?: SocialIdentity, addressNames?: IdentityAddress[]): boolean - + shouldDisplay?(identity?: SocialIdentity, addressNames?: Array>): boolean + /** + * Filter social address. + */ + filter?: (x: SocialAddress) => boolean /** - * Sort address name in expected order. + * Sort social address in expected order. */ - addressNameSorter?: (a: IdentityAddress, z: IdentityAddress) => number + sorter?: (a: SocialAddress, z: SocialAddress) => number } } } diff --git a/packages/plugin-infra/src/web3-state/Identity.ts b/packages/plugin-infra/src/web3-state/Identity.ts index d142d557ddc7..0b58535c59b5 100644 --- a/packages/plugin-infra/src/web3-state/Identity.ts +++ b/packages/plugin-infra/src/web3-state/Identity.ts @@ -1,23 +1,40 @@ -import type { StorageItem } from '@masknet/shared-base' +import LRU from 'lru-cache' import type { - IdentityAddress, + SocialAddress, SocialIdentity, IdentityServiceState as Web3SocialIdentityState, + NetworkPluginID, } from '@masknet/web3-shared-base' -export class IdentityServiceState< - ChainId, - IdentityBook extends Record = Record, -> implements Web3SocialIdentityState -{ - protected storage: StorageItem = null! +export class IdentityServiceState implements Web3SocialIdentityState { + protected cache = new LRU>>({ + max: 20, + ttl: Number.MAX_SAFE_INTEGER, + }) private getIdentityID(identity: SocialIdentity) { if (!identity.identifier) return '' return `${identity.identifier.network}_${identity.identifier.userId}` } - async lookup(identity: SocialIdentity): Promise { - return this.storage.value[this.getIdentityID(identity)] + protected getFromCache(identity: SocialIdentity): Promise>> { + return Promise.resolve(this.cache.get(this.getIdentityID(identity)) ?? []) + } + + protected getFromRemote(identity: SocialIdentity): Promise>> { + throw new Error('Method not implemented.') + } + + async lookup(identity: SocialIdentity): Promise>> { + const ID = this.getIdentityID(identity) + if (!ID) return [] + + const fromCache = await this.getFromCache(identity) + if (fromCache.length) return fromCache + + const fromRemote = await this.getFromRemote(identity) + if (fromRemote.length) this.cache.set(ID, fromRemote) + + return fromRemote } } diff --git a/packages/plugin-infra/src/web3-state/NameService.ts b/packages/plugin-infra/src/web3-state/NameService.ts index 4c35f0799eed..7c64fac0dddc 100644 --- a/packages/plugin-infra/src/web3-state/NameService.ts +++ b/packages/plugin-infra/src/web3-state/NameService.ts @@ -7,7 +7,7 @@ export class NameServiceState< ChainId extends number, DomainBook extends Record = Record, DomainBooks extends Record = Record, -> implements Web3NameServiceState +> implements Web3NameServiceState { protected storage: StorageItem = null! public domainBook?: Subscription diff --git a/packages/plugin-infra/src/web3/index.ts b/packages/plugin-infra/src/web3/index.ts index 9358f70d3159..c51a3a0820cc 100644 --- a/packages/plugin-infra/src/web3/index.ts +++ b/packages/plugin-infra/src/web3/index.ts @@ -2,7 +2,8 @@ export * from './Context' export * from './useAccount' export * from './useAddressBook' -export * from './useAddressNames' +export * from './useSocialAddressList' +export * from './useSocialAddressListAll' export * from './useAllowTestnet' export * from './useBalance' export * from './useBeat' diff --git a/packages/plugin-infra/src/web3/useAddressNames.ts b/packages/plugin-infra/src/web3/useAddressNames.ts deleted file mode 100644 index 1b95d481398a..000000000000 --- a/packages/plugin-infra/src/web3/useAddressNames.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { useAsyncRetry } from 'react-use' -import { EMPTY_LIST } from '@masknet/shared-base' -import type { NetworkPluginID, IdentityAddress, SocialIdentity } from '@masknet/web3-shared-base' -import { useWeb3State } from './useWeb3State' - -export function useAddressNames( - pluginID?: T, - identity?: SocialIdentity, - sorter?: (a: IdentityAddress, z: IdentityAddress) => number, -) { - type Lookup = (identity: SocialIdentity) => Promise - - const { IdentityService } = useWeb3State(pluginID) - - return useAsyncRetry(async () => { - if (!identity || !IdentityService?.lookup) return EMPTY_LIST - const listOfAddress = (await (IdentityService.lookup as Lookup)(identity)) ?? EMPTY_LIST - return sorter && listOfAddress.length ? listOfAddress.sort(sorter) : listOfAddress - }, [identity, sorter, IdentityService]) -} diff --git a/packages/plugin-infra/src/web3/useSocialAddressList.ts b/packages/plugin-infra/src/web3/useSocialAddressList.ts new file mode 100644 index 000000000000..65d331ee78bc --- /dev/null +++ b/packages/plugin-infra/src/web3/useSocialAddressList.ts @@ -0,0 +1,23 @@ +import { useAsyncRetry } from 'react-use' +import { EMPTY_LIST } from '@masknet/shared-base' +import type { NetworkPluginID, SocialAddress, SocialIdentity } from '@masknet/web3-shared-base' +import { useWeb3State } from './useWeb3State' + +/** + * Get all social addresses under a specific network. + */ +export function useSocialAddressList( + pluginID?: T, + identity?: SocialIdentity, + sorter?: (a: SocialAddress, z: SocialAddress) => number, +) { + type Lookup = (identity: SocialIdentity) => Promise>> + + const { IdentityService } = useWeb3State(pluginID) + + return useAsyncRetry(async () => { + if (!identity?.identifier?.userId || !IdentityService?.lookup) return EMPTY_LIST + const listOfAddress = (await (IdentityService.lookup as Lookup)(identity)) ?? EMPTY_LIST + return sorter && listOfAddress.length ? listOfAddress.sort(sorter) : listOfAddress + }, [identity?.identifier?.userId, sorter, IdentityService?.lookup]) +} diff --git a/packages/plugin-infra/src/web3/useSocialAddressListAll.ts b/packages/plugin-infra/src/web3/useSocialAddressListAll.ts new file mode 100644 index 000000000000..e205076c7560 --- /dev/null +++ b/packages/plugin-infra/src/web3/useSocialAddressListAll.ts @@ -0,0 +1,33 @@ +import { useAsyncRetry } from 'react-use' +import { EMPTY_LIST } from '@masknet/shared-base' +import { NetworkPluginID, SocialAddress, SocialIdentity } from '@masknet/web3-shared-base' +import { useWeb3State } from './useWeb3State' + +/** + * Get all social addresses under a specific network. + */ +export function useSocialAddressListAll( + identity?: SocialIdentity, + sorter?: (a: SocialAddress, z: SocialAddress) => number, +) { + const { IdentityService: EVM_IdentityService } = useWeb3State(NetworkPluginID.PLUGIN_EVM) + const { IdentityService: FlowIdentityService } = useWeb3State(NetworkPluginID.PLUGIN_FLOW) + const { IdentityService: SolanaIdentityService } = useWeb3State(NetworkPluginID.PLUGIN_SOLANA) + + return useAsyncRetry(async () => { + const userId = identity?.identifier?.userId + if (!userId || userId === '$unknown') return EMPTY_LIST + + const allSettled = await Promise.allSettled>>( + [EVM_IdentityService, FlowIdentityService, SolanaIdentityService].map((x) => x?.lookup(identity) ?? []), + ) + const listOfAddress = allSettled.flatMap((x) => (x.status === 'fulfilled' ? x.value : [])) + return sorter && listOfAddress.length ? listOfAddress.sort(sorter) : listOfAddress + }, [ + identity?.identifier?.userId, + sorter, + EVM_IdentityService?.lookup, + FlowIdentityService?.lookup, + SolanaIdentityService?.lookup, + ]) +} diff --git a/packages/plugins/Debugger/src/SNSAdaptor/components/TabContent.tsx b/packages/plugins/Debugger/src/SNSAdaptor/components/TabContent.tsx index e7e39c2f23f9..fe0bd4267375 100644 --- a/packages/plugins/Debugger/src/SNSAdaptor/components/TabContent.tsx +++ b/packages/plugins/Debugger/src/SNSAdaptor/components/TabContent.tsx @@ -1,11 +1,11 @@ import { useBalance, useBlockNumber } from '@masknet/plugin-infra/web3' import { makeStyles } from '@masknet/theme' -import type { IdentityAddress, SocialIdentity } from '@masknet/web3-shared-base' +import type { NetworkPluginID, SocialAddress, SocialIdentity } from '@masknet/web3-shared-base' import { Box, List, ListItem, ListItemText, Typography } from '@mui/material' export interface TabContentProps { identity?: SocialIdentity - addressNames?: IdentityAddress[] + socialAddressList?: Array> } const useStyles = makeStyles()({ @@ -14,8 +14,9 @@ const useStyles = makeStyles()({ }, }) -export function TabContent({ identity, addressNames }: TabContentProps) { +export function TabContent({ identity, socialAddressList }: TabContentProps) { const { classes } = useStyles() + const renderIdentity = () => { return ( @@ -53,7 +54,7 @@ export function TabContent({ identity, addressNames }: TabContentProps) { const renderAddressNames = () => { return ( - {addressNames?.map((x) => ( + {socialAddressList?.map((x) => ( - Address Names + Social Address List {renderAddressNames()} diff --git a/packages/plugins/Debugger/src/SNSAdaptor/index.tsx b/packages/plugins/Debugger/src/SNSAdaptor/index.tsx index 20c2fea2dc9e..f3cf3b70610c 100644 --- a/packages/plugins/Debugger/src/SNSAdaptor/index.tsx +++ b/packages/plugins/Debugger/src/SNSAdaptor/index.tsx @@ -1,4 +1,5 @@ import type { Plugin } from '@masknet/plugin-infra' +import { SocialAddressType } from '@masknet/web3-shared-base' import { base } from '../base' import { PLUGIN_ID } from '../constants' import { TabContent } from './components/TabContent' @@ -14,6 +15,17 @@ const sns: Plugin.SNSAdaptor.Definition = { UI: { TabContent, }, + Utils: { + filter(x) { + return x.type !== SocialAddressType.ENS + }, + sorter(a, z) { + if (a.type === SocialAddressType.ADDRESS) return 1 + if (z.type === SocialAddressType.ADDRESS) return -1 + + return 0 + }, + }, }, ], } diff --git a/packages/plugins/EVM/src/state/IdentityService.ts b/packages/plugins/EVM/src/state/IdentityService.ts index 0e97869d5cef..088b89c9e66f 100644 --- a/packages/plugins/EVM/src/state/IdentityService.ts +++ b/packages/plugins/EVM/src/state/IdentityService.ts @@ -1,12 +1,14 @@ import Web3 from 'web3' import type { Plugin } from '@masknet/plugin-infra' -import { SocialIdentity, IdentityAddress, IdentityAddressType } from '@masknet/web3-shared-base' -import { ChainId, createPayload, createWeb3Provider, isValidAddress, isZeroAddress } from '@masknet/web3-shared-evm' +import { NetworkPluginID, SocialIdentity, SocialAddress, SocialAddressType } from '@masknet/web3-shared-base' +import { createPayload, createWeb3Provider, isValidAddress, isZeroAddress } from '@masknet/web3-shared-evm' import { IdentityServiceState } from '@masknet/plugin-infra/web3' import type { RequestArguments } from 'web3-core' +import { RSS3, KeyValue } from '@masknet/web3-providers' +import { getSiteType } from '@masknet/shared-base' const ENS_RE = /\S{1,256}\.(eth|kred|xyz|luxe)\b/ -const ADDRESS_FULL = /0x\w+/ +const ADDRESS_FULL = /0x\w{40,}/ // xxx.cheers.bio xxx.rss3.bio const RSS3_URL_RE = /https?:\/\/(?[\w.]+)\.(rss3|cheers)\.bio/ const RSS3_RNS_RE = /(?[\w.]+)\.rss3/ @@ -33,12 +35,12 @@ function getAddress(text: string) { return '' } -export class IdentityService extends IdentityServiceState { +export class IdentityService extends IdentityServiceState { constructor(protected context: Plugin.Shared.SharedContext) { super() } - override async lookup(identity: SocialIdentity): Promise { + override async getFromRemote(identity: SocialIdentity) { const { identifier, bio = '', nickname = '', homepage = '' } = identity const address = getAddress(bio) @@ -53,11 +55,10 @@ export class IdentityService extends IdentityServiceState { ) const allSettled = await Promise.allSettled([ web3.eth.ens.getAddress(ethereumName), - '', - '', - // TODO: move to external apis - // PluginProfileRPC.getRSS3AddressById(RSS3Id), - // PluginNFTAvatarRPC.getAddress(identifier.userId ?? '', identifier.network), + RSS3.getNameInfo(RSS3Id).then((x) => x?.address ?? ''), + KeyValue.createJSON_Storage>(`com.maskbook.user_${getSiteType()}`) + .get(identifier?.userId ?? '$unknown') + .then((x) => x?.[NetworkPluginID.PLUGIN_EVM] ?? ''), ]) const getSettledAddress = (result: PromiseSettledResult) => { @@ -71,36 +72,36 @@ export class IdentityService extends IdentityServiceState { return [ isValidSocialAddress(address) ? { - type: IdentityAddressType.ADDRESS, - chainId: ChainId.Mainnet, + networkSupporterPluginID: NetworkPluginID.PLUGIN_EVM, + type: SocialAddressType.ADDRESS, label: address, address, } : null, isValidSocialAddress(addressENS) ? { - type: IdentityAddressType.ENS, - chainId: ChainId.Mainnet, + networkSupporterPluginID: NetworkPluginID.PLUGIN_EVM, + type: SocialAddressType.ENS, label: ethereumName, address: addressENS, } : null, isValidSocialAddress(addressRSS3) ? { - type: IdentityAddressType.RSS3, - chainId: ChainId.Mainnet, + networkSupporterPluginID: NetworkPluginID.PLUGIN_EVM, + type: SocialAddressType.RSS3, label: `${RSS3Id}.rss3`, address: addressRSS3, } : null, isValidSocialAddress(addressGUN) ? { - type: IdentityAddressType.GUN, - chainId: ChainId.Mainnet, + networkSupporterPluginID: NetworkPluginID.PLUGIN_EVM, + type: SocialAddressType.GUN, label: addressGUN, address: addressGUN, } : null, - ].filter(Boolean) as IdentityAddress[] + ].filter(Boolean) as Array> } } diff --git a/packages/plugins/Flow/src/state/IdentityService.ts b/packages/plugins/Flow/src/state/IdentityService.ts new file mode 100644 index 000000000000..b021f4390f03 --- /dev/null +++ b/packages/plugins/Flow/src/state/IdentityService.ts @@ -0,0 +1,25 @@ +import type { Plugin } from '@masknet/plugin-infra' +import { IdentityServiceState } from '@masknet/plugin-infra/web3' +import { SocialIdentity, SocialAddress, NetworkPluginID, SocialAddressType } from '@masknet/web3-shared-base' + +export class IdentityService extends IdentityServiceState { + constructor(protected context: Plugin.Shared.SharedContext) { + super() + } + + protected override async getFromRemote(identity: SocialIdentity) { + const addressMatched = identity.bio?.match(/0x\w{16}/) ?? null + const address = addressMatched?.[0] + + return [ + address + ? { + networkSupporterPluginID: NetworkPluginID.PLUGIN_FLOW, + type: SocialAddressType.ADDRESS, + label: address, + address, + } + : null, + ].filter(Boolean) as Array> + } +} diff --git a/packages/plugins/Flow/src/state/index.ts b/packages/plugins/Flow/src/state/index.ts index c5fc80658f3e..14dc81a61f89 100644 --- a/packages/plugins/Flow/src/state/index.ts +++ b/packages/plugins/Flow/src/state/index.ts @@ -8,6 +8,7 @@ import { Transaction } from './Transaction' import { Wallet } from './Wallet' import { Others } from './Others' import type { FlowWeb3State } from './Connection/types' +import { IdentityService } from './IdentityService' export function createWeb3State(context: Plugin.Shared.SharedContext): FlowWeb3State { const Provider_ = new Provider(context) @@ -19,6 +20,7 @@ export function createWeb3State(context: Plugin.Shared.SharedContext): FlowWeb3S chainId: Provider_.chainId, account: Provider_.account, }), + IdentityService: new IdentityService(context), Settings: new Settings(context), Transaction: new Transaction(context, { chainId: Provider_.chainId, diff --git a/packages/plugins/RSS3/src/SNSAdaptor/TabCard.tsx b/packages/plugins/RSS3/src/SNSAdaptor/TabCard.tsx index 281193e664ce..f255d98b8fde 100644 --- a/packages/plugins/RSS3/src/SNSAdaptor/TabCard.tsx +++ b/packages/plugins/RSS3/src/SNSAdaptor/TabCard.tsx @@ -1,6 +1,6 @@ import { AddressViewer } from '@masknet/shared' import { EMPTY_LIST } from '@masknet/shared-base' -import { IdentityAddress, IdentityAddressType } from '@masknet/web3-shared-base' +import { SocialAddressType, NetworkPluginID, SocialAddress } from '@masknet/web3-shared-base' import { Box, Typography } from '@mui/material' import { useI18N } from '../locales' import { useDonations, useFootprints } from './hooks' @@ -13,12 +13,14 @@ export enum TabCardType { export interface TabCardProps { type: TabCardType - addressNames: IdentityAddress[] + socialAddressList?: Array> } -export function TabCard({ type, addressNames }: TabCardProps) { +export function TabCard({ type, socialAddressList }: TabCardProps) { const t = useI18N() - const addressName = addressNames.find((x) => x.type === IdentityAddressType.RSS3) + const addressName = socialAddressList?.find( + (x) => x.type === SocialAddressType.RSS3 && x.networkSupporterPluginID === NetworkPluginID.PLUGIN_EVM, + ) const userAddress = addressName?.address || '' const { value: donations = EMPTY_LIST, loading: loadingDonations } = useDonations(userAddress) const { value: footprints = EMPTY_LIST, loading: loadingFootprints } = useFootprints(userAddress) diff --git a/packages/plugins/RSS3/src/SNSAdaptor/index.tsx b/packages/plugins/RSS3/src/SNSAdaptor/index.tsx index 66c9c4748f02..d91346304fe6 100644 --- a/packages/plugins/RSS3/src/SNSAdaptor/index.tsx +++ b/packages/plugins/RSS3/src/SNSAdaptor/index.tsx @@ -1,17 +1,21 @@ import type { Plugin } from '@masknet/plugin-infra' -import { IdentityAddress, IdentityAddressType, SocialIdentity } from '@masknet/web3-shared-base' +import { NetworkPluginID, SocialAddress, SocialAddressType, SocialIdentity } from '@masknet/web3-shared-base' import { base } from '../base' import { PLUGIN_ID } from '../constants' import { TabCard, TabCardType } from './TabCard' -function addressNameSorter(a: IdentityAddress, z: IdentityAddress) { - if (a.type === IdentityAddressType.RSS3) return -1 - if (z.type === IdentityAddressType.RSS3) return 1 +function sorter(a: SocialAddress, z: SocialAddress) { + if (a.type === SocialAddressType.RSS3) return -1 + if (z.type === SocialAddressType.RSS3) return 1 return 0 } -function shouldDisplay(identity?: SocialIdentity, addressNames?: IdentityAddress[]) { - return addressNames?.some((x) => x.type === IdentityAddressType.RSS3) ?? false +function shouldDisplay(identity?: SocialIdentity, addressNames?: Array>) { + return ( + addressNames?.some( + (x) => x.type === SocialAddressType.RSS3 && x.networkSupporterPluginID === NetworkPluginID.PLUGIN_EVM, + ) ?? false + ) } const sns: Plugin.SNSAdaptor.Definition = { @@ -23,12 +27,12 @@ const sns: Plugin.SNSAdaptor.Definition = { label: 'Donations', priority: 1, UI: { - TabContent: ({ addressNames = [] }) => { - return + TabContent: ({ socialAddressList = [] }) => { + return }, }, Utils: { - addressNameSorter, + sorter, shouldDisplay, }, }, @@ -37,12 +41,12 @@ const sns: Plugin.SNSAdaptor.Definition = { label: 'Footprints', priority: 2, UI: { - TabContent: ({ addressNames = [] }) => { - return + TabContent: ({ socialAddressList = [] }) => { + return }, }, Utils: { - addressNameSorter, + sorter, shouldDisplay, }, }, diff --git a/packages/plugins/RSS3/src/apis/index.ts b/packages/plugins/RSS3/src/apis/index.ts index f84a75e1c787..776304808f76 100644 --- a/packages/plugins/RSS3/src/apis/index.ts +++ b/packages/plugins/RSS3/src/apis/index.ts @@ -39,13 +39,6 @@ export function getFootprints(address: string): Promise { return fetchJSON(url) } -export async function getRSS3AddressById(id: string) { - if (!id) return '' - const url = urlcat('https://rss3.domains/name/:id', { id }) - const rsp = await fetchJSON(url) - return rsp.address -} - export async function getRSS3ProfileByAddress(address: string) { if (!address) return const url = urlcat('https://hub.pass3.me/:address', { address }) diff --git a/packages/plugins/Solana/src/state/IdentityService.ts b/packages/plugins/Solana/src/state/IdentityService.ts new file mode 100644 index 000000000000..daf870d116fc --- /dev/null +++ b/packages/plugins/Solana/src/state/IdentityService.ts @@ -0,0 +1,25 @@ +import type { Plugin } from '@masknet/plugin-infra' +import { IdentityServiceState } from '@masknet/plugin-infra/web3' +import { SocialIdentity, SocialAddress, NetworkPluginID, SocialAddressType } from '@masknet/web3-shared-base' + +export class IdentityService extends IdentityServiceState { + constructor(protected context: Plugin.Shared.SharedContext) { + super() + } + + protected override async getFromRemote(identity: SocialIdentity) { + const addressMatched = identity.bio?.match(/\w{44}/) ?? null + const address = addressMatched?.[0] + + return [ + address && !address.startsWith('0x') + ? { + networkSupporterPluginID: NetworkPluginID.PLUGIN_SOLANA, + type: SocialAddressType.ADDRESS, + label: address, + address, + } + : null, + ].filter(Boolean) as Array> + } +} diff --git a/packages/plugins/Solana/src/state/index.ts b/packages/plugins/Solana/src/state/index.ts index aeef9d59c340..0653561f8cd2 100644 --- a/packages/plugins/Solana/src/state/index.ts +++ b/packages/plugins/Solana/src/state/index.ts @@ -8,6 +8,7 @@ import { Transaction } from './Transaction' import { Wallet } from './Wallet' import { Others } from './Others' import type { SolanaWeb3State } from './Connection/types' +import { IdentityService } from './IdentityService' export function createWeb3State(context: Plugin.Shared.SharedContext): SolanaWeb3State { const Provider_ = new Provider(context) @@ -22,6 +23,7 @@ export function createWeb3State(context: Plugin.Shared.SharedContext): SolanaWeb account: Provider_.account, currencyType: Settings_.currencyType, }), + IdentityService: new IdentityService(context), Settings: Settings_, Transaction: new Transaction(context, { chainId: Provider_.chainId, diff --git a/packages/shared/src/UI/components/AddressViewer/index.tsx b/packages/shared/src/UI/components/AddressViewer/index.tsx index dfc7dff3fa30..026db8689996 100644 --- a/packages/shared/src/UI/components/AddressViewer/index.tsx +++ b/packages/shared/src/UI/components/AddressViewer/index.tsx @@ -1,7 +1,7 @@ import { getMaskColor, makeStyles } from '@masknet/theme' import { Box, Link, Typography, Tooltip } from '@mui/material' import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined' -import { IdentityAddress, IdentityAddressType } from '@masknet/web3-shared-base' +import { NetworkPluginID, SocialAddress, SocialAddressType } from '@masknet/web3-shared-base' import { formatEthereumAddress, ChainId, explorerResolver } from '@masknet/web3-shared-evm' import { useSharedI18N } from '../../..' @@ -30,7 +30,7 @@ const useStyles = makeStyles()((theme) => ({ })) export interface AddressViewerProps { - identityAddress: IdentityAddress + identityAddress: SocialAddress } export function AddressViewer({ identityAddress }: AddressViewerProps) { @@ -38,16 +38,16 @@ export function AddressViewer({ identityAddress }: AddressViewerProps) { const { classes } = useStyles() const { type, label, address } = identityAddress - const typeMap: Record = { - [IdentityAddressType.ADDRESS]: t.address_viewer_address_name_address(), - [IdentityAddressType.ENS]: t.address_viewer_address_name_ens(), - [IdentityAddressType.UNS]: t.address_viewer_address_name_uns(), - [IdentityAddressType.DNS]: t.address_viewer_address_name_dns(), - [IdentityAddressType.RSS3]: t.address_viewer_address_name_rns(), - [IdentityAddressType.GUN]: t.address_viewer_address_name_address(), - [IdentityAddressType.NEXT_ID]: t.address_viewer_address_name_address(), - [IdentityAddressType.THE_GRAPH]: t.address_viewer_address_name_address(), - [IdentityAddressType.TWITTER_BLUE]: t.address_viewer_address_name_twitter(), + const typeMap: Record = { + [SocialAddressType.ADDRESS]: t.address_viewer_address_name_address(), + [SocialAddressType.ENS]: t.address_viewer_address_name_ens(), + [SocialAddressType.UNS]: t.address_viewer_address_name_uns(), + [SocialAddressType.DNS]: t.address_viewer_address_name_dns(), + [SocialAddressType.RSS3]: t.address_viewer_address_name_rns(), + [SocialAddressType.GUN]: t.address_viewer_address_name_address(), + [SocialAddressType.NEXT_ID]: t.address_viewer_address_name_address(), + [SocialAddressType.THE_GRAPH]: t.address_viewer_address_name_address(), + [SocialAddressType.TWITTER_BLUE]: t.address_viewer_address_name_twitter(), } const rulesTipMap = [ diff --git a/packages/web3-providers/src/kv/index.ts b/packages/web3-providers/src/kv/index.ts index af6a18651426..0f8017c48ec8 100644 --- a/packages/web3-providers/src/kv/index.ts +++ b/packages/web3-providers/src/kv/index.ts @@ -3,10 +3,10 @@ import { fetchJSON } from '../helpers' import type { StorageAPI } from '../types' import { KV_ROOT_URL } from './constants' -export class JSON_Storage implements StorageAPI.Storage { +export class JSON_Storage implements StorageAPI.Storage { constructor(private prefix: string) {} - async get(key: string) { + async get(key: string) { try { return fetchJSON( urlcat(KV_ROOT_URL, 'api/:name', { @@ -24,7 +24,7 @@ export class JSON_Storage implements StorageAPI.Storage { } } - async set(key: string, value: any) { + async set(key: string, value: T) { await fetch( urlcat(KV_ROOT_URL, 'api/:name', { name: `${this.prefix}_${key}`, @@ -42,7 +42,7 @@ export class JSON_Storage implements StorageAPI.Storage { } export class KeyValueAPI implements StorageAPI.Provider { - createJSON_Storage(key: string): StorageAPI.Storage { - return new JSON_Storage(key) + createJSON_Storage(key: string): StorageAPI.Storage { + return new JSON_Storage(key) } } diff --git a/packages/web3-providers/src/types.ts b/packages/web3-providers/src/types.ts index ae00b6e74f0e..698518b0b00f 100644 --- a/packages/web3-providers/src/types.ts +++ b/packages/web3-providers/src/types.ts @@ -142,7 +142,10 @@ export namespace NonFungibleTokenAPI { tokenId: string, options?: HubOptions, ) => Promise | undefined> - getAssets?: (address: string) => Promise>> + getAssets?: ( + address: string, + options?: HubOptions, + ) => Promise>> getHistory?: ( address: string, tokenId: string, @@ -192,15 +195,15 @@ export namespace RiskWarningBaseAPI { } export namespace StorageAPI { - export interface Storage { - set(key: string, value: any): Promise - get(key: string): Promise + export interface Storage { + get(key: string): Promise + set(key: string, value: T): Promise delete?(key: string): Promise } export interface Provider { - createJSON_Storage?(key: string): Storage - createBinaryStorage?(key: string): Storage + createJSON_Storage?(key: string): Storage + createBinaryStorage?(key: string): Storage } } diff --git a/packages/web3-shared/base/src/specs/index.ts b/packages/web3-shared/base/src/specs/index.ts index cee76c8bb7b9..1eabca5f1253 100644 --- a/packages/web3-shared/base/src/specs/index.ts +++ b/packages/web3-shared/base/src/specs/index.ts @@ -85,7 +85,7 @@ export enum TransactionDescriptorType { RETRY = 'retry', // speed up } -export enum IdentityAddressType { +export enum SocialAddressType { ADDRESS = 'ADDRESS', ENS = 'ENS', UNS = 'UNS', @@ -112,9 +112,14 @@ export interface SocialIdentity { homepage?: string } -export interface IdentityAddress { - type: IdentityAddressType +export interface SocialAddress { + /** The ID of a plugin that the address belongs to */ + networkSupporterPluginID: PluginID + /** The data source type */ + type: SocialAddressType + /** The address in hex string */ address: string + /** A human readable address title */ label: string } @@ -795,10 +800,10 @@ export interface HubState< } export interface IdentityServiceState { - /** Find all social addresses related to given social identity. */ - lookup(identity: SocialIdentity): Promise + /** Find all social addresses related to the given identity. */ + lookup(identity: SocialIdentity): Promise>> } -export interface NameServiceState> { +export interface NameServiceState { /** get address of domain name */ lookup?: (chainId: ChainId, domain: string) => Promise /** get domain name of address */ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c77b1cd318ec..7fc848cde87b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -660,10 +660,12 @@ importers: '@masknet/web3-shared-solana': workspace:* '@servie/events': ^3.0.0 '@types/ethereum-blockies': ^0.1.0 + '@types/lru-cache': ^7.10.10 '@types/use-subscription': ^1.0.0 async-call-rpc: ^6.0.2 bignumber.js: ^9.0.2 ethereum-blockies: ^0.1.1 + lru-cache: ^7.10.1 react-use: ^17.3.2 unstated-next: ^1.1.0 urlcat: ^2.0.4 @@ -692,6 +694,7 @@ importers: async-call-rpc: 6.0.2 bignumber.js: 9.0.2 ethereum-blockies: 0.1.1 + lru-cache: 7.10.1 react-use: 17.3.2 unstated-next: 1.1.0 urlcat: 2.0.4 @@ -700,6 +703,8 @@ importers: web3: 1.7.3 web3-core-helpers: 1.7.3 web3-utils: 1.7.3 + devDependencies: + '@types/lru-cache': 7.10.10 packages/plugins/CrossChainBridge: specifiers: @@ -19987,7 +19992,7 @@ packages: dev: false /is-fn/1.0.0: - resolution: {integrity: sha1-lUPV3nvPWwiiLsiiC65uKG1RDYw=} + resolution: {integrity: sha512-XoFPJQmsAShb3jEQRfzf2rqXavq7fIqF/jOekp308JlThqrODnMpweVSGilKTCXELfLhltGP2AGgbQGVP8F1dg==} engines: {node: '>=0.10.0'} dev: false @@ -21073,7 +21078,7 @@ packages: resolution: {integrity: sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=} /js-sha3/0.6.1: - resolution: {integrity: sha1-W4n3enR3Z5h39YxKB1JAk0sflcA=} + resolution: {integrity: sha512-2OHj7sAZ9gnJS4lQsgIsTslmqVrNQdDC99bvwYGQKU1w6k/gwsTLeGBfWt8yHCuTOGqk7DXzuVlK8J+dDXnG7A==} dev: false /js-sha3/0.7.0: @@ -21093,7 +21098,7 @@ packages: dev: true /js-tokens/3.0.2: - resolution: {integrity: sha1-mGbfOVECEw449/mWvOtlRDIJwls=} + resolution: {integrity: sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==} dev: false /js-tokens/4.0.0: @@ -21168,7 +21173,7 @@ packages: hasBin: true /jsesc/1.3.0: - resolution: {integrity: sha1-RsP+yMGJKxKwgz25vHYiF226s0s=} + resolution: {integrity: sha512-Mke0DA0QjUWuJlhsE0ZPPhYiJkRap642SmI/4ztCFaUs6V2AiH1sfecc+57NgaryfAA2VR3v6O+CSjC1jZJKOA==} hasBin: true dev: false @@ -21214,7 +21219,7 @@ packages: dev: false /json-rpc-error/2.0.0: - resolution: {integrity: sha1-p6+cICg4tekFxyUOVH8a/3cligI=} + resolution: {integrity: sha512-EwUeWP+KgAZ/xqFpaP6YDAXMtCJi+o/QQpCQFIYyxr01AdADi2y413eM8hSqJcoQym9WMePAJWoaODEJufC4Ug==} dependencies: inherits: 2.0.4 dev: false @@ -21521,7 +21526,7 @@ packages: dev: false /level-iterator-stream/1.3.1: - resolution: {integrity: sha1-5Dt4sagUPm+pek9IXrjqUwNS8u0=} + resolution: {integrity: sha512-1qua0RHNtr4nrZBgYlpV0qHHeHpcRRWTxEZJ8xsemoHAXNL5tbooh4tPEEqIqsbWCAJBmUmkwYK/sW5OrFjWWw==} dependencies: inherits: 2.0.4 level-errors: 1.0.5 @@ -21746,7 +21751,7 @@ packages: dev: false /lodash.assign/4.2.0: - resolution: {integrity: sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=} + resolution: {integrity: sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==} dev: false /lodash.clonedeep/4.5.0: @@ -21784,7 +21789,7 @@ packages: dev: true /lodash.values/4.3.0: - resolution: {integrity: sha1-o6bCsOvsxcLLocF+bmIP6BtT00c=} + resolution: {integrity: sha512-r0RwvdCv8id9TUblb/O7rYPwVy6lerCbcawrfdo9iC/1t1wsNMJknO79WNBgwkH0hIeJ08jmvvESbFpNb4jH0Q==} dev: false /lodash/4.17.21: @@ -21884,7 +21889,7 @@ packages: dev: false /ltgt/2.2.1: - resolution: {integrity: sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=} + resolution: {integrity: sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==} dev: false /magic-string/0.25.9: @@ -22053,7 +22058,7 @@ packages: dev: true /memdown/1.4.1: - resolution: {integrity: sha1-tOThkhdGZP+65BNhqlAPMRnv4hU=} + resolution: {integrity: sha512-iVrGHZB8i4OQfM155xx8akvG9FIj+ht14DX5CQkCTG4EHzZ3d3sgckIf/Lm9ivZalEsFuEVnWv2B2WZvbrro2w==} dependencies: abstract-leveldown: 2.7.2 functional-red-black-tree: 1.0.1 @@ -22099,7 +22104,7 @@ packages: dev: true /memorystream/0.3.1: - resolution: {integrity: sha1-htcJCzDORV1j+64S3aUaR93K+bI=} + resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} engines: {node: '>= 0.10.0'} dev: false @@ -23322,7 +23327,7 @@ packages: resolution: {integrity: sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=} /os-homedir/1.0.2: - resolution: {integrity: sha1-/7xJiDNuDoM94MFox+8VISGqf7M=} + resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==} engines: {node: '>=0.10.0'} dev: false @@ -24106,7 +24111,7 @@ packages: dev: true /promise-to-callback/1.0.0: - resolution: {integrity: sha1-XSp0kBC/tn2WNZj805YHRqaP7vc=} + resolution: {integrity: sha512-uhMIZmKM5ZteDMfLgJnoSq9GCwsNKrYau73Awf1jIy6/eUcuuZ3P+CD9zUv0kJsIUbU+x6uLNIhXhLHDs1pNPA==} engines: {node: '>=0.10.0'} dependencies: is-fn: 1.0.0