Public tags: abstract deposit name tags (#2972)

* Public tags: abstract deposit name tags

Fixes #2964

* rename cex_deposit to cexDeposit
This commit is contained in:
tom goriunov
2025-09-08 11:31:06 +02:00
committed by GitHub
parent 6d135bb291
commit ea91a09ae0
24 changed files with 86 additions and 14 deletions
+1
View File
@@ -31,6 +31,7 @@ export default function parseMetaPayload(meta: AddressMetadataTag['meta']): Addr
'alertBgColor',
'alertTextColor',
'alertStatus',
'cexDeposit',
];
for (const stringField of stringFields) {
+10
View File
@@ -9,6 +9,16 @@ export const nameTag: AddressMetadataTagApi = {
meta: null,
};
export const cexDepositTag: AddressMetadataTagApi = {
name: 'Duck deposit address',
tagType: 'name',
ordinal: 42,
slug: 'duck-deposit-address',
meta: {
cexDeposit: 'true',
},
};
export const customNameTag: AddressMetadataTagApi = {
slug: 'unicorn-uproar',
name: 'Unicorn Uproar',
+3 -1
View File
@@ -169,7 +169,9 @@ export const metatag2: SearchResultMetadataTag = {
metadata: {
name: 'utko',
slug: 'utko',
meta: {},
meta: {
cexDeposit: 'true',
},
tagType: 'name',
ordinal: 1,
},
+1
View File
@@ -36,6 +36,7 @@ export interface AddressMetadataTagApi extends Omit<AddressMetadataTag, 'meta'>
alertBgColor?: string;
alertTextColor?: string;
alertStatus?: string;
cexDeposit?: string;
} | null;
}
@@ -124,6 +124,7 @@ const OpSuperchainAddress = () => {
<EntityTags
tags={ tags }
isLoading={ isLoading }
addressHash={ checkSummedHash }
/>
);
+1
View File
@@ -380,6 +380,7 @@ const AddressPageContent = () => {
const titleContentAfter = (
<EntityTags
tags={ tags }
addressHash={ addressQuery.data?.hash }
isLoading={
isLoading ||
(config.features.userOps.isEnabled && userOpsAccountQuery.isPlaceholderData) ||
Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 41 KiB

+6 -2
View File
@@ -6,19 +6,23 @@ import highlightText from 'lib/highlightText';
import type { TagProps } from 'toolkit/chakra/tag';
import { Tag } from 'toolkit/chakra/tag';
import EntityTagIcon from 'ui/shared/EntityTags/EntityTagIcon';
import { getTagName } from 'ui/shared/EntityTags/utils';
interface Props extends TagProps {
metadata: AddressMetadataTagApi;
searchTerm: string;
addressHash?: string;
}
const SearchResultEntityTag = ({ metadata, searchTerm, ...rest }: Props) => {
const SearchResultEntityTag = ({ metadata, searchTerm, addressHash, ...rest }: Props) => {
const name = getTagName(metadata, addressHash);
return (
<Tag
{ ...rest }
startElement={ <EntityTagIcon data={ metadata } ignoreColor/> }
>
<span dangerouslySetInnerHTML={{ __html: highlightText(metadata.name, searchTerm) }}/>
<span dangerouslySetInnerHTML={{ __html: highlightText(name, searchTerm) }}/>
</Tag>
);
};
+1 -1
View File
@@ -433,7 +433,7 @@ const SearchResultListItem = ({ data, searchTerm, isLoading, addressFormat }: Pr
</Flex>
) }
{ data.type === 'metadata_tag' && (
<SearchResultEntityTag metadata={ data.metadata } searchTerm={ searchTerm }/>
<SearchResultEntityTag metadata={ data.metadata } addressHash={ data.address_hash } searchTerm={ searchTerm }/>
) }
</Flex>
) :
+1 -1
View File
@@ -170,7 +170,7 @@ const SearchResultTableItem = ({ data, searchTerm, isLoading, addressFormat }: P
) }
{ data.type === 'metadata_tag' && (
<TableCell colSpan={ addressName ? 1 : 2 } verticalAlign="middle" textAlign="right">
<SearchResultEntityTag metadata={ data.metadata } searchTerm={ searchTerm }/>
<SearchResultEntityTag metadata={ data.metadata } addressHash={ hash } searchTerm={ searchTerm }/>
</TableCell>
) }
</>
+9
View File
@@ -13,6 +13,15 @@ test('custom name tag +@dark-mode', async({ render }) => {
await expect(component).toHaveScreenshot();
});
test('cex deposit tag', async({ render }) => {
const component = await render(
<Box w="200px">
<EntityTag data={ addressMetadataMock.cexDepositTag } addressHash="0x1234567890123456789012345678901234567890"/>
</Box>,
);
await expect(component).toHaveScreenshot();
});
test('warpcast tag', async({ render }) => {
const component = await render(<Box w="200px"><EntityTag data={ addressMetadataMock.warpcastTag }/></Box>);
await expect(component).toHaveScreenshot();
+4 -3
View File
@@ -11,15 +11,16 @@ import { Tag } from 'toolkit/chakra/tag';
import EntityTagIcon from './EntityTagIcon';
import EntityTagTooltip from './EntityTagTooltip';
import { getTagLinkParams } from './utils';
import { getTagName, getTagLinkParams } from './utils';
interface Props extends HTMLChakraProps<'span'> {
data: TEntityTag;
addressHash?: string;
isLoading?: boolean;
noLink?: boolean;
}
const EntityTag = ({ data, isLoading, noLink, ...rest }: Props) => {
const EntityTag = ({ data, addressHash, isLoading, noLink, ...rest }: Props) => {
const multichainContext = useMultichainContext();
const linkParams = !noLink ? getTagLinkParams(data, multichainContext) : undefined;
@@ -47,7 +48,7 @@ const EntityTag = ({ data, isLoading, noLink, ...rest }: Props) => {
return `@${ data.meta.warpcastHandle }`;
}
return data.name;
return getTagName(data, addressHash);
})();
return (
+13 -4
View File
@@ -13,10 +13,11 @@ import EntityTag from './EntityTag';
interface Props {
className?: string;
tags: Array<TEntityTag>;
addressHash?: string;
isLoading?: boolean;
}
const EntityTags = ({ tags, className, isLoading }: Props) => {
const EntityTags = ({ tags, addressHash, className, isLoading }: Props) => {
const isMobile = useIsMobile();
const visibleNum = isMobile ? 2 : 3;
@@ -43,7 +44,15 @@ const EntityTags = ({ tags, className, isLoading }: Props) => {
if (tags.length > visibleNum) {
return (
<>
{ tags.slice(0, visibleNum).map((tag) => <EntityTag key={ tag.slug } data={ tag } isLoading={ isLoading } maxW={ tagMaxW }/>) }
{ tags.slice(0, visibleNum).map((tag) => (
<EntityTag
key={ tag.slug }
data={ tag }
addressHash={ addressHash }
isLoading={ isLoading }
maxW={ tagMaxW }
/>
)) }
{ metaSuitesPlaceholder }
<PopoverRoot>
<PopoverTrigger>
@@ -54,7 +63,7 @@ const EntityTags = ({ tags, className, isLoading }: Props) => {
<PopoverContent maxW="300px" w="fit-content">
<PopoverBody>
<Flex columnGap={ 2 } rowGap={ 2 } flexWrap="wrap">
{ tags.slice(visibleNum).map((tag) => <EntityTag key={ tag.slug } data={ tag }/>) }
{ tags.slice(visibleNum).map((tag) => <EntityTag key={ tag.slug } data={ tag } addressHash={ addressHash }/>) }
</Flex>
</PopoverBody>
</PopoverContent>
@@ -65,7 +74,7 @@ const EntityTags = ({ tags, className, isLoading }: Props) => {
return (
<>
{ tags.map((tag) => <EntityTag key={ tag.slug } data={ tag } isLoading={ isLoading } maxW={ tagMaxW }/>) }
{ tags.map((tag) => <EntityTag key={ tag.slug } data={ tag } addressHash={ addressHash } isLoading={ isLoading } maxW={ tagMaxW }/>) }
{ metaSuitesPlaceholder }
</>
);
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

+8
View File
@@ -26,3 +26,11 @@ export function getTagLinkParams(data: EntityTag, multichainContext?: TMultichai
};
}
}
export function getTagName(data: EntityTag, addressHash?: string) {
if (data.tagType === 'name' && data.meta?.cexDeposit === 'true' && addressHash) {
return `${ data.name } (${ addressHash.slice(0, 2 + 5) })`;
}
return data.name;
}
@@ -219,6 +219,22 @@ test('with name tag', async({ render }) => {
await expect(component).toHaveScreenshot();
});
test.describe('with cex deposit tag', () => {
test.use({ viewport: { width: 500, height: 140 } });
test('default', async({ render }) => {
const address = { ...addressMock.withNameTag, metadata: { reputation: 1, tags: [ metadataMock.cexDepositTag ] } };
const component = await render(
<AddressEntity
address={ address }
/>,
);
await expect(component).toHaveScreenshot();
});
});
test('external link', async({ render }) => {
const component = await render(
<AddressEntity
+9 -1
View File
@@ -12,6 +12,7 @@ import getIconUrl from 'lib/multichain/getIconUrl';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { Tooltip } from 'toolkit/chakra/tooltip';
import * as EntityBase from 'ui/shared/entities/base/components';
import { getTagName } from 'ui/shared/EntityTags/utils';
import type { IconName } from 'ui/shared/IconSvg';
import { distributeEntityProps, getContentProps, getIconProps } from '../base/utils';
@@ -119,7 +120,14 @@ export type ContentProps = Omit<EntityBase.ContentBaseProps, 'text'> & Pick<Enti
const Content = chakra((props: ContentProps) => {
const displayedAddress = getDisplayedAddress(props.address, props.altHash);
const nameTag = props.address.metadata?.tags.find(tag => tag.tagType === 'name')?.name;
const nameTag = (() => {
const tagData = props.address.metadata?.tags.find(tag => tag.tagType === 'name');
if (!tagData || !tagData.name) {
return;
}
return getTagName(tagData, props.address.hash);
})();
const nameText = nameTag || props.address.ens_domain_name || props.address.name;
const isProxy = props.address.implementations && props.address.implementations.length > 0 && props.address.proxy_type !== 'eip7702';
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

@@ -53,7 +53,7 @@ const SearchBarSuggestAddress = ({ data, isMobile, searchTerm, addressFormat }:
</Flex>
);
const tagEl = data.type === 'metadata_tag' ? (
<SearchResultEntityTag metadata={ data.metadata } searchTerm={ searchTerm } ml={{ base: 0, lg: 'auto' }}/>
<SearchResultEntityTag metadata={ data.metadata } addressHash={ hash } searchTerm={ searchTerm } ml={{ base: 0, lg: 'auto' }}/>
) : null;
const addressEl = <HashStringShortenDynamic hash={ hash } noTooltip/>;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 42 KiB

+1
View File
@@ -100,6 +100,7 @@ const TokenPageTitle = ({ tokenQuery, addressQuery, hash }: Props) => {
<EntityTags
isLoading={ isLoading || (config.features.addressMetadata.isEnabled && addressMetadataQuery.isPending) }
tags={ tags }
addressHash={ addressQuery.data?.hash }
flexGrow={ 1 }
/>
</>