diff --git a/packages/bridge-ui-v2/src/components/Activities/Activities.svelte b/packages/bridge-ui-v2/src/components/Activities/Activities.svelte
index 2096c128262..30bd8f937ea 100644
--- a/packages/bridge-ui-v2/src/components/Activities/Activities.svelte
+++ b/packages/bridge-ui-v2/src/components/Activities/Activities.svelte
@@ -11,7 +11,6 @@
import { Spinner } from '$components/Spinner';
import { activitiesConfig } from '$config';
import { type BridgeTransaction, fetchTransactions } from '$libs/bridge';
- import { web3modal } from '$libs/connect';
import { bridgeTxService } from '$libs/storage';
import { account, network } from '$stores';
import type { Account } from '$stores/account';
@@ -51,8 +50,6 @@
return bridgeTx.slice(start, end);
};
- const onWalletConnect = () => web3modal.openModal();
-
const onAccountChange = async (newAccount: Account, oldAccount?: Account) => {
// We want to make sure that we are connected and only
// fetch if the account has changed
diff --git a/packages/bridge-ui-v2/src/components/Activities/Transaction.svelte b/packages/bridge-ui-v2/src/components/Activities/Transaction.svelte
index d9691a4ba78..271b4163539 100644
--- a/packages/bridge-ui-v2/src/components/Activities/Transaction.svelte
+++ b/packages/bridge-ui-v2/src/components/Activities/Transaction.svelte
@@ -2,12 +2,12 @@
import { t } from 'svelte-i18n';
import { formatEther } from 'viem';
- import type { BridgeTransaction, MessageStatus } from '$libs/bridge';
+ import type { BridgeTransaction } from '$libs/bridge';
import { chainUrlMap } from '$libs/chain';
export let item: BridgeTransaction;
- import { createEventDispatcher, onDestroy } from 'svelte';
+ import { createEventDispatcher } from 'svelte';
import { DesktopOrLarger } from '$components/DesktopOrLarger';
import { Icon } from '$components/Icon';
@@ -23,19 +23,6 @@
const handlePress = () => dispatch('press');
- const mapStatusToText = (status: MessageStatus) => {
- switch (status) {
- case 1:
- return 'Pending';
- case 2:
- return 'Claimed';
- case 3:
- return 'Failed';
- default:
- return 'Unknown';
- }
- };
-
let attrs = isDesktopOrLarger ? {} : { role: 'button' };
diff --git a/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/ProcessingFee.svelte b/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/ProcessingFee.svelte
index 9a98bfd23e6..de20dc72548 100644
--- a/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/ProcessingFee.svelte
+++ b/packages/bridge-ui-v2/src/components/Bridge/ProcessingFee/ProcessingFee.svelte
@@ -9,7 +9,6 @@
import { InputBox } from '$components/InputBox';
import { LoadingText } from '$components/LoadingText';
import { Tooltip } from '$components/Tooltip';
- import { processingFeeComponent } from '$config';
import { ProcessingFeeMethod } from '$libs/fee';
import { parseToWei } from '$libs/util/parseToWei';
import { uid } from '$libs/util/uid';
@@ -63,12 +62,6 @@
closeModal();
}
- function closeModalWithDelay() {
- // By adding delay there is enough time to see the selected option
- // before closing the modal. Better experience for the user.
- setTimeout(closeModal, processingFeeComponent.closingDelayOptionClick);
- }
-
function focusInputBox() {
inputBox.focus();
}
diff --git a/packages/bridge-ui-v2/src/components/InputBox/InputBox.svelte b/packages/bridge-ui-v2/src/components/InputBox/InputBox.svelte
index 8f54a2ac5cc..9183c06a661 100644
--- a/packages/bridge-ui-v2/src/components/InputBox/InputBox.svelte
+++ b/packages/bridge-ui-v2/src/components/InputBox/InputBox.svelte
@@ -1,6 +1,4 @@
@@ -114,7 +125,7 @@
{#if isDesktopOrLarger}
-
+
{:else}
{/if}
diff --git a/packages/bridge-ui-v2/src/libs/bridge/checkBalanceToBridge.ts b/packages/bridge-ui-v2/src/libs/bridge/checkBalanceToBridge.ts
index 393a259fa9f..7563464d2c7 100644
--- a/packages/bridge-ui-v2/src/libs/bridge/checkBalanceToBridge.ts
+++ b/packages/bridge-ui-v2/src/libs/bridge/checkBalanceToBridge.ts
@@ -97,8 +97,8 @@ export async function checkBalanceToBridge({
const isTokenAlreadyDeployed = await isDeployedCrossChain({
token,
- srcChainId: destChainId,
- destChainId: srcChainId,
+ srcChainId,
+ destChainId,
});
try {
diff --git a/packages/bridge-ui-v2/src/libs/fee/recommendProcessingFee.ts b/packages/bridge-ui-v2/src/libs/fee/recommendProcessingFee.ts
index b028386003c..6036406beb6 100644
--- a/packages/bridge-ui-v2/src/libs/fee/recommendProcessingFee.ts
+++ b/packages/bridge-ui-v2/src/libs/fee/recommendProcessingFee.ts
@@ -1,8 +1,10 @@
import { getPublicClient } from '@wagmi/core';
-import { zeroAddress } from 'viem';
import { recommentProcessingFee } from '$config';
-import { getAddress, type Token, TokenType } from '$libs/token';
+import { isDeployedCrossChain, type Token, TokenType } from '$libs/token';
+import { getLogger } from '$libs/util/logger';
+
+const log = getLogger('libs:recommendedProcessingFee');
type RecommendProcessingFeeArgs = {
token: Token;
@@ -25,16 +27,22 @@ export async function recommendProcessingFee({ token, destChainId, srcChainId }:
throw Error('missing required source chain for ERC20 token');
}
- const tokenAddress = await getAddress({ token, srcChainId, destChainId });
+ const isTokenAlreadyDeployed = await isDeployedCrossChain({
+ token,
+ srcChainId,
+ destChainId,
+ });
- if (!tokenAddress || tokenAddress === zeroAddress) {
- // Gas limit for erc20 if not deployed on the destination chain
- // already is about ~2.9m, so we add some to make it enticing
- gasLimit = erc20NotDeployedGasLimit;
- } else {
+ if (isTokenAlreadyDeployed) {
// Gas limit for erc20 if already deployed on the destination chain is
// about ~1m, so again, add some to ensure processing
gasLimit = erc20DeployedGasLimit;
+ log(`token ${token.symbol} is already deployed on chain ${destChainId}`);
+ } else {
+ // Gas limit for erc20 if not deployed on the destination chain
+ // already is about ~2.9m, so we add some to make it enticing
+ gasLimit = erc20NotDeployedGasLimit;
+ log(`token ${token.symbol} is not deployed on chain ${destChainId}`);
}
}
diff --git a/packages/bridge-ui-v2/src/libs/storage/CustomTokenService.ts b/packages/bridge-ui-v2/src/libs/storage/CustomTokenService.ts
index 91d50bcac57..d9e73adaa2e 100644
--- a/packages/bridge-ui-v2/src/libs/storage/CustomTokenService.ts
+++ b/packages/bridge-ui-v2/src/libs/storage/CustomTokenService.ts
@@ -64,7 +64,7 @@ export class CustomTokenService implements TokenService {
// Filter out zero addresses from the new token's addresses
const filteredAddresses = Object.fromEntries(
- Object.entries(token.addresses).filter(([key, value]) => value !== zeroAddress),
+ Object.entries(token.addresses).filter(([, value]) => value !== zeroAddress),
);
// Find the stored token to update
diff --git a/packages/bridge-ui-v2/src/libs/token/getAddress.test.ts b/packages/bridge-ui-v2/src/libs/token/getAddress.test.ts
index 6818da3c2f6..bc11325cd8c 100644
--- a/packages/bridge-ui-v2/src/libs/token/getAddress.test.ts
+++ b/packages/bridge-ui-v2/src/libs/token/getAddress.test.ts
@@ -1,8 +1,6 @@
-import { getContract, type GetContractResult } from '@wagmi/core';
+import { type Address, getContract, type GetContractResult } from '@wagmi/core';
-import { tokenVaultABI } from '$abi';
import { PUBLIC_L1_CHAIN_ID, PUBLIC_L2_CHAIN_ID } from '$env/static/public';
-import { chainContractsMap } from '$libs/chain';
import { getAddress } from './getAddress';
import { ETHToken, testERC20Tokens } from './tokens';
@@ -16,46 +14,33 @@ const HORSEToken = testERC20Tokens[1];
const mockTokenContract = {
read: {
canonicalToBridged: vi.fn(),
+ isBridgedToken: vi.fn(),
+ bridgedToCanonical: vi.fn(),
},
} as unknown as GetContractResult;
describe('getAddress', () => {
- beforeAll(() => {
- vi.mocked(getContract).mockReturnValue(mockTokenContract);
- });
+ beforeEach(() => {
+ vi.resetAllMocks();
- it('should return undefined if ETH', async () => {
- expect(await getAddress({ token: ETHToken, srcChainId: Number(PUBLIC_L1_CHAIN_ID) })).toBeUndefined();
+ vi.mocked(getContract).mockReturnValue(mockTokenContract);
});
- it('should return the address if ERC20 and has address on the source chain', async () => {
- expect(await getAddress({ token: HORSEToken, srcChainId: Number(PUBLIC_L1_CHAIN_ID) })).toEqual(
- HORSEToken.addresses[PUBLIC_L1_CHAIN_ID],
- );
+ describe('ETH Tests', () => {
+ it('should return undefined if ETH', async () => {
+ expect(await getAddress({ token: ETHToken, srcChainId: Number(PUBLIC_L1_CHAIN_ID) })).toBeUndefined();
+ });
});
- it('should return undefined if ERC20 and has no address on the source chain and no destination chain is is passed in', async () => {
- expect(await getAddress({ token: HORSEToken, srcChainId: Number(PUBLIC_L2_CHAIN_ID) })).toBeUndefined();
- });
+ describe('ERC20 Tests', () => {
+ it('should return the address if ERC20 and has address on the source chain', async () => {
+ expect(await getAddress({ token: HORSEToken, srcChainId: Number(PUBLIC_L1_CHAIN_ID) })).toEqual(
+ HORSEToken.addresses[PUBLIC_L1_CHAIN_ID],
+ );
+ });
- it('should return the address of deployed ERC20 token', async () => {
- vi.mocked(mockTokenContract.read.canonicalToBridged).mockResolvedValue('0x456789');
-
- expect(
- await getAddress({
- token: HORSEToken,
- srcChainId: Number(PUBLIC_L2_CHAIN_ID),
- destChainId: Number(PUBLIC_L1_CHAIN_ID),
- }),
- ).toEqual('0x456789');
- expect(mockTokenContract.read.canonicalToBridged).toHaveBeenCalledWith([
- BigInt(1),
- HORSEToken.addresses[PUBLIC_L1_CHAIN_ID],
- ]);
- expect(getContract).toHaveBeenCalledWith({
- abi: tokenVaultABI,
- address: chainContractsMap[PUBLIC_L2_CHAIN_ID].tokenVaultAddress,
- chainId: Number(PUBLIC_L2_CHAIN_ID),
+ it('should return undefined if ERC20 and has no address on the source chain and no destination chain is is passed in', async () => {
+ expect(await getAddress({ token: HORSEToken, srcChainId: Number(PUBLIC_L2_CHAIN_ID) })).toBeUndefined();
});
});
});
diff --git a/packages/bridge-ui-v2/src/libs/token/getCrossChainAddress.test.ts b/packages/bridge-ui-v2/src/libs/token/getCrossChainAddress.test.ts
index 254c7597802..61c0df32d2d 100644
--- a/packages/bridge-ui-v2/src/libs/token/getCrossChainAddress.test.ts
+++ b/packages/bridge-ui-v2/src/libs/token/getCrossChainAddress.test.ts
@@ -1,5 +1,160 @@
+import { getContract, type GetContractResult } from '@wagmi/core';
+import { zeroAddress } from 'viem';
+
+import { getCrossChainAddress } from './getCrossChainAddress';
+import { type Token, TokenType } from './types';
+
+vi.mock('@wagmi/core');
+vi.mock('$abi');
+
+const MockedETH: Token = {
+ name: '',
+ addresses: {},
+ symbol: '',
+ decimals: 0,
+ type: TokenType.ETH,
+};
+
+let mockToken: Token;
+
+const mockTokenVaultContract = {
+ read: {
+ canonicalToBridged: vi.fn(),
+ isBridgedToken: vi.fn(),
+ bridgedToCanonical: vi.fn(),
+ },
+} as unknown as GetContractResult;
+
+vi.mock('$libs/chain', () => ({
+ chainContractsMap: {
+ 1: {
+ tokenVaultAddress: '0x00001',
+ },
+ 2: {
+ tokenVaultAddress: '0x00002',
+ },
+ },
+}));
+
describe('getCrossChainAddress', () => {
- it('TODO', () => {
- expect(true).toBeTruthy();
+ const srcChainId = 1;
+ const destChainId = 2;
+
+ beforeEach(() => {
+ vi.clearAllMocks();
+ vi.mocked(getContract).mockReturnValue(mockTokenVaultContract);
+ mockToken = {
+ name: 'MockToken',
+ addresses: {
+ 1: '0x123456',
+ 2: '0x654321',
+ },
+ symbol: 'MOCK',
+ decimals: 18,
+ type: TokenType.ERC20,
+ };
+ });
+
+ it('should return null for ETH type tokens', async () => {
+ const result = await getCrossChainAddress({
+ token: MockedETH,
+ srcChainId: 1,
+ destChainId: 2,
+ });
+ expect(result).toBeNull();
+ });
+
+ it('should return the bridged address of the token on the destination chain if not already stored', async () => {
+ // Given
+ vi.mocked(mockTokenVaultContract.read.bridgedToCanonical).mockResolvedValue([
+ destChainId,
+ mockToken.addresses[destChainId],
+ ]);
+ vi.mocked(mockTokenVaultContract.read.canonicalToBridged).mockResolvedValue(mockToken.addresses[destChainId]);
+
+ // temporarily store the mocked address so we do not have to hardcode variables
+ const preconfiguredAddress = mockToken.addresses[destChainId];
+
+ // set the destination address it to zero so we can test it
+ mockToken.addresses[destChainId] = zeroAddress;
+
+ // When
+ const result = await getCrossChainAddress({
+ token: mockToken,
+ srcChainId,
+ destChainId,
+ });
+
+ // Then
+ expect(result).toEqual(preconfiguredAddress);
+ expect(mockTokenVaultContract.read.bridgedToCanonical).toHaveBeenCalledWith([mockToken.addresses[srcChainId]]);
+ expect(mockTokenVaultContract.read.canonicalToBridged).toHaveBeenCalledWith([
+ BigInt(destChainId),
+ preconfiguredAddress,
+ ]);
+ });
+
+ it('should return the bridged address if stored or configured', async () => {
+ // When
+ const result = await getCrossChainAddress({
+ token: mockToken,
+ srcChainId,
+ destChainId,
+ });
+
+ // Then
+ expect(result).toEqual(mockToken.addresses[destChainId]);
+ expect(mockTokenVaultContract.read.bridgedToCanonical).not.toHaveBeenCalled();
+ expect(mockTokenVaultContract.read.canonicalToBridged).not.toHaveBeenCalled();
+ });
+
+ it('should return 0x0 if the native token is not bridged yet on the destination chain', async () => {
+ // Given
+ vi.mocked(mockTokenVaultContract.read.bridgedToCanonical).mockResolvedValue([destChainId, zeroAddress]);
+ vi.mocked(mockTokenVaultContract.read.canonicalToBridged).mockResolvedValue(zeroAddress);
+ // set the destination address it to zero so we can test it
+ mockToken.addresses[destChainId] = zeroAddress;
+
+ // When
+ const result = await getCrossChainAddress({
+ token: mockToken,
+ srcChainId,
+ destChainId,
+ });
+
+ // Then
+ expect(result).toEqual(zeroAddress);
+ expect(mockTokenVaultContract.read.bridgedToCanonical).toHaveBeenCalledWith([mockToken.addresses[srcChainId]]);
+ expect(mockTokenVaultContract.read.canonicalToBridged).toHaveBeenCalledWith([
+ BigInt(srcChainId),
+ mockToken.addresses[srcChainId],
+ ]);
+ });
+
+ it('should return 0x0 if the token itself is bridged, but not to the destination chain', async () => {
+ // Given
+ vi.mocked(mockTokenVaultContract.read.bridgedToCanonical).mockResolvedValue([
+ destChainId,
+ mockToken.addresses[srcChainId],
+ ]);
+ vi.mocked(mockTokenVaultContract.read.canonicalToBridged).mockResolvedValue(zeroAddress);
+
+ // set the destination address it to zero so we can test it
+ mockToken.addresses[destChainId] = zeroAddress;
+
+ // When
+ const result = await getCrossChainAddress({
+ token: mockToken,
+ srcChainId,
+ destChainId,
+ });
+
+ // Then
+ expect(result).toEqual(zeroAddress);
+ expect(mockTokenVaultContract.read.bridgedToCanonical).toHaveBeenCalledWith([mockToken.addresses[srcChainId]]);
+ expect(mockTokenVaultContract.read.canonicalToBridged).toHaveBeenCalledWith([
+ BigInt(destChainId),
+ mockToken.addresses[srcChainId],
+ ]);
});
});
diff --git a/packages/bridge-ui-v2/src/libs/token/getCrossChainAddress.ts b/packages/bridge-ui-v2/src/libs/token/getCrossChainAddress.ts
index b282a68432c..b6fa27edacd 100644
--- a/packages/bridge-ui-v2/src/libs/token/getCrossChainAddress.ts
+++ b/packages/bridge-ui-v2/src/libs/token/getCrossChainAddress.ts
@@ -1,32 +1,65 @@
-import { getContract } from '@wagmi/core';
+import { type Address, getContract } from '@wagmi/core';
+import { zeroAddress } from 'viem';
import { tokenVaultABI } from '$abi';
import { chainContractsMap } from '$libs/chain';
+import { getLogger } from '$libs/util/logger';
-import { type Token, TokenType } from './types';
+import { type GetCrossChainAddressArgs, TokenType } from './types';
-type GetCrossChainAddressArgs = {
- token: Token;
- srcChainId: number;
- destChainId: number;
-};
+const log = getLogger('token:getCrossChainAddress');
-export function getCrossChainAddress({ token, srcChainId, destChainId }: GetCrossChainAddressArgs) {
- if (token.type === TokenType.ETH) return; // ETH doesn't have an address
-
- const { tokenVaultAddress } = chainContractsMap[destChainId];
+export async function getCrossChainAddress({
+ token,
+ srcChainId,
+ destChainId,
+}: GetCrossChainAddressArgs): Promise {
+ if (token.type === TokenType.ETH) return null; // ETH doesn't have an address
+ log(
+ `Getting cross chain address for token ${token.symbol} (${token.name}) from chain ${srcChainId} to chain ${destChainId}`,
+ );
const srcChainTokenAddress = token.addresses[srcChainId];
+ const destChainTokenAddress = token.addresses[destChainId];
+
+ // check if we already have it
+ if (destChainTokenAddress && destChainTokenAddress !== zeroAddress) {
+ return token.addresses[destChainId];
+ }
- // We cannot find the address if we don't have
- // the token address on the source chain
- if (!srcChainTokenAddress) return;
+ if (!srcChainTokenAddress || srcChainTokenAddress === zeroAddress) {
+ throw new Error(
+ `Token ${token.symbol} (${token.name}) does not have any valid configured address on chain ${srcChainId} or ${destChainId}`,
+ );
+ }
+
+ const { tokenVaultAddress: srcChainTokenVaultAddress } = chainContractsMap[srcChainId];
+ const { tokenVaultAddress: destChainTokenVaultAddress } = chainContractsMap[destChainId];
+
+ const srcTokenVaultContract = getContract({
+ abi: tokenVaultABI,
+ chainId: srcChainId,
+ address: srcChainTokenVaultAddress,
+ });
const destTokenVaultContract = getContract({
abi: tokenVaultABI,
chainId: destChainId,
- address: tokenVaultAddress,
+ address: destChainTokenVaultAddress,
});
- return destTokenVaultContract.read.canonicalToBridged([BigInt(srcChainId), srcChainTokenAddress]);
+ // first we need to figure out the canonical address of the token
+ const canonicalTokenInfo = await srcTokenVaultContract.read.bridgedToCanonical([srcChainTokenAddress]);
+ const canonicalTokenAddress = canonicalTokenInfo[1]; // this will break if the contracts ever change the order of the return values
+
+ // if the canonical address is 0x0, then the token is canonical
+ if (canonicalTokenAddress === zeroAddress) {
+ // let's check if it is bridged on the destination chain by querying the destination vault
+ // e.g. bridged L1 -> L2 with native L1 token
+ return await destTokenVaultContract.read.canonicalToBridged([BigInt(srcChainId), srcChainTokenAddress]);
+ } else {
+ // if we have found a canonical, we can check for the bridged address on the source token vault
+ // e.g. bridging L2 -> L1 with native L1 token
+ return await srcTokenVaultContract.read.canonicalToBridged([BigInt(destChainId), canonicalTokenAddress]);
+ }
}
diff --git a/packages/bridge-ui-v2/src/libs/token/types.ts b/packages/bridge-ui-v2/src/libs/token/types.ts
index 1fd49199045..bc3fd09a3e9 100644
--- a/packages/bridge-ui-v2/src/libs/token/types.ts
+++ b/packages/bridge-ui-v2/src/libs/token/types.ts
@@ -29,6 +29,12 @@ export type TokenEnv = {
balance: bigint;
};
+export type GetCrossChainAddressArgs = {
+ token: Token;
+ srcChainId: number;
+ destChainId: number;
+};
+
export interface TokenService {
storeToken(token: Token, address: string): Token[];
getTokens(address: string): Token[];