-
Notifications
You must be signed in to change notification settings - Fork 212
refactor: use token list gateways if possible #2172
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
4bcf1c5
8642f3d
019cf8a
e7fa029
c2637a2
954e8ac
2741f5e
4096ac7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import { getProviderForChainId } from '@/token-bridge-sdk/utils' | ||
import { ChainId } from '../../util/networks' | ||
import { processTokensFromList } from '../useArbTokenBridge' | ||
import { | ||
BRIDGE_TOKEN_LISTS, | ||
fetchTokenListFromURL | ||
} from '../../util/TokenListUtils' | ||
import { | ||
fetchErc20L2GatewayAddress, | ||
fetchErc20ParentChainGatewayAddress | ||
} from '../../util/TokenUtils' | ||
|
||
// can be found on https://tokenlist.arbitrum.io/ArbTokenLists/arbed_coinmarketcap.json | ||
// standard gateway token | ||
const wBtcAddressOnEthereum = | ||
'0x2260fac5e5542a773aa44fbcfedf7c193bc2c599'.toLowerCase() | ||
|
||
// custom gateway token | ||
const lptAddressOnEthereum = | ||
'0x58b6a8a3302369daec383334672404ee733ab239'.toLowerCase() | ||
|
||
const parentProvider = getProviderForChainId(ChainId.Ethereum) | ||
Check failure on line 22 in packages/arb-token-bridge-ui/src/hooks/__tests__/useArbTokenBridge.test.ts
|
||
const childProvider = getProviderForChainId(ChainId.ArbitrumOne) | ||
|
||
describe('processTokensFromList', () => { | ||
it('token parent bridge address should be same as fetched parent gateway address', async () => { | ||
const { data: bridgeTokenList } = await fetchTokenListFromURL( | ||
BRIDGE_TOKEN_LISTS[2]!.url | ||
) | ||
|
||
const { bridgeTokensToAdd } = await processTokensFromList({ | ||
arbTokenList: bridgeTokenList!, | ||
listId: BRIDGE_TOKEN_LISTS[2]!.id, | ||
parentChainId: ChainId.Ethereum, | ||
childChainId: ChainId.ArbitrumOne | ||
}) | ||
|
||
// WBTC, standard gateway | ||
const fetchedWbtcParentGatewayAddress = | ||
await fetchErc20ParentChainGatewayAddress({ | ||
erc20ParentChainAddress: wBtcAddressOnEthereum, | ||
parentChainProvider: parentProvider, | ||
childChainProvider: childProvider | ||
}) | ||
|
||
expect( | ||
bridgeTokensToAdd[ | ||
wBtcAddressOnEthereum | ||
]?.parentBridgeAddress?.toLowerCase() | ||
).toEqual(fetchedWbtcParentGatewayAddress.toLowerCase()) | ||
|
||
// LPT, custom gateway | ||
const fetchedLptParentGatewayAddress = | ||
await fetchErc20ParentChainGatewayAddress({ | ||
erc20ParentChainAddress: lptAddressOnEthereum, | ||
parentChainProvider: parentProvider, | ||
childChainProvider: childProvider | ||
}) | ||
|
||
expect( | ||
bridgeTokensToAdd[ | ||
lptAddressOnEthereum | ||
]?.parentBridgeAddress?.toLowerCase() | ||
).toEqual(fetchedLptParentGatewayAddress.toLowerCase()) | ||
}) | ||
|
||
it('token child bridge address should be same as fetched child gateway address', async () => { | ||
const { data: bridgeTokenList } = await fetchTokenListFromURL( | ||
BRIDGE_TOKEN_LISTS[2]!.url | ||
) | ||
|
||
const { bridgeTokensToAdd } = await processTokensFromList({ | ||
arbTokenList: bridgeTokenList!, | ||
listId: BRIDGE_TOKEN_LISTS[2]!.id, | ||
parentChainId: ChainId.Ethereum, | ||
childChainId: ChainId.ArbitrumOne | ||
}) | ||
|
||
// WBTC, standard gateway | ||
const fetchedWbtcChildGatewayAddress = await fetchErc20L2GatewayAddress({ | ||
erc20L1Address: wBtcAddressOnEthereum, | ||
l2Provider: childProvider | ||
}) | ||
|
||
expect( | ||
bridgeTokensToAdd[ | ||
wBtcAddressOnEthereum | ||
]?.childBridgeAddress?.toLowerCase() | ||
).toEqual(fetchedWbtcChildGatewayAddress.toLowerCase()) | ||
|
||
// LPT, custom gateway | ||
const fetchedLptChildGatewayAddress = await fetchErc20L2GatewayAddress({ | ||
erc20L1Address: lptAddressOnEthereum, | ||
l2Provider: childProvider | ||
}) | ||
|
||
expect( | ||
bridgeTokensToAdd[lptAddressOnEthereum]?.childBridgeAddress?.toLowerCase() | ||
).toEqual(fetchedLptChildGatewayAddress.toLowerCase()) | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
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' | ||
|
@@ -79,6 +79,98 @@ class TokenDisabledError extends Error { | |
} | ||
} | ||
|
||
export async function processTokensFromList({ | ||
arbTokenList, | ||
listId, | ||
parentChainId, | ||
childChainId | ||
}: { | ||
arbTokenList: TokenList | ||
listId: string | ||
parentChainId: number | ||
childChainId: number | ||
}) { | ||
const bridgeTokensToAdd: ContractStorage<ERC20BridgeToken> = {} | ||
const candidateUnbridgedTokensToAdd: ERC20BridgeToken[] = [] | ||
|
||
for (const tokenData of arbTokenList.tokens) { | ||
const { address, name, symbol, extensions, decimals, logoURI, chainId } = | ||
tokenData | ||
|
||
if (![parentChainId, childChainId].includes(chainId)) { | ||
continue | ||
} | ||
|
||
const bridgeInfo = (() => { | ||
// TODO: parsing the token list format could be from arbts or the tokenlist package | ||
interface Extensions { | ||
bridgeInfo: { | ||
[chainId: string]: { | ||
tokenAddress: string | ||
originBridgeAddress: string | ||
destBridgeAddress: string | ||
} | ||
} | ||
} | ||
const isExtensions = (obj: any): obj is Extensions => { | ||
if (!obj) return false | ||
if (!obj['bridgeInfo']) return false | ||
return Object.keys(obj['bridgeInfo']) | ||
.map(key => obj['bridgeInfo'][key]) | ||
.every( | ||
e => | ||
e && | ||
'tokenAddress' in e && | ||
'originBridgeAddress' in e && | ||
'destBridgeAddress' in e | ||
) | ||
} | ||
if (!isExtensions(extensions)) { | ||
return null | ||
} else { | ||
return extensions.bridgeInfo | ||
} | ||
})() | ||
|
||
if (bridgeInfo) { | ||
const l1Address = bridgeInfo[parentChainId]?.tokenAddress.toLowerCase() | ||
|
||
if (l1Address) { | ||
bridgeTokensToAdd[l1Address] = { | ||
name, | ||
type: TokenType.ERC20, | ||
symbol, | ||
address: l1Address, | ||
l2Address: address.toLowerCase(), | ||
parentBridgeAddress: bridgeInfo[parentChainId]?.destBridgeAddress, | ||
childBridgeAddress: bridgeInfo[parentChainId]?.originBridgeAddress, | ||
decimals, | ||
logoURI, | ||
listIds: new Set([listId]) | ||
} | ||
} | ||
} | ||
// save potentially unbridged L1 tokens: | ||
// stopgap: giant lists (i.e., CMC list) currently severaly hurts page performace, so for now we only add the bridged tokens | ||
else if (arbTokenList.tokens.length < 1000) { | ||
candidateUnbridgedTokensToAdd.push({ | ||
name, | ||
type: TokenType.ERC20, | ||
symbol, | ||
address: address.toLowerCase(), | ||
decimals, | ||
logoURI, | ||
listIds: new Set([listId]) | ||
}) | ||
} | ||
} | ||
|
||
return { | ||
bridgeTokensToAdd, | ||
candidateUnbridgedTokensToAdd | ||
} | ||
} | ||
|
||
export interface TokenBridgeParams { | ||
l1: { provider: JsonRpcProvider; network: Chain } | ||
l2: { provider: JsonRpcProvider; network: Chain } | ||
|
@@ -134,8 +226,6 @@ export const useArbTokenBridge = ( | |
React.Dispatch<void> | ||
] | ||
|
||
const l1NetworkID = useMemo(() => String(l1.network.id), [l1.network.id]) | ||
|
||
const removeTokensFromList = (listID: string) => { | ||
setBridgeTokens(prevBridgeTokens => { | ||
const newBridgeTokens = { ...prevBridgeTokens } | ||
|
@@ -157,81 +247,13 @@ export const useArbTokenBridge = ( | |
const l1ChainID = l1.network.id | ||
const l2ChainID = l2.network.id | ||
|
||
const bridgeTokensToAdd: ContractStorage<ERC20BridgeToken> = {} | ||
|
||
const candidateUnbridgedTokensToAdd: ERC20BridgeToken[] = [] | ||
|
||
for (const tokenData of arbTokenList.tokens) { | ||
const { address, name, symbol, extensions, decimals, logoURI, chainId } = | ||
tokenData | ||
|
||
if (![l1ChainID, l2ChainID].includes(chainId)) { | ||
continue | ||
} | ||
|
||
const bridgeInfo = (() => { | ||
// TODO: parsing the token list format could be from arbts or the tokenlist package | ||
interface Extensions { | ||
bridgeInfo: { | ||
[chainId: string]: { | ||
tokenAddress: string | ||
originBridgeAddress: string | ||
destBridgeAddress: string | ||
} | ||
} | ||
} | ||
const isExtensions = (obj: any): obj is Extensions => { | ||
if (!obj) return false | ||
if (!obj['bridgeInfo']) return false | ||
return Object.keys(obj['bridgeInfo']) | ||
.map(key => obj['bridgeInfo'][key]) | ||
.every( | ||
e => | ||
e && | ||
'tokenAddress' in e && | ||
'originBridgeAddress' in e && | ||
'destBridgeAddress' in e | ||
) | ||
} | ||
if (!isExtensions(extensions)) { | ||
return null | ||
} else { | ||
return extensions.bridgeInfo | ||
} | ||
})() | ||
|
||
if (bridgeInfo) { | ||
const l1Address = bridgeInfo[l1NetworkID]?.tokenAddress.toLowerCase() | ||
|
||
if (!l1Address) { | ||
return | ||
} | ||
|
||
bridgeTokensToAdd[l1Address] = { | ||
name, | ||
type: TokenType.ERC20, | ||
symbol, | ||
address: l1Address, | ||
l2Address: address.toLowerCase(), | ||
decimals, | ||
logoURI, | ||
listIds: new Set([listId]) | ||
} | ||
} | ||
// save potentially unbridged L1 tokens: | ||
// stopgap: giant lists (i.e., CMC list) currently severaly hurts page performace, so for now we only add the bridged tokens | ||
else if (arbTokenList.tokens.length < 1000) { | ||
candidateUnbridgedTokensToAdd.push({ | ||
name, | ||
type: TokenType.ERC20, | ||
symbol, | ||
address: address.toLowerCase(), | ||
decimals, | ||
logoURI, | ||
listIds: new Set([listId]) | ||
}) | ||
} | ||
} | ||
const { bridgeTokensToAdd, candidateUnbridgedTokensToAdd } = | ||
await processTokensFromList({ | ||
arbTokenList, | ||
listId, | ||
parentChainId: l1ChainID, | ||
childChainId: l2ChainID | ||
}) | ||
|
||
Comment on lines
+250
to
+256
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. had to move the processing code out, otherwise we will have to mock all the external and internal hooks, e.g. |
||
// add L1 tokens only if they aren't already bridged (i.e., if they haven't already beed added as L2 arb-tokens to the list) | ||
const l1AddressesOfBridgedTokens = new Set( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to be updated since the reference has changed. Otherwise test fails.