Skip to content

Commit

Permalink
Merge pull request #453 from lidofinance/develop
Browse files Browse the repository at this point in the history
Develop to Main
  • Loading branch information
itaven authored Sep 3, 2024
2 parents c5cadb4 + 18d09e6 commit a87e1d0
Show file tree
Hide file tree
Showing 34 changed files with 683 additions and 136 deletions.
7 changes: 5 additions & 2 deletions IPFS.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
"1": {
"cid": "bafybeib3zmyqlmantvdd6i5q4ehmo4larvorgquyanne3uoqdbedwgh3aq",
"leastSafeVersion": "0.36.1"
"leastSafeVersion": "0.36.1",
"config": {
"enabledWithdrawalDexes": ["one-inch", "paraswap", "bebop"]
}
},
"5": {
"__warning__": "For testing purposes only",
Expand All @@ -13,7 +16,7 @@
"cid": "bafybeibbsoqlofslw273b4ih2pdxfaz2zbjmred2ijog725tcmfoewix7y",
"leastSafeVersion": "0.36.1",
"config": {
"enabledWithdrawalDexes": ["one-inch", "paraswap", "bebop"]
"enabledWithdrawalDexes": ["one-inch", "paraswap"]
}
}
}
7 changes: 7 additions & 0 deletions config/external-config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export { useExternalConfigContext } from './use-external-config-context';
export type {
ManifestConfig,
Manifest,
ManifestEntry,
ExternalConfig,
} from './types';
20 changes: 20 additions & 0 deletions config/external-config/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { DexWithdrawalApi } from 'features/withdrawals/request/withdrawal-rates';
import { SWRResponse } from 'swr';

export type Manifest = Record<string, ManifestEntry>;

export type ManifestEntry = {
cid?: string;
ens?: string;
leastSafeVersion?: string;
config: ManifestConfig;
};

export type ManifestConfig = {
enabledWithdrawalDexes: DexWithdrawalApi[];
};

export type ExternalConfig = Omit<ManifestEntry, 'config'> &
ManifestConfig & {
fetchMeta: SWRResponse<ManifestEntry>;
};
59 changes: 59 additions & 0 deletions config/external-config/use-external-config-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { useMemo } from 'react';
import useSWR from 'swr';

import { STRATEGY_LAZY } from 'consts/swr-strategies';
import { getConfig } from '../get-config';
import { standardFetcher } from 'utils/standardFetcher';
import { IPFS_MANIFEST_URL } from 'consts/external-links';
import {
getBackwardCompatibleConfig,
isManifestEntryValid,
useFallbackManifestEntry,
} from './utils';

import type { ExternalConfig, ManifestEntry } from './types';

const onFetchError = (error: unknown) => {
console.warn(
'[useExternalConfigContext] while fetching external config:',
error,
);
};

export const useExternalConfigContext = (
prefetchedManifest?: unknown,
): ExternalConfig => {
const { defaultChain } = getConfig();
const fallbackData = useFallbackManifestEntry(
prefetchedManifest,
defaultChain,
);

const swr = useSWR<ManifestEntry>(
['swr:external-config', defaultChain],
async () => {
const result = await standardFetcher<Record<string, any>>(
IPFS_MANIFEST_URL,
{
headers: { Accept: 'application/json' },
},
);
const entry = result[defaultChain.toString()];
if (isManifestEntryValid(entry)) return entry;
throw new Error(
'[useExternalConfigContext] received invalid manifest',
result,
);
},
{
...STRATEGY_LAZY,
onError: onFetchError,
},
);

return useMemo(() => {
const { config, ...rest } = swr.data ?? fallbackData;
const cleanConfig = getBackwardCompatibleConfig(config);
return { ...cleanConfig, ...rest, fetchMeta: swr };
}, [swr, fallbackData]);
};
67 changes: 67 additions & 0 deletions config/external-config/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { useMemo } from 'react';
import type { Manifest, ManifestEntry } from './types';
import { getDexConfig } from 'features/withdrawals/request/withdrawal-rates';

import FallbackLocalManifest from 'IPFS.json' assert { type: 'json' };

// TODO: refactor on config expansion
export const isManifestEntryValid = (
entry?: unknown,
): entry is ManifestEntry => {
if (
// entry = {}
entry &&
typeof entry === 'object' &&
// entry.config = {}
'config' in entry &&
typeof entry.config === 'object' &&
entry.config
) {
const config = entry.config;
if (
'enabledWithdrawalDexes' in config &&
Array.isArray(config.enabledWithdrawalDexes)
) {
const enabledWithdrawalDexes = config.enabledWithdrawalDexes;
return (
new Set(enabledWithdrawalDexes).size === enabledWithdrawalDexes.length
);
}
return false;
}
return false;
};

export const getBackwardCompatibleConfig = (
config: ManifestEntry['config'],
): ManifestEntry['config'] => {
return {
enabledWithdrawalDexes: config.enabledWithdrawalDexes.filter(
(dex) => !!getDexConfig(dex),
),
};
};

