From 4ebe86ee8c87efd61e7ec0302a8382bb4353ea5c Mon Sep 17 00:00:00 2001 From: lbqds Date: Tue, 5 Nov 2024 19:08:10 +0800 Subject: [PATCH 1/9] Rename --- bridge_ui/src/components/NFTOriginVerifier.tsx | 2 +- bridge_ui/src/components/Recovery.tsx | 2 +- bridge_ui/src/components/TokenSelectors/EvmTokenPicker.tsx | 2 +- bridge_ui/src/components/TransactionProgress.tsx | 2 +- bridge_ui/src/components/Transactions/index.tsx | 2 +- bridge_ui/src/components/Transfer/AddToMetamask.tsx | 2 +- bridge_ui/src/hooks/useAllowance.ts | 2 +- bridge_ui/src/hooks/useCheckIfWormholeWrapped.ts | 2 +- bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx | 2 +- bridge_ui/src/hooks/useHandleAttest.tsx | 2 +- bridge_ui/src/hooks/useHandleCreateWrapped.tsx | 2 +- bridge_ui/src/hooks/useHandleRedeem.tsx | 2 +- bridge_ui/src/hooks/useHandleTransfer.tsx | 2 +- bridge_ui/src/utils/{ethereum.ts => evm.ts} | 0 14 files changed, 13 insertions(+), 13 deletions(-) rename bridge_ui/src/utils/{ethereum.ts => evm.ts} (100%) diff --git a/bridge_ui/src/components/NFTOriginVerifier.tsx b/bridge_ui/src/components/NFTOriginVerifier.tsx index c5c38ceb9..773841243 100644 --- a/bridge_ui/src/components/NFTOriginVerifier.tsx +++ b/bridge_ui/src/components/NFTOriginVerifier.tsx @@ -50,7 +50,7 @@ import { getEthereumNFT, isNFT, isValidEthereumAddress, -} from "../utils/ethereum"; +} from "../utils/evm"; import HeaderText from "./HeaderText"; import KeyAndBalance from "./KeyAndBalance"; import NFTViewer from "./TokenSelectors/NFTViewer"; diff --git a/bridge_ui/src/components/Recovery.tsx b/bridge_ui/src/components/Recovery.tsx index bb6c32aa8..166a8ae7b 100644 --- a/bridge_ui/src/components/Recovery.tsx +++ b/bridge_ui/src/components/Recovery.tsx @@ -84,7 +84,7 @@ import ChainSelect from "./ChainSelect"; import KeyAndBalance from "./KeyAndBalance"; import RelaySelector from "./RelaySelector"; import { selectTransferSourceChain, selectTransferTransferTx } from "../store/selectors"; -import { getEVMCurrentBlockNumber, isEVMTxConfirmed } from "../utils/ethereum"; +import { getEVMCurrentBlockNumber, isEVMTxConfirmed } from "../utils/evm"; import { Wallet, useWallet } from "@alephium/web3-react"; import { useTranslation } from "react-i18next"; import i18n from "../i18n"; diff --git a/bridge_ui/src/components/TokenSelectors/EvmTokenPicker.tsx b/bridge_ui/src/components/TokenSelectors/EvmTokenPicker.tsx index 2ff08218c..2b5e2bba0 100644 --- a/bridge_ui/src/components/TokenSelectors/EvmTokenPicker.tsx +++ b/bridge_ui/src/components/TokenSelectors/EvmTokenPicker.tsx @@ -18,7 +18,7 @@ import { getEthereumNFT, getEthereumToken, isValidEthereumAddress, -} from "../../utils/ethereum"; +} from "../../utils/evm"; import TokenPicker, { BasicAccountRender } from "./TokenPicker"; const isWormholev1 = (provider: any, address: string, chainId: ChainId) => { diff --git a/bridge_ui/src/components/TransactionProgress.tsx b/bridge_ui/src/components/TransactionProgress.tsx index 78f146e47..ff96d849c 100644 --- a/bridge_ui/src/components/TransactionProgress.tsx +++ b/bridge_ui/src/components/TransactionProgress.tsx @@ -20,7 +20,7 @@ import { useEthereumProvider } from "../contexts/EthereumProviderContext"; import { Transaction } from "../store/transferSlice"; import { ALEPHIUM_MINIMAL_CONSISTENCY_LEVEL, CLUSTER, CHAINS_BY_ID, SOLANA_HOST } from "../utils/consts"; import SmartBlock from "./SmartBlock"; -import { DefaultEVMChainConfirmations, EpochDuration, getEVMCurrentBlockNumber, getEvmJsonRpcProvider } from "../utils/ethereum"; +import { DefaultEVMChainConfirmations, EpochDuration, getEVMCurrentBlockNumber, getEvmJsonRpcProvider } from "../utils/evm"; import { useWallet } from "@alephium/web3-react"; import { AlephiumBlockTime } from "../utils/alephium"; import { ethers } from "ethers"; diff --git a/bridge_ui/src/components/Transactions/index.tsx b/bridge_ui/src/components/Transactions/index.tsx index 8dfd9bf9f..159d448c3 100644 --- a/bridge_ui/src/components/Transactions/index.tsx +++ b/bridge_ui/src/components/Transactions/index.tsx @@ -24,7 +24,7 @@ import { ethers } from "ethers"; import useSWR from "swr"; import { useSnackbar } from "notistack"; import { PageSwitch, DefaultPageSize } from "./PageSwitch"; -import { getEVMCurrentBlockNumber, getEvmJsonRpcProvider, getIsTxsCompletedEvm, isEVMTxConfirmed } from "../../utils/ethereum"; +import { getEVMCurrentBlockNumber, getEvmJsonRpcProvider, getIsTxsCompletedEvm, isEVMTxConfirmed } from "../../utils/evm"; import { TransactionTable } from "./TransactionTable"; import useIsWalletReady from "../../hooks/useIsWalletReady"; import { useSelector } from "react-redux"; diff --git a/bridge_ui/src/components/Transfer/AddToMetamask.tsx b/bridge_ui/src/components/Transfer/AddToMetamask.tsx index d7aa76fa4..0b1d545fe 100644 --- a/bridge_ui/src/components/Transfer/AddToMetamask.tsx +++ b/bridge_ui/src/components/Transfer/AddToMetamask.tsx @@ -14,7 +14,7 @@ import { getEvmChainId } from "../../utils/consts"; import { ethTokenToParsedTokenAccount, getEthereumToken, -} from "../../utils/ethereum"; +} from "../../utils/evm"; const useStyles = makeStyles((theme) => ({ addButton: { diff --git a/bridge_ui/src/hooks/useAllowance.ts b/bridge_ui/src/hooks/useAllowance.ts index e36a36c30..2206db968 100644 --- a/bridge_ui/src/hooks/useAllowance.ts +++ b/bridge_ui/src/hooks/useAllowance.ts @@ -11,7 +11,7 @@ import { useEthereumProvider } from "../contexts/EthereumProviderContext"; import { selectTransferIsApproving } from "../store/selectors"; import { setIsApproving, setIsWalletApproved } from "../store/transferSlice"; import { getTokenBridgeAddressForChain } from "../utils/consts"; -import { approveEthWithoutWait } from "../utils/ethereum"; +import { approveEthWithoutWait } from "../utils/evm"; export default function useAllowance( chainId: ChainId, diff --git a/bridge_ui/src/hooks/useCheckIfWormholeWrapped.ts b/bridge_ui/src/hooks/useCheckIfWormholeWrapped.ts index f989aba0d..5085302d7 100644 --- a/bridge_ui/src/hooks/useCheckIfWormholeWrapped.ts +++ b/bridge_ui/src/hooks/useCheckIfWormholeWrapped.ts @@ -49,7 +49,7 @@ import { Algodv2 } from "algosdk"; import { errorDataWrapper, fetchDataWrapper, receiveDataWrapper } from "../store/helpers"; import { useWallet } from "@alephium/web3-react"; import { useTranslation } from "react-i18next"; -import { getEvmJsonRpcProvider } from "../utils/ethereum"; +import { getEvmJsonRpcProvider } from "../utils/evm"; export interface StateSafeWormholeWrappedInfo { isWrapped: boolean; diff --git a/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx b/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx index 9691498b4..e68a9b8fe 100644 --- a/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx +++ b/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx @@ -128,7 +128,7 @@ import { ALPH_TOKEN_ID, NodeProvider } from "@alephium/web3"; import { getAvailableBalances, getAlephiumTokenLogoURI } from "../utils/alephium"; import { getRegisteredTokens } from "../utils/tokens"; import { useWallet } from "@alephium/web3-react"; -import { getETHTokenLogoURI } from "../utils/ethereum"; +import { getETHTokenLogoURI } from "../utils/evm"; import { Alert } from "@material-ui/lab"; import parseError from "../utils/parseError"; import i18n from "../i18n"; diff --git a/bridge_ui/src/hooks/useHandleAttest.tsx b/bridge_ui/src/hooks/useHandleAttest.tsx index b3fb17e53..2bdef1689 100644 --- a/bridge_ui/src/hooks/useHandleAttest.tsx +++ b/bridge_ui/src/hooks/useHandleAttest.tsx @@ -72,7 +72,7 @@ import { getSignedVAAWithRetry } from "../utils/getSignedVAAWithRetry"; import parseError from "../utils/parseError"; import { signSendAndConfirm } from "../utils/solana"; import { postWithFees, waitForTerraExecution } from "../utils/terra"; -import { attestFromEthWithoutWait, waitEVMTxConfirmed, checkETHToken } from "../utils/ethereum"; +import { attestFromEthWithoutWait, waitEVMTxConfirmed, checkETHToken } from "../utils/evm"; import { useWallet, Wallet as AlephiumWallet } from "@alephium/web3-react"; import i18n from "../i18n"; diff --git a/bridge_ui/src/hooks/useHandleCreateWrapped.tsx b/bridge_ui/src/hooks/useHandleCreateWrapped.tsx index 7e6ab2614..36c9aa86d 100644 --- a/bridge_ui/src/hooks/useHandleCreateWrapped.tsx +++ b/bridge_ui/src/hooks/useHandleCreateWrapped.tsx @@ -62,7 +62,7 @@ import { signSendAndConfirm } from "../utils/solana"; import { postWithFees } from "../utils/terra"; import { waitALPHTxConfirmed } from "../utils/alephium"; import useAttestSignedVAA from "./useAttestSignedVAA"; -import { createWrappedOnEthWithoutWait, updateWrappedOnEthWithoutWait } from "../utils/ethereum"; +import { createWrappedOnEthWithoutWait, updateWrappedOnEthWithoutWait } from "../utils/evm"; import { useWallet, Wallet as AlephiumWallet } from "@alephium/web3-react"; import { MINIMAL_CONTRACT_DEPOSIT } from "@alephium/web3"; import i18n from "../i18n"; diff --git a/bridge_ui/src/hooks/useHandleRedeem.tsx b/bridge_ui/src/hooks/useHandleRedeem.tsx index c97836291..c21744470 100644 --- a/bridge_ui/src/hooks/useHandleRedeem.tsx +++ b/bridge_ui/src/hooks/useHandleRedeem.tsx @@ -68,7 +68,7 @@ import { signSendAndConfirm } from "../utils/solana"; import { postWithFees } from "../utils/terra"; import { getEmitterChainId, waitALPHTxConfirmed } from "../utils/alephium"; import useTransferSignedVAA from "./useTransferSignedVAA"; -import { redeemOnEthNativeWithoutWait, redeemOnEthWithoutWait } from "../utils/ethereum"; +import { redeemOnEthNativeWithoutWait, redeemOnEthWithoutWait } from "../utils/evm"; import { useWallet, Wallet as AlephiumWallet } from "@alephium/web3-react"; import { SignerProvider } from "@alephium/web3"; import i18n from "../i18n"; diff --git a/bridge_ui/src/hooks/useHandleTransfer.tsx b/bridge_ui/src/hooks/useHandleTransfer.tsx index ca2d3a900..5dc48c7c5 100644 --- a/bridge_ui/src/hooks/useHandleTransfer.tsx +++ b/bridge_ui/src/hooks/useHandleTransfer.tsx @@ -84,7 +84,7 @@ import { postWithFees, waitForTerraExecution } from "../utils/terra"; import useTransferTargetAddressHex from "./useTransferTargetAddress"; import { validateAlephiumRecipientAddress, waitALPHTxConfirmed, waitTxConfirmedAndGetTxInfo } from "../utils/alephium"; import { ExecuteScriptResult } from "@alephium/web3"; -import { transferFromEthNativeWithoutWait, transferFromEthWithoutWait, waitEVMTxConfirmed } from "../utils/ethereum"; +import { transferFromEthNativeWithoutWait, transferFromEthWithoutWait, waitEVMTxConfirmed } from "../utils/evm"; import { useWallet, Wallet as AlephiumWallet } from "@alephium/web3-react"; import i18n from "../i18n"; diff --git a/bridge_ui/src/utils/ethereum.ts b/bridge_ui/src/utils/evm.ts similarity index 100% rename from bridge_ui/src/utils/ethereum.ts rename to bridge_ui/src/utils/evm.ts From f44b02247c767c033dc78f465ac59c89154d84aa Mon Sep 17 00:00:00 2001 From: lbqds Date: Tue, 5 Nov 2024 19:29:35 +0800 Subject: [PATCH 2/9] Overlay the original token logo and symbol for bridged tokens --- .../TokenSelectors/EvmTokenPicker.tsx | 3 +- .../src/components/Transfer/AddToMetamask.tsx | 2 + .../hooks/useGetSourceParsedTokenAccounts.tsx | 38 +++++++++---- bridge_ui/src/utils/alephium.ts | 9 +++ bridge_ui/src/utils/evm.ts | 57 +++++++++++++++---- 5 files changed, 84 insertions(+), 25 deletions(-) diff --git a/bridge_ui/src/components/TokenSelectors/EvmTokenPicker.tsx b/bridge_ui/src/components/TokenSelectors/EvmTokenPicker.tsx index 2b5e2bba0..60ef654b8 100644 --- a/bridge_ui/src/components/TokenSelectors/EvmTokenPicker.tsx +++ b/bridge_ui/src/components/TokenSelectors/EvmTokenPicker.tsx @@ -89,6 +89,7 @@ export default function EvmTokenPicker( ); } else { return ethTokenToParsedTokenAccount( + chainId, tokenAccount as ethers_contracts.TokenImplementation, signerAddress ); @@ -100,7 +101,7 @@ export default function EvmTokenPicker( return Promise.reject({ error: t("Wallet is not connected.") }); } }, - [isReady, nft, provider, signerAddress, t] + [isReady, nft, provider, signerAddress, t, chainId] ); const onChangeWrapper = useCallback( diff --git a/bridge_ui/src/components/Transfer/AddToMetamask.tsx b/bridge_ui/src/components/Transfer/AddToMetamask.tsx index 0b1d545fe..ca1732a1d 100644 --- a/bridge_ui/src/components/Transfer/AddToMetamask.tsx +++ b/bridge_ui/src/components/Transfer/AddToMetamask.tsx @@ -43,6 +43,7 @@ export default function AddToMetamask() { try { const token = await getEthereumToken(targetAsset, provider); const { symbol, decimals } = await ethTokenToParsedTokenAccount( + targetChain, token, signerAddress ); @@ -74,6 +75,7 @@ export default function AddToMetamask() { signerAddress, hasCorrectEvmNetwork, sourceParsedTokenAccount, + targetChain ]); return provider && signerAddress && diff --git a/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx b/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx index e68a9b8fe..5ca878811 100644 --- a/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx +++ b/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx @@ -22,7 +22,8 @@ import { WSOL_ADDRESS, WSOL_DECIMALS, hexToUint8Array, - getTokenPoolId + getTokenPoolId, + tryNativeToHexString } from "@alephium/wormhole-sdk"; import { Dispatch } from "@reduxjs/toolkit"; import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; @@ -125,10 +126,10 @@ import { } from "../utils/solana"; import { fetchSingleMetadata } from "./useAlgoMetadata"; import { ALPH_TOKEN_ID, NodeProvider } from "@alephium/web3"; -import { getAvailableBalances, getAlephiumTokenLogoURI } from "../utils/alephium"; +import { getAvailableBalances, getAlephiumTokenLogoAndSymbol } from "../utils/alephium"; import { getRegisteredTokens } from "../utils/tokens"; import { useWallet } from "@alephium/web3-react"; -import { getETHTokenLogoURI } from "../utils/evm"; +import { getBSCTokenLogoAndSymbol, getETHTokenLogoAndSymbol } from "../utils/evm"; import { Alert } from "@material-ui/lab"; import parseError from "../utils/parseError"; import i18n from "../i18n"; @@ -622,13 +623,13 @@ export const getEVMAccounts = async (chainId: ChainId, signer: ethers.Signer, wa if (tokenAccounts.find((t) => t.contract_address.toLowerCase() === result.tokenId.toLowerCase()) !== undefined) { continue } - const tokenLogoURI = await getTokenLogoURI(token.tokenChain, token.nativeAddress) + const info = await getTokenLogoAndSymbol(token.tokenChain, token.nativeAddress) tokenAccounts.push({ contract_decimals: token.decimals, - contract_ticker_symbol: token.symbol, + contract_ticker_symbol: info?.symbol ?? token.symbol, contract_name: token.name, contract_address: result.tokenId, - logo_url: token.logo ?? tokenLogoURI, + logo_url: info?.logoURI ?? token.logo, balance: result.balance.toString(), quote: undefined, quote_rate: undefined @@ -774,7 +775,7 @@ const getAlephiumParsedTokenAccounts = async (address: string, provider: NodePro const amount = balances.get(localTokenId.toLowerCase()) if (amount === undefined) continue - const tokenLogoURI = await getTokenLogoURI(token.tokenChain, token.nativeAddress) + const info = await getTokenLogoAndSymbol(token.tokenChain, token.nativeAddress) const uiAmount = formatUnits(amount, token.decimals) tokenAccounts.push(createParsedTokenAccount( address, @@ -783,9 +784,9 @@ const getAlephiumParsedTokenAccounts = async (address: string, provider: NodePro token.decimals, parseFloat(uiAmount), uiAmount, - token.symbol, + info?.symbol ?? token.symbol, token.name, - tokenLogoURI, + info?.logoURI ?? token.logo, localTokenId === ALPH_TOKEN_ID )) } @@ -797,11 +798,24 @@ const getAlephiumParsedTokenAccounts = async (address: string, provider: NodePro } } -async function getTokenLogoURI(tokenChainId: ChainId, tokenId: string): Promise { +async function getTokenLogoAndSymbol(tokenChainId: ChainId, tokenId: string): Promise<{ logoURI?: string, symbol?: string } | undefined> { + if (tokenChainId !== CHAIN_ID_ALEPHIUM) { + const wrappedIdOnALPH = getTokenPoolId( + ALEPHIUM_TOKEN_BRIDGE_CONTRACT_ID, + tokenChainId, + tryNativeToHexString(tokenId, tokenChainId), + ALEPHIUM_BRIDGE_GROUP_INDEX + ) + const info = await getAlephiumTokenLogoAndSymbol(wrappedIdOnALPH) + if (info !== undefined) return info + } + if (tokenChainId === CHAIN_ID_ETH) { - return getETHTokenLogoURI(tokenId) + return getETHTokenLogoAndSymbol(tokenId) } else if (tokenChainId === CHAIN_ID_ALEPHIUM) { - return getAlephiumTokenLogoURI(tokenId) + return getAlephiumTokenLogoAndSymbol(tokenId) + } else if (tokenChainId === CHAIN_ID_BSC) { + return getBSCTokenLogoAndSymbol(tokenId) } else { return undefined } diff --git a/bridge_ui/src/utils/alephium.ts b/bridge_ui/src/utils/alephium.ts index b8423d330..f15a37256 100644 --- a/bridge_ui/src/utils/alephium.ts +++ b/bridge_ui/src/utils/alephium.ts @@ -193,6 +193,15 @@ export async function getAlephiumTokenInfo(provider: NodeProvider, tokenId: stri } } +export async function getAlephiumTokenLogoAndSymbol(tokenId: string) { + if (tokenId === ALPH_TOKEN_ID) { + return { logoURI: alephiumIcon, symbol: 'ALPH' } + } + const tokenInfo = await getTokenFromTokenList(tokenId) + if (tokenInfo === undefined) return undefined + return { logoURI: tokenInfo.logoURI, symbol: tokenInfo.symbol } +} + export async function getAlephiumTokenLogoURI(tokenId: string): Promise { return tokenId === ALPH_TOKEN_ID ? alephiumIcon diff --git a/bridge_ui/src/utils/evm.ts b/bridge_ui/src/utils/evm.ts index 456aa2610..61c8f285e 100644 --- a/bridge_ui/src/utils/evm.ts +++ b/bridge_ui/src/utils/evm.ts @@ -22,27 +22,59 @@ interface TokenInfo { logoURI: string } -let _whitelist: TokenInfo[] | undefined = undefined +const TokenListURLs = { + [CHAIN_ID_ETH]: 'https://tokens-1inch-eth.ipns.dweb.link/', + [CHAIN_ID_BSC]: 'https://tokens.coingecko.com/binance-smart-chain/all.json' +} + +let _tokenWhiteList: Map = new Map() + +function getTokenListURL(chainId: ChainId): string { + if (chainId === CHAIN_ID_BSC || chainId === CHAIN_ID_ETH) { + return TokenListURLs[chainId] + } + throw new Error(`Invalid evm chain id: ${chainId}`) +} -async function loadETHTokenWhitelist(): Promise { - if (_whitelist !== undefined) return _whitelist - const { data: { tokens } } = await axios.get('https://tokens-1inch-eth.ipns.dweb.link/') - _whitelist = tokens +async function loadEVMTokenWhitelist(chainId: ChainId): Promise { + const whitelist = _tokenWhiteList.get(chainId) + if (whitelist !== undefined) return whitelist + const url = getTokenListURL(chainId) + const { data: { tokens } } = await axios.get(url) + _tokenWhiteList.set(chainId, tokens) return tokens } -export async function checkETHToken(tokenAddress: string) { +async function checkEVMToken(chainId: ChainId, tokenAddress: string) { if (CLUSTER !== 'mainnet') return - const tokenWhitelist = await loadETHTokenWhitelist() + const tokenWhitelist = await loadEVMTokenWhitelist(chainId) if (tokenWhitelist.find((token) => token.address.toLowerCase() === tokenAddress.toLowerCase()) === undefined) { - throw new Error(`${i18n.t('Token {{ tokenAddress }} does not exist in the token list', { tokenAddress })}: https://tokenlists.org/token-list?url=tokens.1inch.eth`) + throw new Error(`${i18n.t('Token {{ tokenAddress }} does not exist in the token list', { tokenAddress })}: ${getTokenListURL(chainId)}`) } } -export async function getETHTokenLogoURI(tokenAddress: string): Promise { - const tokenWhitelist = await loadETHTokenWhitelist() - return tokenWhitelist.find((token) => token.address.toLowerCase() === tokenAddress.toLowerCase())?.logoURI +async function getEVMTokenLogoAndSymbol(chainId: ChainId, tokenAddress: string) { + const tokenWhitelist = await loadEVMTokenWhitelist(chainId) + const tokenInfo = tokenWhitelist.find((token) => token.address.toLowerCase() === tokenAddress.toLowerCase()) + if (tokenInfo === undefined) return undefined + return { logoURI: tokenInfo.logoURI, symbol: tokenInfo.symbol } +} + +export async function checkETHToken(tokenAddress: string) { + await checkEVMToken(CHAIN_ID_ETH, tokenAddress) +} + +export async function getETHTokenLogoAndSymbol(tokenAddress: string) { + return await getEVMTokenLogoAndSymbol(CHAIN_ID_ETH, tokenAddress) +} + +export async function checkBSCToken(tokenAddress: string) { + await checkEVMToken(CHAIN_ID_BSC, tokenAddress) +} + +export async function getBSCTokenLogoAndSymbol(tokenAddress: string) { + return await getEVMTokenLogoAndSymbol(CHAIN_ID_BSC, tokenAddress) } //This is a valuable intermediate step to the parsed token account, as the token has metadata information on it. @@ -55,6 +87,7 @@ export async function getEthereumToken( } export async function ethTokenToParsedTokenAccount( + chainId: ChainId, token: ethers_contracts.TokenImplementation, signerAddress: string ) { @@ -62,7 +95,7 @@ export async function ethTokenToParsedTokenAccount( const balance = await token.balanceOf(signerAddress); const symbol = await token.symbol(); const name = await token.name(); - const logoURI = await getETHTokenLogoURI(token.address) + const logoURI = (await getEVMTokenLogoAndSymbol(chainId, token.address))?.logoURI return createParsedTokenAccount( signerAddress, token.address, From f81702b7e5a0a5c7a23c093c33f52ba88ceffec6 Mon Sep 17 00:00:00 2001 From: lbqds Date: Tue, 5 Nov 2024 20:38:28 +0800 Subject: [PATCH 3/9] Fix TokenPicker --- .../TokenSelectors/AlephiumTokenPicker.tsx | 7 +++-- .../TokenSelectors/EvmTokenPicker.tsx | 9 +++++- .../hooks/useGetSourceParsedTokenAccounts.tsx | 31 ++----------------- bridge_ui/src/utils/tokens.ts | 29 +++++++++++++++-- 4 files changed, 42 insertions(+), 34 deletions(-) diff --git a/bridge_ui/src/components/TokenSelectors/AlephiumTokenPicker.tsx b/bridge_ui/src/components/TokenSelectors/AlephiumTokenPicker.tsx index 1ec185c53..51c1ae3dc 100644 --- a/bridge_ui/src/components/TokenSelectors/AlephiumTokenPicker.tsx +++ b/bridge_ui/src/components/TokenSelectors/AlephiumTokenPicker.tsx @@ -4,7 +4,7 @@ import { useCallback } from "react"; import { createParsedTokenAccount } from "../../hooks/useGetSourceParsedTokenAccounts"; import useIsWalletReady from "../../hooks/useIsWalletReady"; import { ParsedTokenAccount } from "../../store/transferSlice"; -import { getAlephiumTokenLogoURI, tryGetContractId } from "../../utils/alephium"; +import { getAlephiumTokenLogoAndSymbol, tryGetContractId } from "../../utils/alephium"; import TokenPicker, { BasicAccountRender } from "./TokenPicker"; import { useWallet } from "@alephium/web3-react"; import { useTranslation } from "react-i18next"; @@ -50,6 +50,7 @@ export default function AlephiumTokenPicker(props: AlephiumTokenPickerProps) { try { const contractId = tryGetContractId(address) const tokenInfo = await getLocalTokenInfo(alphWallet.nodeProvider, contractId) + const logoAndSymbol = await getAlephiumTokenLogoAndSymbol(contractId) const amount = balances.get(contractId.toLowerCase()) ?? BigInt(0) const uiAmount = formatUnits(amount, tokenInfo.decimals) return createParsedTokenAccount( @@ -59,9 +60,9 @@ export default function AlephiumTokenPicker(props: AlephiumTokenPickerProps) { tokenInfo.decimals, parseFloat(uiAmount), uiAmount, - tokenInfo.symbol, + logoAndSymbol?.symbol ?? tokenInfo.symbol, tokenInfo.name, - await getAlephiumTokenLogoURI(contractId), + logoAndSymbol?.logoURI, false ) } catch (e) { diff --git a/bridge_ui/src/components/TokenSelectors/EvmTokenPicker.tsx b/bridge_ui/src/components/TokenSelectors/EvmTokenPicker.tsx index 60ef654b8..7e405ad27 100644 --- a/bridge_ui/src/components/TokenSelectors/EvmTokenPicker.tsx +++ b/bridge_ui/src/components/TokenSelectors/EvmTokenPicker.tsx @@ -20,6 +20,7 @@ import { isValidEthereumAddress, } from "../../utils/evm"; import TokenPicker, { BasicAccountRender } from "./TokenPicker"; +import { getTokenLogoAndSymbol } from "../../utils/tokens"; const isWormholev1 = (provider: any, address: string, chainId: ChainId) => { if (chainId !== CHAIN_ID_ETH) { @@ -88,11 +89,17 @@ export default function EvmTokenPicker( signerAddress ); } else { - return ethTokenToParsedTokenAccount( + const logoAndSymbol = await getTokenLogoAndSymbol(chainId, tokenAccount.address) + const tokenInfo = await ethTokenToParsedTokenAccount( chainId, tokenAccount as ethers_contracts.TokenImplementation, signerAddress ); + return { + ...tokenInfo, + symbol: logoAndSymbol?.symbol ?? tokenInfo.symbol, + logo: logoAndSymbol?.logoURI ?? tokenInfo.logo + } } } catch (e) { return Promise.reject(t("Unable to retrive the specific token.")); diff --git a/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx b/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx index 5ca878811..48335e088 100644 --- a/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx +++ b/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx @@ -22,8 +22,7 @@ import { WSOL_ADDRESS, WSOL_DECIMALS, hexToUint8Array, - getTokenPoolId, - tryNativeToHexString + getTokenPoolId } from "@alephium/wormhole-sdk"; import { Dispatch } from "@reduxjs/toolkit"; import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; @@ -126,10 +125,9 @@ import { } from "../utils/solana"; import { fetchSingleMetadata } from "./useAlgoMetadata"; import { ALPH_TOKEN_ID, NodeProvider } from "@alephium/web3"; -import { getAvailableBalances, getAlephiumTokenLogoAndSymbol } from "../utils/alephium"; -import { getRegisteredTokens } from "../utils/tokens"; +import { getAvailableBalances } from "../utils/alephium"; +import { getRegisteredTokens, getTokenLogoAndSymbol } from "../utils/tokens"; import { useWallet } from "@alephium/web3-react"; -import { getBSCTokenLogoAndSymbol, getETHTokenLogoAndSymbol } from "../utils/evm"; import { Alert } from "@material-ui/lab"; import parseError from "../utils/parseError"; import i18n from "../i18n"; @@ -798,29 +796,6 @@ const getAlephiumParsedTokenAccounts = async (address: string, provider: NodePro } } -async function getTokenLogoAndSymbol(tokenChainId: ChainId, tokenId: string): Promise<{ logoURI?: string, symbol?: string } | undefined> { - if (tokenChainId !== CHAIN_ID_ALEPHIUM) { - const wrappedIdOnALPH = getTokenPoolId( - ALEPHIUM_TOKEN_BRIDGE_CONTRACT_ID, - tokenChainId, - tryNativeToHexString(tokenId, tokenChainId), - ALEPHIUM_BRIDGE_GROUP_INDEX - ) - const info = await getAlephiumTokenLogoAndSymbol(wrappedIdOnALPH) - if (info !== undefined) return info - } - - if (tokenChainId === CHAIN_ID_ETH) { - return getETHTokenLogoAndSymbol(tokenId) - } else if (tokenChainId === CHAIN_ID_ALEPHIUM) { - return getAlephiumTokenLogoAndSymbol(tokenId) - } else if (tokenChainId === CHAIN_ID_BSC) { - return getBSCTokenLogoAndSymbol(tokenId) - } else { - return undefined - } -} - /** * Fetches the balance of an asset for the connected wallet * This should handle every type of chain in the future, but only reads the Transfer state. diff --git a/bridge_ui/src/utils/tokens.ts b/bridge_ui/src/utils/tokens.ts index 3d75f1449..bc30be6e1 100644 --- a/bridge_ui/src/utils/tokens.ts +++ b/bridge_ui/src/utils/tokens.ts @@ -1,9 +1,11 @@ import bscIcon from "../icons/bsc.svg"; import ethIcon from "../icons/eth.svg"; import alephiumIcon from "../icons/alephium.svg"; -import { ChainId } from "@alephium/wormhole-sdk"; -import { EXPLORER_API_SERVER_HOST } from "./consts"; +import { CHAIN_ID_ALEPHIUM, CHAIN_ID_BSC, CHAIN_ID_ETH, ChainId, getTokenPoolId, tryNativeToHexString } from "@alephium/wormhole-sdk"; +import { ALEPHIUM_BRIDGE_GROUP_INDEX, ALEPHIUM_TOKEN_BRIDGE_CONTRACT_ID, EXPLORER_API_SERVER_HOST } from "./consts"; import i18n from "../i18n"; +import { getAlephiumTokenLogoAndSymbol } from "./alephium"; +import { getBSCTokenLogoAndSymbol, getETHTokenLogoAndSymbol } from "./evm"; export type RegisteredTokenInfo = { tokenAddress: string @@ -42,3 +44,26 @@ export async function getRegisteredTokens(): Promise { return [] } } + +export async function getTokenLogoAndSymbol(tokenChainId: ChainId, tokenId: string): Promise<{ logoURI?: string, symbol?: string } | undefined> { + if (tokenChainId !== CHAIN_ID_ALEPHIUM) { + const wrappedIdOnALPH = getTokenPoolId( + ALEPHIUM_TOKEN_BRIDGE_CONTRACT_ID, + tokenChainId, + tryNativeToHexString(tokenId, tokenChainId), + ALEPHIUM_BRIDGE_GROUP_INDEX + ) + const info = await getAlephiumTokenLogoAndSymbol(wrappedIdOnALPH) + if (info !== undefined) return info + } + + if (tokenChainId === CHAIN_ID_ETH) { + return getETHTokenLogoAndSymbol(tokenId) + } else if (tokenChainId === CHAIN_ID_ALEPHIUM) { + return getAlephiumTokenLogoAndSymbol(tokenId) + } else if (tokenChainId === CHAIN_ID_BSC) { + return getBSCTokenLogoAndSymbol(tokenId) + } else { + return undefined + } +} From ff4734692239b774955122e08c9d7b33529a1351 Mon Sep 17 00:00:00 2001 From: lbqds Date: Wed, 6 Nov 2024 08:02:55 +0800 Subject: [PATCH 4/9] Skip fetching the balance if the wrapper token does not exist --- bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx b/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx index 48335e088..eae68fa18 100644 --- a/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx +++ b/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx @@ -599,6 +599,7 @@ export const getEVMAccounts = async (chainId: ChainId, signer: ethers.Signer, wa : await ethers_contracts.BridgeImplementation__factory .connect(getTokenBridgeAddressForChain(chainId), signer) .wrappedAsset(token.tokenChain, hexToUint8Array(token.tokenAddress)) + if (tokenId === ethers.constants.AddressZero) return undefined const tokenContract = ethers_contracts.ERC20__factory.connect(tokenId, signer) const amount = await tokenContract.balanceOf(walletAddress) return { From 998bd2e6b9ea3026088c1758fa7fa45945e74bbc Mon Sep 17 00:00:00 2001 From: lbqds Date: Wed, 6 Nov 2024 09:13:37 +0800 Subject: [PATCH 5/9] Remove the featured market tokens --- .../components/TokenSelectors/TokenPicker.tsx | 141 +----------------- .../src/components/Transfer/RedeemPreview.tsx | 2 - 2 files changed, 4 insertions(+), 139 deletions(-) diff --git a/bridge_ui/src/components/TokenSelectors/TokenPicker.tsx b/bridge_ui/src/components/TokenSelectors/TokenPicker.tsx index 2f0ec69b7..d387a6b88 100644 --- a/bridge_ui/src/components/TokenSelectors/TokenPicker.tsx +++ b/bridge_ui/src/components/TokenSelectors/TokenPicker.tsx @@ -6,7 +6,6 @@ import { Dialog, DialogContent, DialogTitle, - Divider, IconButton, List, ListItem, @@ -15,21 +14,14 @@ import { Tooltip, Typography, } from "@material-ui/core"; -import { InfoOutlined, Launch } from "@material-ui/icons"; import KeyboardArrowDownIcon from "@material-ui/icons/KeyboardArrowDown"; import RefreshIcon from "@material-ui/icons/Refresh"; import { Alert } from "@material-ui/lab"; import { useCallback, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; -import { useSelector } from "react-redux"; -import useMarketsMap from "../../hooks/useMarketsMap"; import { NFTParsedTokenAccount } from "../../store/nftSlice"; -import { selectTransferTargetChain } from "../../store/selectors"; import { balancePretty } from "../../utils/balancePretty"; -import { - CHAINS_BY_ID, - getIsTokenTransferDisabled, -} from "../../utils/consts"; +import { getIsTokenTransferDisabled } from "../../utils/consts"; import { shortenAddress } from "../../utils/solana"; import NFTViewer from "./NFTViewer"; @@ -119,10 +111,6 @@ const useStyles = makeStyles((theme) => }) ); -const noClickThrough = (e: any) => { - e.stopPropagation(); -}; - export const BasicAccountRender = ( account: MarketParsedTokenAccount, isMigrationEligible: (address: string) => boolean, @@ -130,7 +118,6 @@ export const BasicAccountRender = ( displayBalance?: (account: NFTParsedTokenAccount) => boolean ) => { const { t } = useTranslation(); - const { data: marketsData } = useMarketsMap(false); const classes = useStyles(); const mintPrettyString = shortenAddress(account.mintKey); const uri = nft ? account.image_256 : account.logo || account.uri; @@ -157,27 +144,6 @@ export const BasicAccountRender = ( const tokenContent = (
- {account.markets ? ( -
- {account.markets.map((market) => - marketsData?.markets?.[market] ? ( - - ) : null - )} -
- ) : null}
{uri && }
@@ -273,9 +239,6 @@ export default function TokenPicker({ const [dialogIsOpen, setDialogIsOpen] = useState(false); const [selectionError, setSelectionError] = useState(""); - const targetChain = useSelector(selectTransferTargetChain); - const { data: marketsData } = useMarketsMap(true); - const openDialog = useCallback(() => { setHolderString(""); setSelectionError(""); @@ -346,61 +309,11 @@ export default function TokenPicker({ [holderString] ); - const marketChainTokens = marketsData?.tokens?.[chainId]; - const featuredMarkets = marketsData?.tokenMarkets?.[chainId]?.[targetChain]; - - const featuredOptions = useMemo(() => { - // only tokens have featured markets - if (!nft && featuredMarkets) { - const ownedMarketTokens = options - .filter( - (option: NFTParsedTokenAccount) => featuredMarkets?.[option.mintKey] - ) - .map( - (option) => { - const fromMarkets = marketsData?.tokens?.[chainId]?.[option.mintKey] - return ({ - ...option, - symbol: fromMarkets?.symbol, - logo: fromMarkets?.logo, - markets: featuredMarkets[option.mintKey].markets, - } as MarketParsedTokenAccount) - } - ); - return [ - ...ownedMarketTokens, - ...Object.keys(featuredMarkets) - .filter( - (mintKey) => - !ownedMarketTokens.find((option) => option.mintKey === mintKey) - ) - .map( - (mintKey) => - ({ - amount: "0", - decimals: 0, - markets: featuredMarkets[mintKey].markets, - mintKey, - publicKey: "", - uiAmount: 0, - uiAmountString: "0", // if we can't look up by address, we can select the market that isn't in the list of holdings, but can't proceed since the balance will be 0 - symbol: marketChainTokens?.[mintKey]?.symbol, - logo: marketChainTokens?.[mintKey]?.logo, - } as MarketParsedTokenAccount) - ), - ].filter(searchFilter); - } - return []; - }, [chainId, nft, marketsData, marketChainTokens, featuredMarkets, options, searchFilter]); - const nonFeaturedOptions = useMemo(() => { return options.filter( - (option: NFTParsedTokenAccount) => - searchFilter(option) && - // only tokens have featured markets - (nft || !featuredMarkets?.[option.mintKey]) + (option: NFTParsedTokenAccount) => searchFilter(option) ); - }, [nft, options, featuredMarkets, searchFilter]); + }, [options, searchFilter]); const localFind = useCallback( (address: string, tokenIdHolderString: string) => { @@ -530,52 +443,6 @@ export default function TokenPicker({ displayLocalError ) : ( - {featuredOptions.length ? ( - <> - - Featured {CHAINS_BY_ID[chainId].name} >{" "} - {CHAINS_BY_ID[targetChain].name} markets{" "} - - - - - {featuredOptions.map((option) => { - return ( - handleSelectOption(option)} - key={ - option.publicKey + - option.mintKey + - (option.tokenId || "") - } - disabled={getIsTokenTransferDisabled( - chainId, - option.mintKey - )} - > - - - ); - })} - {nonFeaturedOptions.length ? ( - <> - - - {t("Other Assets")} - - - ) : null} - - ) : null} {nonFeaturedOptions.map((option) => { return ( ); })} - {featuredOptions.length || nonFeaturedOptions.length ? null : ( + {nonFeaturedOptions.length ? null : (
{t("No results found")}
diff --git a/bridge_ui/src/components/Transfer/RedeemPreview.tsx b/bridge_ui/src/components/Transfer/RedeemPreview.tsx index 13cddaa75..b1916b7e1 100644 --- a/bridge_ui/src/components/Transfer/RedeemPreview.tsx +++ b/bridge_ui/src/components/Transfer/RedeemPreview.tsx @@ -12,7 +12,6 @@ import ButtonWithLoader from "../ButtonWithLoader"; import ShowTx from "../ShowTx"; import AddToAlephium from "./AddToAlephium"; import AddToMetamask from "./AddToMetamask"; -import FeaturedMarkets from "./FeaturedMarkets"; import { useTranslation } from "react-i18next"; const useStyles = makeStyles((theme) => ({ @@ -52,7 +51,6 @@ export default function RedeemPreview({ {redeemTx ? : null} {targetChain === CHAIN_ID_ALEPHIUM ? : null} {isEVMChain(targetChain) ? : null} - {t("Transfer More Tokens!")} From ad985dcd346d12534292fc6a4007b9676be689eb Mon Sep 17 00:00:00 2001 From: lbqds Date: Wed, 6 Nov 2024 09:57:06 +0800 Subject: [PATCH 6/9] Check if the BSC token is on the whitelist --- bridge_ui/src/hooks/useHandleAttest.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bridge_ui/src/hooks/useHandleAttest.tsx b/bridge_ui/src/hooks/useHandleAttest.tsx index 2bdef1689..d7f26d5b7 100644 --- a/bridge_ui/src/hooks/useHandleAttest.tsx +++ b/bridge_ui/src/hooks/useHandleAttest.tsx @@ -20,7 +20,8 @@ import { parseSequenceFromLogTerra, uint8ArrayToHex, parseTargetChainFromLogEth, - CHAIN_ID_ETH + CHAIN_ID_ETH, + CHAIN_ID_BSC } from "@alephium/wormhole-sdk"; import { CHAIN_ID_UNSET } from "@alephium/wormhole-sdk/lib/esm"; import { Alert } from "@material-ui/lab"; @@ -72,7 +73,7 @@ import { getSignedVAAWithRetry } from "../utils/getSignedVAAWithRetry"; import parseError from "../utils/parseError"; import { signSendAndConfirm } from "../utils/solana"; import { postWithFees, waitForTerraExecution } from "../utils/terra"; -import { attestFromEthWithoutWait, waitEVMTxConfirmed, checkETHToken } from "../utils/evm"; +import { attestFromEthWithoutWait, waitEVMTxConfirmed, checkETHToken, checkBSCToken } from "../utils/evm"; import { useWallet, Wallet as AlephiumWallet } from "@alephium/web3-react"; import i18n from "../i18n"; @@ -143,6 +144,8 @@ async function evm( try { if (chainId === CHAIN_ID_ETH) { await checkETHToken(sourceAsset) + } else if (chainId === CHAIN_ID_BSC) { + await checkBSCToken(sourceAsset) } // Klaytn requires specifying gasPrice From 58dd9b60db8740242495219c984e42bc9bd219f9 Mon Sep 17 00:00:00 2001 From: lbqds Date: Wed, 6 Nov 2024 16:34:36 +0800 Subject: [PATCH 7/9] Address comments --- bridge_ui/src/components/TokenSelectors/EvmTokenPicker.tsx | 4 ++-- bridge_ui/src/components/Transfer/AddToMetamask.tsx | 4 ++-- bridge_ui/src/utils/evm.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bridge_ui/src/components/TokenSelectors/EvmTokenPicker.tsx b/bridge_ui/src/components/TokenSelectors/EvmTokenPicker.tsx index 7e405ad27..8ea18627a 100644 --- a/bridge_ui/src/components/TokenSelectors/EvmTokenPicker.tsx +++ b/bridge_ui/src/components/TokenSelectors/EvmTokenPicker.tsx @@ -14,7 +14,7 @@ import { } from "../../utils/consts"; import { ethNFTToNFTParsedTokenAccount, - ethTokenToParsedTokenAccount, + evmTokenToParsedTokenAccount, getEthereumNFT, getEthereumToken, isValidEthereumAddress, @@ -90,7 +90,7 @@ export default function EvmTokenPicker( ); } else { const logoAndSymbol = await getTokenLogoAndSymbol(chainId, tokenAccount.address) - const tokenInfo = await ethTokenToParsedTokenAccount( + const tokenInfo = await evmTokenToParsedTokenAccount( chainId, tokenAccount as ethers_contracts.TokenImplementation, signerAddress diff --git a/bridge_ui/src/components/Transfer/AddToMetamask.tsx b/bridge_ui/src/components/Transfer/AddToMetamask.tsx index ca1732a1d..dc19e12fe 100644 --- a/bridge_ui/src/components/Transfer/AddToMetamask.tsx +++ b/bridge_ui/src/components/Transfer/AddToMetamask.tsx @@ -12,7 +12,7 @@ import { } from "../../store/selectors"; import { getEvmChainId } from "../../utils/consts"; import { - ethTokenToParsedTokenAccount, + evmTokenToParsedTokenAccount, getEthereumToken, } from "../../utils/evm"; @@ -42,7 +42,7 @@ export default function AddToMetamask() { (async () => { try { const token = await getEthereumToken(targetAsset, provider); - const { symbol, decimals } = await ethTokenToParsedTokenAccount( + const { symbol, decimals } = await evmTokenToParsedTokenAccount( targetChain, token, signerAddress diff --git a/bridge_ui/src/utils/evm.ts b/bridge_ui/src/utils/evm.ts index 61c8f285e..f7619dc4e 100644 --- a/bridge_ui/src/utils/evm.ts +++ b/bridge_ui/src/utils/evm.ts @@ -86,7 +86,7 @@ export async function getEthereumToken( return token; } -export async function ethTokenToParsedTokenAccount( +export async function evmTokenToParsedTokenAccount( chainId: ChainId, token: ethers_contracts.TokenImplementation, signerAddress: string From 080599355e943e0cba6b287b8076999e56f8a11a Mon Sep 17 00:00:00 2001 From: lbqds Date: Wed, 6 Nov 2024 20:34:05 +0800 Subject: [PATCH 8/9] Select the tokens based on the source chain and the target chain --- .../hooks/useGetSourceParsedTokenAccounts.tsx | 51 ++++++++++++------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx b/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx index eae68fa18..c0dacf278 100644 --- a/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx +++ b/bridge_ui/src/hooks/useGetSourceParsedTokenAccounts.tsx @@ -69,9 +69,11 @@ import { selectNFTSourceChain, selectNFTSourceParsedTokenAccounts, selectNFTSourceWalletAddress, + selectNFTTargetChain, selectSourceWalletAddress, selectTransferSourceChain, selectTransferSourceParsedTokenAccounts, + selectTransferTargetChain, } from "../store/selectors"; import { errorSourceParsedTokenAccounts, @@ -589,15 +591,21 @@ export type CovalentNFTData = { token_url: string; }; -export const getEVMAccounts = async (chainId: ChainId, signer: ethers.Signer, walletAddress: string): Promise => { +export const getEVMAccounts = async ( + sourceChainId: ChainId, + targetChainId: ChainId, + signer: ethers.Signer, + walletAddress: string +): Promise => { try { - const tokens = await getRegisteredTokens() - const promises = tokens.map(async (token) => { + const registeredTokens = await getRegisteredTokens() + const filteredTokens = registeredTokens.filter((t) => t.tokenChain === sourceChainId || t.tokenChain === targetChainId) + const promises = filteredTokens.map(async (token) => { try { - const tokenId = token.tokenChain === chainId + const tokenId = token.tokenChain === sourceChainId ? token.nativeAddress : await ethers_contracts.BridgeImplementation__factory - .connect(getTokenBridgeAddressForChain(chainId), signer) + .connect(getTokenBridgeAddressForChain(sourceChainId), signer) .wrappedAsset(token.tokenChain, hexToUint8Array(token.tokenAddress)) if (tokenId === ethers.constants.AddressZero) return undefined const tokenContract = ethers_contracts.ERC20__factory.connect(tokenId, signer) @@ -613,9 +621,9 @@ export const getEVMAccounts = async (chainId: ChainId, signer: ethers.Signer, wa }) const tokenAccounts: CovalentData[] = [] const results = await Promise.all(promises) - for (let index = 0; index < tokens.length; index++) { + for (let index = 0; index < filteredTokens.length; index++) { const result = results[index] - const token = tokens[index] + const token = filteredTokens[index] if (result === undefined || result.balance === BigInt(0)) { continue } @@ -762,12 +770,13 @@ const getAlgorandParsedTokenAccounts = async ( } }; -const getAlephiumParsedTokenAccounts = async (address: string, provider: NodeProvider) => { +const getAlephiumParsedTokenAccounts = async (targetChainId: ChainId, address: string, provider: NodeProvider) => { try { const balances = await getAvailableBalances(provider, address) const registeredTokens = await getRegisteredTokens() + const filteredTokens = registeredTokens.filter((t) => t.tokenChain === CHAIN_ID_ALEPHIUM || t.tokenChain === targetChainId) const tokenAccounts: ParsedTokenAccount[] = [] - for (const token of registeredTokens) { + for (const token of filteredTokens) { const localTokenId = token.tokenChain === CHAIN_ID_ALEPHIUM ? token.nativeAddress : getTokenPoolId(ALEPHIUM_TOKEN_BRIDGE_CONTRACT_ID, token.tokenChain, token.tokenAddress, ALEPHIUM_BRIDGE_GROUP_INDEX) @@ -815,6 +824,10 @@ function useGetAvailableTokens(nft: boolean = false) { const lookupChain = useSelector( nft ? selectNFTSourceChain : selectTransferSourceChain ); + const targetChain = useSelector( + nft ? selectNFTTargetChain : selectTransferTargetChain + ) + const [selectedTargetChain, setSelectedTargetChain] = useState(targetChain) const solanaWallet = useSolanaWallet(); const solPK = solanaWallet?.publicKey; const { provider, signerAddress, signer } = useEthereumProvider(); @@ -893,16 +906,20 @@ function useGetAvailableTokens(nft: boolean = false) { //TODO this useEffect could be somewhere else in the codebase //It resets the SourceParsedTokens accounts when the wallet changes useEffect(() => { - if ( + const sourceChainChanged = selectedSourceWalletAddress !== undefined && currentSourceWalletAddress !== undefined && currentSourceWalletAddress !== selectedSourceWalletAddress - ) { + const targetChainChanged = targetChain !== selectedTargetChain + if (sourceChainChanged || targetChainChanged) { resetSourceAccounts(); - return; - } else { + } + if (targetChainChanged) { + setSelectedTargetChain(targetChain) } }, [ + targetChain, + selectedTargetChain, selectedSourceWalletAddress, currentSourceWalletAddress, dispatch, @@ -1433,7 +1450,7 @@ function useGetAvailableTokens(nft: boolean = false) { //TODO less cancel !cancelled && setCovalentLoading(true); !cancelled && dispatch(fetchSourceParsedTokenAccounts()); - getEVMAccounts(lookupChain, signer, walletAddress).then( + getEVMAccounts(lookupChain, targetChain, signer, walletAddress).then( (accounts) => { !cancelled && setCovalentLoading(false); !cancelled && setCovalentError(undefined); @@ -1466,7 +1483,7 @@ function useGetAvailableTokens(nft: boolean = false) { cancelled = true; }; } - }, [lookupChain, provider, signerAddress, dispatch, nft, covalent, signer, t]); + }, [lookupChain, targetChain, provider, signerAddress, dispatch, nft, covalent, signer, t]); useEffect(() => { if ( @@ -1478,7 +1495,7 @@ function useGetAvailableTokens(nft: boolean = false) { ) { setAlphTokenLoading(true) dispatch(fetchSourceParsedTokenAccounts()) - getAlephiumParsedTokenAccounts(currentSourceWalletAddress, alphWallet.nodeProvider) + getAlephiumParsedTokenAccounts(targetChain, currentSourceWalletAddress, alphWallet.nodeProvider) .then((result) => { setAlphTokens(result.tokenAccounts) setAlphBalances(result.balances) @@ -1495,7 +1512,7 @@ function useGetAvailableTokens(nft: boolean = false) { setAlphTokenError(`${error}`) }) } - }, [dispatch, enqueueSnackbar, lookupChain, currentSourceWalletAddress, alphWallet?.nodeProvider, alphTokens, alphTokenLoading]); + }, [dispatch, enqueueSnackbar, lookupChain, targetChain, currentSourceWalletAddress, alphWallet?.nodeProvider, alphTokens, alphTokenLoading]); //Terra accounts load //At present, we don't have any mechanism for doing this. From 5e4ee4491b9dd1488bddc78644bb6375e322d5b3 Mon Sep 17 00:00:00 2001 From: lbqds Date: Wed, 6 Nov 2024 20:37:22 +0800 Subject: [PATCH 9/9] Cache the registered tokens --- bridge_ui/src/utils/tokens.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bridge_ui/src/utils/tokens.ts b/bridge_ui/src/utils/tokens.ts index bc30be6e1..294a23a15 100644 --- a/bridge_ui/src/utils/tokens.ts +++ b/bridge_ui/src/utils/tokens.ts @@ -17,8 +17,12 @@ export type RegisteredTokenInfo = { logo?: string } +let _registeredTokens: RegisteredTokenInfo[] | undefined = undefined + export async function getRegisteredTokens(): Promise { try { + if (_registeredTokens !== undefined) return _registeredTokens + const response = await fetch(`${EXPLORER_API_SERVER_HOST}/api/stats/tokens`) if (!response.ok) { throw new Error(`${i18n.t('Failed to get tokens')}, ${i18n.t('response status')}: ${response.status}`) @@ -27,7 +31,7 @@ export async function getRegisteredTokens(): Promise { if (!Array.isArray(tokenList)) { throw new Error(i18n.t('Invalid response, expect a token list')) } - return (tokenList as any[]).map((item) => { + const tokens = (tokenList as any[]).map((item) => { const symbol = (item['symbol'] as string).toUpperCase() return { tokenAddress: item['tokenAddress'], @@ -39,6 +43,8 @@ export async function getRegisteredTokens(): Promise { logo: symbol === 'ALPH' ? alephiumIcon : symbol === 'WETH' ? ethIcon : symbol === 'WBNB' ? bscIcon : undefined } }) + _registeredTokens = tokens + return tokens } catch (error) { console.log(error) return []