From 8dc0b0c3e6e830dad0ccda6287d1d56caf6118fa Mon Sep 17 00:00:00 2001 From: Roman Petriv Date: Mon, 5 Aug 2024 12:43:53 +0300 Subject: [PATCH] fix: fixes for non ETH based chains (#186) --- composables/transaction/useAllowance.ts | 3 +- composables/zksync/deposit/useFee.ts | 4 +- composables/zksync/useFee.ts | 6 +- composables/zksync/useTransaction.ts | 4 +- .../zksync/useWithdrawalFinalization.ts | 4 +- scripts/hyperchains/utils.ts | 5 -- store/ethereumBalance.ts | 3 +- store/zksync/ethereumBalance.ts | 7 +- store/zksync/tokens.ts | 73 +++++++++++++++++-- store/zksync/wallet.ts | 25 ++++--- types/index.d.ts | 1 + utils/constants.ts | 11 +-- utils/mappers.ts | 11 +-- views/transactions/Deposit.vue | 7 +- views/transactions/Transfer.vue | 4 +- 15 files changed, 106 insertions(+), 62 deletions(-) diff --git a/composables/transaction/useAllowance.ts b/composables/transaction/useAllowance.ts index f95bbe05..7c84f7f6 100644 --- a/composables/transaction/useAllowance.ts +++ b/composables/transaction/useAllowance.ts @@ -1,4 +1,5 @@ import { BigNumber } from "ethers"; +import { utils } from "zksync-ethers"; import IERC20 from "zksync-ethers/abi/IERC20.json"; import type { Hash } from "@/types"; @@ -36,7 +37,7 @@ export default ( ); const requestAllowance = async () => { - if (accountAddress.value && tokenAddress.value && tokenAddress.value !== ETH_TOKEN.l1Address) { + if (accountAddress.value && tokenAddress.value && tokenAddress.value !== utils.ETH_ADDRESS) { await getAllowance(); } else { reset(); diff --git a/composables/zksync/deposit/useFee.ts b/composables/zksync/deposit/useFee.ts index 2f694073..1ca1d7b8 100644 --- a/composables/zksync/deposit/useFee.ts +++ b/composables/zksync/deposit/useFee.ts @@ -42,7 +42,7 @@ export default (tokens: Ref, balances: Ref) }); const feeToken = computed(() => { - return tokens.value.find((e) => e.address === ETH_TOKEN.l1Address); + return tokens.value.find((e) => e.address === utils.ETH_ADDRESS); }); const enoughBalanceToCoverFee = computed(() => { if (!feeToken.value || !balances.value || inProgress.value) { @@ -62,7 +62,7 @@ export default (tokens: Ref, balances: Ref) return await retry(() => signer.getFullRequiredDepositFee({ - token: ETH_TOKEN.l1Address!, + token: utils.ETH_ADDRESS, to: params.to, }) ); diff --git a/composables/zksync/useFee.ts b/composables/zksync/useFee.ts index eb869020..43222907 100644 --- a/composables/zksync/useFee.ts +++ b/composables/zksync/useFee.ts @@ -1,8 +1,8 @@ import { BigNumber } from "ethers"; +import { type Provider } from "zksync-ethers"; import type { Token, TokenAmount } from "@/types"; import type { BigNumberish } from "ethers"; -import type { Provider } from "zksync-ethers"; export type FeeEstimationParams = { type: "transfer" | "withdrawal"; @@ -27,7 +27,7 @@ export default ( }); const feeToken = computed(() => { - return tokens.value?.[ETH_TOKEN.address]; + return tokens.value?.[L2_BASE_TOKEN_ADDRESS]; }); const enoughBalanceToCoverFee = computed(() => { if (!feeToken.value || inProgress.value) { @@ -58,7 +58,7 @@ export default ( 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!.tokenAddress, amount: tokenBalance, }); }), diff --git a/composables/zksync/useTransaction.ts b/composables/zksync/useTransaction.ts index fdfe81d6..06780676 100644 --- a/composables/zksync/useTransaction.ts +++ b/composables/zksync/useTransaction.ts @@ -39,7 +39,7 @@ export default (getSigner: () => Promise, getProvider: () => const provider = getProvider(); const getRequiredBridgeAddress = async () => { - if (transaction.tokenAddress === ETH_TOKEN.address) return undefined; + if (transaction.tokenAddress === L2_BASE_TOKEN_ADDRESS) return undefined; const bridgeAddresses = await retrieveBridgeAddresses(); return bridgeAddresses.sharedL2; }; @@ -52,7 +52,7 @@ export default (getSigner: () => Promise, getProvider: () => const txRequest = await provider[transaction.type === "transfer" ? "getTransferTx" : "getWithdrawTx"]({ from: await signer.getAddress(), to: transaction.to, - token: transaction.tokenAddress === ETH_TOKEN.address ? ETH_TOKEN.l1Address! : transaction.tokenAddress, + token: transaction.tokenAddress, amount: transaction.amount, bridgeAddress, overrides: { diff --git a/composables/zksync/useWithdrawalFinalization.ts b/composables/zksync/useWithdrawalFinalization.ts index 06ea67a3..5eb7186a 100644 --- a/composables/zksync/useWithdrawalFinalization.ts +++ b/composables/zksync/useWithdrawalFinalization.ts @@ -13,7 +13,7 @@ export default (transactionInfo: ComputedRef) => { const providerStore = useZkSyncProviderStore(); const tokensStore = useZkSyncTokensStore(); const { isCorrectNetworkSet } = storeToRefs(onboardStore); - const { tokens } = storeToRefs(tokensStore); + const { ethToken } = storeToRefs(tokensStore); const retrieveBridgeAddresses = useMemoize(() => providerStore.requestProvider().getDefaultBridgeAddresses()); @@ -42,7 +42,7 @@ export default (transactionInfo: ComputedRef) => { return calculateFee(gasLimit.value, gasPrice.value).toString(); }); const feeToken = computed(() => { - return tokens.value?.[ETH_TOKEN.address]; + return ethToken; }); const getFinalizationParams = async () => { diff --git a/scripts/hyperchains/utils.ts b/scripts/hyperchains/utils.ts index 6a0b5e92..0b2a1d59 100644 --- a/scripts/hyperchains/utils.ts +++ b/scripts/hyperchains/utils.ts @@ -45,11 +45,6 @@ export const promptNetworkReplacement = async (network: Network) => { export const generateNetworkConfig = (network: Network, tokens: Token[]) => { const config = getConfig(); - /* // Add ETH token if it's not in the list - if (!tokens.some((token: Token) => token.address === ETH_TOKEN.address)) { - tokens.unshift(ETH_TOKEN); - } */ - config.unshift({ network, tokens }); saveConfig(config); }; diff --git a/store/ethereumBalance.ts b/store/ethereumBalance.ts index 8f8307b3..de269d57 100644 --- a/store/ethereumBalance.ts +++ b/store/ethereumBalance.ts @@ -1,5 +1,6 @@ import { AnkrProvider } from "@ankr.com/ankr.js"; import { BigNumber } from "ethers"; +import { utils } from "zksync-ethers"; import { l1Networks } from "@/data/networks"; @@ -42,7 +43,7 @@ export const useEthereumBalanceStore = defineStore("ethereumBalance", () => { .filter((e) => e.contractAddress || e.tokenType === "NATIVE") .map((e) => { return { - address: e.tokenType === "NATIVE" ? ETH_TOKEN.l1Address : checksumAddress(e.contractAddress!), + address: e.tokenType === "NATIVE" ? utils.ETH_ADDRESS : checksumAddress(e.contractAddress!), symbol: e.tokenSymbol, name: e.tokenName, decimals: e.tokenDecimals, diff --git a/store/zksync/ethereumBalance.ts b/store/zksync/ethereumBalance.ts index 775d8c85..43105f22 100644 --- a/store/zksync/ethereumBalance.ts +++ b/store/zksync/ethereumBalance.ts @@ -1,4 +1,5 @@ import { getBalance } from "@wagmi/core"; +import { utils } from "zksync-ethers"; import { l1Networks } from "@/data/networks"; import { wagmiConfig } from "@/data/wagmi"; @@ -40,8 +41,8 @@ export const useZkSyncEthereumBalanceStore = defineStore("zkSyncEthereumBalances amount: "0", })), ].sort((a, b) => { - if (a.address === ETH_TOKEN.l1Address) return -1; // Always bring ETH to the beginning - if (b.address === ETH_TOKEN.l1Address) return 1; // Keep ETH at the beginning if comparing with any other token + if (a.address === utils.ETH_ADDRESS) return -1; // Always bring ETH to the beginning + if (b.address === utils.ETH_ADDRESS) return 1; // Keep ETH at the beginning if comparing with any other token return 0; // Keep other tokens' order unchanged }); }; @@ -55,7 +56,7 @@ export const useZkSyncEthereumBalanceStore = defineStore("zkSyncEthereumBalances const amount = await getBalance(wagmiConfig, { address: account.value.address!, chainId: l1Network.value!.id, - token: token.address === ETH_TOKEN.l1Address ? undefined : (token.address! as Hash), + token: token.address === utils.ETH_ADDRESS ? undefined : (token.address! as Hash), }); return { ...token, diff --git a/store/zksync/tokens.ts b/store/zksync/tokens.ts index 944351c3..19f54f42 100644 --- a/store/zksync/tokens.ts +++ b/store/zksync/tokens.ts @@ -1,4 +1,5 @@ import { $fetch } from "ofetch"; +import { utils } from "zksync-ethers"; import { customBridgeTokens } from "@/data/customBridgeTokens"; @@ -15,22 +16,68 @@ export const useZkSyncTokensStore = defineStore("zkSyncTokens", () => { execute: requestTokens, reset: resetTokens, } = usePromise(async () => { + const provider = providerStore.requestProvider(); + const ethL2TokenAddress = await provider.l2TokenAddress(utils.ETH_ADDRESS); + + let baseToken = null; + let ethToken = null; + let explorerTokens: Token[] = []; + let configTokens: Token[] = []; + if (eraNetwork.value.blockExplorerApi) { const responses: Api.Response.Collection[] = await Promise.all([ $fetch(`${eraNetwork.value.blockExplorerApi}/tokens?minLiquidity=0&limit=100&page=1`), $fetch(`${eraNetwork.value.blockExplorerApi}/tokens?minLiquidity=0&limit=100&page=2`), $fetch(`${eraNetwork.value.blockExplorerApi}/tokens?minLiquidity=0&limit=100&page=3`), ]); - const explorerTokens = responses.map((response) => response.items.map(mapApiToken)).flat(); - const etherExplorerToken = explorerTokens.find((token) => token.address === ETH_TOKEN.address); - const tokensWithoutEther = explorerTokens.filter((token) => token.address !== ETH_TOKEN.address); - return [etherExplorerToken || ETH_TOKEN, ...tokensWithoutEther] as Token[]; + explorerTokens = responses.map((response) => response.items.map(mapApiToken)).flat(); + baseToken = explorerTokens.find((token) => token.address === L2_BASE_TOKEN_ADDRESS); + ethToken = explorerTokens.find((token) => token.address === ethL2TokenAddress); + } + + if (eraNetwork.value.getTokens && (!baseToken || !ethToken)) { + configTokens = await eraNetwork.value.getTokens(); + if (!baseToken) { + baseToken = configTokens.find((token) => token.address === L2_BASE_TOKEN_ADDRESS); + } + if (!ethToken) { + ethToken = configTokens.find((token) => token.address === ethL2TokenAddress); + } + } + + if (!baseToken) { + baseToken = { + address: "0x000000000000000000000000000000000000800A", + l1Address: await provider.getBaseTokenContractAddress(), + symbol: "BASETOKEN", + name: "Base Token", + decimals: 18, + iconUrl: "/img/eth.svg", + }; } - if (eraNetwork.value.getTokens) { - return await eraNetwork.value.getTokens(); - } else { - return [ETH_TOKEN]; + if (!ethToken) { + ethToken = { + address: ethL2TokenAddress, + l1Address: utils.ETH_ADDRESS, + symbol: "ETH", + name: "Ether", + decimals: 18, + iconUrl: "/img/eth.svg", + }; } + + const tokens = explorerTokens.length ? explorerTokens : configTokens; + const nonBaseOrEthExplorerTokens = tokens.filter( + (token) => token.address !== L2_BASE_TOKEN_ADDRESS && token.address !== ethL2TokenAddress + ); + return [ + baseToken, + ...(baseToken.address !== ethToken.address ? [ethToken] : []), + ...nonBaseOrEthExplorerTokens, + ].map((token) => ({ + ...token, + isETH: token.address === ethL2TokenAddress, + })); }); const tokens = computed<{ [tokenAddress: string]: Token } | undefined>(() => { @@ -52,10 +99,20 @@ export const useZkSyncTokensStore = defineStore("zkSyncTokens", () => { }) ); }); + const baseToken = computed(() => { + if (!tokensRaw.value) return undefined; + return tokensRaw.value.find((token) => token.address === L2_BASE_TOKEN_ADDRESS); + }); + const ethToken = computed(() => { + if (!tokensRaw.value) return undefined; + return tokensRaw.value.find((token) => token.isETH); + }); return { l1Tokens, tokens, + baseToken, + ethToken, tokensRequestInProgress: computed(() => tokensRequestInProgress.value), tokensRequestError: computed(() => tokensRequestError.value), requestTokens, diff --git a/store/zksync/wallet.ts b/store/zksync/wallet.ts index 01525e5d..468f700e 100644 --- a/store/zksync/wallet.ts +++ b/store/zksync/wallet.ts @@ -45,7 +45,7 @@ export const useZkSyncWalletStore = defineStore("zkSyncWallet", () => { const web3Provider = new ethers.providers.Web3Provider(onboardStore.getPublicClient() as any, "any"); return new L1VoidSigner( - account.value.address || ETH_TOKEN.address, + account.value.address || L2_BASE_TOKEN_ADDRESS, web3Provider, providerStore.requestProvider() ) as unknown as L1Signer; @@ -68,16 +68,17 @@ export const useZkSyncWalletStore = defineStore("zkSyncWallet", () => { if (!accountState.value) throw new Error("Account state is not available"); if (!tokens.value) throw new Error("Tokens are not available"); return Object.entries(accountState.value.balances) - .filter(([, { token }]) => token) - .map(([, { balance, token }]) => { + .filter(([tokenAddress, { token }]) => token || tokens.value?.[tokenAddress]) + .map(([tokenAddress, { balance, token }]) => { + const tokenInfo = token ? mapApiToken(token) : tokens.value?.[tokenAddress]; return { - address: token!.l2Address, - l1Address: token!.l1Address || undefined, - name: token!.name || undefined, - symbol: token!.symbol!, - decimals: token!.decimals, - iconUrl: token!.iconURL || undefined, - price: token?.usdPrice || undefined, + address: tokenInfo!.address, + l1Address: tokenInfo!.l1Address || undefined, + name: tokenInfo!.name || undefined, + symbol: tokenInfo!.symbol!, + decimals: tokenInfo!.decimals, + iconUrl: tokenInfo!.iconUrl || undefined, + price: tokenInfo?.price || undefined, amount: balance, }; }); @@ -126,8 +127,8 @@ export const useZkSyncWalletStore = defineStore("zkSyncWallet", () => { return { ...token, amount }; }) .sort((a, b) => { - if (a.address === ETH_TOKEN.address) return -1; // Always bring ETH to the beginning - if (b.address === ETH_TOKEN.address) return 1; // Keep ETH at the beginning if comparing with any other token + if (a.address === L2_BASE_TOKEN_ADDRESS) return -1; // Always bring ETH to the beginning + if (b.address === L2_BASE_TOKEN_ADDRESS) return 1; // Keep ETH at the beginning if comparing with any other token return 0; // Keep other tokens' order unchanged }); const knownTokenAddresses = new Set(knownTokens.map((token) => token.address)); diff --git a/types/index.d.ts b/types/index.d.ts index b64fdc37..879d03ae 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -9,6 +9,7 @@ export type Token = { decimals: number; iconUrl?: string; price?: TokenPrice; + isETH?: boolean; }; export type TokenAmount = Token & { amount: BigNumberish }; diff --git a/utils/constants.ts b/utils/constants.ts index f87ca221..06916005 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -1,10 +1 @@ -import type { Token } from "@/types"; - -export const ETH_TOKEN: Token = { - address: "0x000000000000000000000000000000000000800A", - l1Address: "0x0000000000000000000000000000000000000000", - symbol: "ETH", - name: "Ether", - decimals: 18, - iconUrl: "/img/eth.svg", -}; +export const L2_BASE_TOKEN_ADDRESS = "0x000000000000000000000000000000000000800A"; diff --git a/utils/mappers.ts b/utils/mappers.ts index 6b2e69d0..472f0c21 100644 --- a/utils/mappers.ts +++ b/utils/mappers.ts @@ -1,3 +1,5 @@ +import { utils } from "zksync-ethers"; + import type { Api, Token, TokenAmount } from "@/types"; export const groupBalancesByAmount = (balances: Ref) => @@ -33,20 +35,13 @@ export const groupBalancesByAmount = (balances: Ref) => }); export const mapApiToken = (token: Api.Response.Token): Token => { - if (token.l2Address === ETH_TOKEN.address) { - return { - ...ETH_TOKEN, - price: token.usdPrice || undefined, - }; - } - return { l1Address: token.l1Address || undefined, address: token.l2Address, symbol: token.symbol || "unknown", name: token.name || "unknown", decimals: token.decimals, - iconUrl: token.iconURL || undefined, + iconUrl: token.l1Address === utils.ETH_ADDRESS ? "/img/eth.svg" : token.iconURL || undefined, price: token.usdPrice || undefined, }; }; diff --git a/views/transactions/Deposit.vue b/views/transactions/Deposit.vue index 819e3e5d..9d3158b8 100644 --- a/views/transactions/Deposit.vue +++ b/views/transactions/Deposit.vue @@ -189,7 +189,7 @@ >{{ feeToken?.price ? removeSmallAmountPretty(recommendedBalance, feeToken?.decimals, feeToken?.price) - : parseTokenAmount(recommendedBalance, ETH_TOKEN.decimals) + : parseTokenAmount(recommendedBalance, feeToken?.decimals || 18) }} {{ feeToken?.symbol }} @@ -398,7 +398,7 @@ const { account, isConnected, walletNotSupported, walletWarningDisabled } = stor const { eraNetwork } = storeToRefs(providerStore); const { destinations } = storeToRefs(useDestinationsStore()); const { l1BlockExplorerUrl } = storeToRefs(useNetworkStore()); -const { l1Tokens, tokensRequestInProgress, tokensRequestError } = storeToRefs(tokensStore); +const { l1Tokens, baseToken, tokensRequestInProgress, tokensRequestError } = storeToRefs(tokensStore); const { balance, balanceInProgress, balanceError } = storeToRefs(zkSyncEthereumBalance); const toNetworkModalOpened = ref(false); @@ -431,7 +431,8 @@ const routeTokenAddress = computed(() => { return checksumAddress(route.query.token); }); const defaultToken = computed( - () => availableTokens.value.find((e) => e.address === ETH_TOKEN.l1Address) ?? availableTokens.value[0] ?? undefined + () => + availableTokens.value.find((e) => e.address === baseToken.value?.l1Address) ?? availableTokens.value[0] ?? undefined ); const selectedTokenAddress = ref(routeTokenAddress.value ?? defaultToken.value?.address); const selectedToken = computed(() => { diff --git a/views/transactions/Transfer.vue b/views/transactions/Transfer.vue index 0f68a36c..c70c334d 100644 --- a/views/transactions/Transfer.vue +++ b/views/transactions/Transfer.vue @@ -245,7 +245,7 @@

{{ - selectedToken?.address === ETH_TOKEN.address + selectedToken?.address === L2_BASE_TOKEN_ADDRESS ? "The fee has changed since the last estimation. " : "" }}Insufficient {{ selectedToken?.symbol }} balance to pay for @@ -352,7 +352,7 @@ const routeTokenAddress = computed(() => { return checksumAddress(route.query.token); }); const defaultToken = computed( - () => availableTokens.value.find((e) => e.address === ETH_TOKEN.l1Address) ?? availableTokens.value[0] ?? undefined + () => availableTokens.value.find((e) => e.address === L2_BASE_TOKEN_ADDRESS) ?? availableTokens.value[0] ?? undefined ); const selectedTokenAddress = ref(routeTokenAddress.value ?? defaultToken.value?.address); const selectedToken = computed(() => {