From dfb3cf7b3d42fac9af1bb69400516ba64216d1fc Mon Sep 17 00:00:00 2001 From: micky Date: Mon, 30 Sep 2024 23:10:31 +0100 Subject: [PATCH 1/7] alchemy for large accounts --- src/config/localStorage.ts | 4 + .../SyntheticsStateContextProvider.tsx | 6 ++ src/domain/synthetics/accountStats/index.ts | 1 + .../accountStats/useAccountVolumeStats.ts | 94 +++++++++++++++++++ .../accountStats/useIsLargeAccount.ts | 40 ++++++++ src/lib/rpc/bestRpcTracker.ts | 35 ++++++- 6 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 src/domain/synthetics/accountStats/useAccountVolumeStats.ts create mode 100644 src/domain/synthetics/accountStats/useIsLargeAccount.ts diff --git a/src/config/localStorage.ts b/src/config/localStorage.ts index 45e704c522..59c96a5164 100644 --- a/src/config/localStorage.ts +++ b/src/config/localStorage.ts @@ -138,6 +138,10 @@ export function getSubaccountConfigKey(chainId: number | undefined, account: str return [chainId, account, "one-click-trading-config"]; } +export function getIsLargeAccountKey(account: string) { + return [account, "is-large-account"]; +} + export function getSyntheticsReceiveMoneyTokenKey( chainId: number, marketName: string | undefined, diff --git a/src/context/SyntheticsStateContext/SyntheticsStateContextProvider.tsx b/src/context/SyntheticsStateContext/SyntheticsStateContextProvider.tsx index e997a77580..f940abd510 100644 --- a/src/context/SyntheticsStateContext/SyntheticsStateContextProvider.tsx +++ b/src/context/SyntheticsStateContext/SyntheticsStateContextProvider.tsx @@ -7,6 +7,7 @@ import { useAccountStats, usePeriodAccountStats, } from "domain/synthetics/accountStats"; +import { useIsLargeAccountTracker } from "lib/rpc/bestRpcTracker"; import { useGasLimits, useGasPrice } from "domain/synthetics/fees"; import { RebateInfoItem, useRebatesInfoRequest } from "domain/synthetics/fees/useRebatesInfo"; import useUiFeeFactorRequest from "domain/synthetics/fees/utils/useUiFeeFactor"; @@ -87,6 +88,7 @@ export type SyntheticsState = { accountStats?: AccountStats; isCandlesLoaded: boolean; setIsCandlesLoaded: (isLoaded: boolean) => void; + isLargeAccount?: boolean; }; claims: { accruedPositionPriceImpactFees: RebateInfoItem[]; @@ -195,6 +197,8 @@ export function SyntheticsStateContextProvider({ const timePerios = useMemo(() => getTimePeriodsInSeconds(), []); + const isLargeAccount = useIsLargeAccountTracker(walletAccount); + const { data: lastWeekAccountStats } = usePeriodAccountStats(chainId, { account, from: timePerios.week[0], @@ -268,6 +272,7 @@ export function SyntheticsStateContextProvider({ accountStats, isCandlesLoaded, setIsCandlesLoaded, + isLargeAccount, }, claims: { accruedPositionPriceImpactFees, claimablePositionPriceImpactFees }, leaderboard, @@ -313,6 +318,7 @@ export function SyntheticsStateContextProvider({ positionSellerState, positionEditorState, confirmationBoxState, + isLargeAccount, ]); latestState = state; diff --git a/src/domain/synthetics/accountStats/index.ts b/src/domain/synthetics/accountStats/index.ts index 72ba019d40..50f1ccbae5 100644 --- a/src/domain/synthetics/accountStats/index.ts +++ b/src/domain/synthetics/accountStats/index.ts @@ -1,3 +1,4 @@ export * from "./useAccountStats"; export * from "./usePeriodAccountStats"; export * from "./usePnlSummaryData"; +export * from "./useIsLargeAccount"; diff --git a/src/domain/synthetics/accountStats/useAccountVolumeStats.ts b/src/domain/synthetics/accountStats/useAccountVolumeStats.ts new file mode 100644 index 0000000000..7c49e89848 --- /dev/null +++ b/src/domain/synthetics/accountStats/useAccountVolumeStats.ts @@ -0,0 +1,94 @@ +import { gql } from "@apollo/client"; +import { getSubsquidGraphClient } from "lib/subgraph"; +import { useMemo } from "react"; +import useSWR from "swr"; +import { subDays, format, eachDayOfInterval } from "date-fns"; +import { toUtcDayStart } from "lib/dates"; +import { CONFIG_UPDATE_INTERVAL } from "lib/timeConstants"; +import { ARBITRUM, AVALANCHE } from "config/chains"; + +const LARGE_ACCOUNT_CHAINS = [ARBITRUM, AVALANCHE]; + +export function useAccountVolumeStats(params: { account?: string; enabled?: boolean }) { + const { account, enabled = true } = params; + + const now = new Date(); + const date30dAgo = subDays(now, 30); + const last30dList = eachDayOfInterval({ + start: date30dAgo, + end: now, + }); + + const { data, error, isLoading } = useSWR<{ + totalVolume: bigint; + dailyVolume: { date: string; volume: bigint }[]; + }>(enabled && account ? ["useAccountVolumeStats", account] : null, { + fetcher: async () => { + const clientPromises = LARGE_ACCOUNT_CHAINS.map(async (chainId) => { + const client = getSubsquidGraphClient(chainId); + + const dailyQueries = last30dList.map((day, index) => { + const from = Math.floor(toUtcDayStart(day)); + const to = Math.floor(toUtcDayStart(day) + 24 * 60 * 60); + + return ` + day${index}: periodAccountStats( + where: { id_eq: "${account}", from: ${from}, to: ${to} } + ) { + volume + } + `; + }); + + const query = gql` + query AccountVolumeStats { + total: accountStats(where: { id_eq: "${account}" }) { + volume + } + ${dailyQueries.join("\n")} + } + `; + + const res = await client?.query({ + query, + fetchPolicy: "no-cache", + }); + + const totalVolume = BigInt(res?.data?.total?.[0]?.volume ?? 0); + + const dailyVolume = last30dList.map((day, index) => { + const volume = BigInt(res?.data[`day${index}`]?.[0]?.volume ?? 0); + return { + date: format(day, "yyyy-MM-dd"), + volume, + }; + }); + + return { + totalVolume, + dailyVolume, + }; + }); + + const results = await Promise.all(clientPromises); + + const totalVolume = results.reduce((acc, result) => acc + result.totalVolume, 0n); + + const dailyVolume = last30dList.map((day, index) => { + const volume = results.reduce((acc, result) => acc + result.dailyVolume[index]?.volume ?? 0n, 0n); + return { + date: format(day, "yyyy-MM-dd"), + volume, + }; + }); + + return { + totalVolume, + dailyVolume, + }; + }, + refreshInterval: CONFIG_UPDATE_INTERVAL, + }); + + return useMemo(() => ({ data, error, isLoading }), [data, error, isLoading]); +} diff --git a/src/domain/synthetics/accountStats/useIsLargeAccount.ts b/src/domain/synthetics/accountStats/useIsLargeAccount.ts new file mode 100644 index 0000000000..535740378f --- /dev/null +++ b/src/domain/synthetics/accountStats/useIsLargeAccount.ts @@ -0,0 +1,40 @@ +import { useMemo } from "react"; +import { USD_DECIMALS } from "config/factors"; +import { expandDecimals } from "lib/numbers"; + +import { useAccountVolumeStats } from "./useAccountVolumeStats"; + +// Thresholds to recognise large accounts +const X1_MAX_DAILY_VOLUME = expandDecimals(1000n, USD_DECIMALS); +const X2_AGG_7_DAYS_VOLUME = expandDecimals(10000n, USD_DECIMALS); +const X3_AGG_14_DAYS_VOLUME = expandDecimals(20000n, USD_DECIMALS); +const X4_AGG_30_DAYS_VOLUME = expandDecimals(40000n, USD_DECIMALS); +const X5_AGG_ALL_TIME_VOLUME = expandDecimals(100000n, USD_DECIMALS); + +export function useIsLargeAccount(account?: string) { + const { data, error, isLoading } = useAccountVolumeStats({ account }); + + const isLargeAccount = useMemo(() => { + if (!data || isLoading || error) return undefined; + + const { totalVolume, dailyVolume } = data; + + const maxDailyVolume = dailyVolume.reduce((max, day) => (day.volume > max ? day.volume : max), 0n); + + const last7DaysVolume = dailyVolume.slice(-7).reduce((acc, day) => acc + day.volume, 0n); + + const last14DaysVolume = dailyVolume.slice(-14).reduce((acc, day) => acc + day.volume, 0n); + + const last30DaysVolume = dailyVolume.slice(-30).reduce((acc, day) => acc + day.volume, 0n); + + return ( + maxDailyVolume >= X1_MAX_DAILY_VOLUME || + last7DaysVolume >= X2_AGG_7_DAYS_VOLUME || + last14DaysVolume >= X3_AGG_14_DAYS_VOLUME || + last30DaysVolume >= X4_AGG_30_DAYS_VOLUME || + totalVolume >= X5_AGG_ALL_TIME_VOLUME + ); + }, [data, isLoading, error]); + + return isLargeAccount; +} diff --git a/src/lib/rpc/bestRpcTracker.ts b/src/lib/rpc/bestRpcTracker.ts index 9d444af374..efe41cb16c 100644 --- a/src/lib/rpc/bestRpcTracker.ts +++ b/src/lib/rpc/bestRpcTracker.ts @@ -7,7 +7,7 @@ import { AVALANCHE_FUJI, getFallbackRpcUrl, } from "config/chains"; -import { getRpcProviderKey } from "config/localStorage"; +import { getRpcProviderKey, getIsLargeAccountKey } from "config/localStorage"; import { isDebugMode } from "lib/localStorage"; import orderBy from "lodash/orderBy"; import minBy from "lodash/minBy"; @@ -19,10 +19,13 @@ import { HASHED_MARKET_CONFIG_KEYS } from "prebuilt"; import { sleep } from "lib/sleep"; import sample from "lodash/sample"; import { useEffect, useState } from "react"; +import { useLocalStorageSerializeKey } from "lib/localStorage"; +import { zeroAddress } from "viem"; import { getProviderNameFromUrl } from "lib/rpc/getProviderNameFromUrl"; import { emitMetricCounter } from "lib/metrics/emitMetricEvent"; import { RpcTrackerRankingCounter } from "lib/metrics"; +import { useIsLargeAccount } from "domain/synthetics/accountStats/useIsLargeAccount"; const PROBE_TIMEOUT = 10 * 1000; // 10 seconds / Frequency of RPC probing const PROBE_FAIL_TIMEOUT = 10 * 1000; // 10 seconds / Abort RPC probe if it takes longer @@ -69,6 +72,7 @@ type RpcTrackerState = { const trackerState = initTrackerState(); let trackerTimeoutId: number | null = null; +let isLargeAccount = false; trackRpcProviders({ warmUp: true }); @@ -79,7 +83,7 @@ function trackRpcProviders({ warmUp = false } = {}) { const hasMultipleProviders = Object.keys(providers).length > 1; const isUnusedChain = !lastUsage || differenceInMilliseconds(Date.now(), lastUsage) > DISABLE_UNUSED_TRACKING_TIMEOUT; - const isChainTrackingEnabled = (warmUp || !isUnusedChain) && hasMultipleProviders; + const isChainTrackingEnabled = !isLargeAccount && (warmUp || !isUnusedChain) && hasMultipleProviders; if (!isChainTrackingEnabled) { return; @@ -363,6 +367,10 @@ function initTrackerState() { } export function getBestRpcUrl(chainId: number) { + if (isLargeAccount) { + return getFallbackRpcUrl(chainId); + } + if (!trackerState[chainId]) { if (RPC_PROVIDERS[chainId]?.length) { return sample(RPC_PROVIDERS[chainId]); @@ -401,3 +409,26 @@ export function useBestRpcUrl(chainId: number) { return bestRpcUrl; } + +export function useIsLargeAccountTracker(account?: string) { + const isLargeCurrentAccount = useIsLargeAccount(account); + const [isLargeAccountStoredValue, setIsLargeAccountStoredValue] = useLocalStorageSerializeKey( + getIsLargeAccountKey(account ?? zeroAddress), + false + ); + + useEffect(() => { + if (!account) { + isLargeAccount = false; + } else if (isLargeCurrentAccount !== undefined) { + setIsLargeAccountStoredValue(isLargeCurrentAccount); + isLargeAccount = isLargeCurrentAccount; + } else if (isLargeAccountStoredValue) { + isLargeAccount = true; + } else { + isLargeAccount = false; + } + }, [account, isLargeCurrentAccount, isLargeAccountStoredValue, setIsLargeAccountStoredValue]); + + return isLargeAccount; +} From 2a6a26c50c6d1f99118a6c01ef46e7d1b9d7acc1 Mon Sep 17 00:00:00 2001 From: micky Date: Tue, 1 Oct 2024 17:25:24 +0100 Subject: [PATCH 2/7] update constants --- .../synthetics/accountStats/useAccountVolumeStats.ts | 6 +++--- .../synthetics/accountStats/useIsLargeAccount.ts | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/domain/synthetics/accountStats/useAccountVolumeStats.ts b/src/domain/synthetics/accountStats/useAccountVolumeStats.ts index 7c49e89848..f863e16b9a 100644 --- a/src/domain/synthetics/accountStats/useAccountVolumeStats.ts +++ b/src/domain/synthetics/accountStats/useAccountVolumeStats.ts @@ -9,8 +9,8 @@ import { ARBITRUM, AVALANCHE } from "config/chains"; const LARGE_ACCOUNT_CHAINS = [ARBITRUM, AVALANCHE]; -export function useAccountVolumeStats(params: { account?: string; enabled?: boolean }) { - const { account, enabled = true } = params; +export function useAccountVolumeStats(params: { account?: string }) { + const { account } = params; const now = new Date(); const date30dAgo = subDays(now, 30); @@ -22,7 +22,7 @@ export function useAccountVolumeStats(params: { account?: string; enabled?: bool const { data, error, isLoading } = useSWR<{ totalVolume: bigint; dailyVolume: { date: string; volume: bigint }[]; - }>(enabled && account ? ["useAccountVolumeStats", account] : null, { + }>(account ? ["useAccountVolumeStats", account] : null, { fetcher: async () => { const clientPromises = LARGE_ACCOUNT_CHAINS.map(async (chainId) => { const client = getSubsquidGraphClient(chainId); diff --git a/src/domain/synthetics/accountStats/useIsLargeAccount.ts b/src/domain/synthetics/accountStats/useIsLargeAccount.ts index 535740378f..caed92c567 100644 --- a/src/domain/synthetics/accountStats/useIsLargeAccount.ts +++ b/src/domain/synthetics/accountStats/useIsLargeAccount.ts @@ -5,11 +5,11 @@ import { expandDecimals } from "lib/numbers"; import { useAccountVolumeStats } from "./useAccountVolumeStats"; // Thresholds to recognise large accounts -const X1_MAX_DAILY_VOLUME = expandDecimals(1000n, USD_DECIMALS); -const X2_AGG_7_DAYS_VOLUME = expandDecimals(10000n, USD_DECIMALS); -const X3_AGG_14_DAYS_VOLUME = expandDecimals(20000n, USD_DECIMALS); -const X4_AGG_30_DAYS_VOLUME = expandDecimals(40000n, USD_DECIMALS); -const X5_AGG_ALL_TIME_VOLUME = expandDecimals(100000n, USD_DECIMALS); +const X1_MAX_DAILY_VOLUME = expandDecimals(220_000n, USD_DECIMALS); +const X2_AGG_7_DAYS_VOLUME = expandDecimals(500_000n, USD_DECIMALS); +const X3_AGG_14_DAYS_VOLUME = expandDecimals(1_200_000n, USD_DECIMALS); +const X4_AGG_30_DAYS_VOLUME = expandDecimals(3_000_000n, USD_DECIMALS); +const X5_AGG_ALL_TIME_VOLUME = expandDecimals(5_000_000n, USD_DECIMALS); export function useIsLargeAccount(account?: string) { const { data, error, isLoading } = useAccountVolumeStats({ account }); From 2a2328efe14e20b139c5ff9d074223720a986987 Mon Sep 17 00:00:00 2001 From: micky Date: Tue, 1 Oct 2024 19:01:31 +0100 Subject: [PATCH 3/7] use alchemy only if it works --- .../accountStats/useAccountVolumeStats.ts | 9 +- src/lib/metrics/types.ts | 2 + src/lib/multicall/Multicall.ts | 12 ++- .../multicall/executeMulticallMainThread.ts | 5 +- src/lib/multicall/executeMulticallWorker.ts | 3 +- src/lib/multicall/multicall.worker.ts | 9 +- src/lib/rpc/bestRpcTracker.ts | 84 ++++++++++++++----- 7 files changed, 90 insertions(+), 34 deletions(-) diff --git a/src/domain/synthetics/accountStats/useAccountVolumeStats.ts b/src/domain/synthetics/accountStats/useAccountVolumeStats.ts index f863e16b9a..aab79b6724 100644 --- a/src/domain/synthetics/accountStats/useAccountVolumeStats.ts +++ b/src/domain/synthetics/accountStats/useAccountVolumeStats.ts @@ -24,7 +24,7 @@ export function useAccountVolumeStats(params: { account?: string }) { dailyVolume: { date: string; volume: bigint }[]; }>(account ? ["useAccountVolumeStats", account] : null, { fetcher: async () => { - const clientPromises = LARGE_ACCOUNT_CHAINS.map(async (chainId) => { + const chainPromises = LARGE_ACCOUNT_CHAINS.map(async (chainId) => { const client = getSubsquidGraphClient(chainId); const dailyQueries = last30dList.map((day, index) => { @@ -70,12 +70,13 @@ export function useAccountVolumeStats(params: { account?: string }) { }; }); - const results = await Promise.all(clientPromises); + const chainResults = await Promise.all(chainPromises); - const totalVolume = results.reduce((acc, result) => acc + result.totalVolume, 0n); + const totalVolume = chainResults.reduce((acc, result) => acc + result.totalVolume, 0n); const dailyVolume = last30dList.map((day, index) => { - const volume = results.reduce((acc, result) => acc + result.dailyVolume[index]?.volume ?? 0n, 0n); + const volume = chainResults.reduce((acc, { dailyVolume }) => acc + dailyVolume[index].volume, 0n); + return { date: format(day, "yyyy-MM-dd"), volume, diff --git a/src/lib/metrics/types.ts b/src/lib/metrics/types.ts index 08f3bd49d3..de98b02685 100644 --- a/src/lib/metrics/types.ts +++ b/src/lib/metrics/types.ts @@ -202,6 +202,7 @@ export type MulticallTimeoutEvent = { isInMainThread: boolean; requestType?: "initial" | "retry"; rpcProvider?: string; + isLargeAccount?: boolean; errorMessage: string; }; }; @@ -213,6 +214,7 @@ export type MulticallErrorEvent = { isInMainThread: boolean; rpcProvider?: string; requestType?: "initial" | "retry"; + isLargeAccount?: boolean; errorMessage: string; }; }; diff --git a/src/lib/multicall/Multicall.ts b/src/lib/multicall/Multicall.ts index e58d7f7b10..f6fa90abdc 100644 --- a/src/lib/multicall/Multicall.ts +++ b/src/lib/multicall/Multicall.ts @@ -153,7 +153,12 @@ export class Multicall { private abFlags: Record ) {} - async call(providerUrls: MulticallProviderUrls, request: MulticallRequestConfig, maxTimeout: number) { + async call( + providerUrls: MulticallProviderUrls, + request: MulticallRequestConfig, + maxTimeout: number, + isLargeAccount: boolean + ) { const originalKeys: { contractKey: string; callKey: string; @@ -214,6 +219,7 @@ export class Multicall { isInMainThread: !isWebWorker, requestType, rpcProvider, + isLargeAccount, }, }); }; @@ -233,6 +239,7 @@ export class Multicall { data: { requestType, rpcProvider, + isLargeAccount, }, }); }; @@ -338,6 +345,7 @@ export class Multicall { rpcProvider: fallbackProviderName, isInMainThread: !isWebWorker, errorMessage: _viemError.message, + isLargeAccount, }, }); @@ -381,6 +389,7 @@ export class Multicall { isInMainThread: !isWebWorker, requestType: "initial", rpcProvider: rpcProviderName, + isLargeAccount, errorMessage: _viemError.message.slice(0, 150), }, }); @@ -404,6 +413,7 @@ export class Multicall { requestType: "initial", rpcProvider: rpcProviderName, isInMainThread: !isWebWorker, + isLargeAccount, errorMessage: serializeMulticallErrors(result.errors), }, }); diff --git a/src/lib/multicall/executeMulticallMainThread.ts b/src/lib/multicall/executeMulticallMainThread.ts index 56cc3b0db5..89f75cadfd 100644 --- a/src/lib/multicall/executeMulticallMainThread.ts +++ b/src/lib/multicall/executeMulticallMainThread.ts @@ -1,7 +1,7 @@ import { MAX_TIMEOUT, Multicall } from "./Multicall"; import type { MulticallRequestConfig } from "./types"; import { getAbFlags } from "config/ab"; -import { getBestRpcUrl } from "lib/rpc/bestRpcTracker"; +import { getBestRpcUrl, getIsLargeAccount } from "lib/rpc/bestRpcTracker"; import { getFallbackRpcUrl } from "config/chains"; export async function executeMulticallMainThread(chainId: number, request: MulticallRequestConfig) { @@ -10,6 +10,7 @@ export async function executeMulticallMainThread(chainId: number, request: Multi primary: getBestRpcUrl(chainId), secondary: getFallbackRpcUrl(chainId), }; + const isLargeAccount = getIsLargeAccount(); - return multicall?.call(providerUrls, request, MAX_TIMEOUT); + return multicall?.call(providerUrls, request, MAX_TIMEOUT, isLargeAccount); } diff --git a/src/lib/multicall/executeMulticallWorker.ts b/src/lib/multicall/executeMulticallWorker.ts index 13f4516ad3..c6fe014e79 100644 --- a/src/lib/multicall/executeMulticallWorker.ts +++ b/src/lib/multicall/executeMulticallWorker.ts @@ -10,7 +10,7 @@ import { executeMulticallMainThread } from "./executeMulticallMainThread"; import type { MulticallRequestConfig, MulticallResult } from "./types"; import { MetricEventParams, MulticallTimeoutEvent } from "lib/metrics"; import { getAbFlags } from "config/ab"; -import { getBestRpcUrl } from "lib/rpc/bestRpcTracker"; +import { getBestRpcUrl, getIsLargeAccount } from "lib/rpc/bestRpcTracker"; import { getFallbackRpcUrl } from "config/chains"; const executorWorker: Worker = new Worker(new URL("./multicall.worker", import.meta.url), { type: "module" }); @@ -86,6 +86,7 @@ export async function executeMulticallWorker( providerUrls, request, abFlags: getAbFlags(), + isLargeAccount: getIsLargeAccount(), PRODUCTION_PREVIEW_KEY: localStorage.getItem(PRODUCTION_PREVIEW_KEY), }); diff --git a/src/lib/multicall/multicall.worker.ts b/src/lib/multicall/multicall.worker.ts index 7ac626294d..3248370019 100644 --- a/src/lib/multicall/multicall.worker.ts +++ b/src/lib/multicall/multicall.worker.ts @@ -12,22 +12,23 @@ async function executeMulticall( chainId: number, providerUrls: MulticallProviderUrls, request: MulticallRequestConfig, - abFlags: Record + abFlags: Record, + isLargeAccount: boolean ) { const multicall = await Multicall.getInstance(chainId, abFlags); - return multicall?.call(providerUrls, request, MAX_TIMEOUT); + return multicall?.call(providerUrls, request, MAX_TIMEOUT, isLargeAccount); } self.addEventListener("message", run); async function run(event) { - const { PRODUCTION_PREVIEW_KEY, chainId, providerUrls, request, id, abFlags } = event.data; + const { PRODUCTION_PREVIEW_KEY, chainId, providerUrls, request, id, abFlags, isLargeAccount } = event.data; // @ts-ignore self.PRODUCTION_PREVIEW_KEY = PRODUCTION_PREVIEW_KEY; try { - const result = await executeMulticall(chainId, providerUrls, request, abFlags); + const result = await executeMulticall(chainId, providerUrls, request, abFlags, isLargeAccount); postMessage({ id, diff --git a/src/lib/rpc/bestRpcTracker.ts b/src/lib/rpc/bestRpcTracker.ts index efe41cb16c..de7b31aa3c 100644 --- a/src/lib/rpc/bestRpcTracker.ts +++ b/src/lib/rpc/bestRpcTracker.ts @@ -1,6 +1,7 @@ import { Provider, ethers } from "ethers"; import { RPC_PROVIDERS, + FALLBACK_PROVIDERS, SUPPORTED_CHAIN_IDS, ARBITRUM, AVALANCHE, @@ -52,11 +53,13 @@ type ProbeData = { responseTime: number | null; blockNumber: number | null; timestamp: Date; + isPublic: boolean; }; type ProviderData = { url: string; provider: Provider; + isPublic: boolean; }; type RpcTrackerState = { @@ -70,10 +73,11 @@ type RpcTrackerState = { }; }; -const trackerState = initTrackerState(); let trackerTimeoutId: number | null = null; let isLargeAccount = false; +const trackerState = initTrackerState(); + trackRpcProviders({ warmUp: true }); function trackRpcProviders({ warmUp = false } = {}) { @@ -83,7 +87,7 @@ function trackRpcProviders({ warmUp = false } = {}) { const hasMultipleProviders = Object.keys(providers).length > 1; const isUnusedChain = !lastUsage || differenceInMilliseconds(Date.now(), lastUsage) > DISABLE_UNUSED_TRACKING_TIMEOUT; - const isChainTrackingEnabled = !isLargeAccount && (warmUp || !isUnusedChain) && hasMultipleProviders; + const isChainTrackingEnabled = (warmUp || !isUnusedChain) && hasMultipleProviders; if (!isChainTrackingEnabled) { return; @@ -115,8 +119,10 @@ function trackRpcProviders({ warmUp = false } = {}) { async function getBestRpcProviderForChain({ providers, chainId }: RpcTrackerState[number]) { const providersList = Object.values(providers); - const probePromises = providersList.map((providerInfo) => { - return probeRpc(chainId, providerInfo.provider, providerInfo.url); + const providersToProbe = isLargeAccount ? providersList : providersList.filter(({ isPublic }) => isPublic); + + const probePromises = providersToProbe.map((providerInfo) => { + return probeRpc(chainId, providerInfo.provider, providerInfo.url, providerInfo.isPublic); }); const probeResults = await Promise.all(probePromises); @@ -162,16 +168,31 @@ async function getBestRpcProviderForChain({ providers, chainId }: RpcTrackerStat const bestResponseTimeValidProbe = minBy(validProbesStats, "responseTime"); const bestBlockNumberValidProbe = maxBy(validProbesStats, "blockNumber"); + if (!bestResponseTimeValidProbe?.url) { + throw new Error("no-success-probes"); + } + + let nextBestRpc = bestResponseTimeValidProbe; + + if (isLargeAccount) { + const privateRpcResult = validProbesStats.find((probe) => !probe.isPublic); + + if (privateRpcResult) { + nextBestRpc = privateRpcResult; + } + } + if (isDebugMode()) { // eslint-disable-next-line no-console console.table( orderBy( probeStats.map((probe) => ({ url: probe.url, - isSelected: probe.url === bestResponseTimeValidProbe?.url ? "✅" : "", + isSelected: probe.url === nextBestRpc.url ? "✅" : "", isValid: probe.isValid ? "✅" : "❌", responseTime: probe.responseTime, blockNumber: probe.blockNumber, + isPublic: probe.isPublic ? "yes" : "no", })), ["responseTime"], ["asc"] @@ -179,17 +200,13 @@ async function getBestRpcProviderForChain({ providers, chainId }: RpcTrackerStat ); } - if (!bestResponseTimeValidProbe?.url) { - throw new Error("no-success-probes"); - } - const bestBlockGap = - bestBlockNumberValidProbe?.blockNumber && bestResponseTimeValidProbe.blockNumber - ? bestBlockNumberValidProbe.blockNumber - bestResponseTimeValidProbe.blockNumber + bestBlockNumberValidProbe?.blockNumber && nextBestRpc.blockNumber + ? bestBlockNumberValidProbe.blockNumber - nextBestRpc.blockNumber : undefined; return { - url: bestResponseTimeValidProbe.url, + url: nextBestRpc.url, bestBlockGap, }; } @@ -204,6 +221,7 @@ function setCurrentProvider(chainId: number, newProviderUrl: string, bestBlockGa data: { rpcProvider: getProviderNameFromUrl(newProviderUrl), bestBlockGap: bestBlockGap ?? "unknown", + isLargeAccount, }, }); @@ -218,7 +236,12 @@ function setCurrentProvider(chainId: number, newProviderUrl: string, bestBlockGa ); } -async function probeRpc(chainId: number, provider: Provider, providerUrl: string): Promise { +async function probeRpc( + chainId: number, + provider: Provider, + providerUrl: string, + isPublic: boolean +): Promise { const controller = new AbortController(); let responseTime: number | null = null; @@ -304,6 +327,7 @@ async function probeRpc(chainId: number, provider: Provider, providerUrl: string blockNumber, timestamp: new Date(), isSuccess, + isPublic, }; })(), ]).catch(() => { @@ -313,6 +337,7 @@ async function probeRpc(chainId: number, provider: Provider, providerUrl: string blockNumber: null, timestamp: new Date(), isSuccess: false, + isPublic, }; }); } @@ -321,17 +346,24 @@ function initTrackerState() { const now = Date.now(); return SUPPORTED_CHAIN_IDS.reduce((acc, chainId) => { - const providersList = RPC_PROVIDERS[chainId] as string[]; - const providers = providersList.reduce>((acc, rpcUrl) => { - acc[rpcUrl] = { - url: rpcUrl, - provider: new ethers.JsonRpcProvider(rpcUrl), - }; + const prepareProviders = (urls: string[], { isPublic }: { isPublic: boolean }) => { + return urls.reduce>((acc, rpcUrl) => { + acc[rpcUrl] = { + url: rpcUrl, + provider: new ethers.JsonRpcProvider(rpcUrl), + isPublic, + }; + + return acc; + }, {}); + }; - return acc; - }, {}); + const providers = { + ...prepareProviders(RPC_PROVIDERS[chainId], { isPublic: true }), + ...prepareProviders(FALLBACK_PROVIDERS[chainId], { isPublic: false }), + }; - let currentBestProviderUrl: string = RPC_PROVIDERS[chainId][0]; + let currentBestProviderUrl: string = isLargeAccount ? FALLBACK_PROVIDERS[chainId][0] : RPC_PROVIDERS[chainId][0]; const storageKey = JSON.stringify(getRpcProviderKey(chainId)); const storedProviderData = localStorage.getItem(storageKey); @@ -372,6 +404,10 @@ export function getBestRpcUrl(chainId: number) { } if (!trackerState[chainId]) { + if (isLargeAccount) { + return getFallbackRpcUrl(chainId); + } + if (RPC_PROVIDERS[chainId]?.length) { return sample(RPC_PROVIDERS[chainId]); } @@ -432,3 +468,7 @@ export function useIsLargeAccountTracker(account?: string) { return isLargeAccount; } + +export function getIsLargeAccount() { + return isLargeAccount; +} From a50aeac3d8066d79b406b6015b1f711955dd3384 Mon Sep 17 00:00:00 2001 From: micky Date: Wed, 2 Oct 2024 14:09:15 +0100 Subject: [PATCH 4/7] update thresholds --- .../accountStats/useIsLargeAccount.ts | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/domain/synthetics/accountStats/useIsLargeAccount.ts b/src/domain/synthetics/accountStats/useIsLargeAccount.ts index caed92c567..3d6da93922 100644 --- a/src/domain/synthetics/accountStats/useIsLargeAccount.ts +++ b/src/domain/synthetics/accountStats/useIsLargeAccount.ts @@ -5,11 +5,9 @@ import { expandDecimals } from "lib/numbers"; import { useAccountVolumeStats } from "./useAccountVolumeStats"; // Thresholds to recognise large accounts -const X1_MAX_DAILY_VOLUME = expandDecimals(220_000n, USD_DECIMALS); -const X2_AGG_7_DAYS_VOLUME = expandDecimals(500_000n, USD_DECIMALS); -const X3_AGG_14_DAYS_VOLUME = expandDecimals(1_200_000n, USD_DECIMALS); -const X4_AGG_30_DAYS_VOLUME = expandDecimals(3_000_000n, USD_DECIMALS); -const X5_AGG_ALL_TIME_VOLUME = expandDecimals(5_000_000n, USD_DECIMALS); +const MAX_DAILY_VOLUME = expandDecimals(220_000n, USD_DECIMALS); +const AGG_14_DAYS_VOLUME = expandDecimals(1_200_000n, USD_DECIMALS); +const AGG_ALL_TIME_VOLUME = expandDecimals(3_500_000n, USD_DECIMALS); export function useIsLargeAccount(account?: string) { const { data, error, isLoading } = useAccountVolumeStats({ account }); @@ -21,18 +19,10 @@ export function useIsLargeAccount(account?: string) { const maxDailyVolume = dailyVolume.reduce((max, day) => (day.volume > max ? day.volume : max), 0n); - const last7DaysVolume = dailyVolume.slice(-7).reduce((acc, day) => acc + day.volume, 0n); - const last14DaysVolume = dailyVolume.slice(-14).reduce((acc, day) => acc + day.volume, 0n); - const last30DaysVolume = dailyVolume.slice(-30).reduce((acc, day) => acc + day.volume, 0n); - return ( - maxDailyVolume >= X1_MAX_DAILY_VOLUME || - last7DaysVolume >= X2_AGG_7_DAYS_VOLUME || - last14DaysVolume >= X3_AGG_14_DAYS_VOLUME || - last30DaysVolume >= X4_AGG_30_DAYS_VOLUME || - totalVolume >= X5_AGG_ALL_TIME_VOLUME + maxDailyVolume >= MAX_DAILY_VOLUME || last14DaysVolume >= AGG_14_DAYS_VOLUME || totalVolume >= AGG_ALL_TIME_VOLUME ); }, [data, isLoading, error]); From 90f21091eb62d16fd2f1d4ed317f895284395ada Mon Sep 17 00:00:00 2001 From: micky Date: Mon, 14 Oct 2024 16:39:49 +0300 Subject: [PATCH 5/7] rebase --- src/lib/metrics/types.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/metrics/types.ts b/src/lib/metrics/types.ts index de98b02685..909accea75 100644 --- a/src/lib/metrics/types.ts +++ b/src/lib/metrics/types.ts @@ -380,6 +380,7 @@ export type MulticallRequestTiming = { data: { requestType: string; rpcProvider: string; + isLargeAccount: boolean; }; }; @@ -404,6 +405,7 @@ export type MulticallRequestCounter = { isInMainThread: boolean; requestType: string; rpcProvider: string; + isLargeAccount: boolean; }; }; @@ -419,6 +421,7 @@ export type RpcTrackerRankingCounter = { data: { rpcProvider: string; bestBlockGap: number | "unknown"; + isLargeAccount: boolean; }; }; From ba3ed4c84868d8ccdd0b974aa0cb70c088ed0e07 Mon Sep 17 00:00:00 2001 From: micky Date: Tue, 15 Oct 2024 13:55:46 +0300 Subject: [PATCH 6/7] fallback if alchemy is down --- src/config/localStorage.ts | 9 +- .../SubaccountContext/SubaccountContext.tsx | 19 +-- .../SyntheticsStateContextProvider.tsx | 2 +- src/domain/synthetics/accountStats/index.ts | 2 +- ...rgeAccount.ts => useIsLargeAccountData.ts} | 2 +- src/lib/account/isLargeAccount.ts | 38 +++++ src/lib/multicall/Multicall.ts | 5 +- .../multicall/executeMulticallMainThread.ts | 9 +- src/lib/multicall/executeMulticallWorker.ts | 9 +- src/lib/rpc/bestRpcTracker.ts | 150 ++++++++---------- src/lib/rpc/index.ts | 14 +- 11 files changed, 129 insertions(+), 130 deletions(-) rename src/domain/synthetics/accountStats/{useIsLargeAccount.ts => useIsLargeAccountData.ts} (94%) create mode 100644 src/lib/account/isLargeAccount.ts diff --git a/src/config/localStorage.ts b/src/config/localStorage.ts index 59c96a5164..c0664d4a4c 100644 --- a/src/config/localStorage.ts +++ b/src/config/localStorage.ts @@ -72,7 +72,8 @@ export const DEBUG_MULTICALL_BATCHING_KEY = "debug-multicall-batching"; export const AB_FLAG_STORAGE_KEY = "ab-flags"; -export const RPC_PROVIDER = "rpc-provider"; +export const RPC_PROVIDER_KEY = "rpc-provider"; +export const IS_LARGE_ACCOUNT_KEY = "is-large-account"; export const getSubgraphUrlKey = (chainId: number, subgraph: string) => `subgraphUrl:${chainId}:${subgraph}`; @@ -125,7 +126,7 @@ export function getExecutionFeeBufferBpsKey(chainId: number) { } export function getRpcProviderKey(chainId: number | string) { - return [chainId, RPC_PROVIDER]; + return [chainId, RPC_PROVIDER_KEY]; } // TODO: this was made on 07.06.2024, remove this in 6 months, because everyone would be migrated to new defaults by then @@ -138,10 +139,6 @@ export function getSubaccountConfigKey(chainId: number | undefined, account: str return [chainId, account, "one-click-trading-config"]; } -export function getIsLargeAccountKey(account: string) { - return [account, "is-large-account"]; -} - export function getSyntheticsReceiveMoneyTokenKey( chainId: number, marketName: string | undefined, diff --git a/src/context/SubaccountContext/SubaccountContext.tsx b/src/context/SubaccountContext/SubaccountContext.tsx index f8257205ef..774d0dea60 100644 --- a/src/context/SubaccountContext/SubaccountContext.tsx +++ b/src/context/SubaccountContext/SubaccountContext.tsx @@ -1,12 +1,6 @@ import { Trans } from "@lingui/macro"; import DataStore from "abis/DataStore.json"; -import { - ARBITRUM, - AVALANCHE, - AVALANCHE_FUJI, - getFallbackRpcUrl, - NETWORK_EXECUTION_TO_CREATE_FEE_FACTOR, -} from "config/chains"; +import { ARBITRUM, AVALANCHE, AVALANCHE_FUJI, NETWORK_EXECUTION_TO_CREATE_FEE_FACTOR } from "config/chains"; import { getContract } from "config/contracts"; import { SUBACCOUNT_ORDER_ACTION, @@ -39,7 +33,7 @@ import { Context, PropsWithChildren, useCallback, useEffect, useMemo, useState } import { createContext, useContextSelector } from "use-context-selector"; import { clientToSigner } from "lib/wallets/useEthersSigner"; import { estimateOrderOraclePriceCount } from "domain/synthetics/fees/utils/estimateOraclePriceCount"; -import { useBestRpcUrl } from "lib/rpc/bestRpcTracker"; +import { useCurrentRpcUrls } from "lib/rpc/bestRpcTracker"; export type Subaccount = ReturnType; @@ -302,14 +296,13 @@ function useSubaccountCustomSigners() { const { chainId } = useChainId(); const privateKey = useSubaccountPrivateKey(); - const primaryRpc = useBestRpcUrl(chainId); - const fallbackRpc = getFallbackRpcUrl(chainId); + const { primary, secondary } = useCurrentRpcUrls(chainId); return useMemo(() => { const rpcUrls: string[] = []; - if (primaryRpc) rpcUrls.push(primaryRpc); - if (fallbackRpc) rpcUrls.push(fallbackRpc); + if (primary) rpcUrls.push(primary); + if (secondary) rpcUrls.push(secondary); if (!rpcUrls.length || !privateKey) return undefined; @@ -320,7 +313,7 @@ function useSubaccountCustomSigners() { return new ethers.Wallet(privateKey, provider); }); - }, [chainId, privateKey, primaryRpc, fallbackRpc]); + }, [chainId, privateKey, primary, secondary]); } export function useSubaccount(requiredBalance: bigint | null, requiredActions = 1) { diff --git a/src/context/SyntheticsStateContext/SyntheticsStateContextProvider.tsx b/src/context/SyntheticsStateContext/SyntheticsStateContextProvider.tsx index f940abd510..e40b3b7583 100644 --- a/src/context/SyntheticsStateContext/SyntheticsStateContextProvider.tsx +++ b/src/context/SyntheticsStateContext/SyntheticsStateContextProvider.tsx @@ -7,7 +7,7 @@ import { useAccountStats, usePeriodAccountStats, } from "domain/synthetics/accountStats"; -import { useIsLargeAccountTracker } from "lib/rpc/bestRpcTracker"; +import { useIsLargeAccountTracker } from "lib/account/isLargeAccount"; import { useGasLimits, useGasPrice } from "domain/synthetics/fees"; import { RebateInfoItem, useRebatesInfoRequest } from "domain/synthetics/fees/useRebatesInfo"; import useUiFeeFactorRequest from "domain/synthetics/fees/utils/useUiFeeFactor"; diff --git a/src/domain/synthetics/accountStats/index.ts b/src/domain/synthetics/accountStats/index.ts index 50f1ccbae5..65c917fbfd 100644 --- a/src/domain/synthetics/accountStats/index.ts +++ b/src/domain/synthetics/accountStats/index.ts @@ -1,4 +1,4 @@ export * from "./useAccountStats"; export * from "./usePeriodAccountStats"; export * from "./usePnlSummaryData"; -export * from "./useIsLargeAccount"; +export * from "./useIsLargeAccountData"; diff --git a/src/domain/synthetics/accountStats/useIsLargeAccount.ts b/src/domain/synthetics/accountStats/useIsLargeAccountData.ts similarity index 94% rename from src/domain/synthetics/accountStats/useIsLargeAccount.ts rename to src/domain/synthetics/accountStats/useIsLargeAccountData.ts index 3d6da93922..b18935b407 100644 --- a/src/domain/synthetics/accountStats/useIsLargeAccount.ts +++ b/src/domain/synthetics/accountStats/useIsLargeAccountData.ts @@ -9,7 +9,7 @@ const MAX_DAILY_VOLUME = expandDecimals(220_000n, USD_DECIMALS); const AGG_14_DAYS_VOLUME = expandDecimals(1_200_000n, USD_DECIMALS); const AGG_ALL_TIME_VOLUME = expandDecimals(3_500_000n, USD_DECIMALS); -export function useIsLargeAccount(account?: string) { +export function useIsLargeAccountData(account?: string) { const { data, error, isLoading } = useAccountVolumeStats({ account }); const isLargeAccount = useMemo(() => { diff --git a/src/lib/account/isLargeAccount.ts b/src/lib/account/isLargeAccount.ts new file mode 100644 index 0000000000..dcb8549aee --- /dev/null +++ b/src/lib/account/isLargeAccount.ts @@ -0,0 +1,38 @@ +import { IS_LARGE_ACCOUNT_KEY } from "config/localStorage"; +import { useEffect } from "react"; +import { useLocalStorageSerializeKey } from "lib/localStorage"; +import { useIsLargeAccountData } from "domain/synthetics/accountStats/useIsLargeAccountData"; +import { getStorageItem } from "lib/metrics/storage"; + +let isLargeAccount = getIsLargeAccountStoredValue(); + +function getIsLargeAccountStoredValue() { + return getStorageItem(IS_LARGE_ACCOUNT_KEY, true) === "true"; +} + +export function getIsLargeAccount() { + return isLargeAccount; +} + +export function useIsLargeAccountTracker(account?: string) { + const isLargeCurrentAccount = useIsLargeAccountData(account); + const [isLargeAccountStoredValue, setIsLargeAccountStoredValue] = useLocalStorageSerializeKey( + IS_LARGE_ACCOUNT_KEY, + false + ); + + useEffect(() => { + if (!account) { + isLargeAccount = false; + } else if (isLargeCurrentAccount !== undefined) { + setIsLargeAccountStoredValue(isLargeCurrentAccount); + isLargeAccount = isLargeCurrentAccount; + } else if (isLargeAccountStoredValue) { + isLargeAccount = true; + } else { + isLargeAccount = false; + } + }, [account, isLargeCurrentAccount, isLargeAccountStoredValue, setIsLargeAccountStoredValue]); + + return isLargeAccount; +} diff --git a/src/lib/multicall/Multicall.ts b/src/lib/multicall/Multicall.ts index f6fa90abdc..d38a2c852e 100644 --- a/src/lib/multicall/Multicall.ts +++ b/src/lib/multicall/Multicall.ts @@ -206,7 +206,6 @@ export class Multicall { const providerUrl = this.fallbackRpcSwitcher?.isFallbackMode ? providerUrls.secondary : providerUrls.primary; const client = Multicall.getViemClient(this.chainId, providerUrl); - const isAlchemy = providerUrl === providerUrls.secondary; const rpcProviderName = getProviderNameFromUrl(providerUrl); const sendCounterEvent = ( @@ -306,7 +305,7 @@ export class Multicall { // eslint-disable-next-line no-console console.groupEnd(); - if (!isAlchemy) { + if (!this.fallbackRpcSwitcher?.isFallbackMode) { this.fallbackRpcSwitcher?.trigger(); } @@ -423,7 +422,7 @@ export class Multicall { rpcProvider: rpcProviderName, }); - if (!isAlchemy) { + if (!this.fallbackRpcSwitcher?.isFallbackMode) { this.fallbackRpcSwitcher?.trigger(); } diff --git a/src/lib/multicall/executeMulticallMainThread.ts b/src/lib/multicall/executeMulticallMainThread.ts index 89f75cadfd..2ae982e9c5 100644 --- a/src/lib/multicall/executeMulticallMainThread.ts +++ b/src/lib/multicall/executeMulticallMainThread.ts @@ -1,15 +1,12 @@ import { MAX_TIMEOUT, Multicall } from "./Multicall"; import type { MulticallRequestConfig } from "./types"; import { getAbFlags } from "config/ab"; -import { getBestRpcUrl, getIsLargeAccount } from "lib/rpc/bestRpcTracker"; -import { getFallbackRpcUrl } from "config/chains"; +import { getCurrentRpcUrls } from "lib/rpc/bestRpcTracker"; +import { getIsLargeAccount } from "lib/account/isLargeAccount"; export async function executeMulticallMainThread(chainId: number, request: MulticallRequestConfig) { const multicall = await Multicall.getInstance(chainId, getAbFlags()); - const providerUrls = { - primary: getBestRpcUrl(chainId), - secondary: getFallbackRpcUrl(chainId), - }; + const providerUrls = getCurrentRpcUrls(chainId); const isLargeAccount = getIsLargeAccount(); return multicall?.call(providerUrls, request, MAX_TIMEOUT, isLargeAccount); diff --git a/src/lib/multicall/executeMulticallWorker.ts b/src/lib/multicall/executeMulticallWorker.ts index c6fe014e79..8a25a47fc7 100644 --- a/src/lib/multicall/executeMulticallWorker.ts +++ b/src/lib/multicall/executeMulticallWorker.ts @@ -10,8 +10,8 @@ import { executeMulticallMainThread } from "./executeMulticallMainThread"; import type { MulticallRequestConfig, MulticallResult } from "./types"; import { MetricEventParams, MulticallTimeoutEvent } from "lib/metrics"; import { getAbFlags } from "config/ab"; -import { getBestRpcUrl, getIsLargeAccount } from "lib/rpc/bestRpcTracker"; -import { getFallbackRpcUrl } from "config/chains"; +import { getCurrentRpcUrls } from "lib/rpc/bestRpcTracker"; +import { getIsLargeAccount } from "lib/account/isLargeAccount"; const executorWorker: Worker = new Worker(new URL("./multicall.worker", import.meta.url), { type: "module" }); @@ -75,10 +75,7 @@ export async function executeMulticallWorker( ): Promise | undefined> { const id = uniqueId("multicall-"); - const providerUrls: MulticallProviderUrls = { - primary: getBestRpcUrl(chainId), - secondary: getFallbackRpcUrl(chainId), - }; + const providerUrls: MulticallProviderUrls = getCurrentRpcUrls(chainId); executorWorker.postMessage({ id, diff --git a/src/lib/rpc/bestRpcTracker.ts b/src/lib/rpc/bestRpcTracker.ts index de7b31aa3c..6e65df6cbe 100644 --- a/src/lib/rpc/bestRpcTracker.ts +++ b/src/lib/rpc/bestRpcTracker.ts @@ -8,7 +8,7 @@ import { AVALANCHE_FUJI, getFallbackRpcUrl, } from "config/chains"; -import { getRpcProviderKey, getIsLargeAccountKey } from "config/localStorage"; +import { getRpcProviderKey } from "config/localStorage"; import { isDebugMode } from "lib/localStorage"; import orderBy from "lodash/orderBy"; import minBy from "lodash/minBy"; @@ -18,15 +18,12 @@ import { getMulticallContract, getDataStoreContract } from "config/contracts"; import { getContract } from "config/contracts"; import { HASHED_MARKET_CONFIG_KEYS } from "prebuilt"; import { sleep } from "lib/sleep"; -import sample from "lodash/sample"; import { useEffect, useState } from "react"; -import { useLocalStorageSerializeKey } from "lib/localStorage"; -import { zeroAddress } from "viem"; import { getProviderNameFromUrl } from "lib/rpc/getProviderNameFromUrl"; import { emitMetricCounter } from "lib/metrics/emitMetricEvent"; import { RpcTrackerRankingCounter } from "lib/metrics"; -import { useIsLargeAccount } from "domain/synthetics/accountStats/useIsLargeAccount"; +import { getIsLargeAccount } from "lib/account/isLargeAccount"; const PROBE_TIMEOUT = 10 * 1000; // 10 seconds / Frequency of RPC probing const PROBE_FAIL_TIMEOUT = 10 * 1000; // 10 seconds / Abort RPC probe if it takes longer @@ -66,7 +63,8 @@ type RpcTrackerState = { [chainId: number]: { chainId: number; lastUsage: Date | null; - currentBestProviderUrl: string; + currentPrimaryUrl: string; + currentSecondaryUrl: string; providers: { [providerUrl: string]: ProviderData; }; @@ -74,7 +72,6 @@ type RpcTrackerState = { }; let trackerTimeoutId: number | null = null; -let isLargeAccount = false; const trackerState = initTrackerState(); @@ -93,19 +90,23 @@ function trackRpcProviders({ warmUp = false } = {}) { return; } - const { url: nextProviderUrl, bestBlockGap } = await getBestRpcProviderForChain(chainTrackerState).catch((e) => { - if (e.message !== "no-success-probes") { - // eslint-disable-next-line no-console - console.error(e); - } + await getBestRpcProvidersForChain(chainTrackerState) + .catch((e) => { + if (e.message !== "no-success-probes") { + // eslint-disable-next-line no-console + console.error(e); + } - return { - url: getFallbackRpcUrl(chainId), - bestBlockGap: undefined, - }; - }); + // Use fallback provider both as primary and secondary if no successful probes received + const fallbackRpcUrl = getFallbackRpcUrl(chainId); - setCurrentProvider(chainId, nextProviderUrl, bestBlockGap); + return { + primaryUrl: fallbackRpcUrl, + secondaryUrl: fallbackRpcUrl, + bestBestBlockGap: undefined, + }; + }) + .then((nextProviderUrls) => setCurrentProviders(chainId, nextProviderUrls)); }) ).finally(() => { if (trackerTimeoutId) { @@ -116,10 +117,10 @@ function trackRpcProviders({ warmUp = false } = {}) { }); } -async function getBestRpcProviderForChain({ providers, chainId }: RpcTrackerState[number]) { +async function getBestRpcProvidersForChain({ providers, chainId }: RpcTrackerState[number]) { const providersList = Object.values(providers); - const providersToProbe = isLargeAccount ? providersList : providersList.filter(({ isPublic }) => isPublic); + const providersToProbe = getIsLargeAccount() ? providersList : providersList.filter(({ isPublic }) => isPublic); const probePromises = providersToProbe.map((providerInfo) => { return probeRpc(chainId, providerInfo.provider, providerInfo.url, providerInfo.isPublic); @@ -172,14 +173,19 @@ async function getBestRpcProviderForChain({ providers, chainId }: RpcTrackerStat throw new Error("no-success-probes"); } - let nextBestRpc = bestResponseTimeValidProbe; + let nextPrimaryRpc = bestResponseTimeValidProbe; + let nextSecondaryRpc = { + url: getFallbackRpcUrl(chainId), + }; - if (isLargeAccount) { + if (getIsLargeAccount()) { const privateRpcResult = validProbesStats.find((probe) => !probe.isPublic); if (privateRpcResult) { - nextBestRpc = privateRpcResult; + nextPrimaryRpc = privateRpcResult; } + + nextSecondaryRpc = bestResponseTimeValidProbe; } if (isDebugMode()) { @@ -188,7 +194,7 @@ async function getBestRpcProviderForChain({ providers, chainId }: RpcTrackerStat orderBy( probeStats.map((probe) => ({ url: probe.url, - isSelected: probe.url === nextBestRpc.url ? "✅" : "", + isPrimary: probe.url === nextPrimaryRpc.url ? "✅" : "", isValid: probe.isValid ? "✅" : "❌", responseTime: probe.responseTime, blockNumber: probe.blockNumber, @@ -200,28 +206,30 @@ async function getBestRpcProviderForChain({ providers, chainId }: RpcTrackerStat ); } - const bestBlockGap = - bestBlockNumberValidProbe?.blockNumber && nextBestRpc.blockNumber - ? bestBlockNumberValidProbe.blockNumber - nextBestRpc.blockNumber + const bestBestBlockGap = + bestBlockNumberValidProbe?.blockNumber && nextPrimaryRpc.blockNumber + ? bestBlockNumberValidProbe.blockNumber - nextPrimaryRpc.blockNumber : undefined; return { - url: nextBestRpc.url, - bestBlockGap, + primaryUrl: nextPrimaryRpc.url, + secondaryUrl: nextSecondaryRpc.url, + bestBestBlockGap, }; } -function setCurrentProvider(chainId: number, newProviderUrl: string, bestBlockGap?: number) { - trackerState[chainId].currentBestProviderUrl = newProviderUrl; +function setCurrentProviders(chainId: number, { primaryUrl, secondaryUrl, bestBestBlockGap }) { + trackerState[chainId].currentPrimaryUrl = primaryUrl; + trackerState[chainId].currentSecondaryUrl = secondaryUrl; window.dispatchEvent(new CustomEvent(RPC_TRACKER_UPDATE_EVENT)); emitMetricCounter({ event: "rpcTracker.ranking.setBestRpc", data: { - rpcProvider: getProviderNameFromUrl(newProviderUrl), - bestBlockGap: bestBlockGap ?? "unknown", - isLargeAccount, + rpcProvider: getProviderNameFromUrl(primaryUrl), + bestBlockGap: bestBestBlockGap ?? "unknown", + isLargeAccount: getIsLargeAccount(), }, }); @@ -230,7 +238,7 @@ function setCurrentProvider(chainId: number, newProviderUrl: string, bestBlockGa localStorage.setItem( storageKey, JSON.stringify({ - rpcUrl: newProviderUrl, + rpcUrl: primaryUrl, timestamp: Date.now(), }) ); @@ -363,7 +371,8 @@ function initTrackerState() { ...prepareProviders(FALLBACK_PROVIDERS[chainId], { isPublic: false }), }; - let currentBestProviderUrl: string = isLargeAccount ? FALLBACK_PROVIDERS[chainId][0] : RPC_PROVIDERS[chainId][0]; + let currentPrimaryUrl: string = getIsLargeAccount() ? FALLBACK_PROVIDERS[chainId][0] : RPC_PROVIDERS[chainId][0]; + let currentSecondaryUrl: string = getIsLargeAccount() ? RPC_PROVIDERS[chainId][0] : FALLBACK_PROVIDERS[chainId][0]; const storageKey = JSON.stringify(getRpcProviderKey(chainId)); const storedProviderData = localStorage.getItem(storageKey); @@ -383,14 +392,15 @@ function initTrackerState() { } if (rpcUrl && providers[rpcUrl] && timestamp && now - timestamp < STORAGE_EXPIRE_TIMEOUT) { - currentBestProviderUrl = rpcUrl; + currentPrimaryUrl = rpcUrl; } } acc[chainId] = { chainId, lastUsage: null, - currentBestProviderUrl, + currentPrimaryUrl, + currentSecondaryUrl, providers, }; @@ -398,40 +408,35 @@ function initTrackerState() { }, {}); } -export function getBestRpcUrl(chainId: number) { - if (isLargeAccount) { - return getFallbackRpcUrl(chainId); +export function getCurrentRpcUrls(chainId: number) { + if (!RPC_PROVIDERS[chainId]?.length) { + throw new Error(`No RPC providers found for chainId: ${chainId}`); } - if (!trackerState[chainId]) { - if (isLargeAccount) { - return getFallbackRpcUrl(chainId); - } - - if (RPC_PROVIDERS[chainId]?.length) { - return sample(RPC_PROVIDERS[chainId]); - } - - throw new Error(`No RPC providers found for chainId: ${chainId}`); + if (trackerState[chainId]) { + trackerState[chainId].lastUsage = new Date(); } - trackerState[chainId].lastUsage = new Date(); + const primary = trackerState?.[chainId]?.currentPrimaryUrl ?? RPC_PROVIDERS[chainId][0]; + const secondary = trackerState?.[chainId]?.currentSecondaryUrl ?? FALLBACK_PROVIDERS?.[chainId]?.[0] ?? primary; - return trackerState[chainId].currentBestProviderUrl ?? RPC_PROVIDERS[chainId][0]; + return { primary, secondary }; } -export function useBestRpcUrl(chainId: number) { - const [bestRpcUrl, setBestRpcUrl] = useState(() => getBestRpcUrl(chainId)); +export function useCurrentRpcUrls(chainId: number) { + const [bestRpcUrls, setBestRpcUrls] = useState<{ + primary: string; + secondary?: string; + }>(() => getCurrentRpcUrls(chainId)); useEffect(() => { let isMounted = true; - setBestRpcUrl(getBestRpcUrl(chainId)); + setBestRpcUrls(getCurrentRpcUrls(chainId)); function handleRpcUpdate() { if (isMounted) { - const newRpcUrl = getBestRpcUrl(chainId); - setBestRpcUrl(newRpcUrl); + setBestRpcUrls(getCurrentRpcUrls(chainId)); } } @@ -443,32 +448,5 @@ export function useBestRpcUrl(chainId: number) { }; }, [chainId]); - return bestRpcUrl; -} - -export function useIsLargeAccountTracker(account?: string) { - const isLargeCurrentAccount = useIsLargeAccount(account); - const [isLargeAccountStoredValue, setIsLargeAccountStoredValue] = useLocalStorageSerializeKey( - getIsLargeAccountKey(account ?? zeroAddress), - false - ); - - useEffect(() => { - if (!account) { - isLargeAccount = false; - } else if (isLargeCurrentAccount !== undefined) { - setIsLargeAccountStoredValue(isLargeCurrentAccount); - isLargeAccount = isLargeCurrentAccount; - } else if (isLargeAccountStoredValue) { - isLargeAccount = true; - } else { - isLargeAccount = false; - } - }, [account, isLargeCurrentAccount, isLargeAccountStoredValue, setIsLargeAccountStoredValue]); - - return isLargeAccount; -} - -export function getIsLargeAccount() { - return isLargeAccount; + return bestRpcUrls; } diff --git a/src/lib/rpc/index.ts b/src/lib/rpc/index.ts index fd37a75fe2..2fcbb8041e 100644 --- a/src/lib/rpc/index.ts +++ b/src/lib/rpc/index.ts @@ -11,7 +11,7 @@ import { import { Signer, ethers } from "ethers"; import { useEffect, useState } from "react"; import { isDevelopment } from "config/env"; -import { getBestRpcUrl, useBestRpcUrl } from "lib/rpc/bestRpcTracker"; +import { getCurrentRpcUrls, useCurrentRpcUrls } from "lib/rpc/bestRpcTracker"; export function getProvider(signer: undefined, chainId: number): ethers.JsonRpcProvider; export function getProvider(signer: Signer, chainId: number): Signer; @@ -23,7 +23,7 @@ export function getProvider(signer: Signer | undefined, chainId: number): ethers return signer; } - url = getBestRpcUrl(chainId); + url = getCurrentRpcUrls(chainId).primary; const network = Network.from(chainId); @@ -48,7 +48,7 @@ export function getWsProvider(chainId: number): WebSocketProvider | JsonRpcProvi } if (chainId === AVALANCHE_FUJI) { - const provider = new ethers.JsonRpcProvider(getBestRpcUrl(AVALANCHE_FUJI), network, { + const provider = new ethers.JsonRpcProvider(getCurrentRpcUrls(AVALANCHE_FUJI).primary, network, { staticNetwork: network, }); provider.pollingInterval = 2000; @@ -71,13 +71,13 @@ export function getFallbackProvider(chainId: number) { export function useJsonRpcProvider(chainId: number) { const [provider, setProvider] = useState(); - const rpcUrl = useBestRpcUrl(chainId); + const { primary: primaryRpcUrl } = useCurrentRpcUrls(chainId); useEffect(() => { async function initializeProvider() { - if (!rpcUrl) return; + if (!primaryRpcUrl) return; - const provider = new ethers.JsonRpcProvider(rpcUrl, chainId); + const provider = new ethers.JsonRpcProvider(primaryRpcUrl, chainId); provider._start(); await provider._waitUntilReady(); @@ -86,7 +86,7 @@ export function useJsonRpcProvider(chainId: number) { } initializeProvider(); - }, [chainId, rpcUrl]); + }, [chainId, primaryRpcUrl]); return { provider }; } From 0fffd20699c4f814f5d3c07031658d1d5ba4660d Mon Sep 17 00:00:00 2001 From: micky Date: Wed, 16 Oct 2024 00:02:10 +0300 Subject: [PATCH 7/7] review fixes --- .../SyntheticsStateContextProvider.tsx | 2 +- .../stats}/isLargeAccount.ts | 19 ++-- .../accountStats/useAccountVolumeStats.ts | 95 ------------------ .../accountStats/useIsLargeAccountData.ts | 97 ++++++++++++++++++- src/lib/metrics/types.ts | 1 + src/lib/metrics/useConfigureMetrics.ts | 5 +- src/lib/multicall/Multicall.ts | 3 - .../multicall/executeMulticallMainThread.ts | 2 +- src/lib/multicall/executeMulticallWorker.ts | 2 +- src/lib/rpc/bestRpcTracker.ts | 2 +- 10 files changed, 114 insertions(+), 114 deletions(-) rename src/{lib/account => domain/stats}/isLargeAccount.ts (52%) delete mode 100644 src/domain/synthetics/accountStats/useAccountVolumeStats.ts diff --git a/src/context/SyntheticsStateContext/SyntheticsStateContextProvider.tsx b/src/context/SyntheticsStateContext/SyntheticsStateContextProvider.tsx index e40b3b7583..c138c5187f 100644 --- a/src/context/SyntheticsStateContext/SyntheticsStateContextProvider.tsx +++ b/src/context/SyntheticsStateContext/SyntheticsStateContextProvider.tsx @@ -7,7 +7,7 @@ import { useAccountStats, usePeriodAccountStats, } from "domain/synthetics/accountStats"; -import { useIsLargeAccountTracker } from "lib/account/isLargeAccount"; +import { useIsLargeAccountTracker } from "domain/stats/isLargeAccount"; import { useGasLimits, useGasPrice } from "domain/synthetics/fees"; import { RebateInfoItem, useRebatesInfoRequest } from "domain/synthetics/fees/useRebatesInfo"; import useUiFeeFactorRequest from "domain/synthetics/fees/utils/useUiFeeFactor"; diff --git a/src/lib/account/isLargeAccount.ts b/src/domain/stats/isLargeAccount.ts similarity index 52% rename from src/lib/account/isLargeAccount.ts rename to src/domain/stats/isLargeAccount.ts index dcb8549aee..6941b05935 100644 --- a/src/lib/account/isLargeAccount.ts +++ b/src/domain/stats/isLargeAccount.ts @@ -2,12 +2,13 @@ import { IS_LARGE_ACCOUNT_KEY } from "config/localStorage"; import { useEffect } from "react"; import { useLocalStorageSerializeKey } from "lib/localStorage"; import { useIsLargeAccountData } from "domain/synthetics/accountStats/useIsLargeAccountData"; -import { getStorageItem } from "lib/metrics/storage"; let isLargeAccount = getIsLargeAccountStoredValue(); function getIsLargeAccountStoredValue() { - return getStorageItem(IS_LARGE_ACCOUNT_KEY, true) === "true"; + const storedValue = localStorage.getItem(JSON.stringify(IS_LARGE_ACCOUNT_KEY)); + + return storedValue === "true"; } export function getIsLargeAccount() { @@ -15,8 +16,8 @@ export function getIsLargeAccount() { } export function useIsLargeAccountTracker(account?: string) { - const isLargeCurrentAccount = useIsLargeAccountData(account); - const [isLargeAccountStoredValue, setIsLargeAccountStoredValue] = useLocalStorageSerializeKey( + const isCurrentAccountLarge = useIsLargeAccountData(account); + const [isCurrentAccountLargeStoredValue, setIsCurrentAccountLargeStoredValue] = useLocalStorageSerializeKey( IS_LARGE_ACCOUNT_KEY, false ); @@ -24,15 +25,15 @@ export function useIsLargeAccountTracker(account?: string) { useEffect(() => { if (!account) { isLargeAccount = false; - } else if (isLargeCurrentAccount !== undefined) { - setIsLargeAccountStoredValue(isLargeCurrentAccount); - isLargeAccount = isLargeCurrentAccount; - } else if (isLargeAccountStoredValue) { + } else if (isCurrentAccountLarge !== undefined) { + setIsCurrentAccountLargeStoredValue(isCurrentAccountLarge); + isLargeAccount = isCurrentAccountLarge; + } else if (isCurrentAccountLargeStoredValue) { isLargeAccount = true; } else { isLargeAccount = false; } - }, [account, isLargeCurrentAccount, isLargeAccountStoredValue, setIsLargeAccountStoredValue]); + }, [account, isCurrentAccountLarge, isCurrentAccountLargeStoredValue, setIsCurrentAccountLargeStoredValue]); return isLargeAccount; } diff --git a/src/domain/synthetics/accountStats/useAccountVolumeStats.ts b/src/domain/synthetics/accountStats/useAccountVolumeStats.ts deleted file mode 100644 index aab79b6724..0000000000 --- a/src/domain/synthetics/accountStats/useAccountVolumeStats.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { gql } from "@apollo/client"; -import { getSubsquidGraphClient } from "lib/subgraph"; -import { useMemo } from "react"; -import useSWR from "swr"; -import { subDays, format, eachDayOfInterval } from "date-fns"; -import { toUtcDayStart } from "lib/dates"; -import { CONFIG_UPDATE_INTERVAL } from "lib/timeConstants"; -import { ARBITRUM, AVALANCHE } from "config/chains"; - -const LARGE_ACCOUNT_CHAINS = [ARBITRUM, AVALANCHE]; - -export function useAccountVolumeStats(params: { account?: string }) { - const { account } = params; - - const now = new Date(); - const date30dAgo = subDays(now, 30); - const last30dList = eachDayOfInterval({ - start: date30dAgo, - end: now, - }); - - const { data, error, isLoading } = useSWR<{ - totalVolume: bigint; - dailyVolume: { date: string; volume: bigint }[]; - }>(account ? ["useAccountVolumeStats", account] : null, { - fetcher: async () => { - const chainPromises = LARGE_ACCOUNT_CHAINS.map(async (chainId) => { - const client = getSubsquidGraphClient(chainId); - - const dailyQueries = last30dList.map((day, index) => { - const from = Math.floor(toUtcDayStart(day)); - const to = Math.floor(toUtcDayStart(day) + 24 * 60 * 60); - - return ` - day${index}: periodAccountStats( - where: { id_eq: "${account}", from: ${from}, to: ${to} } - ) { - volume - } - `; - }); - - const query = gql` - query AccountVolumeStats { - total: accountStats(where: { id_eq: "${account}" }) { - volume - } - ${dailyQueries.join("\n")} - } - `; - - const res = await client?.query({ - query, - fetchPolicy: "no-cache", - }); - - const totalVolume = BigInt(res?.data?.total?.[0]?.volume ?? 0); - - const dailyVolume = last30dList.map((day, index) => { - const volume = BigInt(res?.data[`day${index}`]?.[0]?.volume ?? 0); - return { - date: format(day, "yyyy-MM-dd"), - volume, - }; - }); - - return { - totalVolume, - dailyVolume, - }; - }); - - const chainResults = await Promise.all(chainPromises); - - const totalVolume = chainResults.reduce((acc, result) => acc + result.totalVolume, 0n); - - const dailyVolume = last30dList.map((day, index) => { - const volume = chainResults.reduce((acc, { dailyVolume }) => acc + dailyVolume[index].volume, 0n); - - return { - date: format(day, "yyyy-MM-dd"), - volume, - }; - }); - - return { - totalVolume, - dailyVolume, - }; - }, - refreshInterval: CONFIG_UPDATE_INTERVAL, - }); - - return useMemo(() => ({ data, error, isLoading }), [data, error, isLoading]); -} diff --git a/src/domain/synthetics/accountStats/useIsLargeAccountData.ts b/src/domain/synthetics/accountStats/useIsLargeAccountData.ts index b18935b407..ffb322172d 100644 --- a/src/domain/synthetics/accountStats/useIsLargeAccountData.ts +++ b/src/domain/synthetics/accountStats/useIsLargeAccountData.ts @@ -2,7 +2,15 @@ import { useMemo } from "react"; import { USD_DECIMALS } from "config/factors"; import { expandDecimals } from "lib/numbers"; -import { useAccountVolumeStats } from "./useAccountVolumeStats"; +import { gql } from "@apollo/client"; +import { getSubsquidGraphClient } from "lib/subgraph"; +import useSWR from "swr"; +import { subDays, format, eachDayOfInterval } from "date-fns"; +import { toUtcDayStart } from "lib/dates"; +import { CONFIG_UPDATE_INTERVAL } from "lib/timeConstants"; +import { ARBITRUM, AVALANCHE } from "config/chains"; + +const LARGE_ACCOUNT_CHAINS = [ARBITRUM, AVALANCHE]; // Thresholds to recognise large accounts const MAX_DAILY_VOLUME = expandDecimals(220_000n, USD_DECIMALS); @@ -10,7 +18,7 @@ const AGG_14_DAYS_VOLUME = expandDecimals(1_200_000n, USD_DECIMALS); const AGG_ALL_TIME_VOLUME = expandDecimals(3_500_000n, USD_DECIMALS); export function useIsLargeAccountData(account?: string) { - const { data, error, isLoading } = useAccountVolumeStats({ account }); + const { data, error, isLoading } = useIsLargeAccountVolumeStats({ account }); const isLargeAccount = useMemo(() => { if (!data || isLoading || error) return undefined; @@ -28,3 +36,88 @@ export function useIsLargeAccountData(account?: string) { return isLargeAccount; } + +function useIsLargeAccountVolumeStats(params: { account?: string }) { + const { account } = params; + + const now = new Date(); + const date30dAgo = subDays(now, 30); + const last30dList = eachDayOfInterval({ + start: date30dAgo, + end: now, + }); + + const { data, error, isLoading } = useSWR<{ + totalVolume: bigint; + dailyVolume: { date: string; volume: bigint }[]; + }>(account ? ["useIsLargeAccountVolumeStats", account] : null, { + refreshInterval: CONFIG_UPDATE_INTERVAL, + fetcher: async () => { + const chainPromises = LARGE_ACCOUNT_CHAINS.map(async (chainId) => { + const client = getSubsquidGraphClient(chainId); + + const dailyQueries = last30dList.map((day, index) => { + const from = Math.floor(toUtcDayStart(day)); + const to = Math.floor(toUtcDayStart(day) + 24 * 60 * 60); + + return ` + day${index}: periodAccountStats( + where: { id_eq: "${account}", from: ${from}, to: ${to} } + ) { + volume + } + `; + }); + + const query = gql` + query AccountVolumeStats { + total: accountStats(where: { id_eq: "${account}" }) { + volume + } + ${dailyQueries.join("\n")} + } + `; + + const res = await client?.query({ + query, + fetchPolicy: "no-cache", + }); + + const totalVolume = BigInt(res?.data?.total?.[0]?.volume ?? 0); + + const dailyVolume = last30dList.map((day, index) => { + const volume = BigInt(res?.data[`day${index}`]?.[0]?.volume ?? 0); + return { + date: format(day, "yyyy-MM-dd"), + volume, + }; + }); + + return { + totalVolume, + dailyVolume, + }; + }); + + const chainResults = await Promise.all(chainPromises); + + const totalVolume = chainResults.reduce((acc, result) => acc + result.totalVolume, 0n); + + const dailyVolume = last30dList.map((day, index) => { + const volume = chainResults.reduce((acc, { dailyVolume }) => acc + dailyVolume[index].volume, 0n); + + return { + date: format(day, "yyyy-MM-dd"), + volume, + }; + }); + + return { + totalVolume, + dailyVolume, + }; + }, + }); + + return useMemo(() => ({ data, error, isLoading }), [data, error, isLoading]); +} diff --git a/src/lib/metrics/types.ts b/src/lib/metrics/types.ts index 909accea75..6979228c9c 100644 --- a/src/lib/metrics/types.ts +++ b/src/lib/metrics/types.ts @@ -6,6 +6,7 @@ export type GlobalMetricData = { isMobileMetamask: boolean; isWindowVisible: boolean; isAuthorised: boolean; + isLargeAccount: boolean; abFlags: Record; isMobile: boolean; isHomeSite: boolean; diff --git a/src/lib/metrics/useConfigureMetrics.ts b/src/lib/metrics/useConfigureMetrics.ts index 55a0f67ee8..19df67cde5 100644 --- a/src/lib/metrics/useConfigureMetrics.ts +++ b/src/lib/metrics/useConfigureMetrics.ts @@ -10,6 +10,7 @@ import Bowser from "bowser"; import { useEffect } from "react"; import { metrics } from "./Metrics"; import { isHomeSite } from "../legacy"; +import { getIsLargeAccount } from "domain/stats/isLargeAccount"; export function useConfigureMetrics() { const { chainId } = useChainId(); @@ -18,6 +19,7 @@ export function useConfigureMetrics() { const [showDebugValues] = useLocalStorageSerializeKey(SHOW_DEBUG_VALUES_KEY, false); const isMobileMetamask = useIsMetamaskMobile(); const isWindowVisible = useIsWindowVisible(); + const isLargeAccount = getIsLargeAccount(); useEffect(() => { metrics.subscribeToEvents(); @@ -44,11 +46,12 @@ export function useConfigureMetrics() { abFlags: getAbFlags(), isMobile: getIsMobileUserAgent(), isHomeSite: isHomeSite(), + isLargeAccount, browserName: bowser.browser.name, browserVersion: bowser.browser.version, platform: bowser.platform.type, }); - }, [active, isMobileMetamask, isWindowVisible]); + }, [active, isMobileMetamask, isWindowVisible, isLargeAccount]); useEffect(() => { metrics.updateWalletNames(); diff --git a/src/lib/multicall/Multicall.ts b/src/lib/multicall/Multicall.ts index d38a2c852e..6aa3eb04de 100644 --- a/src/lib/multicall/Multicall.ts +++ b/src/lib/multicall/Multicall.ts @@ -344,7 +344,6 @@ export class Multicall { rpcProvider: fallbackProviderName, isInMainThread: !isWebWorker, errorMessage: _viemError.message, - isLargeAccount, }, }); @@ -388,7 +387,6 @@ export class Multicall { isInMainThread: !isWebWorker, requestType: "initial", rpcProvider: rpcProviderName, - isLargeAccount, errorMessage: _viemError.message.slice(0, 150), }, }); @@ -412,7 +410,6 @@ export class Multicall { requestType: "initial", rpcProvider: rpcProviderName, isInMainThread: !isWebWorker, - isLargeAccount, errorMessage: serializeMulticallErrors(result.errors), }, }); diff --git a/src/lib/multicall/executeMulticallMainThread.ts b/src/lib/multicall/executeMulticallMainThread.ts index 2ae982e9c5..dbf8d7c2ea 100644 --- a/src/lib/multicall/executeMulticallMainThread.ts +++ b/src/lib/multicall/executeMulticallMainThread.ts @@ -2,7 +2,7 @@ import { MAX_TIMEOUT, Multicall } from "./Multicall"; import type { MulticallRequestConfig } from "./types"; import { getAbFlags } from "config/ab"; import { getCurrentRpcUrls } from "lib/rpc/bestRpcTracker"; -import { getIsLargeAccount } from "lib/account/isLargeAccount"; +import { getIsLargeAccount } from "domain/stats/isLargeAccount"; export async function executeMulticallMainThread(chainId: number, request: MulticallRequestConfig) { const multicall = await Multicall.getInstance(chainId, getAbFlags()); diff --git a/src/lib/multicall/executeMulticallWorker.ts b/src/lib/multicall/executeMulticallWorker.ts index 8a25a47fc7..e96446fa86 100644 --- a/src/lib/multicall/executeMulticallWorker.ts +++ b/src/lib/multicall/executeMulticallWorker.ts @@ -11,7 +11,7 @@ import type { MulticallRequestConfig, MulticallResult } from "./types"; import { MetricEventParams, MulticallTimeoutEvent } from "lib/metrics"; import { getAbFlags } from "config/ab"; import { getCurrentRpcUrls } from "lib/rpc/bestRpcTracker"; -import { getIsLargeAccount } from "lib/account/isLargeAccount"; +import { getIsLargeAccount } from "domain/stats/isLargeAccount"; const executorWorker: Worker = new Worker(new URL("./multicall.worker", import.meta.url), { type: "module" }); diff --git a/src/lib/rpc/bestRpcTracker.ts b/src/lib/rpc/bestRpcTracker.ts index 6e65df6cbe..958435839e 100644 --- a/src/lib/rpc/bestRpcTracker.ts +++ b/src/lib/rpc/bestRpcTracker.ts @@ -23,7 +23,7 @@ import { useEffect, useState } from "react"; import { getProviderNameFromUrl } from "lib/rpc/getProviderNameFromUrl"; import { emitMetricCounter } from "lib/metrics/emitMetricEvent"; import { RpcTrackerRankingCounter } from "lib/metrics"; -import { getIsLargeAccount } from "lib/account/isLargeAccount"; +import { getIsLargeAccount } from "domain/stats/isLargeAccount"; const PROBE_TIMEOUT = 10 * 1000; // 10 seconds / Frequency of RPC probing const PROBE_FAIL_TIMEOUT = 10 * 1000; // 10 seconds / Abort RPC probe if it takes longer