From 47471d09da7ed11d6baf2430ec3d579354500a8a Mon Sep 17 00:00:00 2001
From: Nikola Pavlov <nikola@txfusion.io>
Date: Fri, 5 Apr 2024 12:35:38 +0200
Subject: [PATCH 1/3] feat: integrate wstETH bridge addresses

---
 composables/zksync/deposit/useTransaction.ts |  8 ++++++--
 composables/zksync/useTransaction.ts         | 10 ++++++----
 data/customBridgeTokens.ts                   | 12 +-----------
 utils/constants.ts                           | 15 +++++++++++++++
 utils/helpers.ts                             |  8 +++++++-
 utils/mappers.ts                             |  6 ++++++
 views/transactions/Deposit.vue               | 15 +++++++++++++--
 views/transactions/Transfer.vue              |  2 +-
 8 files changed, 55 insertions(+), 21 deletions(-)

diff --git a/composables/zksync/deposit/useTransaction.ts b/composables/zksync/deposit/useTransaction.ts
index 1b8811878..c9bd2d382 100644
--- a/composables/zksync/deposit/useTransaction.ts
+++ b/composables/zksync/deposit/useTransaction.ts
@@ -1,4 +1,5 @@
 import type { DepositFeeValues } from "@/composables/zksync/deposit/useFee";
+import type { Token } from "@/types";
 import type { BigNumberish } from "ethers";
 import type { L1Signer } from "zksync-ethers";
 
