Skip to content

Commit

Permalink
Merge pull request #311 from lidofinance/develop
Browse files Browse the repository at this point in the history
Develop to main
  • Loading branch information
itaven authored Apr 9, 2024
2 parents 45b7faf + 0f1f377 commit 9da207c
Show file tree
Hide file tree
Showing 213 changed files with 1,241 additions and 898 deletions.
31 changes: 16 additions & 15 deletions utilsApi/withCSP.ts → config/csp/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import { FC } from 'react';
import { AppProps } from 'next/app';
import getConfig from 'next/config';
import { withSecureHeaders } from 'next-secure-headers';

import { dynamics } from 'config';
import type { ContentSecurityPolicyOption } from 'next-secure-headers/lib/rules';

const { serverRuntimeConfig } = getConfig();
const { cspTrustedHosts, cspReportOnly, cspReportUri, developmentMode } =
serverRuntimeConfig;

const trustedHosts = cspTrustedHosts ? cspTrustedHosts.split(',') : [];
// Don't use absolute import here!
// code'''
// import { config, secretConfig } from 'config';
// '''
// otherwise you will get something like a cyclic error!
import { config } from '../get-config';
import { secretConfig } from '../get-secret-config';

const reportOnly = cspReportOnly == 'true';
const trustedHosts = secretConfig.cspTrustedHosts
? secretConfig.cspTrustedHosts.split(',')
: [];

export const contentSecurityPolicy: ContentSecurityPolicyOption = {
directives: {
Expand All @@ -28,7 +29,7 @@ export const contentSecurityPolicy: ContentSecurityPolicyOption = {
scriptSrc: [
"'self'",
"'unsafe-inline'",
...(developmentMode ? ["'unsafe-eval'"] : []), // for HMR
...(config.developmentMode ? ["'unsafe-eval'"] : []), // for HMR
...trustedHosts,
],

Expand All @@ -37,24 +38,24 @@ export const contentSecurityPolicy: ContentSecurityPolicyOption = {
"'self'",
'https:',
'wss:',
...(developmentMode ? ['ws:'] : []), // for HMR
...(config.developmentMode ? ['ws:'] : []), // for HMR
],

...(!dynamics.ipfsMode && {
...(!config.ipfsMode && {
// CSP directive 'frame-ancestors' is ignored when delivered via a <meta> element.
// CSP directive 'report-uri' is ignored when delivered via a <meta> element.
frameAncestors: ['*'],
reportURI: cspReportUri,
reportURI: secretConfig.cspReportUri,
}),
childSrc: [
"'self'",
'https://*.walletconnect.org',
'https://*.walletconnect.com',
],
workerSrc: ["'none'"],
'base-uri': dynamics.ipfsMode ? undefined : ["'none'"],
'base-uri': config.ipfsMode ? undefined : ["'none'"],
},
reportOnly,
reportOnly: secretConfig.cspReportOnly,
};

export const withCsp = (app: FC<AppProps>): FC =>
Expand Down
8 changes: 7 additions & 1 deletion config/dynamics.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as dynamics from '../env-dynamics.mjs';
import * as dynamics from 'env-dynamics.mjs';
// We're making dynamic env variables
// so we can inject selected envs from Docker runtime too,
// not only during build-time for static pages
Expand All @@ -9,4 +9,10 @@ declare global {
}
}

// Don't use dynamics directly in the project!
// Only through:
// code```
// import { config } from 'config'; // or
// import { config } from './get-config'; // in config "namespace"
// ```
export default typeof window !== 'undefined' ? window.__env__ : dynamics;
44 changes: 44 additions & 0 deletions config/feature-flags/context-hook.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useMemo, useState, useCallback } from 'react';
import { useLocalStorage } from '@lido-sdk/react';

import { getFeatureFlagsDefault } from './utils';
import { FeatureFlagsType } from './types';

const STORAGE_FEATURE_FLAGS = 'lido-feature-flags';

const FEATURE_FLAGS_DEFAULT = getFeatureFlagsDefault();

export type FeatureFlagsContextType = FeatureFlagsType & {
setFeatureFlag: (featureFlag: keyof FeatureFlagsType, value: boolean) => void;
};

export const useFeatureFlagsContext = () => {
const [featureFlagsLocalStorage, setFeatureFlagsLocalStorage] =
useLocalStorage(STORAGE_FEATURE_FLAGS, FEATURE_FLAGS_DEFAULT);

const [featureFlagsState, setFeatureFlagsState] = useState<FeatureFlagsType>(
featureFlagsLocalStorage,
);

const setFeatureFlag = useCallback(
(featureFlag: keyof FeatureFlagsType, value: boolean) => {
setFeatureFlagsLocalStorage({
...featureFlagsState,
[featureFlag]: value,
});
setFeatureFlagsState({
...featureFlagsState,
[featureFlag]: value,
});
},
[featureFlagsState, setFeatureFlagsLocalStorage],
);

return useMemo(() => {
return {
...FEATURE_FLAGS_DEFAULT,
...featureFlagsState,
setFeatureFlag: setFeatureFlag,
};
}, [featureFlagsState, setFeatureFlag]);
};
31 changes: 31 additions & 0 deletions config/feature-flags/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useContext, useMemo } from 'react';
import invariant from 'tiny-invariant';

