diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx
index 6709ca10d1..da78b114ef 100644
--- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx
+++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenImportDialog.tsx
@@ -176,7 +176,7 @@ export function TokenImportDialog({
const selectToken = useCallback(
async (_token: ERC20BridgeToken) => {
- await token.updateTokenData(_token.address)
+ await token.updateTokenData(_token.address.toLowerCase())
actions.app.setSelectedToken(_token)
},
[token, actions]
diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenRow.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenRow.tsx
index 6017331763..6723b1348d 100644
--- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenRow.tsx
+++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenRow.tsx
@@ -8,10 +8,7 @@ import { Chain } from 'wagmi'
import { Loader } from '../common/atoms/Loader'
import { useAppState } from '../../state'
-import {
- listIdsToNames,
- SPECIAL_ARBITRUM_TOKEN_TOKEN_LIST_ID
-} from '../../util/TokenListUtils'
+import { listIdsToNames } from '../../util/TokenListUtils'
import { formatAmount } from '../../util/NumberUtils'
import { shortenAddress } from '../../util/CommonUtils'
import {
@@ -20,6 +17,7 @@ import {
sanitizeTokenName,
sanitizeTokenSymbol
} from '../../util/TokenUtils'
+import { isArbitrumToken as isArbitrumTokenCheck } from '../../util/ArbTokenUtils'
import { SafeImage } from '../common/SafeImage'
import { getExplorerUrl, getNetworkName } from '../../util/networks'
import { Tooltip } from '../common/Tooltip'
@@ -178,13 +176,7 @@ function useTokenInfo(token: ERC20BridgeToken | null) {
const balance = useBalanceOnSourceChain(token)
- const isArbitrumToken = useMemo(() => {
- if (!token) {
- return false
- }
-
- return token.listIds.has(SPECIAL_ARBITRUM_TOKEN_TOKEN_LIST_ID)
- }, [token])
+ const isArbitrumToken = isArbitrumTokenCheck(token)
const isPotentialFakeArbitrumToken = useMemo(() => {
if (!token || isArbitrumToken) {
@@ -192,8 +184,8 @@ function useTokenInfo(token: ERC20BridgeToken | null) {
}
return (
- token.name.toLowerCase().startsWith('arb') ||
- token.symbol.toLowerCase().startsWith('arb')
+ token.name.toLowerCase().startsWith('arbitrum') ||
+ token.symbol.toLowerCase() === 'arb'
)
}, [token, isArbitrumToken])
@@ -249,6 +241,8 @@ function TokenBalance({ token }: { token: ERC20BridgeToken | null }) {
isTokenArbitrumOneNativeUSDC(token?.address) ||
isTokenArbitrumSepoliaNativeUSDC(token?.address)
+ const isArbitrumToken = isArbitrumTokenCheck(token)
+
const tokenIsAddedToTheBridge = useMemo(() => {
// Can happen when switching networks.
if (typeof bridgeTokens === 'undefined') {
@@ -263,8 +257,12 @@ function TokenBalance({ token }: { token: ERC20BridgeToken | null }) {
return true
}
- return typeof bridgeTokens[token.address] !== 'undefined'
- }, [bridgeTokens, isArbitrumNativeUSDC, token])
+ if (isArbitrumToken) {
+ return true
+ }
+
+ return typeof bridgeTokens[token.address.toLowerCase()] !== 'undefined'
+ }, [bridgeTokens, isArbitrumNativeUSDC, isArbitrumToken, token])
if (!tokenIsAddedToTheBridge) {
return Import
diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx
index 93af1c6977..b9ec18292f 100644
--- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx
+++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TokenSearch.tsx
@@ -68,16 +68,23 @@ function TokenListRow({ tokenList }: { tokenList: BridgeTokenList }) {
app: { arbTokenBridge }
} = useAppState()
const { bridgeTokens, token } = arbTokenBridge
+ const [networks] = useNetworks()
+ const { childChain, parentChain } = useNetworksRelationship(networks)
const toggleTokenList = useCallback(
(bridgeTokenList: BridgeTokenList, isActive: boolean) => {
if (isActive) {
token.removeTokensFromList(bridgeTokenList.id)
} else {
- addBridgeTokenListToBridge(bridgeTokenList, arbTokenBridge)
+ addBridgeTokenListToBridge({
+ bridgeTokenList,
+ arbTokenBridge,
+ parentChainId: parentChain.id,
+ childChainId: childChain.id
+ })
}
},
- [arbTokenBridge, token]
+ [arbTokenBridge, childChain.id, parentChain.id, token]
)
const isActive = Object.keys(bridgeTokens ?? []).some(address => {
@@ -311,9 +318,9 @@ function TokensPanel({
return true
}
- // Always show official ARB token except from or to Orbit chain
+ // Always show official ARB token
if (token?.listIds.has(SPECIAL_ARBITRUM_TOKEN_TOKEN_LIST_ID)) {
- return !isOrbitChain
+ return true
}
const balance = getBalance(address)
@@ -554,7 +561,9 @@ export function TokenSearch({
return
}
- if (!_token.address) {
+ const lowercasedTokenAddress = _token.address.toLowerCase()
+
+ if (!lowercasedTokenAddress) {
return
}
@@ -566,8 +575,8 @@ export function TokenSearch({
try {
// Native USDC on L2 won't have a corresponding L1 address
const isL2NativeUSDC =
- isTokenArbitrumOneNativeUSDC(_token.address) ||
- isTokenArbitrumSepoliaNativeUSDC(_token.address)
+ isTokenArbitrumOneNativeUSDC(lowercasedTokenAddress) ||
+ isTokenArbitrumSepoliaNativeUSDC(lowercasedTokenAddress)
if (isL2NativeUSDC) {
if (isLoadingAccountType) {
@@ -596,7 +605,7 @@ export function TokenSearch({
name: 'USD Coin',
type: TokenType.ERC20,
symbol: 'USDC',
- address: _token.address,
+ address: lowercasedTokenAddress,
l2Address: childChainUsdcAddress,
decimals: 6,
listIds: new Set()
@@ -609,8 +618,8 @@ export function TokenSearch({
}
// Token not added to the bridge, so we'll handle importing it
- if (typeof bridgeTokens[_token.address] === 'undefined') {
- setTokenQueryParam(_token.address)
+ if (typeof bridgeTokens[lowercasedTokenAddress] === 'undefined') {
+ setTokenQueryParam(lowercasedTokenAddress)
return
}
@@ -619,19 +628,19 @@ export function TokenSearch({
}
const data = await fetchErc20Data({
- address: _token.address,
+ address: lowercasedTokenAddress,
provider: parentChainProvider
})
if (data) {
- token.updateTokenData(_token.address)
+ token.updateTokenData(lowercasedTokenAddress)
setSelectedToken({
...erc20DataToErc20BridgeToken(data),
l2Address: _token.l2Address
})
}
- if (isTransferDisabledToken(_token.address, childChain.id)) {
+ if (isTransferDisabledToken(lowercasedTokenAddress, childChain.id)) {
openTransferDisabledDialog()
return
}
diff --git a/packages/arb-token-bridge-ui/src/components/syncers/TokenListSyncer.tsx b/packages/arb-token-bridge-ui/src/components/syncers/TokenListSyncer.tsx
index d2bdb11462..9101a0c060 100644
--- a/packages/arb-token-bridge-ui/src/components/syncers/TokenListSyncer.tsx
+++ b/packages/arb-token-bridge-ui/src/components/syncers/TokenListSyncer.tsx
@@ -1,5 +1,4 @@
import { useEffect } from 'react'
-import { useAccount } from 'wagmi'
import { useNetworks } from '../../hooks/useNetworks'
import { useNetworksRelationship } from '../../hooks/useNetworksRelationship'
@@ -15,19 +14,14 @@ const TokenListSyncer = (): JSX.Element => {
const {
app: { arbTokenBridge, arbTokenBridgeLoaded }
} = useAppState()
- const { address: walletAddress } = useAccount()
const [networks] = useNetworks()
- const { childChain } = useNetworksRelationship(networks)
+ const { childChain, parentChain } = useNetworksRelationship(networks)
useEffect(() => {
if (!arbTokenBridgeLoaded) {
return
}
- if (!walletAddress) {
- return
- }
-
const tokenListsToSet = BRIDGE_TOKEN_LISTS.filter(bridgeTokenList => {
// Always load the Arbitrum Token token list
if (bridgeTokenList.isArbitrumTokenTokenList) {
@@ -41,9 +35,14 @@ const TokenListSyncer = (): JSX.Element => {
})
tokenListsToSet.forEach(bridgeTokenList => {
- addBridgeTokenListToBridge(bridgeTokenList, arbTokenBridge)
+ addBridgeTokenListToBridge({
+ bridgeTokenList,
+ arbTokenBridge,
+ parentChainId: parentChain.id,
+ childChainId: childChain.id
+ })
})
- }, [walletAddress, childChain.id, arbTokenBridgeLoaded])
+ }, [childChain.id, arbTokenBridgeLoaded, parentChain.id])
return <>>
}
diff --git a/packages/arb-token-bridge-ui/src/hooks/arbTokenBridge.types.ts b/packages/arb-token-bridge-ui/src/hooks/arbTokenBridge.types.ts
index c4b4451e12..f384449470 100644
--- a/packages/arb-token-bridge-ui/src/hooks/arbTokenBridge.types.ts
+++ b/packages/arb-token-bridge-ui/src/hooks/arbTokenBridge.types.ts
@@ -21,9 +21,17 @@ import {
Transaction,
ParentToChildMessageData
} from './useTransactions'
+import { ChainId } from '../util/networks'
export { OutgoingMessageState }
+export type AddTokensFromListArgs = {
+ arbTokenList: TokenList
+ listId: number
+ parentChainId: ChainId
+ childChainId: ChainId
+}
+
export enum TokenType {
ERC20 = 'ERC20'
}
@@ -147,7 +155,12 @@ export interface ArbTokenBridgeEth {
export interface ArbTokenBridgeToken {
add: (erc20L1orL2Address: string) => Promise
addL2NativeToken: (erc20L2Address: string) => void
- addTokensFromList: (tokenList: TokenList, listID: number) => void
+ addTokensFromList: ({
+ arbTokenList,
+ listId,
+ parentChainId,
+ childChainId
+ }: AddTokensFromListArgs) => void
removeTokensFromList: (listID: number) => void
updateTokenData: (l1Address: string) => Promise
triggerOutbox: (params: {
diff --git a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts
index 955e15a097..f083d3f0e3 100644
--- a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts
+++ b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts
@@ -1,10 +1,9 @@
-import { useCallback, useState, useMemo } from 'react'
+import { useCallback, useState } from 'react'
import { Chain, useAccount } from 'wagmi'
import { BigNumber } from 'ethers'
import { Signer } from '@ethersproject/abstract-signer'
import { JsonRpcProvider } from '@ethersproject/providers'
import { useLocalStorage } from '@rehooks/local-storage'
-import { TokenList } from '@uniswap/token-lists'
import {
EventArgs,
ChildToParentMessage,
@@ -19,7 +18,8 @@ import {
ERC20BridgeToken,
L2ToL1EventResultPlus,
TokenType,
- L2ToL1EventResult
+ L2ToL1EventResult,
+ AddTokensFromListArgs
} from './arbTokenBridge.types'
import { useBalance } from './useBalance'
import {
@@ -33,6 +33,7 @@ import {
import { getL2NativeToken } from '../util/L2NativeUtils'
import { CommonAddress } from '../util/CommonAddressUtils'
import { isNetwork } from '../util/networks'
+import { isArbitrumTokenList } from '../util/TokenListUtils'
import { useDestinationAddressStore } from '../components/TransferPanel/AdvancedSettings'
import { isValidTeleportChainPair } from '@/token-bridge-sdk/teleport'
import { getProviderForChainId } from '@/token-bridge-sdk/utils'
@@ -135,8 +136,6 @@ export const useArbTokenBridge = (
React.Dispatch
]
- const l1NetworkID = useMemo(() => String(l1.network.id), [l1.network.id])
-
const [transactions, { addTransaction, updateTransaction }] =
useTransactions()
@@ -157,9 +156,13 @@ export const useArbTokenBridge = (
})
}
- const addTokensFromList = async (arbTokenList: TokenList, listId: number) => {
- const l1ChainID = l1.network.id
- const l2ChainID = l2.network.id
+ const addTokensFromList = async ({
+ arbTokenList,
+ listId,
+ parentChainId,
+ childChainId
+ }: AddTokensFromListArgs) => {
+ const isChildChainOrbit = isNetwork(childChainId).isOrbitChain
const bridgeTokensToAdd: ContractStorage = {}
@@ -169,7 +172,7 @@ export const useArbTokenBridge = (
const { address, name, symbol, extensions, decimals, logoURI, chainId } =
tokenData
- if (![l1ChainID, l2ChainID].includes(chainId)) {
+ if (![parentChainId, childChainId].includes(chainId)) {
continue
}
@@ -205,18 +208,25 @@ export const useArbTokenBridge = (
})()
if (bridgeInfo) {
- const l1Address = bridgeInfo[l1NetworkID]?.tokenAddress.toLowerCase()
-
- if (!l1Address) {
+ const isArbitrumTokenAndIsChildChainOrbit =
+ isArbitrumTokenList(listId) && isChildChainOrbit
+ const parentChainAddress = isArbitrumTokenAndIsChildChainOrbit
+ ? address.toLowerCase()
+ : bridgeInfo[parentChainId]?.tokenAddress.toLowerCase()
+ const childChainAddress = isArbitrumTokenAndIsChildChainOrbit
+ ? undefined
+ : address.toLowerCase()
+
+ if (!parentChainAddress) {
return
}
- bridgeTokensToAdd[l1Address] = {
+ bridgeTokensToAdd[parentChainAddress] = {
name,
type: TokenType.ERC20,
symbol,
- address: l1Address,
- l2Address: address.toLowerCase(),
+ address: parentChainAddress,
+ l2Address: childChainAddress,
decimals,
logoURI,
listIds: new Set([listId])
@@ -257,10 +267,10 @@ export const useArbTokenBridge = (
// USDC is not on any token list as it's unbridgeable
// but we still want to detect its balance on user's wallet
- if (isNetwork(l2ChainID).isArbitrumOne) {
+ if (isNetwork(childChainId).isArbitrumOne) {
l2Addresses.push(CommonAddress.ArbitrumOne.USDC)
}
- if (isNetwork(l2ChainID).isArbitrumSepolia) {
+ if (isNetwork(childChainId).isArbitrumSepolia) {
l2Addresses.push(CommonAddress.ArbitrumSepolia.USDC)
}
@@ -270,6 +280,7 @@ export const useArbTokenBridge = (
return
}
const { address, l2Address } = tokenToAdd
+
if (address) {
l1Addresses.push(address)
}
@@ -409,6 +420,7 @@ export const useArbTokenBridge = (
}
},
[
+ destinationAddress,
bridgeTokens,
setBridgeTokens,
updateErc20L1Balance,
diff --git a/packages/arb-token-bridge-ui/src/hooks/useTokenLists.ts b/packages/arb-token-bridge-ui/src/hooks/useTokenLists.ts
index fc69158be0..b9263d05c8 100644
--- a/packages/arb-token-bridge-ui/src/hooks/useTokenLists.ts
+++ b/packages/arb-token-bridge-ui/src/hooks/useTokenLists.ts
@@ -5,18 +5,16 @@ import {
fetchTokenListFromURL,
TokenListWithId
} from '../util/TokenListUtils'
-import { isNetwork } from '../util/networks'
export function fetchTokenLists(
forL2ChainId: number
): Promise {
return new Promise(resolve => {
- const { isOrbitChain } = isNetwork(forL2ChainId)
const requestListArray = BRIDGE_TOKEN_LISTS.filter(
bridgeTokenList =>
bridgeTokenList.originChainID === forL2ChainId ||
- // Always load the Arbitrum Token token list except from or to Orbit chain
- (bridgeTokenList.isArbitrumTokenTokenList && !isOrbitChain)
+ // Always load the Arbitrum Token token list
+ bridgeTokenList.isArbitrumTokenTokenList
)
Promise.all(
diff --git a/packages/arb-token-bridge-ui/src/util/ArbTokenUtils.ts b/packages/arb-token-bridge-ui/src/util/ArbTokenUtils.ts
new file mode 100644
index 0000000000..84250e1bfb
--- /dev/null
+++ b/packages/arb-token-bridge-ui/src/util/ArbTokenUtils.ts
@@ -0,0 +1,10 @@
+import { ERC20BridgeToken } from '../hooks/arbTokenBridge.types'
+import { SPECIAL_ARBITRUM_TOKEN_TOKEN_LIST_ID } from './TokenListUtils'
+
+export function isArbitrumToken(token: ERC20BridgeToken | null) {
+ if (!token) {
+ return false
+ }
+
+ return token.listIds.has(SPECIAL_ARBITRUM_TOKEN_TOKEN_LIST_ID)
+}
diff --git a/packages/arb-token-bridge-ui/src/util/TokenListUtils.ts b/packages/arb-token-bridge-ui/src/util/TokenListUtils.ts
index 3374dd115c..cd887d3f86 100644
--- a/packages/arb-token-bridge-ui/src/util/TokenListUtils.ts
+++ b/packages/arb-token-bridge-ui/src/util/TokenListUtils.ts
@@ -3,6 +3,7 @@ import { schema, TokenList } from '@uniswap/token-lists'
import Ajv from 'ajv'
import addFormats from 'ajv-formats'
import { ImageProps } from 'next/image'
+
import UniswapLogo from '@/images/lists/uniswap.png'
import CMCLogo from '@/images/lists/cmc.png'
import CoinGeckoLogo from '@/images/lists/coinGecko.svg'
@@ -12,6 +13,9 @@ import { ChainId } from './networks'
export const SPECIAL_ARBITRUM_TOKEN_TOKEN_LIST_ID = 0
+export const isArbitrumTokenList = (listId: number) =>
+ listId === SPECIAL_ARBITRUM_TOKEN_TOKEN_LIST_ID
+
export interface BridgeTokenList {
id: number
originChainID: number
@@ -190,15 +194,28 @@ export const validateTokenList = (tokenList: TokenList) => {
return validate(tokenList)
}
-export const addBridgeTokenListToBridge = (
- bridgeTokenList: BridgeTokenList,
+export const addBridgeTokenListToBridge = ({
+ bridgeTokenList,
+ arbTokenBridge,
+ parentChainId,
+ childChainId
+}: {
+ bridgeTokenList: BridgeTokenList
arbTokenBridge: ArbTokenBridge
-) => {
+ parentChainId: ChainId
+ childChainId: ChainId
+}) => {
fetchTokenListFromURL(bridgeTokenList.url).then(
({ isValid, data: tokenList }) => {
if (!isValid) return
-
- arbTokenBridge.token.addTokensFromList(tokenList!, bridgeTokenList.id)
+ if (!tokenList) return
+
+ arbTokenBridge.token.addTokensFromList({
+ arbTokenList: tokenList,
+ listId: bridgeTokenList.id,
+ parentChainId,
+ childChainId
+ })
}
)
}