MarketSwitcher.tsx
1 import { ChevronDownIcon } from '@heroicons/react/outline'; 2 import { Trans } from '@lingui/macro'; 3 import { 4 Box, 5 BoxProps, 6 ListItemText, 7 MenuItem, 8 SvgIcon, 9 TextField, 10 Tooltip, 11 Typography, 12 useMediaQuery, 13 useTheme, 14 } from '@mui/material'; 15 import React, { useState } from 'react'; 16 import { useRootStore } from 'src/store/root'; 17 import { BaseNetworkConfig } from 'src/ui-config/networksConfig'; 18 import { DASHBOARD } from 'src/utils/mixPanelEvents'; 19 import { useShallow } from 'zustand/shallow'; 20 21 import { 22 availableMarkets, 23 CustomMarket, 24 ENABLE_TESTNET, 25 MarketDataType, 26 marketsData, 27 networkConfigs, 28 STAGING_ENV, 29 } from '../utils/marketsAndNetworksConfig'; 30 import StyledToggleButton from './StyledToggleButton'; 31 import StyledToggleButtonGroup from './StyledToggleButtonGroup'; 32 33 export const getMarketInfoById = (marketId: CustomMarket) => { 34 const market: MarketDataType = marketsData[marketId as CustomMarket]; 35 const network: BaseNetworkConfig = networkConfigs[market.chainId]; 36 const logo = market.logo || network.networkLogoPath; 37 38 return { market, logo }; 39 }; 40 41 export const getMarketHelpData = (marketName: string) => { 42 const testChains = [ 43 'Görli', 44 'Ropsten', 45 'Mumbai', 46 'Sepolia', 47 'Fuji', 48 'Testnet', 49 'Kovan', 50 'Rinkeby', 51 ]; 52 const arrayName = marketName.split(' '); 53 const testChainName = arrayName.filter((el) => testChains.indexOf(el) > -1); 54 const marketTitle = arrayName.filter((el) => !testChainName.includes(el)).join(' '); 55 56 return { 57 name: marketTitle, 58 testChainName: testChainName[0], 59 }; 60 }; 61 62 export type Market = { 63 marketTitle: string; 64 networkName: string; 65 networkLogo: string; 66 selected?: boolean; 67 }; 68 69 type MarketLogoProps = { 70 size: number; 71 logo: string; 72 testChainName?: string; 73 sx?: BoxProps; 74 }; 75 76 export const MarketLogo = ({ size, logo, testChainName, sx }: MarketLogoProps) => { 77 return ( 78 <Box sx={{ mr: 2, width: size, height: size, position: 'relative', ...sx }}> 79 <img src={logo} alt="" width="100%" height="100%" /> 80 81 {testChainName && ( 82 <Tooltip title={testChainName} arrow> 83 <Box 84 sx={{ 85 bgcolor: '#29B6F6', 86 width: '16px', 87 height: '16px', 88 borderRadius: '50%', 89 color: 'common.white', 90 fontSize: '12px', 91 lineHeight: '16px', 92 display: 'flex', 93 alignItems: 'center', 94 justifyContent: 'center', 95 position: 'absolute', 96 right: '-2px', 97 bottom: '-2px', 98 }} 99 > 100 {testChainName.split('')[0]} 101 </Box> 102 </Tooltip> 103 )} 104 </Box> 105 ); 106 }; 107 108 enum SelectedMarketVersion { 109 V2, 110 V3, 111 } 112 113 export const MarketSwitcher = () => { 114 const [selectedMarketVersion, setSelectedMarketVersion] = useState<SelectedMarketVersion>( 115 SelectedMarketVersion.V3 116 ); 117 const theme = useTheme(); 118 const upToLG = useMediaQuery(theme.breakpoints.up('lg')); 119 const downToXSM = useMediaQuery(theme.breakpoints.down('xsm')); 120 const [trackEvent, currentMarket, setCurrentMarket] = useRootStore( 121 useShallow((store) => [store.trackEvent, store.currentMarket, store.setCurrentMarket]) 122 ); 123 124 const isV3MarketsAvailable = availableMarkets 125 .map((marketId: CustomMarket) => { 126 const { market } = getMarketInfoById(marketId); 127 128 return market.v3; 129 }) 130 .some((item) => !!item); 131 132 const handleMarketSelect = (e: React.ChangeEvent<HTMLInputElement>) => { 133 trackEvent(DASHBOARD.CHANGE_MARKET, { market: e.target.value }); 134 setCurrentMarket(e.target.value as unknown as CustomMarket); 135 }; 136 137 const marketBlurbs: { [key: string]: JSX.Element } = { 138 proto_mainnet_v3: ( 139 <Trans>Main Ethereum market with the largest selection of assets and yield options</Trans> 140 ), 141 proto_lido_v3: ( 142 <Trans>Optimized for efficiency and risk by supporting blue-chip collateral assets</Trans> 143 ), 144 }; 145 146 return ( 147 <TextField 148 select 149 aria-label="select market" 150 data-cy="marketSelector" 151 value={currentMarket} 152 onChange={handleMarketSelect} 153 sx={{ 154 mr: 2, 155 '& .MuiOutlinedInput-notchedOutline': { 156 border: 'none', 157 }, 158 }} 159 SelectProps={{ 160 native: false, 161 className: 'MarketSwitcher__select', 162 IconComponent: () => null, 163 renderValue: (marketId) => { 164 const { market, logo } = getMarketInfoById(marketId as CustomMarket); 165 166 return ( 167 <Box> 168 {/* Main Row with Market Name */} 169 <Box sx={{ display: 'flex', alignItems: 'center' }}> 170 <MarketLogo 171 size={upToLG ? 32 : 28} 172 logo={logo} 173 testChainName={getMarketHelpData(market.marketTitle).testChainName} 174 /> 175 <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}> 176 <Typography 177 variant={upToLG ? 'display1' : 'h1'} 178 sx={{ 179 fontSize: downToXSM ? '1.55rem' : undefined, 180 color: 'common.white', 181 mr: 1, 182 }} 183 > 184 {getMarketHelpData(market.marketTitle).name} {market.isFork ? 'Fork' : ''} 185 {upToLG && 186 (currentMarket === 'proto_mainnet_v3' || currentMarket === 'proto_lido_v3') 187 ? 'Instance' 188 : ' Market'} 189 </Typography> 190 {market.v3 ? ( 191 <Box sx={{ display: 'flex', alignItems: 'center' }}> 192 <Box 193 sx={{ 194 color: '#fff', 195 px: 2, 196 borderRadius: '12px', 197 background: (theme) => theme.palette.gradients.aaveGradient, 198 display: 'flex', 199 alignItems: 'center', 200 }} 201 > 202 <Typography variant="subheader2">V3</Typography> 203 </Box> 204 <SvgIcon 205 fontSize="medium" 206 sx={{ 207 ml: 1, 208 color: '#F1F1F3', 209 }} 210 > 211 <ChevronDownIcon /> 212 </SvgIcon> 213 </Box> 214 ) : ( 215 <Box sx={{ display: 'flex', alignItems: 'center' }}> 216 <Box 217 sx={{ 218 color: '#A5A8B6', 219 px: 2, 220 borderRadius: '12px', 221 backgroundColor: '#383D51', 222 display: 'flex', 223 alignItems: 'center', 224 }} 225 > 226 <Typography variant="subheader2">V2</Typography> 227 </Box> 228 <SvgIcon 229 fontSize="medium" 230 sx={{ 231 ml: 1, 232 color: '#F1F1F3', 233 }} 234 > 235 <ChevronDownIcon /> 236 </SvgIcon> 237 </Box> 238 )} 239 </Box> 240 </Box> 241 242 {marketBlurbs[currentMarket] && ( 243 <Typography 244 sx={{ 245 color: 'common.white', 246 mt: 0.5, 247 fontSize: '0.85rem', 248 wordWrap: 'break-word', 249 whiteSpace: 'normal', 250 lineHeight: 1.3, 251 maxWidth: '100%', 252 }} 253 > 254 {marketBlurbs[currentMarket]} 255 </Typography> 256 )} 257 </Box> 258 ); 259 }, 260 261 sx: { 262 '&.MarketSwitcher__select .MuiSelect-outlined': { 263 pl: 0, 264 py: 0, 265 backgroundColor: 'transparent !important', 266 }, 267 '.MuiSelect-icon': { color: '#F1F1F3' }, 268 }, 269 MenuProps: { 270 anchorOrigin: { 271 vertical: 'bottom', 272 horizontal: 'right', 273 }, 274 transformOrigin: { 275 vertical: 'top', 276 horizontal: 'right', 277 }, 278 PaperProps: { 279 style: { 280 minWidth: 240, 281 }, 282 variant: 'outlined', 283 elevation: 0, 284 }, 285 }, 286 }} 287 > 288 <Box> 289 <Typography variant="subheader2" color="text.secondary" sx={{ px: 4, pt: 2 }}> 290 <Trans> 291 {ENABLE_TESTNET || STAGING_ENV ? 'Select Aave Testnet Market' : 'Select Aave Market'} 292 </Trans> 293 </Typography> 294 </Box> 295 {isV3MarketsAvailable && ( 296 <Box sx={{ mx: '18px', display: 'flex', justifyContent: 'center' }}> 297 <StyledToggleButtonGroup 298 value={selectedMarketVersion} 299 exclusive 300 onChange={(_, value) => { 301 if (value !== null) { 302 setSelectedMarketVersion(value); 303 } 304 }} 305 sx={{ 306 width: '100%', 307 height: '36px', 308 background: theme.palette.primary.main, 309 border: `1px solid ${ 310 theme.palette.mode === 'dark' ? 'rgba(235, 235, 237, 0.12)' : '#1B2030' 311 }`, 312 borderRadius: '6px', 313 marginTop: '16px', 314 marginBottom: '12px', 315 padding: '2px', 316 }} 317 > 318 <StyledToggleButton 319 value={SelectedMarketVersion.V3} 320 data-cy={`markets_switch_button_v3`} 321 sx={{ 322 backgroundColor: theme.palette.mode === 'dark' ? '#EAEBEF' : '#383D51', 323 '&.Mui-selected, &.Mui-selected:hover': { 324 backgroundColor: theme.palette.mode === 'dark' ? '#292E41' : '#FFFFFF', 325 boxShadow: '0px 1px 0px rgba(0, 0, 0, 0.05)', 326 }, 327 borderRadius: '4px', 328 }} 329 > 330 <Typography 331 variant="buttonM" 332 sx={ 333 selectedMarketVersion === SelectedMarketVersion.V3 334 ? { 335 backgroundImage: (theme) => theme.palette.gradients.aaveGradient, 336 backgroundClip: 'text', 337 color: 'transparent', 338 } 339 : { 340 color: theme.palette.mode === 'dark' ? '#0F121D' : '#FFFFFF', 341 } 342 } 343 > 344 <Trans>Version 3</Trans> 345 </Typography> 346 </StyledToggleButton> 347 <StyledToggleButton 348 value={SelectedMarketVersion.V2} 349 data-cy={`markets_switch_button_v2`} 350 sx={{ 351 backgroundColor: theme.palette.mode === 'dark' ? '#EAEBEF' : '#383D51', 352 '&.Mui-selected, &.Mui-selected:hover': { 353 backgroundColor: theme.palette.mode === 'dark' ? '#292E41' : '#FFFFFF', 354 boxShadow: '0px 1px 0px rgba(0, 0, 0, 0.05)', 355 }, 356 borderRadius: '4px', 357 }} 358 > 359 <Typography 360 variant="buttonM" 361 sx={ 362 selectedMarketVersion === SelectedMarketVersion.V2 363 ? { 364 backgroundImage: (theme) => theme.palette.gradients.aaveGradient, 365 backgroundClip: 'text', 366 color: 'transparent', 367 } 368 : { 369 color: theme.palette.mode === 'dark' ? '#0F121D' : '#FFFFFF', 370 } 371 } 372 > 373 <Trans>Version 2</Trans> 374 </Typography> 375 </StyledToggleButton> 376 </StyledToggleButtonGroup> 377 </Box> 378 )} 379 {availableMarkets.map((marketId: CustomMarket) => { 380 const { market, logo } = getMarketInfoById(marketId); 381 const marketNaming = getMarketHelpData(market.marketTitle); 382 return ( 383 <MenuItem 384 key={marketId} 385 data-cy={`marketSelector_${marketId}`} 386 value={marketId} 387 sx={{ 388 '.MuiListItemIcon-root': { minWidth: 'unset' }, 389 display: 390 (market.v3 && selectedMarketVersion === SelectedMarketVersion.V2) || 391 (!market.v3 && selectedMarketVersion === SelectedMarketVersion.V3) 392 ? 'none' 393 : 'flex', 394 }} 395 > 396 <MarketLogo size={32} logo={logo} testChainName={marketNaming.testChainName} /> 397 <ListItemText sx={{ mr: 0 }}> 398 {marketNaming.name} {market.isFork ? 'Fork' : ''} 399 </ListItemText> 400 <ListItemText sx={{ textAlign: 'right' }}> 401 <Typography color="text.muted" variant="description"> 402 {marketNaming.testChainName} 403 </Typography> 404 </ListItemText> 405 </MenuItem> 406 ); 407 })} 408 </TextField> 409 ); 410 };