export const isManifestValid = (
manifest: unknown,
chain: number,
): manifest is Manifest => {
const stringChain = chain.toString();
if (manifest && typeof manifest === 'object' && stringChain in manifest)
return isManifestEntryValid(
(manifest as Record<string, unknown>)[stringChain],
);
return false;
};

export const useFallbackManifestEntry = (
prefetchedManifest: unknown,
chain: number,
): ManifestEntry => {
return useMemo(() => {
const isValid = isManifestValid(prefetchedManifest, chain);
return isValid
? prefetchedManifest[chain]
: (FallbackLocalManifest as unknown as Manifest)[chain];
}, [prefetchedManifest, chain]);
};
3 changes: 3 additions & 0 deletions config/get-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as cache from './groups/cache';
import * as ipfs from './groups/ipfs';
import * as locale from './groups/locale';
import * as stake from './groups/stake';
import * as revalidation from './groups/revalidation';
import * as web3 from './groups/web3';
import * as withdrawalQueueEstimate from './groups/withdrawal-queue-estimate';

Expand All @@ -14,6 +15,7 @@ export type ConfigType = {
typeof web3 &
typeof locale &
typeof stake &
typeof revalidation &
typeof withdrawalQueueEstimate &
PreConfigType;

Expand All @@ -27,6 +29,7 @@ export const getConfig = (): ConfigType => {
...ipfs,
...locale,
...stake,
...revalidation,
...withdrawalQueueEstimate,

// highest priority
Expand Down
4 changes: 4 additions & 0 deletions config/groups/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ export const CACHE_ONE_INCH_RATE_TTL = ms('5m');

export const CACHE_TOTAL_SUPPLY_KEY = 'cache-total-supply';
export const CACHE_TOTAL_SUPPLY_TTL = ms('1m');

export const CACHE_EXTERNAL_CONFIG_KEY = 'cache-external-config';
export const CACHE_EXTERNAL_CONFIG_TTL = ms('10m');

export const CACHE_TOTAL_SUPPLY_HEADERS =
'public, max-age=60, stale-if-error=1200, stale-while-revalidate=30';

Expand Down
15 changes: 15 additions & 0 deletions config/groups/revalidation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { ManifestConfig, ManifestEntry } from 'config/external-config';

export const DEFAULT_REVALIDATION = 60 * 15; // 15 minutes
export const ERROR_REVALIDATION_SECONDS = 60; // 1 minute

export const FALLBACK_CONFIG: ManifestConfig = {
enabledWithdrawalDexes: [],
};

export const FALLBACK_MANIFEST_ENTRY: ManifestEntry = {
cid: undefined,
ens: undefined,
leastSafeVersion: undefined,
config: FALLBACK_CONFIG,
};
23 changes: 21 additions & 2 deletions config/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,45 @@ import {
useFeatureFlagsContext,
FeatureFlagsContextType,
} from './feature-flags';
import {
type ExternalConfig,
useExternalConfigContext,
} from './external-config';

type ConfigProviderType = {
config: ConfigType;
userConfig: UserConfigContextType;
featureFlags: FeatureFlagsContextType;
externalConfig: ExternalConfig;
};

export const ConfigContext = createContext<ConfigProviderType | null>(null);

export const ConfigProvider = ({ children }: PropsWithChildren) => {
type ConfigProviderProps = {
prefetchedManifest?: unknown;
};

export const ConfigProvider = ({
children,
prefetchedManifest,
}: PropsWithChildren<ConfigProviderProps>) => {
const userConfigContextValue = useUserConfigContext();
const featureFlagsContextValue = useFeatureFlagsContext();
const externalConfigContextValue =
useExternalConfigContext(prefetchedManifest);

const contextValue = useMemo(
() => ({
config: getConfig(),
userConfig: userConfigContextValue,
featureFlags: featureFlagsContextValue,
externalConfig: externalConfigContextValue,
}),
[userConfigContextValue, featureFlagsContextValue],
[
userConfigContextValue,
featureFlagsContextValue,
externalConfigContextValue,
],
);

return (
Expand Down
4 changes: 4 additions & 0 deletions consts/external-links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ export const LINK_ADD_NFT_GUIDE = `${config.helpOrigin}/en/articles/7858367-how-

export const OPEN_OCEAN_REFERRAL_ADDRESS =
'0xbb1263222b2c020f155d409dba05c4a3861f18f8';

// for dev and local testing you can set to 'http:/localhost:3000/runtime/IPFS.json' and have file at /public/runtime/IPFS.json
export const IPFS_MANIFEST_URL =
'https://raw.githubusercontent.com/lidofinance/ethereum-staking-widget/main/IPFS.json';
2 changes: 2 additions & 0 deletions consts/metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ export const METRICS_PREFIX = 'eth_stake_widget_ui_';

export const enum METRIC_NAMES {
REQUESTS_TOTAL = 'requests_total',
STARTUP_CHECKS_RPC = 'startup_checks_rpc',
API_RESPONSE = 'api_response',
ETH_CALL_ADDRESS_TO = 'eth_call_address_to',
SSR_COUNT = 'ssr_count',
}
Loading

0 comments on commit a87e1d0

Please sign in to comment.