diff --git a/src/components/Logo/AssetLogo.tsx b/src/components/Logo/AssetLogo.tsx
index f418f8bf..9cf18c1a 100644
--- a/src/components/Logo/AssetLogo.tsx
+++ b/src/components/Logo/AssetLogo.tsx
@@ -1,6 +1,6 @@
import { ChainId, Currency } from '@vnaysn/jediswap-sdk-core'
import useTokenLogoSource from 'hooks/useAssetLogoSource'
-import React, { useState } from 'react'
+import React, { useMemo, useState } from 'react'
import styled from 'styled-components'
import EthereumLogo from 'assets/images/ethereum-logo.png'
@@ -55,48 +55,38 @@ const LogoContainer = styled.div`
display: flex;
`
+const StyledEthereumLogo = styled.img<{ size: number }>`
+ width: ${({ size }) => size};
+ height: ${({ size }) => size};
+ transition: background-color ${({ theme }) => `${theme.transition.duration.medium} ${theme.transition.timing.in}`};
+ box-shadow: 0 0 1px white;
+ border-radius: 50%;
+`
+
+const CurrencyLogo = ({ currency, symbol, size }: { currency: any; symbol: any; size: any }) => {
+ const currencyLogo: any = currency
+ if (currencyLogo && (currencyLogo.name === 'ETHER' || currencyLogo.name === 'ETH')) {
+ return
+ } else if (currencyLogo && currencyLogo.logoURI) {
+ return (
+
+ )
+ }
+
+ return (
+
+ {symbol?.toUpperCase().replace('$', '').replace(/\s+/g, '').slice(0, 3)}
+
+ )
+}
+
/**
* Renders an image by prioritizing a list of sources, and then eventually a fallback triangle alert
*/
-export default function AssetLogo({
- currency,
- isNative,
- address,
- chainId = ChainId.MAINNET,
- symbol,
- backupImg,
- size = '24px',
- style,
-}: AssetLogoProps) {
- const [src, nextSrc] = useTokenLogoSource(address, chainId, isNative, backupImg)
- const [imgLoaded, setImgLoaded] = useState(() => {
- const img = document.createElement('img')
- img.src = src ?? ''
- return src ? img.complete : false
- })
-
- const logoURI = currency && currency.name === 'ETHER' ? EthereumLogo : (currency as any)?.logoURI
-
+export default function AssetLogo({ currency, symbol, size = '24px', style }: AssetLogoProps) {
return (
- {logoURI ? (
-
- void setImgLoaded(true)}
- onError={nextSrc}
- imgLoaded={imgLoaded}
- loading="lazy"
- />
-
- ) : (
-
- {/* use only first 3 characters of Symbol for design reasons */}
- {symbol?.toUpperCase().replace('$', '').replace(/\s+/g, '').slice(0, 3)}
-
- )}
+
)
}
diff --git a/src/components/SearchModal/CurrencyList/index.tsx b/src/components/SearchModal/CurrencyList/index.tsx
index 0773a85d..1a2c6e44 100644
--- a/src/components/SearchModal/CurrencyList/index.tsx
+++ b/src/components/SearchModal/CurrencyList/index.tsx
@@ -291,16 +291,7 @@ export default function CurrencyList({
}
return null
},
- [
- selectedCurrency,
- otherCurrency,
- isLoading,
- onCurrencySelect,
- showCurrencyAmount,
- searchQuery,
- isAddressSearch,
- balances,
- ]
+ [searchQuery, isAddressSearch]
)
const itemKey = useCallback((index: number, data: typeof itemData) => {
diff --git a/src/hooks/Tokens.ts b/src/hooks/Tokens.ts
index 83bf60b5..80ee2335 100644
--- a/src/hooks/Tokens.ts
+++ b/src/hooks/Tokens.ts
@@ -2,7 +2,7 @@ import { ChainId, Currency, Token } from '@vnaysn/jediswap-sdk-core'
import { useAccountDetails } from 'hooks/starknet-react'
import { getChainInfo } from 'constants/chainInfo'
import { DEFAULT_INACTIVE_LIST_URLS, DEFAULT_LIST_OF_LISTS } from 'constants/lists'
-import { useCurrencyFromMap, useTokenFromMapOrNetwork } from 'lib/hooks/useCurrency'
+import { parseStringFromArgs, useCurrencyFromMap, useTokenFromMapOrNetwork } from 'lib/hooks/useCurrency'
import { getTokenFilter } from 'lib/hooks/useTokenList/filtering'
import { TokenAddressMap } from 'lib/hooks/useTokenList/utils'
import { useMemo } from 'react'
@@ -12,6 +12,9 @@ import { isL2ChainId } from 'utils/chains'
import { useAllLists, useCombinedActiveList, useCombinedTokenMapFromUrls } from '../state/lists/hooks'
import { WrappedTokenInfo } from '../state/lists/wrappedTokenInfo'
import { deserializeToken, useUserAddedTokens } from '../state/user/hooks'
+import { isAddressValidForStarknet } from 'utils/addresses'
+import { useTokenContract } from './useContractV2'
+import { NEVER_RELOAD, useSingleCallResult } from 'state/multicall/hooks'
type Maybe = T | null | undefined
@@ -186,7 +189,33 @@ export function useIsUserAddedToken(currency: Currency | undefined | null): bool
export function useToken(tokenAddress?: string | null): Token | null | undefined {
const { chainId } = useAccountDetails()
const tokens = useDefaultActiveTokens(chainId)
- return useTokenFromMapOrNetwork(tokens, tokenAddress)
+ const address = isAddressValidForStarknet(tokenAddress)
+ const token: Token | undefined = address ? tokens[address] : undefined
+
+ const tokenContract = useTokenContract(address ? address : undefined)
+
+ const tokenName = useSingleCallResult(token ? undefined : tokenContract, 'name', undefined, NEVER_RELOAD)
+
+ const symbol = useSingleCallResult(token ? undefined : tokenContract, 'symbol', undefined, NEVER_RELOAD)
+
+ const decimals = useSingleCallResult(token ? undefined : tokenContract, 'decimals', undefined, NEVER_RELOAD)
+
+ return useMemo(() => {
+ if (token) return token
+ if (!chainId || !address) return undefined
+ if (decimals.loading || symbol.loading || tokenName.loading) return null
+ if (decimals.result) {
+ const token = new Token(
+ chainId,
+ address,
+ parseInt(decimals.result[0]),
+ parseStringFromArgs(symbol.result?.[0]),
+ parseStringFromArgs(symbol.result?.[0])
+ )
+ return token
+ }
+ return undefined
+ }, [address, chainId, decimals, symbol, token, tokenName])
}
export function useCurrency(currencyId: Maybe, chainId?: ChainId): Currency | undefined {
diff --git a/src/hooks/useContractV2.ts b/src/hooks/useContractV2.ts
index 17e0e2c5..6162bccf 100644
--- a/src/hooks/useContractV2.ts
+++ b/src/hooks/useContractV2.ts
@@ -5,8 +5,8 @@ import { useAccountDetails } from './starknet-react'
import { DEFAULT_CHAIN_ID, NONFUNGIBLE_POOL_MANAGER_ADDRESS } from 'constants/tokens'
import { getContractV2 } from 'utils/getContract'
import { MULTICALL_ABI, MULTICALL_NETWORKS } from 'contracts/multicall'
-import { NonfungiblePositionManager } from '@vnaysn/jediswap-sdk-v3'
import NFTPositionManagerABI from 'contracts/nonfungiblepositionmanager/abi.json'
+import ERC20_ABI from 'abis/erc20.json'
// returns null on errors
function useContract(address: string | undefined, ABI: any, withSignerIfPossible = true): Contract | null {
@@ -24,9 +24,9 @@ function useContract(address: string | undefined, ABI: any, withSignerIfPossible
}, [address, ABI, account, connector, chainId])
}
-// export function useTokenContract(tokenAddress?: string, withSignerIfPossible?: boolean): Contract | null {
-// return useContract(tokenAddress, ERC20_ABI, withSignerIfPossible)
-// }
+export function useTokenContract(tokenAddress?: string, withSignerIfPossible?: boolean): Contract | null {
+ return useContract(tokenAddress, ERC20_ABI, withSignerIfPossible)
+}
// export function usePairContract(pairAddress?: string, withSignerIfPossible?: boolean): Contract | null {
// return useContract(pairAddress, PAIR_ABI, withSignerIfPossible)
diff --git a/src/lib/hooks/useCurrency.ts b/src/lib/hooks/useCurrency.ts
index b6f97718..a80db941 100644
--- a/src/lib/hooks/useCurrency.ts
+++ b/src/lib/hooks/useCurrency.ts
@@ -5,8 +5,7 @@ import { ChainId, Currency, Token } from '@vnaysn/jediswap-sdk-core'
import { useAccountDetails } from 'hooks/starknet-react'
import { sendAnalyticsEvent } from 'analytics'
import { isSupportedChain } from 'constants/chains'
-import { useBytes32TokenContract, useTokenContract } from 'hooks/useContract'
-import { NEVER_RELOAD, useSingleCallResult } from 'lib/hooks/multicall'
+import { useTokenContract } from 'hooks/useContractV2'
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
import { useEffect, useMemo } from 'react'
@@ -14,6 +13,9 @@ import { DEFAULT_CHAIN_ID, DEFAULT_ERC20_DECIMALS, WETH } from '../../constants/
// import { TOKEN_SHORTHANDS } from '../../constants/tokens'
import { isAddress } from '../../utils'
import { isAddressValidForStarknet } from 'utils/addresses'
+import { useContractRead } from '@starknet-react/core'
+import ERC20_ABI from 'abis/erc20.json'
+import { cairo, num, shortString } from 'starknet'
// parse a name or symbol from a token response
const BYTES32_REGEX = /^0x[a-fA-F0-9]{64}$/
@@ -30,6 +32,30 @@ function parseStringOrBytes32(str: string | undefined, bytes32: string | undefin
export const UNKNOWN_TOKEN_SYMBOL = 'UNKNOWN'
const UNKNOWN_TOKEN_NAME = 'Unknown Token'
+const useSingleCallResult = (address: string | undefined, type: string) => {
+ const { data: result, isLoading } = useContractRead({
+ functionName: type,
+ args: [],
+ abi: ERC20_ABI,
+ address,
+ watch: true,
+ })
+
+ return { result, isLoading }
+}
+
+export function parseStringFromArgs(data: any, isHexNumber?: boolean): string | undefined {
+ if (typeof data === 'string') {
+ if (isHexNumber) {
+ return num.hexToDecimalString(data)
+ } else if (shortString.isShortString(data)) {
+ return shortString.decodeShortString(data)
+ }
+ return data
+ }
+ return undefined
+}
+
/**
* Returns a Token from the tokenAddress.
* Returns null if token is loading or null was passed.
@@ -38,40 +64,46 @@ const UNKNOWN_TOKEN_NAME = 'Unknown Token'
export function useTokenFromActiveNetwork(tokenAddress: string | undefined): Token | null | undefined {
const { chainId } = useAccountDetails()
- const formattedAddress = isAddress(tokenAddress)
+ const formattedAddress = isAddressValidForStarknet(tokenAddress)
const tokenContract = useTokenContract(formattedAddress ? formattedAddress : undefined, false)
- const tokenContractBytes32 = useBytes32TokenContract(formattedAddress ? formattedAddress : undefined, false)
// TODO (WEB-1709): reduce this to one RPC call instead of 5
// TODO: Fix redux-multicall so that these values do not reload.
- const tokenName = useSingleCallResult(tokenContract, 'name', undefined, NEVER_RELOAD)
- const tokenNameBytes32 = useSingleCallResult(tokenContractBytes32, 'name', undefined, NEVER_RELOAD)
- const symbol = useSingleCallResult(tokenContract, 'symbol', undefined, NEVER_RELOAD)
- const symbolBytes32 = useSingleCallResult(tokenContractBytes32, 'symbol', undefined, NEVER_RELOAD)
- const decimals = useSingleCallResult(tokenContract, 'decimals', undefined, NEVER_RELOAD)
+ const tokenName: any = useSingleCallResult(tokenContract?.address, 'name')
+ const symbol: any = useSingleCallResult(tokenContract?.address, 'symbol')
+ const decimals: any = useSingleCallResult(tokenContract?.address, 'decimals')
const isLoading = useMemo(
- () => decimals.loading || symbol.loading || tokenName.loading,
- [decimals.loading, symbol.loading, tokenName.loading]
+ () => decimals.isLoading || symbol.isLoading || tokenName.isLoading,
+ [decimals.isLoading, symbol.isLoading, tokenName.isLoading]
)
- const parsedDecimals = useMemo(() => decimals?.result?.[0] ?? DEFAULT_ERC20_DECIMALS, [decimals.result])
- const parsedSymbol = useMemo(
- () => parseStringOrBytes32(symbol.result?.[0], symbolBytes32.result?.[0], UNKNOWN_TOKEN_SYMBOL),
- [symbol.result, symbolBytes32.result]
- )
- const parsedName = useMemo(
- () => parseStringOrBytes32(tokenName.result?.[0], tokenNameBytes32.result?.[0], UNKNOWN_TOKEN_NAME),
- [tokenName.result, tokenNameBytes32.result]
+ const parsedDecimals = useMemo(
+ () => parseInt(decimals?.result?.decimals) ?? DEFAULT_ERC20_DECIMALS,
+ [decimals.result]
)
return useMemo(() => {
- // If the token is on another chain, we cannot fetch it on-chain, and it is invalid.
- if (typeof tokenAddress !== 'string' || !isSupportedChain(chainId) || !formattedAddress) return undefined
- if (isLoading || !chainId) return null
-
- return new Token(chainId, formattedAddress, parsedDecimals, parsedSymbol, parsedName)
- }, [chainId, tokenAddress, formattedAddress, isLoading, parsedDecimals, parsedSymbol, parsedName])
+ if (!chainId || !formattedAddress || isLoading) return undefined
+ if (decimals.isLoading || symbol.isLoading || tokenName.isLoading) return null
+ const parsedTokenNameHexString =
+ tokenName && tokenName.result ? num.getHexString(tokenName?.result?.name.toString()) : UNKNOWN_TOKEN_NAME
+
+ const parsedSymbolHexString =
+ tokenName && tokenName.result ? num.getHexString(symbol?.result?.symbol.toString()) : UNKNOWN_TOKEN_SYMBOL
+
+ if (decimals.result) {
+ const token = new Token(
+ chainId,
+ formattedAddress,
+ parsedDecimals,
+ parseStringFromArgs(parsedSymbolHexString),
+ parseStringFromArgs(parsedTokenNameHexString)
+ )
+ return token
+ }
+ return undefined
+ }, [formattedAddress, chainId, decimals, symbol, tokenName])
}
type TokenMap = { [address: string]: Token }