Skip to content
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

fix: improve config and error messages #176

Merged
merged 3 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions composables/usePortalRuntimeConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const usePortalRuntimeConfig = () => {
const runtimeConfig = window && window["##runtimeConfig"];

return {
nodeType: runtimeConfig?.nodeType || (process.env.NODE_TYPE as undefined | "memory" | "dockerized" | "hyperchain"),
walletConnectProjectId: runtimeConfig?.walletConnectProjectId || process.env.WALLET_CONNECT_PROJECT_ID,
ankrToken: runtimeConfig?.ankrToken || process.env.ANKR_TOKEN,
screeningApiUrl: runtimeConfig?.screeningApiUrl || process.env.SCREENING_API_URL,
analytics: {
rudder: runtimeConfig?.analytics?.rudder
? {
key: (runtimeConfig.analytics.rudder.key || process.env.RUDDER_KEY)!,
dataplaneUrl: (runtimeConfig.analytics.rudder.dataplaneUrl || process.env.DATAPLANE_URL)!,
}
: undefined,
},
};
};
9 changes: 4 additions & 5 deletions composables/useScreening.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import { $fetch } from "ofetch";
/* Returns void if address screening was successful */
/* Fails if address screening was unsuccessful */
const validateAddress = useMemoize(async (address: string) => {
const runtimeConfig = useRuntimeConfig();
if (!runtimeConfig.public.screeningApiUrl) {
return;
}
const url = new URL(runtimeConfig.public.screeningApiUrl);
const portalRuntimeConfig = usePortalRuntimeConfig();
if (!portalRuntimeConfig.screeningApiUrl) return;

const url = new URL(portalRuntimeConfig.screeningApiUrl);
url.searchParams.append("address", address);
const response = await $fetch(url.toString()).catch(() => ({ result: true }));
if (!response.result) {
Expand Down
47 changes: 25 additions & 22 deletions composables/zksync/deposit/useFee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,24 +59,12 @@ export default (tokens: Ref<Token[]>, balances: Ref<TokenAmount[] | undefined>)
const signer = getL1VoidSigner();
if (!signer) throw new Error("Signer is not available");

return await retry(async () => {
try {
return await signer.getFullRequiredDepositFee({
token: ETH_TOKEN.l1Address!,
to: params.to,
});
} catch (err) {
if (err instanceof Error && err.message.startsWith("Not enough balance for deposit!")) {
const match = err.message.match(/([\d\\.]+) ETH/);
if (feeToken.value && match?.length) {
const ethAmount = match[1].split(" ")?.[0];
recommendedBalance.value = parseEther(ethAmount);
return;
}
}
throw err;
}
});
return await retry(() =>
signer.getFullRequiredDepositFee({
token: ETH_TOKEN.l1Address!,
to: params.to,
})
);
};
const getERC20TransactionFee = () => {
return {
Expand All @@ -98,10 +86,25 @@ export default (tokens: Ref<Token[]>, balances: Ref<TokenAmount[] | undefined>)
recommendedBalance.value = undefined;
if (!feeToken.value) throw new Error("Fee tokens is not available");

if (params.tokenAddress === feeToken.value?.address) {
fee.value = await getEthTransactionFee();
} else {
fee.value = getERC20TransactionFee();
try {
if (params.tokenAddress === feeToken.value?.address) {
fee.value = await getEthTransactionFee();
} else {
fee.value = getERC20TransactionFee();
}
} catch (err) {
const message = (err as any)?.message;
if (message?.startsWith("Not enough balance for deposit!")) {
const match = message.match(/([\d\\.]+) ETH/);
if (feeToken.value && match?.length) {
const ethAmount = match[1].split(" ")?.[0];
recommendedBalance.value = parseEther(ethAmount);
return;
}
} else if (message?.includes("insufficient funds for gas * price + value")) {
throw new Error("Insufficient funds to cover deposit fee! Please, top up your account with ETH.");
}
throw err;
}
/* It can be either maxFeePerGas or gasPrice */
if (fee.value && !fee.value?.maxFeePerGas) {
Expand Down
4 changes: 3 additions & 1 deletion data/networks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import Hyperchains from "@/hyperchains/config.json";
import type { Token } from "@/types";
import type { Chain } from "@wagmi/core/chains";

const portalRuntimeConfig = usePortalRuntimeConfig();

export const l1Networks = {
mainnet: {
...mainnet,
Expand Down Expand Up @@ -99,6 +101,7 @@ const publicChains: ZkSyncNetwork[] = [
},
];

const nodeType = portalRuntimeConfig.nodeType;
const determineChainList = (): ZkSyncNetwork[] => {
switch (nodeType) {
case "memory":
Expand All @@ -114,7 +117,6 @@ const determineChainList = (): ZkSyncNetwork[] => {
return [...publicChains];
}
};
const nodeType = process.env.NODE_TYPE as undefined | "memory" | "dockerized" | "hyperchain";
export const isCustomNode = !!nodeType;
export const chainList: ZkSyncNetwork[] = determineChainList();
export const defaultNetwork = chainList[0];
18 changes: 14 additions & 4 deletions data/wagmi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,24 @@ import { defaultWagmiConfig } from "@web3modal/wagmi";

import { chainList, type ZkSyncNetwork } from "@/data/networks";

const portalRuntimeConfig = usePortalRuntimeConfig();

const metadata = {
name: "zkSync Portal",
description: "zkSync Portal - view balances, transfer and bridge tokens",
url: "https://portal.zksync.io",
icons: ["https://portal.zksync.io/icon.png"],
};

if (!process.env.WALLET_CONNECT_PROJECT_ID) {
if (!portalRuntimeConfig.walletConnectProjectId) {
throw new Error("WALLET_CONNECT_PROJECT_ID is not set. Please set it in .env file");
}

const useExistingEraChain = (network: ZkSyncNetwork) => {
const existingNetworks = [zkSync, zkSyncSepoliaTestnet, zkSyncTestnet];
return existingNetworks.find((existingNetwork) => existingNetwork.id === network.id);
};
const createEraChain = (network: ZkSyncNetwork) => {
const formatZkSyncChain = (network: ZkSyncNetwork) => {
return {
id: network.id,
name: network.name,
Expand All @@ -29,6 +31,14 @@ const createEraChain = (network: ZkSyncNetwork) => {
default: { http: [network.rpcUrl] },
public: { http: [network.rpcUrl] },
},
blockExplorers: network.blockExplorerUrl
? {
default: {
name: "Explorer",
url: network.blockExplorerUrl,
},
}
: undefined,
};
};
const getAllChains = () => {
Expand All @@ -39,10 +49,10 @@ const getAllChains = () => {
}
};
for (const network of chainList) {
addUniqueChain(useExistingEraChain(network) ?? formatZkSyncChain(network));
if (network.l1Network) {
addUniqueChain(network.l1Network);
}
addUniqueChain(useExistingEraChain(network) ?? createEraChain(network));
}

return chains;
Expand All @@ -54,7 +64,7 @@ export const wagmiConfig = defaultWagmiConfig({
transports: Object.fromEntries(
chains.map((chain) => [chain.id, fallback(chain.rpcUrls.default.http.map((e) => http(e)))])
),
projectId: process.env.WALLET_CONNECT_PROJECT_ID,
projectId: portalRuntimeConfig.walletConnectProjectId,
metadata,
enableCoinbase: false,
});
23 changes: 8 additions & 15 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,15 @@ export default defineNuxtConfig({
],
script: [
{
hid: "Rudder-JS",
src: "https://cdn.rudderlabs.com/v1.1/rudder-analytics.min.js",
defer: true,
src: "/config.js",
},
process.env.RUDDER_KEY
? {
hid: "Rudder-JS",
src: "https://cdn.rudderlabs.com/v1.1/rudder-analytics.min.js",
defer: true,
}
: undefined,
],
},
},
Expand Down Expand Up @@ -72,18 +77,6 @@ export default defineNuxtConfig({
autoprefixer: {},
},
},
runtimeConfig: {
public: {
ankrToken: process.env.ANKR_TOKEN,
screeningApiUrl: process.env.SCREENING_API_URL,
analytics: {
rudder: {
key: process.env.RUDDER_KEY,
dataplaneUrl: process.env.DATAPLANE_URL,
},
},
},
},
vite: {
define: {
// make these env available even outside of the Nuxt context
Expand Down
Empty file added public/config.js
Empty file.
12 changes: 5 additions & 7 deletions scripts/create-release-assets.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@
# Ensure the script stops if any command fails
set -e

# Run the final npm command
npm run generate

# Run the first npm command and move folder
npm run generate:node:memory
mv .output/public ./dist-node-memory
cp -r .output/public/ ./dist-node-memory/

# Run the second npm command and move folder
npm run generate:node:docker
mv .output/public ./dist-node-docker

# Run the final npm command
npm run generate
cp -r .output/public/ ./dist-node-docker/
7 changes: 4 additions & 3 deletions store/ethereumBalance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import type { TokenAmount } from "@/types";
import type { Blockchain as AnkrSupportedChains } from "@ankr.com/ankr.js";

export const useEthereumBalanceStore = defineStore("ethereumBalance", () => {
const runtimeConfig = useRuntimeConfig();
const portalRuntimeConfig = usePortalRuntimeConfig();

const onboardStore = useOnboardStore();
const { account } = storeToRefs(onboardStore);
const { eraNetwork } = storeToRefs(useZkSyncProviderStore());
Expand All @@ -22,9 +23,9 @@ export const useEthereumBalanceStore = defineStore("ethereumBalance", () => {
async () => {
if (!account.value.address) throw new Error("Account is not available");
if (!eraNetwork.value.l1Network) throw new Error(`L1 network is not available on ${eraNetwork.value.name}`);
if (!runtimeConfig.public.ankrToken) throw new Error("Ankr token is not available");
if (!portalRuntimeConfig.ankrToken) throw new Error("Ankr token is not available");

const ankrProvider = new AnkrProvider(`https://rpc.ankr.com/multichain/${runtimeConfig.public.ankrToken}`);
const ankrProvider = new AnkrProvider(`https://rpc.ankr.com/multichain/${portalRuntimeConfig.ankrToken}`);
const networkIdToAnkr = new Map<number, AnkrSupportedChains>([[l1Networks.mainnet.id, "eth"]]);
if (!networkIdToAnkr.has(eraNetwork.value.l1Network.id)) {
throw new Error(`Ankr does not support ${eraNetwork.value.l1Network.name}`);
Expand Down
3 changes: 2 additions & 1 deletion store/onboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { wagmiConfig } from "@/data/wagmi";
import { confirmedSupportedWallets, disabledWallets } from "@/data/wallets";

export const useOnboardStore = defineStore("onboard", () => {
const portalRuntimeConfig = usePortalRuntimeConfig();
const { selectedColorMode } = useColorMode();
const { selectedNetwork, l1Network } = storeToRefs(useNetworkStore());

Expand Down Expand Up @@ -49,7 +50,7 @@ export const useOnboardStore = defineStore("onboard", () => {

const web3modal = createWeb3Modal({
wagmiConfig,
projectId: process.env.WALLET_CONNECT_PROJECT_ID!,
projectId: portalRuntimeConfig.walletConnectProjectId!,
termsConditionsUrl: "https://zksync.io/terms",
privacyPolicyUrl: "https://zksync.io/privacy",
themeMode: selectedColorMode.value,
Expand Down
5 changes: 3 additions & 2 deletions store/zksync/ethereumBalance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { wagmiConfig } from "@/data/wagmi";
import type { Hash, TokenAmount } from "@/types";

export const useZkSyncEthereumBalanceStore = defineStore("zkSyncEthereumBalances", () => {
const runtimeConfig = useRuntimeConfig();
const portalRuntimeConfig = usePortalRuntimeConfig();

const onboardStore = useOnboardStore();
const ethereumBalancesStore = useEthereumBalanceStore();
const tokensStore = useZkSyncTokensStore();
Expand Down Expand Up @@ -73,7 +74,7 @@ export const useZkSyncEthereumBalanceStore = defineStore("zkSyncEthereumBalances
async () => {
if (!l1Network.value) throw new Error(`L1 network is not available on ${selectedNetwork.value.name}`);

if (([l1Networks.mainnet.id] as number[]).includes(l1Network.value?.id) && runtimeConfig.public.ankrToken) {
if (([l1Networks.mainnet.id] as number[]).includes(l1Network.value?.id) && portalRuntimeConfig.ankrToken) {
return await getBalancesFromApi();
} else {
return await getBalancesFromRPC();
Expand Down
12 changes: 12 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,5 +131,17 @@ declare global {
track: (eventName: string, params?: unknown) => void;
initialized: boolean;
};
'##runtimeConfig'?: {
nodeType?: string;
walletConnectProjectId?: string;
ankrToken?: string;
screeningApiUrl?: string;
analytics?: {
rudder?: {
key: string;
dataplaneUrl: string;
}
}
}
}
}
17 changes: 5 additions & 12 deletions utils/analytics.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,26 @@
const portalRuntimeConfig = usePortalRuntimeConfig();
let analyticsLoaded = false;

async function loadRudder() {
if (!window.rudderanalytics) {
await new Promise((resolve) => setTimeout(resolve, 250));
throw new Error("Rudder not loaded");
}
const runtimeConfig = useRuntimeConfig();
window.rudderanalytics.load(
runtimeConfig.public.analytics.rudder.key,
runtimeConfig.public.analytics.rudder.dataplaneUrl
portalRuntimeConfig.analytics.rudder!.key,
portalRuntimeConfig.analytics.rudder!.dataplaneUrl
);
}

export async function initAnalytics(): Promise<boolean> {
if (analyticsLoaded) return true;

const runtimeConfig = useRuntimeConfig();
const useRudder = Boolean(
runtimeConfig.public.analytics.rudder.key && runtimeConfig.public.analytics.rudder.dataplaneUrl
);

const useRudder = Boolean(portalRuntimeConfig.analytics.rudder);
if (!useRudder || analyticsLoaded) {
return false;
}

const services = [];
if (useRudder) services.push(loadRudder());

await Promise.all(services);
await loadRudder();
analyticsLoaded = true;
return true;
}
Expand Down
9 changes: 9 additions & 0 deletions utils/formatters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,15 @@ export function formatError(error?: Error) {
(error instanceof BaseError && error?.details?.startsWith("Failed to fetch"))
) {
return new Error("Network error. Check your internet connection and try again.");
} else if (message.includes("missing response")) {
return new Error("Server error. Please try again later.");
} else if (
// eslint-disable-next-line prettier/prettier
message.includes("\"finalizeEthWithdrawal\" reverted with the following reason: xx") ||
// eslint-disable-next-line prettier/prettier
message.includes("\"finalizeWithdrawal\" reverted with the following reason: xx")
) {
return new Error("Withdrawal is already finalized!");
}
}
return error;
Expand Down
Loading