Files
explore/ui/shared/address/AddressAddToWallet.tsx
tom goriunovandGitHub ebbee2ba57 Multichain explorer v2 (#3259)
* Support redirect to a correct view from search

* show domains on address page

* migrate to new API version to display address domains

* another api update

* new design for chain widget on home page + tests

* chain widget styles for mobile devices

* ecosystems page

* update types

* add ecosystems to navigation

* address portfolio: pt.1

* portfolio tokens list

* filter portfolio by chain

* skip redirect when navigating to search result by clicking on "show all results" link

* net worth block design

* add filter input to chain select and fix tokens table

* calculate address portfolio

* adjust card logic

* support custom token types in multichain explorer

* handle case when new worth is 0

* add text filter to tokens list

* fixes

* demo envs

* portfolio tests

* ecosystems test

* change signature for getTokenTypes function

* fix adding token to the wallet from the list

* lazy load all address domains

* show contract creator and implementations info

* fix tests

* add poor reputation tokens toggle

* tweaks

* dim celo native tokens

* disable env validator

* fix tests

* remove tokens with 0 usd value from top tokens

* fixes

* preserve selected chain when switching tabs on address page

* add hints to stats widgets on home page

* update envs for demo

* fix portfolio cards and home page

* update styles for contract creator and implementations

* fix select issues

* add chains from cluster to network menu

* hide csv export link on empty logs page

* update screenshots

* rollback select fix

* update cut length calculation for portfolio cards

* fix chain select

* update screenshots

* fix tests

* more fixes

* add sorting to ecosystems table

* fix test

* fixes

* fix loading state in ecosystems page

* review fixes

* fix tests
2026-02-25 10:45:09 +01:00

158 lines
4.5 KiB
TypeScript

import { Box, chakra } from '@chakra-ui/react';
import React from 'react';
import type { WatchAssetParams } from 'viem';
import type { TokenInfo } from 'types/api/token';
import config from 'configs/app';
import useIsMobile from 'lib/hooks/useIsMobile';
import useRewardsActivity from 'lib/hooks/useRewardsActivity';
import * as mixpanel from 'lib/mixpanel/index';
import useProvider from 'lib/web3/useProvider';
import useSwitchOrAddChain from 'lib/web3/useSwitchOrAddChain';
import { WALLETS_INFO } from 'lib/web3/wallets';
import { IconButton } from 'toolkit/chakra/icon-button';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { toaster } from 'toolkit/chakra/toaster';
import { Tooltip } from 'toolkit/chakra/tooltip';
import IconSvg from 'ui/shared/IconSvg';
function getRequestParams(token: TokenInfo, tokenId?: string): WatchAssetParams | undefined {
switch (token.type) {
case 'ERC-20':
return {
type: 'ERC20',
options: {
address: token.address_hash,
symbol: token.symbol || '',
decimals: Number(token.decimals ?? '18'),
image: token.icon_url || '',
},
};
case 'ERC-721':
case 'ERC-1155': {
if (!tokenId) {
return;
}
return {
type: token.type === 'ERC-721' ? 'ERC721' : 'ERC1155',
options: {
address: token.address_hash,
tokenId: tokenId,
},
} as never; // There is no official EIP, and therefore no typings for these token types.
}
default:
return;
}
}
interface Props {
className?: string;
token: TokenInfo;
tokenId?: string;
isLoading?: boolean;
variant?: 'icon' | 'button';
iconSize?: number;
chainConfig?: typeof config;
}
const AddressAddToWallet = ({ className, token, tokenId, isLoading, variant = 'icon', iconSize = 6, chainConfig }: Props) => {
const { data: { wallet, provider } = {} } = useProvider();
const switchOrAddChain = useSwitchOrAddChain({ chainConfig });
const isMobile = useIsMobile();
const { trackUsage } = useRewardsActivity();
const feature = (chainConfig ?? config).features.web3Wallet;
const handleClick = React.useCallback(async() => {
if (!wallet) {
return;
}
try {
const params = getRequestParams(token, tokenId);
if (!params) {
throw new Error('Unsupported token type');
}
// switch to the correct network otherwise the token will be added to the wrong one
await switchOrAddChain();
const wasAdded = await provider?.request?.({
method: 'wallet_watchAsset',
params,
});
if (wasAdded) {
toaster.success({
title: 'Success',
description: 'Successfully added token to your wallet',
});
await trackUsage('add_token');
mixpanel.logEvent(mixpanel.EventTypes.ADD_TO_WALLET, {
Target: 'token',
Wallet: wallet,
Token: token.symbol || '',
});
}
} catch (error) {
toaster.error({
title: 'Error',
description: (error as Error)?.message || 'Something went wrong',
});
}
}, [ wallet, token, tokenId, switchOrAddChain, provider, trackUsage ]);
if (!provider || !wallet) {
return null;
}
if (isLoading) {
return <Skeleton loading className={ className } boxSize={ iconSize } borderRadius="base"/>;
}
const canBeAdded = (
// MetaMask can add NFTs now, but this is still experimental feature, and doesn't work on mobile devices
// https://docs.metamask.io/wallet/how-to/display/tokens/#display-nfts
wallet === 'metamask' &&
[ 'ERC-721', 'ERC-1155' ].includes(token.type) &&
tokenId &&
!isMobile
) || token.type === 'ERC-20';
if (!feature.isEnabled || !canBeAdded) {
return null;
}
if (variant === 'button') {
return (
<Tooltip content={ `Add token to ${ WALLETS_INFO[wallet].name }` }>
<IconButton
className={ className }
aria-label="Add token to wallet"
variant="icon_background"
size="md"
onClick={ handleClick }
>
<IconSvg name={ WALLETS_INFO[wallet].icon }/>
</IconButton>
</Tooltip>
);
}
return (
<Tooltip content={ `Add token to ${ WALLETS_INFO[wallet].name }` }>
<Box className={ className } display="inline-flex" cursor="pointer" onClick={ handleClick } flexShrink={ 0 } aria-label="Add token to wallet">
<IconSvg name={ WALLETS_INFO[wallet].icon } boxSize={ iconSize }/>
</Box>
</Tooltip>
);
};
export default React.memo(chakra(AddressAddToWallet));