Skip to content

Commit

Permalink
refactor: move IPFS.json utils to config/external-config
Browse files Browse the repository at this point in the history
  • Loading branch information
ev-d committed Dec 9, 2024
1 parent 1ee38a1 commit d1e9f38
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 153 deletions.
54 changes: 54 additions & 0 deletions config/external-config/frontend-fallback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { useMemo } from 'react';
import {
Manifest,
ManifestConfig,
ManifestConfigPage,
ManifestConfigPageList,
ManifestEntry,
isManifestValid,
} from 'config/external-config';
import { getDexConfig } from 'features/withdrawals/request/withdrawal-rates';

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

export const getBackwardCompatibleConfig = (
config: ManifestEntry['config'],
): ManifestEntry['config'] => {
let pages: ManifestConfig['pages'];
const configPages = config.pages;
if (configPages) {
pages = (Object.keys(configPages) as ManifestConfigPage[])
.filter((key) => ManifestConfigPageList.has(key))
.reduce(
(acc, key) => {
if (acc) {
acc[key] = { ...configPages[key] };
}

return acc;
},
{} as ManifestConfig['pages'],
);
}

return {
enabledWithdrawalDexes: config.enabledWithdrawalDexes.filter(
(dex) => !!getDexConfig(dex),
),
featureFlags: { ...(config.featureFlags ?? {}) },
multiChainBanner: config.multiChainBanner ?? [],
pages,
};
};

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]);
};
8 changes: 8 additions & 0 deletions config/external-config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,11 @@ export type {
ManifestConfigPage,
} from './types';
export { ManifestConfigPageList, ManifestConfigPageEnum } from './types';
export {
isManifestValid,
isManifestEntryValid,
isEnabledDexesValid,
isFeatureFlagsValid,
isMultiChainBannerValid,
isPagesValid,
} from './utils';
7 changes: 5 additions & 2 deletions config/external-config/use-external-config-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import useSWR from 'swr';
import { STRATEGY_LAZY } from 'consts/swr-strategies';
import { getConfig } from '../get-config';
import { standardFetcher } from 'utils/standardFetcher';
import { isManifestEntryValid } from 'utils/validate-ipfs-json';
import { IPFS_MANIFEST_URL } from 'consts/external-links';
import { getBackwardCompatibleConfig, useFallbackManifestEntry } from './utils';
import { isManifestEntryValid } from 'config/external-config';
import {
getBackwardCompatibleConfig,
useFallbackManifestEntry,
} from './frontend-fallback';

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

Expand Down
137 changes: 94 additions & 43 deletions config/external-config/utils.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,105 @@
import { useMemo } from 'react';
import {
Manifest,
ManifestConfig,
ManifestConfigPage,
ManifestConfigPageList,
ManifestConfigPageEnum,
ManifestEntry,
} from './types';
import { isManifestValid } from 'utils/validate-ipfs-json';
import { getDexConfig } from 'features/withdrawals/request/withdrawal-rates';

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

export const getBackwardCompatibleConfig = (
config: ManifestEntry['config'],
): ManifestEntry['config'] => {
let pages: ManifestConfig['pages'];
const configPages = config.pages;
if (configPages) {
pages = (Object.keys(configPages) as ManifestConfigPage[])
.filter((key) => ManifestConfigPageList.has(key))
.reduce(
(acc, key) => {
if (acc) {
acc[key] = { ...configPages[key] };
}

return acc;
},
{} as ManifestConfig['pages'],
);

export const isMultiChainBannerValid = (config: object) => {
// allow empty config
if (!('multiChainBanner' in config) || !config.multiChainBanner) return true;

if (!Array.isArray(config.multiChainBanner)) return false;

const multiChainBanner = config.multiChainBanner;

if (
!multiChainBanner.every(
(chainId) => typeof chainId === 'number' && chainId > 0,
)
)
return false;

return !(new Set(multiChainBanner).size !== multiChainBanner.length);
};

export const isFeatureFlagsValid = (config: object) => {
// allow empty config
if (!('featureFlags' in config) || !config.featureFlags) return true;

// only objects
return !(typeof config.featureFlags !== 'object');
};

export const isEnabledDexesValid = (config: object) => {
if (
!(
'enabledWithdrawalDexes' in config &&
Array.isArray(config.enabledWithdrawalDexes)
)
)
return false;

const enabledWithdrawalDexes = config.enabledWithdrawalDexes;

if (
!enabledWithdrawalDexes.every(
(dex) => typeof dex === 'string' && dex !== '',
)
)
return false;

return new Set(enabledWithdrawalDexes).size === enabledWithdrawalDexes.length;
};

export const isPagesValid = (config: object) => {
if (!('pages' in config)) {
return true;
}

const pages = config.pages as ManifestConfig['pages'];
if (pages && typeof pages === 'object') {
// INFO: exclude possible issue when stack interface can be deactivated
return !pages[ManifestConfigPageEnum.Stake]?.shouldDisable;
}

return {
enabledWithdrawalDexes: config.enabledWithdrawalDexes.filter(
(dex) => !!getDexConfig(dex),
),
featureFlags: { ...(config.featureFlags ?? {}) },
multiChainBanner: config.multiChainBanner ?? [],
pages,
};
return false;
};

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;

return [
isEnabledDexesValid,
isMultiChainBannerValid,
isFeatureFlagsValid,
isPagesValid,
]
.map((validator) => validator(config))
.every((isValid) => isValid);
}
return false;
};

export const useFallbackManifestEntry = (
prefetchedManifest: unknown,
export const isManifestValid = (
manifest: unknown,
chain: number,
): ManifestEntry => {
return useMemo(() => {
const isValid = isManifestValid(prefetchedManifest, chain);
return isValid
? prefetchedManifest[chain]
: (FallbackLocalManifest as unknown as Manifest)[chain];
}, [prefetchedManifest, chain]);
): 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;
};
5 changes: 3 additions & 2 deletions consts/external-links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ 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';
// export const IPFS_MANIFEST_URL =
// 'https://raw.githubusercontent.com/lidofinance/ethereum-staking-widget/main/IPFS.json';
export const IPFS_MANIFEST_URL = 'http://localhost:3000/runtime/IPFS.json';
105 changes: 0 additions & 105 deletions utils/validate-ipfs-json.ts

This file was deleted.

2 changes: 1 addition & 1 deletion utilsApi/fetch-external-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { responseTimeExternalMetricWrapper } from './fetchApiWrapper';
import { standardFetcher } from 'utils/standardFetcher';

import { config } from 'config';
import { isManifestValid } from 'utils/validate-ipfs-json';
import { isManifestValid } from 'config/external-config';

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

Expand Down

0 comments on commit d1e9f38

Please sign in to comment.