AddTokenDropdown.tsx
1 import { Trans } from '@lingui/macro'; 2 import { Box, Menu, MenuItem, Typography } from '@mui/material'; 3 import * as React from 'react'; 4 import { useEffect, useState } from 'react'; 5 import { CircleIcon } from 'src/components/CircleIcon'; 6 import { WalletIcon } from 'src/components/icons/WalletIcon'; 7 import { Base64Token, TokenIcon } from 'src/components/primitives/TokenIcon'; 8 import { ComputedReserveData } from 'src/hooks/app-data-provider/useAppDataProvider'; 9 import { ERC20TokenType } from 'src/libs/web3-data-provider/Web3Provider'; 10 import { useRootStore } from 'src/store/root'; 11 import { RESERVE_DETAILS } from 'src/utils/mixPanelEvents'; 12 13 interface AddTokenDropdownProps { 14 poolReserve: ComputedReserveData; 15 downToSM: boolean; 16 switchNetwork: (chainId: number) => Promise<void>; 17 addERC20Token: (args: ERC20TokenType) => Promise<boolean>; 18 currentChainId: number; 19 connectedChainId: number; 20 hideAToken?: boolean; 21 } 22 23 export const AddTokenDropdown = ({ 24 poolReserve, 25 downToSM, 26 switchNetwork, 27 addERC20Token, 28 currentChainId, 29 connectedChainId, 30 hideAToken, 31 }: AddTokenDropdownProps) => { 32 const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null); 33 const [changingNetwork, setChangingNetwork] = useState(false); 34 const [underlyingBase64, setUnderlyingBase64] = useState(''); 35 const [aTokenBase64, setATokenBase64] = useState(''); 36 const open = Boolean(anchorEl); 37 const trackEvent = useRootStore((store) => store.trackEvent); 38 39 const handleClick = (event: React.MouseEvent<HTMLDivElement>) => { 40 setAnchorEl(event.currentTarget); 41 }; 42 const handleClose = () => { 43 setAnchorEl(null); 44 }; 45 46 // The switchNetwork function has no return type, so to detect if a user successfully switched networks before adding token to wallet, check the selected vs connected chain id 47 useEffect(() => { 48 if (changingNetwork && currentChainId === connectedChainId) { 49 addERC20Token({ 50 address: poolReserve.underlyingAsset, 51 decimals: poolReserve.decimals, 52 symbol: poolReserve.symbol, 53 image: !/_/.test(poolReserve.iconSymbol) ? underlyingBase64 : undefined, 54 }); 55 setChangingNetwork(false); 56 } 57 }, [ 58 currentChainId, 59 connectedChainId, 60 changingNetwork, 61 addERC20Token, 62 poolReserve?.underlyingAsset, 63 poolReserve?.decimals, 64 poolReserve?.symbol, 65 poolReserve?.iconSymbol, 66 underlyingBase64, 67 ]); 68 69 if (!poolReserve) { 70 return null; 71 } 72 73 return ( 74 <> 75 {/* Load base64 token symbol for adding underlying and aTokens to wallet */} 76 {poolReserve?.symbol && !/_/.test(poolReserve.symbol) && ( 77 <> 78 <Base64Token 79 symbol={poolReserve.iconSymbol} 80 onImageGenerated={setUnderlyingBase64} 81 aToken={false} 82 /> 83 {!hideAToken && ( 84 <Base64Token 85 symbol={poolReserve.iconSymbol} 86 onImageGenerated={setATokenBase64} 87 aToken={true} 88 /> 89 )} 90 </> 91 )} 92 <Box onClick={handleClick}> 93 <CircleIcon tooltipText="Add token to wallet" downToSM={downToSM}> 94 <Box 95 onClick={() => { 96 trackEvent(RESERVE_DETAILS.ADD_TOKEN_TO_WALLET_DROPDOWN, { 97 asset: poolReserve.underlyingAsset, 98 assetName: poolReserve.name, 99 }); 100 }} 101 sx={{ 102 display: 'inline-flex', 103 alignItems: 'center', 104 '&:hover': { 105 '.Wallet__icon': { opacity: '0 !important' }, 106 '.Wallet__iconHover': { opacity: '1 !important' }, 107 }, 108 cursor: 'pointer', 109 }} 110 > 111 <WalletIcon sx={{ width: '14px', height: '14px', '&:hover': { stroke: '#F1F1F3' } }} /> 112 </Box> 113 </CircleIcon> 114 </Box> 115 <Menu 116 anchorEl={anchorEl} 117 open={open} 118 onClose={handleClose} 119 MenuListProps={{ 120 'aria-labelledby': 'basic-button', 121 }} 122 keepMounted={true} 123 data-cy="addToWaletSelector" 124 > 125 <Box sx={{ px: 4, pt: 3, pb: 2 }}> 126 <Typography variant="secondary12" color="text.secondary"> 127 <Trans>Underlying token</Trans> 128 </Typography> 129 </Box> 130 131 <MenuItem 132 key="underlying" 133 value="underlying" 134 divider 135 onClick={() => { 136 if (currentChainId !== connectedChainId) { 137 switchNetwork(currentChainId).then(() => { 138 setChangingNetwork(true); 139 }); 140 } else { 141 trackEvent(RESERVE_DETAILS.ADD_TO_WALLET, { 142 type: 'Underlying token', 143 asset: poolReserve.underlyingAsset, 144 assetName: poolReserve.name, 145 }); 146 147 addERC20Token({ 148 address: poolReserve.underlyingAsset, 149 decimals: poolReserve.decimals, 150 symbol: poolReserve.symbol, 151 image: !/_/.test(poolReserve.symbol) ? underlyingBase64 : undefined, 152 }); 153 } 154 handleClose(); 155 }} 156 > 157 <TokenIcon symbol={poolReserve.iconSymbol} sx={{ fontSize: '20px' }} /> 158 <Typography variant="subheader1" sx={{ ml: 3 }} noWrap data-cy={`assetName`}> 159 {poolReserve.symbol} 160 </Typography> 161 </MenuItem> 162 {!hideAToken && ( 163 <Box> 164 <Box sx={{ px: 4, pt: 3, pb: 2 }}> 165 <Typography variant="secondary12" color="text.secondary"> 166 <Trans>Aave aToken</Trans> 167 </Typography> 168 </Box> 169 <MenuItem 170 key="atoken" 171 value="atoken" 172 onClick={() => { 173 if (currentChainId !== connectedChainId) { 174 switchNetwork(currentChainId).then(() => { 175 setChangingNetwork(true); 176 }); 177 } else { 178 trackEvent(RESERVE_DETAILS.ADD_TO_WALLET, { 179 asset: poolReserve.underlyingAsset, 180 assetName: poolReserve.name, 181 }); 182 183 addERC20Token({ 184 address: poolReserve.aTokenAddress, 185 decimals: poolReserve.decimals, 186 symbol: '', 187 image: !/_/.test(poolReserve.symbol) ? aTokenBase64 : undefined, 188 }); 189 } 190 handleClose(); 191 }} 192 > 193 <TokenIcon symbol={poolReserve.iconSymbol} sx={{ fontSize: '20px' }} aToken={true} /> 194 <Typography variant="subheader1" sx={{ ml: 3 }} noWrap data-cy={`assetName`}> 195 {`a${poolReserve.symbol}`} 196 </Typography> 197 </MenuItem> 198 </Box> 199 )} 200 </Menu> 201 </> 202 ); 203 };