@@ -13,7 +14,7 @@ export default (getL1Signer: () => Promise<L1Signer | undefined>) => {
   const commitTransaction = async (
     transaction: {
       to: string;
-      tokenAddress: string;
+      token: Token;
       amount: BigNumberish;
     },
     fee: DepositFeeValues
@@ -39,11 +40,14 @@ export default (getL1Signer: () => Promise<L1Signer | undefined>) => {
       }
 
       status.value = "waiting-for-signature";
+      const isCustomBridge = isExternalBridgeToken(transaction.token);
+      const bridgeAddress = isCustomBridge ? EXTERNAL_BRIDGES[transaction.token.address] : "";
       const depositResponse = await wallet.deposit({
         to: transaction.to,
-        token: transaction.tokenAddress,
+        token: transaction.token.address,
         amount: transaction.amount,
         l2GasLimit: fee.l2GasLimit,
+        ...(isCustomBridge ? { bridgeAddress } : {}),
         overrides,
       });
 
diff --git a/composables/zksync/useTransaction.ts b/composables/zksync/useTransaction.ts
index 19ca672aa..82ba4a59c 100644
--- a/composables/zksync/useTransaction.ts
+++ b/composables/zksync/useTransaction.ts
@@ -3,13 +3,13 @@ import { type BigNumberish } from "ethers";
 
 import { isCustomNode } from "@/data/networks";
 
-import type { TokenAmount } from "@/types";
+import type { Token, TokenAmount } from "@/types";
 import type { Provider, Signer } from "zksync-ethers";
 
 type TransactionParams = {
   type: "transfer" | "withdrawal";
   to: string;
-  tokenAddress: string;
+  token: Token;
   amount: BigNumberish;
 };
 
@@ -38,7 +38,9 @@ export default (getSigner: () => Promise<Signer | undefined>, getProvider: () =>
       if (!signer) throw new Error("zkSync Signer is not available");
 
       const getRequiredBridgeAddress = async () => {
-        if (transaction.tokenAddress === ETH_TOKEN.address) return undefined;
+        if (transaction.token.address === ETH_TOKEN.address) return undefined;
+        const isCustomBridge = isExternalBridgeToken(transaction.token);
+        if (isCustomBridge) return EXTERNAL_BRIDGES[transaction.token.address];
         const bridgeAddresses = await retrieveBridgeAddresses();
         return bridgeAddresses.erc20L2;
       };
@@ -50,7 +52,7 @@ export default (getSigner: () => Promise<Signer | undefined>, getProvider: () =>
       status.value = "waiting-for-signature";
       const tx = await signer[transaction.type === "transfer" ? "transfer" : "withdraw"]({
         to: transaction.to,
-        token: transaction.tokenAddress === ETH_TOKEN.address ? ETH_TOKEN.l1Address! : transaction.tokenAddress,
+        token: transaction.token.address === ETH_TOKEN.address ? ETH_TOKEN.l1Address! : transaction.token.address,
         amount: transaction.amount,
         bridgeAddress,
         overrides: {
diff --git a/data/customBridgeTokens.ts b/data/customBridgeTokens.ts
index b70f507ad..da1794a93 100644
--- a/data/customBridgeTokens.ts
+++ b/data/customBridgeTokens.ts
@@ -8,14 +8,4 @@ type CustomBridgeToken = {
   symbol: string;
 };
 
-export const customBridgeTokens: CustomBridgeToken[] = [
-  {
-    chainId: 1,
-    l1Address: "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0",
-    l2Address: "0x703b52F2b28fEbcB60E1372858AF5b18849FE867",
-    bridgeName: "txSync Bridge",
-    bridgeUrlDeposit: "https://portal.txsync.io/bridge/?token=0x703b52F2b28fEbcB60E1372858AF5b18849FE867",
-    bridgeUrlWithdraw: "https://portal.txsync.io/bridge/withdraw/?token=0x703b52F2b28fEbcB60E1372858AF5b18849FE867",
-    symbol: "wstETH",
-  },
-];
+export const customBridgeTokens: CustomBridgeToken[] = [];
diff --git a/utils/constants.ts b/utils/constants.ts
index f87ca221d..ca62636d3 100644
--- a/utils/constants.ts
+++ b/utils/constants.ts
@@ -8,3 +8,18 @@ export const ETH_TOKEN: Token = {
   decimals: 18,
   iconUrl: "/img/eth.svg",
 };
+
+export const WST_ETH_TOKEN: Token = {
+  address: "0x703b52F2b28fEbcB60E1372858AF5b18849FE867",
+  l1Address: "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0",
+  symbol: "wstETH",
+  name: "Wrapped liquid staked Ether 2.0",
+  decimals: 18,
+  iconUrl: "https://assets.coingecko.com/coins/images/18834/large/wstETH.png",
+};
+
+// External non-native bridge addresses:
+export const EXTERNAL_BRIDGES = {
+  [WST_ETH_TOKEN.l1Address!]: "0x41527B2d03844dB6b0945f25702cB958b6d55989",
+  [WST_ETH_TOKEN.address]: "0xE1D6A50E7101c8f8db77352897Ee3f1AC53f782B",
+};
diff --git a/utils/helpers.ts b/utils/helpers.ts
index 447191211..392f885cf 100644
--- a/utils/helpers.ts
+++ b/utils/helpers.ts
@@ -1,7 +1,9 @@
 import { BigNumber } from "ethers";
 
+import { EXTERNAL_BRIDGES } from "./constants";
+
 import type { ZkSyncNetwork } from "@/data/networks";
-import type { TokenAmount } from "@/types";
+import type { Token, TokenAmount } from "@/types";
 import type { BigNumberish } from "ethers";
 
 export function isOnlyZeroes(value: string) {
@@ -52,3 +54,7 @@ export async function retry<T>(func: () => Promise<T>, options: RetryOptions = {
     }
   }
 }
+
+export const isExternalBridgeToken = (token: Token) => {
+  return Object.hasOwn(EXTERNAL_BRIDGES, token.address);
+};
diff --git a/utils/mappers.ts b/utils/mappers.ts
index 6b2e69d05..968cde277 100644
--- a/utils/mappers.ts
+++ b/utils/mappers.ts
@@ -40,6 +40,12 @@ export const mapApiToken = (token: Api.Response.Token): Token => {
     };
   }
 
+  // TODO: Update the address on the Block explorer API side
+  // Update the old wstETH address with new one
+  if (token.l1Address === WST_ETH_TOKEN.l1Address) {
+    token.l2Address = WST_ETH_TOKEN.address;
+  }
+
   return {
     l1Address: token.l1Address || undefined,
     address: token.l2Address,
diff --git a/views/transactions/Deposit.vue b/views/transactions/Deposit.vue
index 2ba75be52..895ed7915 100644
--- a/views/transactions/Deposit.vue
+++ b/views/transactions/Deposit.vue
@@ -461,6 +461,17 @@ const tokenBalance = computed<BigNumberish | undefined>(() => {
   return balance.value?.find((e) => e.address === selectedToken.value?.address)?.amount;
 });
 
+const getContractAddress = async () => {
+  if (selectedToken.value) {
+    const isExternalBridge = isExternalBridgeToken(selectedToken.value);
+    if (isExternalBridge) {
+      return EXTERNAL_BRIDGES[selectedToken.value.address];
+    } else {
+      return (await providerStore.requestProvider().getDefaultBridgeAddresses()).erc20L1;
+    }
+  }
+};
+
 const {
   result: allowance,
   inProgress: allowanceRequestInProgress,
@@ -477,7 +488,7 @@ const {
 } = useAllowance(
   computed(() => account.value.address),
   computed(() => selectedToken.value?.address),
-  async () => (await providerStore.requestProvider().getDefaultBridgeAddresses()).erc20L1
+  getContractAddress
 );
 const enoughAllowance = computed(() => {
   if (!allowance.value || !selectedToken.value) {
@@ -681,7 +692,7 @@ const makeTransaction = async () => {
   const tx = await commitTransaction(
     {
       to: transaction.value!.to.address,
-      tokenAddress: transaction.value!.token.address,
+      token: transaction.value!.token,
       amount: transaction.value!.token.amount,
     },
     feeValues.value!
diff --git a/views/transactions/Transfer.vue b/views/transactions/Transfer.vue
index f412ba126..6417ed613 100644
--- a/views/transactions/Transfer.vue
+++ b/views/transactions/Transfer.vue
@@ -606,7 +606,7 @@ const makeTransaction = async () => {
     {
       type: props.type,
       to: transaction.value!.to.address,
-      tokenAddress: transaction.value!.token.address,
+      token: transaction.value!.token,
       amount: transaction.value!.token.amount,
     },
     {

From b7dabc8fe8b510491bc5f485562ac0afce634e0f Mon Sep 17 00:00:00 2001
From: Nikola Pavlov <nikola@txfusion.io>
Date: Fri, 5 Apr 2024 14:08:37 +0200
Subject: [PATCH 2/3] feat: add custom bridge address for fee estimation

---
 composables/zksync/useFee.ts    | 9 ++++++---
 views/transactions/Transfer.vue | 2 +-
 2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/composables/zksync/useFee.ts b/composables/zksync/useFee.ts
index eb8690202..17a94689c 100644
--- a/composables/zksync/useFee.ts
+++ b/composables/zksync/useFee.ts
@@ -8,7 +8,7 @@ export type FeeEstimationParams = {
   type: "transfer" | "withdrawal";
   from: string;
   to: string;
-  tokenAddress: string;
+  token: Token;
 };
 
 export default (
@@ -51,15 +51,18 @@ export default (
       if (!params) throw new Error("Params are not available");
 
       const provider = getProvider();
-      const tokenBalance = balances.value.find((e) => e.address === params!.tokenAddress)?.amount || "1";
+      const tokenBalance = balances.value.find((e) => e.address === params!.token.address)?.amount || "1";
       const [price, limit] = await Promise.all([
         retry(() => provider.getGasPrice()),
         retry(() => {
+          const isCustomBridge = isExternalBridgeToken(params!.token);
+          const bridgeAddress = isCustomBridge ? EXTERNAL_BRIDGES[params!.token.address] : "";
           return provider[params!.type === "transfer" ? "estimateGasTransfer" : "estimateGasWithdraw"]({
             from: params!.from,
             to: params!.to,
-            token: params!.tokenAddress === ETH_TOKEN.address ? ETH_TOKEN.l1Address! : params!.tokenAddress,
+            token: params!.token.address === ETH_TOKEN.address ? ETH_TOKEN.l1Address! : params!.token.address,
             amount: tokenBalance,
+            ...(isCustomBridge ? { bridgeAddress } : {}),
           });
         }),
       ]);
diff --git a/views/transactions/Transfer.vue b/views/transactions/Transfer.vue
index 6417ed613..fa09e43e7 100644
--- a/views/transactions/Transfer.vue
+++ b/views/transactions/Transfer.vue
@@ -522,7 +522,7 @@ const estimate = async () => {
     type: props.type,
     from: transaction.value.from.address,
     to: transaction.value.to.address,
-    tokenAddress: selectedToken.value.address,
+    token: selectedToken.value,
   });
 };
 watch(

From 2acf66bd6ff0bbf72a45b5b7c4c1af4ff5075997 Mon Sep 17 00:00:00 2001
From: Nikola Pavlov <nikola@txfusion.io>
Date: Fri, 5 Apr 2024 15:14:43 +0200
Subject: [PATCH 3/3] feat: add custom bridge for useWithdrawalFinalization
 hook

---
 composables/zksync/useWithdrawalFinalization.ts | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/composables/zksync/useWithdrawalFinalization.ts b/composables/zksync/useWithdrawalFinalization.ts
index 64dc508e1..2ad3de3ad 100644
--- a/composables/zksync/useWithdrawalFinalization.ts
+++ b/composables/zksync/useWithdrawalFinalization.ts
@@ -16,12 +16,16 @@ export default (transactionInfo: ComputedRef<TransactionInfo>) => {
   const { isCorrectNetworkSet } = storeToRefs(onboardStore);
   const { tokens } = storeToRefs(tokensStore);
 
-  const retrieveBridgeAddress = useMemoize(() =>
-    providerStore
+  const retrieveBridgeAddress = useMemoize(() => {
+    const isCustomBridge = isExternalBridgeToken(transactionInfo.value.token);
+    if (isCustomBridge) {
+      return EXTERNAL_BRIDGES[transactionInfo.value.token.address];
+    }
+    return providerStore
       .requestProvider()
       .getDefaultBridgeAddresses()
-      .then((e) => e.erc20L1)
-  );
+      .then((e) => e.erc20L1);
+  });
   const retrieveMainContractAddress = useMemoize(() => providerStore.requestProvider().getMainContractAddress());
 
   const gasLimit = ref<BigNumberish | undefined>();