diff --git a/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte b/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte index 47540db0a3c..56bf4a46863 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/Bridge.svelte @@ -12,10 +12,9 @@ import { PUBLIC_L1_EXPLORER_URL } from '$env/static/public'; import { type Bridge, type BridgeArgs, bridges, type ERC20BridgeArgs, type ETHBridgeArgs } from '$libs/bridge'; import type { ERC20Bridge } from '$libs/bridge/ERC20Bridge'; - import type { ETHBridge } from '$libs/bridge/ETHBridge'; import { chainContractsMap, chains } from '$libs/chain'; import { ApproveError, NoAllowanceRequiredError, SendERC20Error, SendMessageError } from '$libs/error'; - import { ETHToken, getAddress, isDeployedCrossChain, isETH, tokens } from '$libs/token'; + import { ETHToken, getAddress, isDeployedCrossChain, tokens, TokenType } from '$libs/token'; import { getConnectedWallet } from '$libs/util/getConnectedWallet'; import { type Account, account } from '$stores/account'; import { type Network, network } from '$stores/network'; @@ -25,7 +24,7 @@ import Amount from './Amount.svelte'; import { ProcessingFee } from './ProcessingFee'; import Recipient from './Recipient.svelte'; - import { destNetwork, enteredAmount, processingFee, recipientAddress, selectedToken } from './state'; + import { bridgeService, destNetwork, enteredAmount, processingFee, recipientAddress, selectedToken } from './state'; import SwitchChainsButton from './SwitchChainsButton.svelte'; let amountComponent: Amount; @@ -119,13 +118,11 @@ } async function bridge() { - if (!$selectedToken || !$network || !$destNetwork || !$account?.address) return; + if (!$bridgeService || !$selectedToken || !$network || !$destNetwork || !$account?.address) return; try { const walletClient = await getConnectedWallet($network.id); - let bridge: Bridge; - // Common arguments for both ETH and ERC20 bridges let bridgeArgs = { to: $recipientAddress || $account.address, @@ -136,47 +133,52 @@ processingFee: $processingFee, } as BridgeArgs; - if (isETH($selectedToken)) { - bridge = bridges.ETH as ETHBridge; - - // Specific arguments for ETH bridge: - // - bridgeAddress - const bridgeAddress = chainContractsMap[$network.id].bridgeAddress; - bridgeArgs = { ...bridgeArgs, bridgeAddress } as ETHBridgeArgs; - } else { - bridge = bridges.ERC20 as ERC20Bridge; - - // Specific arguments for ERC20 bridge - // - tokenAddress - // - tokenVaultAddress - // - isTokenAlreadyDeployed - const tokenAddress = await getAddress({ - token: $selectedToken, - srcChainId: $network.id, - destChainId: $destNetwork.id, - }); - - if (!tokenAddress) { - throw new Error('token address not found'); + switch ($selectedToken.type) { + case TokenType.ETH: { + // Specific arguments for ETH bridge: + // - bridgeAddress + const bridgeAddress = chainContractsMap[$network.id].bridgeAddress; + bridgeArgs = { ...bridgeArgs, bridgeAddress } as ETHBridgeArgs; + break; } - const tokenVaultAddress = chainContractsMap[$network.id].tokenVaultAddress; - - const isTokenAlreadyDeployed = await isDeployedCrossChain({ - token: $selectedToken, - srcChainId: $network.id, - destChainId: $destNetwork.id, - }); + case TokenType.ERC20: { + // Specific arguments for ERC20 bridge + // - tokenAddress + // - tokenVaultAddress + // - isTokenAlreadyDeployed + const tokenAddress = await getAddress({ + token: $selectedToken, + srcChainId: $network.id, + destChainId: $destNetwork.id, + }); + + if (!tokenAddress) { + throw new Error('token address not found'); + } + + const tokenVaultAddress = chainContractsMap[$network.id].tokenVaultAddress; + + const isTokenAlreadyDeployed = await isDeployedCrossChain({ + token: $selectedToken, + srcChainId: $network.id, + destChainId: $destNetwork.id, + }); + + bridgeArgs = { + ...bridgeArgs, + tokenAddress, + tokenVaultAddress, + isTokenAlreadyDeployed, + } as ERC20BridgeArgs; + break; + } - bridgeArgs = { - ...bridgeArgs, - tokenAddress, - tokenVaultAddress, - isTokenAlreadyDeployed, - } as ERC20BridgeArgs; + default: + throw new Error('invalid token type'); } - const txHash = await bridge.bridge(bridgeArgs); + const txHash = await $bridgeService.bridge(bridgeArgs); infoToast( $t('bridge.bridge.tx', { diff --git a/packages/bridge-ui-v2/src/components/Bridge/state.ts b/packages/bridge-ui-v2/src/components/Bridge/state.ts index 52eda1b3cfc..8dc806c3051 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/state.ts +++ b/packages/bridge-ui-v2/src/components/Bridge/state.ts @@ -1,6 +1,7 @@ import type { Address, Chain, FetchBalanceResult } from '@wagmi/core'; -import { writable } from 'svelte/store'; +import { derived, writable } from 'svelte/store'; +import { bridges } from '$libs/bridge'; import type { Token } from '$libs/token'; // Note: we could combine this with Context API, but since we'll only @@ -35,3 +36,6 @@ export const errorComputingBalance = writable(false); // is a warning but the user must approve allowance before bridging export const insufficientBalance = writable(false); export const insufficientAllowance = writable(false); + +// Derived state +export const bridgeService = derived(selectedToken, (token) => (token ? bridges[token.type] : null)); diff --git a/packages/bridge-ui-v2/src/libs/bridge/bridges.ts b/packages/bridge-ui-v2/src/libs/bridge/bridges.ts index 8eefde0a6dd..56262c7ad4b 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/bridges.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/bridges.ts @@ -1,10 +1,12 @@ +import type { TokenType } from '$libs/token'; + import { ERC20Bridge } from './ERC20Bridge'; import { ERC721Bridge } from './ERC721Bridge'; import { ERC1155Bridge } from './ERC1155Bridge'; import { ETHBridge } from './ETHBridge'; -import type { Bridge, BridgeType } from './types'; +import type { Bridge } from './types'; -export const bridges: Record = { +export const bridges: Record = { ETH: new ETHBridge(), ERC20: new ERC20Bridge(), ERC721: new ERC721Bridge(), diff --git a/packages/bridge-ui-v2/src/libs/bridge/checkBalanceToBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/checkBalanceToBridge.ts index 1ed7f30a507..b91d5356792 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/checkBalanceToBridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/checkBalanceToBridge.ts @@ -2,7 +2,7 @@ import { type Address, zeroAddress } from 'viem'; import { chainContractsMap } from '$libs/chain'; import { InsufficientAllowanceError, InsufficientBalanceError, RevertedWithFailedError } from '$libs/error'; -import { getAddress, isETH, type Token } from '$libs/token'; +import { getAddress, type Token,TokenType } from '$libs/token'; import { isDeployedCrossChain } from '$libs/token/isDeployedCrossChain'; import { getConnectedWallet } from '$libs/util/getConnectedWallet'; @@ -41,7 +41,7 @@ export async function checkBalanceToBridge({ processingFee, } as BridgeArgs; - if (isETH(token)) { + if (token.type === TokenType.ETH) { const { bridgeAddress } = chainContractsMap[srcChainId]; try { diff --git a/packages/bridge-ui-v2/src/libs/bridge/getMaxAmountToBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/getMaxAmountToBridge.ts index 7852a117dcc..38651073a36 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/getMaxAmountToBridge.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/getMaxAmountToBridge.ts @@ -1,7 +1,7 @@ import type { Address } from 'viem'; import { chainContractsMap } from '$libs/chain'; -import { isETH, type Token } from '$libs/token'; +import { type Token,TokenType } from '$libs/token'; import { getConnectedWallet } from '$libs/util/getConnectedWallet'; import { getLogger } from '$libs/util/logger'; @@ -33,7 +33,7 @@ export async function getMaxAmountToBridge({ // For ERC20 tokens, we can bridge the whole balance let maxAmount = balance; - if (isETH(token)) { + if (token.type === TokenType.ETH) { // We cannot really compute the cost of bridging ETH without if (!to || !srcChainId || !destChainId) { throw Error('missing required arguments to compute cost'); diff --git a/packages/bridge-ui-v2/src/libs/bridge/types.ts b/packages/bridge-ui-v2/src/libs/bridge/types.ts index cbb3d35c39b..274906e45cf 100644 --- a/packages/bridge-ui-v2/src/libs/bridge/types.ts +++ b/packages/bridge-ui-v2/src/libs/bridge/types.ts @@ -1,19 +1,6 @@ import type { WalletClient } from '@wagmi/core'; import type { Address, Hex } from 'viem'; -export enum BridgeType { - ETH = 'ETH', - - // https://ethereum.org/en/developers/docs/standards/tokens/erc-20/ - ERC20 = 'ERC20', - - // https://ethereum.org/en/developers/docs/standards/tokens/erc-721/ - ERC721 = 'ERC721', - - // https://ethereum.org/en/developers/docs/standards/tokens/erc-1155/ - ERC1155 = 'ERC1155', -} - // Bridge sendMessage(message: Message) export type Message = { // Message ID. Will be set in contract diff --git a/packages/bridge-ui-v2/src/libs/token/getAddress.ts b/packages/bridge-ui-v2/src/libs/token/getAddress.ts index c2d8e022cde..988981c67dc 100644 --- a/packages/bridge-ui-v2/src/libs/token/getAddress.ts +++ b/packages/bridge-ui-v2/src/libs/token/getAddress.ts @@ -4,8 +4,7 @@ import { NoTokenAddressError } from '$libs/error'; import { getLogger } from '$libs/util/logger'; import { getCrossChainAddress } from './getCrossChainAddress'; -import { isETH } from './tokens'; -import type { Token } from './types'; +import { type Token,TokenType } from './types'; type GetAddressArgs = { token: Token; @@ -16,7 +15,7 @@ type GetAddressArgs = { const log = getLogger('token:getAddress'); export async function getAddress({ token, srcChainId, destChainId }: GetAddressArgs) { - if (isETH(token)) return; // ETH doesn't have an address + if (token.type === TokenType.ETH) return; // ETH doesn't have an address // Get the address for the token on the source chain let address: Maybe
= token.addresses[srcChainId]; diff --git a/packages/bridge-ui-v2/src/libs/token/getBalance.ts b/packages/bridge-ui-v2/src/libs/token/getBalance.ts index 33aad51707f..d8c69f03aef 100644 --- a/packages/bridge-ui-v2/src/libs/token/getBalance.ts +++ b/packages/bridge-ui-v2/src/libs/token/getBalance.ts @@ -4,8 +4,7 @@ import { type Address, zeroAddress } from 'viem'; import { getLogger } from '$libs/util/logger'; import { getAddress } from './getAddress'; -import { isETH } from './tokens'; -import type { Token } from './types'; +import { type Token,TokenType } from './types'; type GetBalanceArgs = { userAddress: Address; @@ -19,7 +18,7 @@ const log = getLogger('token:getBalance'); export async function getBalance({ userAddress, token, srcChainId, destChainId }: GetBalanceArgs) { let tokenBalance: FetchBalanceResult; - if (!token || isETH(token)) { + if (!token || token.type === TokenType.ETH) { // If no token is passed in, we assume is ETH tokenBalance = await fetchBalance({ address: userAddress }); } else { diff --git a/packages/bridge-ui-v2/src/libs/token/getCrossChainAddress.ts b/packages/bridge-ui-v2/src/libs/token/getCrossChainAddress.ts index 7b30e2c2a23..eaa5e35b040 100644 --- a/packages/bridge-ui-v2/src/libs/token/getCrossChainAddress.ts +++ b/packages/bridge-ui-v2/src/libs/token/getCrossChainAddress.ts @@ -3,8 +3,7 @@ import { getContract } from '@wagmi/core'; import { tokenVaultABI } from '$abi'; import { chainContractsMap } from '$libs/chain'; -import { isETH } from './tokens'; -import type { Token } from './types'; +import { type Token,TokenType } from './types'; type GetCrossChainAddressArgs = { token: Token; @@ -13,7 +12,7 @@ type GetCrossChainAddressArgs = { }; export function getCrossChainAddress({ token, srcChainId, destChainId }: GetCrossChainAddressArgs) { - if (isETH(token)) return; // ETH doesn't have an address + if (token.type === TokenType.ETH) return; // ETH doesn't have an address const { tokenVaultAddress } = chainContractsMap[destChainId]; diff --git a/packages/bridge-ui-v2/src/libs/token/tokens.test.ts b/packages/bridge-ui-v2/src/libs/token/tokens.test.ts deleted file mode 100644 index 1f1734e8c3d..00000000000 --- a/packages/bridge-ui-v2/src/libs/token/tokens.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { ETHToken, isERC20, isETH, isTestToken, testERC20Tokens, TKOToken } from './tokens'; - -vi.mock('$env/static/public'); - -const BLL = testERC20Tokens[0]; -const HORSE = testERC20Tokens[1]; - -describe('Tokens', () => { - it('tests isTestToken', () => { - expect(isTestToken(BLL)).toBeTruthy(); - expect(isTestToken(HORSE)).toBeTruthy(); - expect(isTestToken(ETHToken)).toBeFalsy(); - }); - - it('tests isETH', () => { - expect(isETH(ETHToken)).toBeTruthy(); - expect(isETH(BLL)).toBeFalsy(); - expect(isETH(HORSE)).toBeFalsy(); - expect(isETH(TKOToken)).toBeFalsy(); - }); - - it('tests isERC20', () => { - expect(isERC20(ETHToken)).toBeFalsy(); - expect(isERC20(BLL)).toBeTruthy(); - expect(isERC20(HORSE)).toBeTruthy(); - expect(isERC20(TKOToken)).toBeTruthy(); - }); -}); diff --git a/packages/bridge-ui-v2/src/libs/token/tokens.ts b/packages/bridge-ui-v2/src/libs/token/tokens.ts index 5635c9100ae..c55ec4f7993 100644 --- a/packages/bridge-ui-v2/src/libs/token/tokens.ts +++ b/packages/bridge-ui-v2/src/libs/token/tokens.ts @@ -3,7 +3,7 @@ import { zeroAddress } from 'viem'; import { PUBLIC_L1_CHAIN_ID, PUBLIC_L2_CHAIN_ID, PUBLIC_TEST_ERC20 } from '$env/static/public'; import { jsonParseWithDefault } from '$libs/util/jsonParseWithDefault'; -import type { Token, TokenEnv } from './types'; +import { type Token, type TokenEnv, TokenType } from './types'; export const ETHToken: Token = { name: 'Ether', @@ -13,6 +13,7 @@ export const ETHToken: Token = { }, decimals: 18, symbol: 'ETH', + type: TokenType.ETH, }; export const TKOToken: Token = { @@ -23,6 +24,7 @@ export const TKOToken: Token = { }, decimals: 8, symbol: 'TKO', + type: TokenType.ERC20, }; export const testERC20Tokens: Token[] = jsonParseWithDefault(PUBLIC_TEST_ERC20, []).map( @@ -35,21 +37,8 @@ export const testERC20Tokens: Token[] = jsonParseWithDefault(PUBLIC_ [PUBLIC_L2_CHAIN_ID]: zeroAddress, }, decimals: 18, + type: TokenType.ERC20, }), ); export const tokens = [ETHToken, ...testERC20Tokens]; - -export function isETH(token: Token) { - // Should be fine just by checking the symbol - return token.symbol.toLocaleLowerCase() === ETHToken.symbol.toLocaleLowerCase(); -} - -export function isERC20(token: Token): boolean { - return !isETH(token); -} - -export function isTestToken(token: Token): boolean { - const testTokenSymbols = testERC20Tokens.map((testToken) => testToken.symbol.toLocaleLowerCase()); - return testTokenSymbols.includes(token.symbol.toLocaleLowerCase()); -} diff --git a/packages/bridge-ui-v2/src/libs/token/types.ts b/packages/bridge-ui-v2/src/libs/token/types.ts index e68098c660b..96e180efb64 100644 --- a/packages/bridge-ui-v2/src/libs/token/types.ts +++ b/packages/bridge-ui-v2/src/libs/token/types.ts @@ -1,10 +1,24 @@ import type { Address } from 'viem'; +export enum TokenType { + ETH = 'ETH', + + // https://ethereum.org/en/developers/docs/standards/tokens/erc-20/ + ERC20 = 'ERC20', + + // https://ethereum.org/en/developers/docs/standards/tokens/erc-721/ + ERC721 = 'ERC721', + + // https://ethereum.org/en/developers/docs/standards/tokens/erc-1155/ + ERC1155 = 'ERC1155', +} + export type Token = { name: string; addresses: Record; symbol: string; decimals: number; + type: TokenType; }; export type TokenEnv = {