diff --git a/components/allowances/dashboard/cells/AllowanceCell.tsx b/components/allowances/dashboard/cells/AllowanceCell.tsx index 14832bc9..a4187d9c 100644 --- a/components/allowances/dashboard/cells/AllowanceCell.tsx +++ b/components/allowances/dashboard/cells/AllowanceCell.tsx @@ -1,8 +1,4 @@ -import { PencilIcon } from '@heroicons/react/24/outline'; -import ControlsWrapper from 'components/allowances/controls/ControlsWrapper'; -import Button from 'components/common/Button'; import WithHoverTooltip from 'components/common/WithHoverTooltip'; -import { useRevoke } from 'lib/hooks/ethereum/useRevoke'; import type { AllowanceData, OnUpdate } from 'lib/interfaces'; import { getAllowanceI18nValues } from 'lib/utils/allowances'; import { SECOND } from 'lib/utils/time'; @@ -21,9 +17,9 @@ const AllowanceCell = ({ allowance, onUpdate }: Props) => { const t = useTranslations(); const locale = useLocale(); const [editing, setEditing] = useState(); - const { update } = useRevoke(allowance, onUpdate); const { i18nKey, amount, tokenId, symbol } = getAllowanceI18nValues(allowance); + const classes = twMerge( !allowance.spender && 'text-zinc-500 dark:text-zinc-400', 'flex items-center gap-2', diff --git a/components/allowances/dashboard/cells/AssetCell.tsx b/components/allowances/dashboard/cells/AssetCell.tsx index c707b69b..cf9362c6 100644 --- a/components/allowances/dashboard/cells/AssetCell.tsx +++ b/components/allowances/dashboard/cells/AssetCell.tsx @@ -1,12 +1,13 @@ 'use client'; -import ChainOverlayLogo from 'components/common/ChainOverlayLogo'; import Href from 'components/common/Href'; import WithHoverTooltip from 'components/common/WithHoverTooltip'; import type { BaseTokenData } from 'lib/interfaces'; import { getChainExplorerUrl } from 'lib/utils/chains'; import { formatBalance, formatFiatBalance } from 'lib/utils/formatting'; import { useLayoutEffect, useRef, useState } from 'react'; +import Image from 'next/image'; +import { getVeChainAssetIcon } from 'lib/utils/tokens'; interface Props { asset: BaseTokenData; @@ -15,6 +16,7 @@ interface Props { const AssetCell = ({ asset }: Props) => { const ref = useRef(null); const [showTooltip, setShowTooltip] = useState(false); + const [tokenIcon, setTokenIcon] = useState(undefined); // This is pretty hacky, but it works to detect that we're on the address page, so single chain usage const isOnAddressPage = typeof window !== 'undefined' && window.location.pathname.includes('/address/'); @@ -25,6 +27,14 @@ const AssetCell = ({ asset }: Props) => { } }, [ref]); + useLayoutEffect(() => { + const loadIcon = async () => { + const icon = await getVeChainAssetIcon(asset.contract); + setTokenIcon(icon); + }; + loadIcon(); + }, [asset.contract]); + const explorerUrl = `${getChainExplorerUrl(asset.chainId)}/account/${asset.contract.address}`; let link = ( @@ -44,13 +54,17 @@ const AssetCell = ({ asset }: Props) => {
- + {tokenIcon ? ( + {asset.metadata.symbol} + ) : ( +
+ )} {link}
diff --git a/lib/utils/tokens.ts b/lib/utils/tokens.ts index beab1cbc..0f85d4ad 100644 --- a/lib/utils/tokens.ts +++ b/lib/utils/tokens.ts @@ -22,6 +22,67 @@ import { deduplicateArray } from '.'; import { withFallback } from './promises'; import { formatFixedPointBigInt } from './formatting'; +interface VeChainToken { + name: string; + symbol: string; + decimals: number; + address: string; + desc?: string; + icon?: string; + totalSupply?: string; +} + +interface VeChainNFT { + name: string; + symbol: string; + address: string; + desc?: string; + icon?: string; +} + +let veChainTokens: VeChainToken[] = []; +let veChainNFTs: VeChainNFT[] = []; +let tokensInitialized = false; +let nftsInitialized = false; + +export const initVeChainTokens = async () => { + if (tokensInitialized) return; + + try { + const response = await fetch('https://vechain.github.io/token-registry/main.json'); + veChainTokens = await response.json(); + tokensInitialized = true; + } catch (error) { + console.error('Failed to load VeChain token registry:', error); + } +}; + +export const initVeChainNFTs = async () => { + if (nftsInitialized) return; + + try { + const response = await fetch('https://vechain.github.io/nft-registry/main.json'); + veChainNFTs = await response.json(); + nftsInitialized = true; + } catch (error) { + console.error('Failed to load VeChain NFT registry:', error); + } +}; + +export const getVeChainAssetIcon = async (contract: TokenContract): Promise => { + if (isErc721Contract(contract)) { + await initVeChainNFTs(); + const nft = veChainNFTs.find(t => t.address.toLowerCase() === contract.address.toLowerCase()); + if (!nft?.icon) return undefined; + return `https://vechain.github.io/nft-registry/${nft.icon}`; + } else { + await initVeChainTokens(); + const token = veChainTokens.find(t => t.address.toLowerCase() === contract.address.toLowerCase()); + if (!token?.icon) return undefined; + return `https://vechain.github.io/token-registry/assets/${token.icon}`; + } +}; + export const isSpamToken = (symbol: string) => { const spamRegexes = [ // Includes http(s):// diff --git a/next.config.js b/next.config.js index f9b29fd0..7d142d95 100644 --- a/next.config.js +++ b/next.config.js @@ -13,6 +13,20 @@ const nextConfig = { // Optimize client bundle removeConsole: process.env.NODE_ENV === 'production', }, + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: 'vechain.github.io', + pathname: '/token-registry/assets/**', + }, + { + protocol: 'https', + hostname: 'vechain.github.io', + pathname: '/nft-registry/assets/**', + }, + ], + }, modularizeImports: { '@heroicons/react/24/outline': { transform: '@heroicons/react/24/outline/{{member}}',