Files
explore/ui/shared/address/AddressAddToWallet.tsx
T
0c5a297834 "Add chain" button in the header (#3084)
* redesign "add chain" button

* fix wallet detection

* [skip ci] update default value for ENV

* fix tests

* return button to the footer and add source to mixpanel event

* regcred from helmfile for review

* [skip ci] remove wallets env from values for demo stand

* use react-query to cache detection result

* add cookie to hide button in the topbar

* add prefix to svg ids to fix rabby icon

* update screenshot

* remove unnecessary comment

---------

Co-authored-by: Nick Zenchik <[email protected]>
2025-11-17 18:15:21 +01:00

157 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';
const feature = config.features.web3Wallet;
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;
}
const AddressAddToWallet = ({ className, token, tokenId, isLoading, variant = 'icon', iconSize = 6 }: Props) => {
const { data: { wallet, provider } = {} } = useProvider();
const switchOrAddChain = useSwitchOrAddChain();
const isMobile = useIsMobile();
const { trackUsage } = useRewardsActivity();
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));