import { ConfigContext } from '../provider';
import { FeatureFlagsContextType } from './context-hook';
import { FeatureFlagsType } from './types';

type UseFeatureFlagReturnType = {
[key in keyof FeatureFlagsType]: boolean;
} & {
setFeatureFlag: (featureFlag: keyof FeatureFlagsType, value: boolean) => void;
};

export const useFeatureFlag = (
flag: keyof FeatureFlagsType,
): UseFeatureFlagReturnType | null => {
const context = useContext(ConfigContext);
invariant(context, 'Attempt to use `feature flag` outside of provider');
return useMemo(() => {
return {
[flag]: context.featureFlags[flag],
setFeatureFlag: context.featureFlags?.setFeatureFlag,
};
}, [context.featureFlags, flag]);
};

export const useFeatureFlags = (): FeatureFlagsContextType | null => {
const context = useContext(ConfigContext);
invariant(context, 'Attempt to use `feature flag` outside of provider');
return useMemo(() => context.featureFlags, [context.featureFlags]);
};
4 changes: 4 additions & 0 deletions config/feature-flags/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './hooks';
export * from './context-hook';
export * from './types';
export * from './utils';
6 changes: 6 additions & 0 deletions config/feature-flags/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const RPC_SETTINGS_PAGE_ON_INFRA_IS_ENABLED =
'rpcSettingsPageOnInfraIsEnabled';

export type FeatureFlagsType = {
[RPC_SETTINGS_PAGE_ON_INFRA_IS_ENABLED]: boolean;
};
7 changes: 7 additions & 0 deletions config/feature-flags/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { FeatureFlagsType } from './types';

export const getFeatureFlagsDefault = (): FeatureFlagsType => {
return {
rpcSettingsPageOnInfraIsEnabled: false,
};
};
37 changes: 37 additions & 0 deletions config/get-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { getPreConfig, PreConfigType } from './get-preconfig';
import * as cache from './groups/cache';
import * as estimate from './groups/estimate';
import * as ipfs from './groups/ipfs';
import * as locale from './groups/locale';
import * as stake from './groups/stake';
import * as withdrawalQueueEstimate from './groups/withdrawal-queue-estimate';

export type ConfigType = {
isClientSide: boolean;
isServerSide: boolean;
} & typeof cache &
typeof estimate &
typeof ipfs &
typeof locale &
typeof stake &
typeof withdrawalQueueEstimate &
PreConfigType;

export const getConfig = (): ConfigType => {
return {
isClientSide: typeof window !== 'undefined',
isServerSide: typeof window === 'undefined',

...cache,
...estimate,
...ipfs,
...locale,
...stake,
...withdrawalQueueEstimate,

// highest priority
...getPreConfig(),
};
};

export const config = getConfig();
30 changes: 30 additions & 0 deletions config/get-preconfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import getConfigNext from 'next/config';
import { default as dynamics } from './dynamics';

const { publicRuntimeConfig, serverRuntimeConfig } = getConfigNext();

export type PreConfigType = {
BASE_PATH_ASSET: string;
} & typeof publicRuntimeConfig &
typeof dynamics;

// `getPreConfig()` needs for internal using in 'config/groups/*'
// Do not use `getPreConfig()` outside of 'config/groups/*'
export const getPreConfig = (): PreConfigType => {
const BASE_PATH_ASSET = dynamics.ipfsMode
? '.'
: (serverRuntimeConfig.basePath ?? '') ||
(publicRuntimeConfig.basePath ?? '');

return {
BASE_PATH_ASSET,

...publicRuntimeConfig,

...(typeof window !== 'undefined' ? window.__env__ : dynamics),
};
};

// `preConfig` needs for external internal in 'config/groups/*'
// Not use `preConfig` outside of 'config/groups/*'
export const preConfig = getPreConfig();
60 changes: 60 additions & 0 deletions config/get-secret-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import getConfigNext from 'next/config';
import { type Modify, toBoolean } from './helpers';

const { serverRuntimeConfig } = getConfigNext();

export type SecretConfigType = Modify<
typeof serverRuntimeConfig,
{
defaultChain: number;

rpcUrls_1: [string, ...string[]];
rpcUrls_5: [string, ...string[]];
rpcUrls_17000: [string, ...string[]];

cspReportOnly: boolean;

subgraphRequestTimeout: number;

rateLimit: number;
rateLimitTimeFrame: number;
}
>;

// 'getSecretConfig()' is required for the backend side.
// We can't merge with 'getPreConfig()' because we want to split responsibility
//
// Also you can note that 'getSecretConfig' is just a proxy for 'serverRuntimeConfig'
// because we want similar approach with 'getConfig'
export const getSecretConfig = (): SecretConfigType => {
return {
...serverRuntimeConfig,

// Keep fallback as in 'env-dynamics.mjs'
defaultChain: Number(serverRuntimeConfig.defaultChain) || 17000,

// Hack: in the current implementation we can treat an empty array as a "tuple" (conditionally)
rpcUrls_1: (serverRuntimeConfig.rpcUrls_1?.split(',') ?? []) as [
string,
...string[],
],
rpcUrls_5: (serverRuntimeConfig.rpcUrls_5?.split(',') ?? []) as [
string,
...string[],
],
rpcUrls_17000: (serverRuntimeConfig.rpcUrls_17000?.split(',') ?? []) as [
string,
...string[],
],

cspReportOnly: toBoolean(serverRuntimeConfig.cspReportOnly),

subgraphRequestTimeout:
Number(serverRuntimeConfig.subgraphRequestTimeout) || 5000,

rateLimit: Number(serverRuntimeConfig.rateLimit) || 100,
rateLimitTimeFrame: Number(serverRuntimeConfig.rateLimitTimeFrame) || 60, // 1 minute;
};
};

export const secretConfig = getSecretConfig();
File renamed without changes.
8 changes: 8 additions & 0 deletions config/groups/estimate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { parseEther } from '@ethersproject/units';

// account for gas estimation
// will always have >=0.001 ether, >=0.001 stETH, >=0.001 wstETH
// on Mainnet, Goerli, Holesky
export const ESTIMATE_ACCOUNT = '0x87c0e047F4e4D3e289A56a36570D4CB957A37Ef1';

export const ESTIMATE_AMOUNT = parseEther('0.001');
2 changes: 2 additions & 0 deletions config/groups/ipfs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const IPFS_REFERRAL_ADDRESS =
'0x74d6e4Fd83A0b5623BDE3B2dF9a9A7F31fE02325';
File renamed without changes.
14 changes: 10 additions & 4 deletions config/stake.ts → config/groups/stake.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { parseEther } from '@ethersproject/units';
import { BigNumber } from 'ethers';
import dynamics from './dynamics';
import { IPFS_REFERRAL_ADDRESS } from './ipfs';
import { AddressZero } from '@ethersproject/constants';
import { parseEther } from '@ethersproject/units';

import { StakeSwapDiscountIntegrationKey } from 'features/stake/swap-discount-banner';

import { IPFS_REFERRAL_ADDRESS } from './ipfs';

// Don't use here:
// import { config } from '../get-config';
// otherwise you will get something like a cyclic error!
import { preConfig } from '../get-preconfig';

export const PRECISION = 10 ** 6;

// how much to leave out on user balance when max is pressed
Expand All @@ -20,7 +26,7 @@ export const STAKE_GASLIMIT_FALLBACK = BigNumber.from(
),
);

export const STAKE_FALLBACK_REFERRAL_ADDRESS = dynamics.ipfsMode
export const STAKE_FALLBACK_REFERRAL_ADDRESS = preConfig.ipfsMode
? IPFS_REFERRAL_ADDRESS
: AddressZero;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
import { parseEther } from '@ethersproject/units';
import { BigNumber } from 'ethers';
// account for gas estimation
// will always have >=0.001 ether, >=0.001 stETH, >=0.001 wstETH
// on Mainnet, Goerli, Holesky
export const ESTIMATE_ACCOUNT = '0x87c0e047F4e4D3e289A56a36570D4CB957A37Ef1';
export const ESTIMATE_AMOUNT = parseEther('0.001');

// fallback gas limits per 1 withdraw request
export const WITHDRAWAL_QUEUE_REQUEST_STETH_PERMIT_GAS_LIMIT_DEFAULT =
Expand Down
9 changes: 9 additions & 0 deletions config/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type Modify<T, R> = Omit<T, keyof R> & R;

export const toBoolean = (val: any) => {
return (
val?.toLowerCase?.() === 'true' ||
val === true ||
Number.parseInt(val, 10) === 1
);
};
24 changes: 4 additions & 20 deletions config/index.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,4 @@
import getConfig from 'next/config';
export const { serverRuntimeConfig } = getConfig();
export { default as dynamics } from './dynamics';
export * from './aggregator';
export * from './api';
export * from './cache';
export * from './estimate';
export * from './locale';
export * from './ipfs';
export * from './metrics';
export * from './rpc';
export * from './storage';
export * from './text';
export * from './tx';
export * from './types';
export * from './units';
export * from './metrics';
export * from './rateLimit';
export * from './stake';
export * from './matomoClickEvents';
export * from './get-config';
export * from './get-secret-config';
export * from './provider';
export * from './use-config';
Loading

0 comments on commit 9da207c

Please sign in to comment.