-
-
-
-
+
+
+
);
}
diff --git a/apps/token-e2e/.env b/apps/token-e2e/.env
index dd881f4d0..cb95c70e4 100644
--- a/apps/token-e2e/.env
+++ b/apps/token-e2e/.env
@@ -21,7 +21,6 @@ REACT_APP_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
# App configuration variables
NX_VEGA_ENV=TESTNET
NX_VEGA_URL=https://lb.testnet.vega.xyz/query
-NX_ETHEREUM_CHAIN_ID=3
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
NX_FAIRGROUND=false
diff --git a/apps/token-e2e/.env.devnet b/apps/token-e2e/.env.devnet
index 1d2f6b979..1b684b256 100644
--- a/apps/token-e2e/.env.devnet
+++ b/apps/token-e2e/.env.devnet
@@ -1,6 +1,5 @@
# App configuration variables
NX_VEGA_ENV=DEVNET
NX_VEGA_URL=https://n04.d.vega.xyz/query
-NX_ETHEREUM_CHAIN_ID=3
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
diff --git a/apps/token-e2e/.env.mainnet b/apps/token-e2e/.env.mainnet
index 92c3a7893..333947031 100644
--- a/apps/token-e2e/.env.mainnet
+++ b/apps/token-e2e/.env.mainnet
@@ -1,6 +1,5 @@
# App configuration variables
NX_VEGA_ENV=MAINNET
NX_VEGA_URL=https://api.token.vega.xyz/query
-NX_ETHEREUM_CHAIN_ID=1
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://etherscan.io
diff --git a/apps/token-e2e/.env.stagnet1 b/apps/token-e2e/.env.stagnet1
index 6a0b047bc..bc321d731 100644
--- a/apps/token-e2e/.env.stagnet1
+++ b/apps/token-e2e/.env.stagnet1
@@ -1,6 +1,5 @@
# App configuration variables
NX_VEGA_ENV=STAGNET
NX_VEGA_URL=https://n03.s.vega.xyz/query
-NX_ETHEREUM_CHAIN_ID=3
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
diff --git a/apps/token-e2e/.env.stagnet2 b/apps/token-e2e/.env.stagnet2
index 06904d1ed..813dce012 100644
--- a/apps/token-e2e/.env.stagnet2
+++ b/apps/token-e2e/.env.stagnet2
@@ -1,6 +1,5 @@
# App configuration variables
NX_VEGA_ENV=STAGNET2
NX_VEGA_URL=https://n03.stagnet2.vega.xyz/query
-NX_ETHEREUM_CHAIN_ID=3
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
diff --git a/apps/token-e2e/.env.testnet b/apps/token-e2e/.env.testnet
index 65316dde5..9da0c78ab 100644
--- a/apps/token-e2e/.env.testnet
+++ b/apps/token-e2e/.env.testnet
@@ -1,6 +1,5 @@
# App configuration variables
NX_VEGA_ENV=TESTNET
NX_VEGA_URL=https://lb.testnet.vega.xyz/query
-NX_ETHEREUM_CHAIN_ID=3
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
diff --git a/apps/token/.env b/apps/token/.env
index ee3673919..a9843234a 100644
--- a/apps/token/.env
+++ b/apps/token/.env
@@ -20,8 +20,8 @@ REACT_APP_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
# App configuration variables
NX_VEGA_ENV=TESTNET
+NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/testnet-network.json
NX_VEGA_URL=https://lb.testnet.vega.xyz/query
-NX_ETHEREUM_CHAIN_ID=3
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
NX_FAIRGROUND=false
diff --git a/apps/token/.env.devnet b/apps/token/.env.devnet
index 23f0ec1f2..7e2b6866b 100644
--- a/apps/token/.env.devnet
+++ b/apps/token/.env.devnet
@@ -1,7 +1,7 @@
# App configuration variables
NX_VEGA_ENV=DEVNET
+NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/devnet-network.json
NX_VEGA_URL=https://n04.d.vega.xyz/query
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET":"https://dev.token.vega.xyz","STAGNET2":"staging2.token.vega.xyz","TESTNET":"token.fairground.wtf","MAINNET":"token.vega.xyz"}'
-NX_ETHEREUM_CHAIN_ID=3
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
diff --git a/apps/token/.env.mainnet b/apps/token/.env.mainnet
index 910217e5c..830652c43 100644
--- a/apps/token/.env.mainnet
+++ b/apps/token/.env.mainnet
@@ -1,7 +1,7 @@
# App configuration variables
NX_VEGA_ENV=MAINNET
+NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/mainnet-network.json
NX_VEGA_URL=https://api.token.vega.xyz/query
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET":"https://dev.token.vega.xyz","STAGNET2":"staging2.token.vega.xyz","TESTNET":"token.fairground.wtf","MAINNET":"token.vega.xyz"}'
-NX_ETHEREUM_CHAIN_ID=1
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://etherscan.io
diff --git a/apps/token/.env.stagnet1 b/apps/token/.env.stagnet1
index 06c33fb72..b329754dc 100644
--- a/apps/token/.env.stagnet1
+++ b/apps/token/.env.stagnet1
@@ -1,7 +1,7 @@
# App configuration variables
NX_VEGA_ENV=STAGNET
+NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet1-network.json
NX_VEGA_URL=https://n03.s.vega.xyz/query
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET":"https://dev.token.vega.xyz","STAGNET2":"staging2.token.vega.xyz","TESTNET":"token.fairground.wtf","MAINNET":"token.vega.xyz"}'
-NX_ETHEREUM_CHAIN_ID=3
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
diff --git a/apps/token/.env.stagnet2 b/apps/token/.env.stagnet2
index c245d632b..bb04f26af 100644
--- a/apps/token/.env.stagnet2
+++ b/apps/token/.env.stagnet2
@@ -1,7 +1,7 @@
# App configuration variables
NX_VEGA_ENV=STAGNET2
+NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet2-network.json
NX_VEGA_URL=https://n03.stagnet2.vega.xyz/query
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET":"https://dev.token.vega.xyz","STAGNET2":"staging2.token.vega.xyz","TESTNET":"token.fairground.wtf","MAINNET":"token.vega.xyz"}'
-NX_ETHEREUM_CHAIN_ID=3
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
diff --git a/apps/token/.env.testnet b/apps/token/.env.testnet
index b4abf6a60..b9f668029 100644
--- a/apps/token/.env.testnet
+++ b/apps/token/.env.testnet
@@ -1,7 +1,7 @@
# App configuration variables
NX_VEGA_ENV=TESTNET
+NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/testnet-network.json
NX_VEGA_URL=https://lb.testnet.vega.xyz/query
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET":"https://dev.token.vega.xyz","STAGNET2":"staging2.token.vega.xyz","TESTNET":"token.fairground.wtf","MAINNET":"token.vega.xyz"}'
-NX_ETHEREUM_CHAIN_ID=3
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
diff --git a/apps/token/README.md b/apps/token/README.md
index ada33d46e..18d550969 100644
--- a/apps/token/README.md
+++ b/apps/token/README.md
@@ -48,13 +48,13 @@ There are a few different configuration options offered for this app:
| `NX_APP_INFURA_ID` | Infura fallback for if the user does not have a web3 compatible browser |
| `NX_APP_HOSTED_WALLET_ENABLED` | If the hosted wallet is enabled or not. If so then allow users to login using the hosted wallet |
| `NX_APP_ENV` | Change network to connect to. When set to CUSTOM use CUSTOM\_\* vars for network parameters |
-| `CUSTOM_URLS` | When NX_APP_ENV=CUSTOM use these Data Node REST URLs, optional if CUSTOM_URLS_WITH_GRAPHQL is used. |
-| `CUSTOM_URLS_WITH_GRAPHQL` | When NX_APP_ENV=CUSTOM use these Data Node GraphQL URLs, optional if CUSTOM_URLS is used. |
-| `CUSTOM_TOKEN_ADDRESS` | When NX_APP_ENV=CUSTOM specify Vega token address. |
-| `CUSTOM_CLAIM_ADDRESS` | When NX_APP_ENV=CUSTOM specify Vega claim address. |
-| `CUSTOM_LOCKED_ADDRESS` | When NX_APP_ENV=CUSTOM specify Vega locked address. |
-| `CUSTOM_VESTING_ADDRESS` | When NX_APP_ENV=CUSTOM specify Vega vesting address. |
-| `CUSTOM_STAKING_BRIDGE` | When NX_APP_ENV=CUSTOM specify Vega staking bridge address. |
+| `NX_CUSTOM_URLS` | When NX_APP_ENV=CUSTOM use these Data Node REST URLs, optional if CUSTOM_URLS_WITH_GRAPHQL is used. |
+| `NX_CUSTOM_URLS_WITH_GRAPHQL` | When NX_APP_ENV=CUSTOM use these Data Node GraphQL URLs, optional if CUSTOM_URLS is used. |
+| `NX_CUSTOM_TOKEN_ADDRESS` | When NX_APP_ENV=CUSTOM specify Vega token address. |
+| `NX_CUSTOM_CLAIM_ADDRESS` | When NX_APP_ENV=CUSTOM specify Vega claim address. |
+| `NX_CUSTOM_LOCKED_ADDRESS` | When NX_APP_ENV=CUSTOM specify Vega locked address. |
+| `NX_CUSTOM_VESTING_ADDRESS` | When NX_APP_ENV=CUSTOM specify Vega vesting address. |
+| `NX_CUSTOM_STAKING_BRIDGE` | When NX_APP_ENV=CUSTOM specify Vega staking bridge address. |
## Example configs:
diff --git a/apps/token/src/app.tsx b/apps/token/src/app.tsx
index 7baef0696..1c4da6e53 100644
--- a/apps/token/src/app.tsx
+++ b/apps/token/src/app.tsx
@@ -17,60 +17,67 @@ import { AppRouter } from './routes';
import { Web3Provider } from '@vegaprotocol/web3';
import { VegaWalletDialogs } from './components/vega-wallet-dialogs';
import { VegaWalletProvider } from '@vegaprotocol/wallet';
-import { createConnectors } from './lib/web3-connectors';
-import { ApolloProvider } from '@apollo/client';
-import { createClient } from './lib/apollo-client';
+import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
+import { useEthereumConfig } from '@vegaprotocol/web3';
import {
- EnvironmentProvider,
useEnvironment,
-} from '@vegaprotocol/network-switcher';
+ EnvironmentProvider,
+ NetworkLoader,
+} from '@vegaprotocol/environment';
+import { createClient } from './lib/apollo-client';
+import { createConnectors } from './lib/web3-connectors';
const AppContainer = () => {
const sideBar = React.useMemo(() => [
,
], []);
- const { ETHEREUM_PROVIDER_URL, ETHEREUM_CHAIN_ID, VEGA_URL } =
- useEnvironment();
- const Connectors = useMemo(
- () => createConnectors(ETHEREUM_PROVIDER_URL, ETHEREUM_CHAIN_ID),
- [ETHEREUM_CHAIN_ID, ETHEREUM_PROVIDER_URL]
- );
- const client = useMemo(() => createClient(VEGA_URL), [VEGA_URL]);
+ const { config, loading, error } = useEthereumConfig();
+ const { ETHEREUM_PROVIDER_URL } = useEnvironment();
+ const Connectors = useMemo(() => {
+ if (config?.chain_id) {
+ return createConnectors(ETHEREUM_PROVIDER_URL, Number(config.chain_id));
+ }
+ return undefined;
+ }, [config?.chain_id, ETHEREUM_PROVIDER_URL]);
return (
-
-
-
-
-
-
-
-
-
- <>
-
-
-
- >
-
-
-
-
-
-
-
-
-
+
+
+
+ {Connectors && (
+
+
+
+
+
+
+ <>
+
+
+
+ >
+
+
+
+
+
+
+ )}
+
+
+
);
};
function App() {
return (
-
+
+
+
);
}
diff --git a/apps/token/src/components/add-locked-token/add-locked-token.tsx b/apps/token/src/components/add-locked-token/add-locked-token.tsx
index a715e6bfa..771a699e9 100644
--- a/apps/token/src/components/add-locked-token/add-locked-token.tsx
+++ b/apps/token/src/components/add-locked-token/add-locked-token.tsx
@@ -1,15 +1,14 @@
import { useTranslation } from 'react-i18next';
-import { useEnvironment } from '@vegaprotocol/network-switcher';
import { useAddAssetSupported } from '../../hooks/use-add-asset-to-wallet';
import vegaVesting from '../../images/vega_vesting.png';
import { AddTokenButtonLink } from '../add-token-button/add-token-button';
import { Callout } from '@vegaprotocol/ui-toolkit';
+import { ENV } from '../../config/env';
export const AddLockedTokenAddress = () => {
const { t } = useTranslation();
const addSupported = useAddAssetSupported();
- const { ADDRESSES } = useEnvironment();
return (
{
<>
{
{t(
'The token address is {{address}}. Hit the add token button in your ERC20 wallet and enter this address.',
{
- address: ADDRESSES.lockedAddress,
+ address: ENV.addresses.lockedAddress,
}
)}
diff --git a/apps/token/src/components/transaction-callout/transaction-complete.tsx b/apps/token/src/components/transaction-callout/transaction-complete.tsx
index b859ffbb9..079ca8004 100644
--- a/apps/token/src/components/transaction-callout/transaction-complete.tsx
+++ b/apps/token/src/components/transaction-callout/transaction-complete.tsx
@@ -1,7 +1,7 @@
import { Callout, Intent } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next';
import { Link } from '@vegaprotocol/ui-toolkit';
-import { useEnvironment } from '@vegaprotocol/network-switcher';
+import { useEnvironment } from '@vegaprotocol/environment';
import type { ReactElement } from 'react';
export const TransactionComplete = ({
diff --git a/apps/token/src/components/transaction-callout/transaction-error.tsx b/apps/token/src/components/transaction-callout/transaction-error.tsx
index 2bbf4601d..393dc491c 100644
--- a/apps/token/src/components/transaction-callout/transaction-error.tsx
+++ b/apps/token/src/components/transaction-callout/transaction-error.tsx
@@ -2,7 +2,7 @@ import { Button, Callout, Intent } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next';
import { Link } from '@vegaprotocol/ui-toolkit';
-import { useEnvironment } from '@vegaprotocol/network-switcher';
+import { useEnvironment } from '@vegaprotocol/environment';
export interface TransactionErrorProps {
error: Error | null;
diff --git a/apps/token/src/components/transaction-callout/transaction-pending.tsx b/apps/token/src/components/transaction-callout/transaction-pending.tsx
index 42192a124..07fecf1ec 100644
--- a/apps/token/src/components/transaction-callout/transaction-pending.tsx
+++ b/apps/token/src/components/transaction-callout/transaction-pending.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import { Callout, Loader } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next';
import { Link } from '@vegaprotocol/ui-toolkit';
-import { useEnvironment } from '@vegaprotocol/network-switcher';
+import { useEnvironment } from '@vegaprotocol/environment';
export const TransactionPending = ({
hash,
diff --git a/apps/token/src/components/transactions-modal/transactions-modal.tsx b/apps/token/src/components/transactions-modal/transactions-modal.tsx
index 26e31a80b..066f1cffa 100644
--- a/apps/token/src/components/transactions-modal/transactions-modal.tsx
+++ b/apps/token/src/components/transactions-modal/transactions-modal.tsx
@@ -1,5 +1,5 @@
import { Dialog, Link } from '@vegaprotocol/ui-toolkit';
-import { useEnvironment } from '@vegaprotocol/network-switcher';
+import { useEnvironment } from '@vegaprotocol/environment';
import React from 'react';
import { useTranslation } from 'react-i18next';
diff --git a/apps/token/src/components/web3-connector/web3-connector.tsx b/apps/token/src/components/web3-connector/web3-connector.tsx
index dd5d6e2ea..a0aa80562 100644
--- a/apps/token/src/components/web3-connector/web3-connector.tsx
+++ b/apps/token/src/components/web3-connector/web3-connector.tsx
@@ -1,5 +1,6 @@
-import { useEnvironment } from '@vegaprotocol/network-switcher';
-import { Button, Splash } from '@vegaprotocol/ui-toolkit';
+import { useEnvironment } from '@vegaprotocol/environment';
+import { useEthereumConfig } from '@vegaprotocol/web3';
+import { Button, Splash, AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { Web3ConnectDialog } from '@vegaprotocol/web3';
import { useWeb3React } from '@web3-react/core';
import type { ReactElement } from 'react';
@@ -16,30 +17,35 @@ interface Web3ConnectorProps {
export function Web3Connector({ children }: Web3ConnectorProps) {
const { appState, appDispatch } = useAppState();
- const { ETHEREUM_PROVIDER_URL, ETHEREUM_CHAIN_ID } = useEnvironment();
- const Connectors = useMemo(
- () => createConnectors(ETHEREUM_PROVIDER_URL, ETHEREUM_CHAIN_ID),
- [ETHEREUM_CHAIN_ID, ETHEREUM_PROVIDER_URL]
- );
+ const { ETHEREUM_PROVIDER_URL } = useEnvironment();
+ const { config, loading, error } = useEthereumConfig();
+ const Connectors = useMemo(() => {
+ if (config?.chain_id) {
+ return createConnectors(ETHEREUM_PROVIDER_URL, Number(config.chain_id));
+ }
+ return undefined;
+ }, [config?.chain_id, ETHEREUM_PROVIDER_URL]);
const setDialogOpen = useCallback(
(isOpen: boolean) => {
appDispatch({ type: AppStateActionType.SET_ETH_WALLET_OVERLAY, isOpen });
},
[appDispatch]
);
- const appChainId = Number(ETHEREUM_CHAIN_ID);
+ const appChainId = Number(config?.chain_id);
return (
- <>
+
{children}
-
- >
+ {Connectors && (
+
+ )}
+
);
}
@@ -49,11 +55,7 @@ interface Web3ContentProps {
setDialogOpen: (isOpen: boolean) => void;
}
-export const Web3Content = ({
- children,
- appChainId,
- setDialogOpen,
-}: Web3ContentProps) => {
+export const Web3Content = ({ children, appChainId }: Web3ContentProps) => {
const { error, connector, chainId } = useWeb3React();
useEffect(() => {
diff --git a/apps/token/src/config/env.ts b/apps/token/src/config/env.ts
index fc335b544..48df11587 100644
--- a/apps/token/src/config/env.ts
+++ b/apps/token/src/config/env.ts
@@ -1,3 +1,5 @@
+import type { Networks } from '@vegaprotocol/environment';
+
const windowOrDefault = (key: string) => {
if (window._env_ && window._env_[key]) {
return window._env_[key];
@@ -7,10 +9,49 @@ const windowOrDefault = (key: string) => {
const TRUTHY = ['1', 'true'];
+interface VegaContracts {
+ claimAddress: string;
+ lockedAddress: string;
+}
+
+const customClaimAddress = process.env['NX_CUSTOM_CLAIM_ADDRESS'] as string;
+const customLockedAddress = process.env['NX_CUSTOM_LOCKED_ADDRESS'] as string;
+
+export const ContractAddresses: {
+ [key in Networks | 'CUSTOM']: VegaContracts;
+} = {
+ CUSTOM: {
+ claimAddress: customClaimAddress ?? '0x0',
+ lockedAddress: customLockedAddress ?? '0x0',
+ },
+ DEVNET: {
+ claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994',
+ lockedAddress: '0x0',
+ },
+ STAGNET: {
+ claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error
+ lockedAddress: '0x0', // TODO not deployed to this env
+ },
+ STAGNET2: {
+ claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error
+ lockedAddress: '0x0', // TODO not deployed to this env
+ },
+ TESTNET: {
+ claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error
+ lockedAddress: '0x0', // TODO not deployed to this env
+ },
+ MAINNET: {
+ claimAddress: '0x0ee1fb382caf98e86e97e51f9f42f8b4654020f3',
+ lockedAddress: '0x78344c7305d73a7a0ac3c94cd9960f4449a1814e',
+ },
+};
+
+const envName = windowOrDefault('NX_VEGA_ENV') ?? 'local';
+
export const ENV = {
// Environment
dsn: windowOrDefault('NX_SENTRY_DSN'),
- envName: windowOrDefault('NX_VEGA_ENV'),
+ envName,
commit: windowOrDefault('NX_COMMIT_REF'),
branch: windowOrDefault('NX_BRANCH'),
vegaUrl: windowOrDefault('NX_VEGA_URL'),
@@ -26,4 +67,6 @@ export const ENV = {
process.env['NX_IS_NEW_BRIDGE_CONTRACT'] as string
),
},
+ addresses:
+ ContractAddresses[(envName === 'local' ? 'CUSTOM' : envName) as Networks],
};
diff --git a/apps/token/src/config/index.ts b/apps/token/src/config/index.ts
index 9df05ee55..095810f4f 100644
--- a/apps/token/src/config/index.ts
+++ b/apps/token/src/config/index.ts
@@ -1,4 +1,3 @@
export * from './flags';
export * from './links';
export * from './network-params';
-export * from './vega';
diff --git a/apps/token/src/config/vega.ts b/apps/token/src/config/vega.ts
deleted file mode 100644
index 29752076f..000000000
--- a/apps/token/src/config/vega.ts
+++ /dev/null
@@ -1,176 +0,0 @@
-import { Networks } from '@vegaprotocol/react-helpers';
-
-interface VegaNode {
- url: string;
- api: {
- GraphQL: boolean;
- };
-}
-
-type VegaNets = {
- [N in Networks]: {
- nodes: VegaNode[];
- };
-};
-
-export type NetworkConfig = {
- [N in Networks]: string[];
-};
-
-export const VegaNetworks: VegaNets = {
- [Networks.DEVNET]: {
- nodes: [
- {
- url: 'https://n01.d.vega.xyz',
- api: {
- GraphQL: false,
- },
- },
- {
- url: 'https://n02.d.vega.xyz',
- api: {
- GraphQL: false,
- },
- },
- {
- url: 'https://n03.d.vega.xyz',
- api: {
- GraphQL: false,
- },
- },
- {
- url: 'https://n04.d.vega.xyz',
- api: {
- GraphQL: true,
- },
- },
- ],
- },
- [Networks.STAGNET]: {
- nodes: [
- {
- url: 'https://n01.s.vega.xyz',
- api: {
- GraphQL: false,
- },
- },
- {
- url: 'https://n02.s.vega.xyz',
- api: {
- GraphQL: false,
- },
- },
- {
- url: 'https://n03.s.vega.xyz',
- api: {
- GraphQL: true,
- },
- },
- {
- url: 'https://n04.s.vega.xyz',
- api: {
- GraphQL: false,
- },
- },
- {
- url: 'https://n05.s.vega.xyz',
- api: {
- GraphQL: false,
- },
- },
- ],
- },
- [Networks.STAGNET2]: {
- nodes: [
- {
- url: 'https://n03.stagnet2.vega.xyz',
- api: {
- GraphQL: true,
- },
- },
- ],
- },
- [Networks.TESTNET]: {
- nodes: [
- {
- url: 'https://lb.testnet.vega.xyz',
- api: {
- GraphQL: true,
- },
- },
- {
- url: 'https://n01.testnet.vega.xyz',
- api: {
- GraphQL: false,
- },
- },
- {
- url: 'https://n02.testnet.vega.xyz',
- api: {
- GraphQL: false,
- },
- },
- {
- url: 'https://n03.testnet.vega.xyz',
- api: {
- GraphQL: false,
- },
- },
- {
- url: 'https://n04.testnet.vega.xyz',
- api: {
- GraphQL: false,
- },
- },
- {
- url: 'https://n05.testnet.vega.xyz',
- api: {
- GraphQL: false,
- },
- },
- {
- url: 'https://n06.testnet.vega.xyz',
- api: {
- GraphQL: true,
- },
- },
- {
- url: 'https://n07.testnet.vega.xyz',
- api: {
- GraphQL: true,
- },
- },
- {
- url: 'https://n08.testnet.vega.xyz',
- api: {
- GraphQL: true,
- },
- },
- {
- url: 'https://n09.testnet.vega.xyz',
- api: {
- GraphQL: true,
- },
- },
- ],
- },
- [Networks.MAINNET]: {
- nodes: [],
- },
-};
-
-export const GraphQLNodes = Object.keys(VegaNetworks).reduce(
- (obj: Record, network) => {
- const rawNodes: VegaNode[] = VegaNetworks[network as Networks].nodes;
- const nodesWithGraphQL = rawNodes
- .filter((n) => n.api.GraphQL)
- .map((n) => n.url);
- obj[network] = nodesWithGraphQL;
- return obj;
- },
- {}
-);
-
-export const EnvironmentNodes = GraphQLNodes[
- process.env['NX_VEGA_ENV'] as Networks
-] as string[];
diff --git a/apps/token/src/contexts/contracts/contracts-provider.tsx b/apps/token/src/contexts/contracts/contracts-provider.tsx
index d99d416fc..c3896dc3c 100644
--- a/apps/token/src/contexts/contracts/contracts-provider.tsx
+++ b/apps/token/src/contexts/contracts/contracts-provider.tsx
@@ -6,14 +6,15 @@ import {
} from '@vegaprotocol/smart-contracts';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useWeb3React } from '@web3-react/core';
-import React, { useMemo } from 'react';
+import React from 'react';
import { SplashLoader } from '../../components/splash-loader';
import type { ContractsContextShape } from './contracts-context';
import { ContractsContext } from './contracts-context';
import { createDefaultProvider } from '../../lib/web3-connectors';
import { useEthereumConfig } from '@vegaprotocol/web3';
-import { useEnvironment } from '@vegaprotocol/network-switcher';
+import { useEnvironment } from '@vegaprotocol/environment';
+import { ENV } from '../../config/env';
/**
* Provides Vega Ethereum contract instances to its children.
@@ -21,14 +22,9 @@ import { useEnvironment } from '@vegaprotocol/network-switcher';
export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
const { provider: activeProvider, account } = useWeb3React();
const { config } = useEthereumConfig();
- const { VEGA_ENV, ADDRESSES, ETHEREUM_PROVIDER_URL, ETHEREUM_CHAIN_ID } =
- useEnvironment();
+ const { VEGA_ENV, ETHEREUM_PROVIDER_URL } = useEnvironment();
const [contracts, setContracts] =
React.useState(null);
- const defaultProvider = useMemo(
- () => createDefaultProvider(ETHEREUM_PROVIDER_URL, ETHEREUM_CHAIN_ID),
- [ETHEREUM_PROVIDER_URL, ETHEREUM_CHAIN_ID]
- );
// Create instances of contract classes. If we have an account use a signer for the
// contracts so that we can sign transactions, otherwise use the provider for just
@@ -37,39 +33,46 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
const run = async () => {
let signer = null;
- const provider = activeProvider ? activeProvider : defaultProvider;
-
- if (
- account &&
- activeProvider &&
- typeof activeProvider.getSigner === 'function'
- ) {
- signer = provider.getSigner();
- }
-
- if (provider && config) {
- const staking = new StakingBridge(
- config.staking_bridge_contract.address,
- signer || provider
+ if (config) {
+ const defaultProvider = createDefaultProvider(
+ ETHEREUM_PROVIDER_URL,
+ Number(config.chain_id)
);
- const vegaAddress = await staking.stakingToken();
- setContracts({
- token: new Token(vegaAddress, signer || provider),
- staking: new StakingBridge(
+ const provider = activeProvider ? activeProvider : defaultProvider;
+
+ if (
+ account &&
+ activeProvider &&
+ typeof activeProvider.getSigner === 'function'
+ ) {
+ signer = provider.getSigner();
+ }
+
+ if (provider && config) {
+ const staking = new StakingBridge(
config.staking_bridge_contract.address,
signer || provider
- ),
- vesting: new TokenVesting(
- config.token_vesting_contract.address,
- signer || provider
- ),
- claim: new Claim(ADDRESSES.claimAddress, signer || provider),
- });
+ );
+ const vegaAddress = await staking.stakingToken();
+
+ setContracts({
+ token: new Token(vegaAddress, signer || provider),
+ staking: new StakingBridge(
+ config.staking_bridge_contract.address,
+ signer || provider
+ ),
+ vesting: new TokenVesting(
+ config.token_vesting_contract.address,
+ signer || provider
+ ),
+ claim: new Claim(ENV.addresses.claimAddress, signer || provider),
+ });
+ }
}
};
run();
- }, [activeProvider, account, config, ADDRESSES, VEGA_ENV, defaultProvider]);
+ }, [activeProvider, account, config, VEGA_ENV, ETHEREUM_PROVIDER_URL]);
if (!contracts) {
return (
diff --git a/apps/token/src/hooks/use-add-asset-to-wallet.ts b/apps/token/src/hooks/use-add-asset-to-wallet.ts
index 636439505..efffe0de9 100644
--- a/apps/token/src/hooks/use-add-asset-to-wallet.ts
+++ b/apps/token/src/hooks/use-add-asset-to-wallet.ts
@@ -1,9 +1,9 @@
import React from 'react';
import * as Sentry from '@sentry/react';
-import { Networks } from '@vegaprotocol/react-helpers';
+import { Networks } from '@vegaprotocol/environment';
import { useWeb3React } from '@web3-react/core';
import { MetaMask } from '@web3-react/metamask';
-import { useEnvironment } from '@vegaprotocol/network-switcher';
+import { useEnvironment } from '@vegaprotocol/environment';
export const useAddAssetSupported = () => {
const { connector } = useWeb3React();
diff --git a/apps/token/src/hooks/use-tranches.ts b/apps/token/src/hooks/use-tranches.ts
index d95268d6b..da9b94d42 100644
--- a/apps/token/src/hooks/use-tranches.ts
+++ b/apps/token/src/hooks/use-tranches.ts
@@ -1,8 +1,8 @@
-import type { Networks } from '@vegaprotocol/react-helpers';
import { useFetch } from '@vegaprotocol/react-helpers';
import type { Tranche } from '@vegaprotocol/smart-contracts';
import React, { useEffect } from 'react';
-import { useEnvironment } from '@vegaprotocol/network-switcher';
+import type { Networks } from '@vegaprotocol/environment';
+import { useEnvironment } from '@vegaprotocol/environment';
import { BigNumber } from '../lib/bignumber';
@@ -12,6 +12,7 @@ const TRANCHES_URLS: { [N in Networks]: string } = {
STAGNET: 'https://static.vega.xyz/assets/stagnet1-tranches.json',
STAGNET2: 'https://static.vega.xyz/assets/stagnet2-tranches.json',
DEVNET: 'https://static.vega.xyz/assets/devnet-tranches.json',
+ CUSTOM: 'https://static.vega.xyz/assets/testnet-tranches.json',
};
export function useTranches() {
diff --git a/apps/token/src/routes/claim/complete.tsx b/apps/token/src/routes/claim/complete.tsx
index 8c2eb2c27..41d60e7e9 100644
--- a/apps/token/src/routes/claim/complete.tsx
+++ b/apps/token/src/routes/claim/complete.tsx
@@ -1,5 +1,5 @@
import { Callout, Intent, Link, Button } from '@vegaprotocol/ui-toolkit';
-import { useEnvironment } from '@vegaprotocol/network-switcher';
+import { useEnvironment } from '@vegaprotocol/environment';
import { Trans, useTranslation } from 'react-i18next';
import { Link as RouteLink } from 'react-router-dom';
diff --git a/apps/token/src/routes/contracts/index.tsx b/apps/token/src/routes/contracts/index.tsx
index 7acad9dcb..81cd33dc2 100644
--- a/apps/token/src/routes/contracts/index.tsx
+++ b/apps/token/src/routes/contracts/index.tsx
@@ -2,13 +2,14 @@ import { t } from '@vegaprotocol/react-helpers';
import { Link, Splash } from '@vegaprotocol/ui-toolkit';
import type { EthereumConfig } from '@vegaprotocol/web3';
import { useEthereumConfig } from '@vegaprotocol/web3';
-import { useEnvironment } from '@vegaprotocol/network-switcher';
+import { useEnvironment } from '@vegaprotocol/environment';
import { Heading } from '../../components/heading';
import { SplashLoader } from '../../components/splash-loader';
+import { ENV } from '../../config/env';
const Contracts = () => {
const { config } = useEthereumConfig();
- const { ADDRESSES, ETHERSCAN_URL } = useEnvironment();
+ const { ETHERSCAN_URL } = useEnvironment();
if (!config) {
return (
@@ -47,7 +48,7 @@ const Contracts = () => {
);
})}
- {Object.entries(ADDRESSES).map(([key, value]) => (
+ {Object.entries(ENV.addresses).map(([key, value]) => (
{children}>;
+ return
{children} ;
}
diff --git a/apps/trading/components/web3-container/web3-container.spec.tsx b/apps/trading/components/web3-container/web3-container.spec.tsx
index 438899e96..ca3b08a7a 100644
--- a/apps/trading/components/web3-container/web3-container.spec.tsx
+++ b/apps/trading/components/web3-container/web3-container.spec.tsx
@@ -5,7 +5,7 @@ import { Web3Container } from './web3-container';
import type { useWeb3React } from '@web3-react/core';
import type { NetworkParamsQuery } from '@vegaprotocol/web3';
import { NETWORK_PARAMS_QUERY } from '@vegaprotocol/web3';
-import { EnvironmentProvider } from '@vegaprotocol/network-switcher';
+import { EnvironmentProvider } from '@vegaprotocol/environment';
const defaultHookValue = {
isActive: false,
diff --git a/apps/trading/components/web3-container/web3-container.tsx b/apps/trading/components/web3-container/web3-container.tsx
index 783b93b5c..e0da31b5a 100644
--- a/apps/trading/components/web3-container/web3-container.tsx
+++ b/apps/trading/components/web3-container/web3-container.tsx
@@ -8,7 +8,7 @@ import { useWeb3React } from '@web3-react/core';
import type { ReactNode } from 'react';
import { useEffect, useState, useMemo } from 'react';
import { t } from '@vegaprotocol/react-helpers';
-import { useEnvironment } from '@vegaprotocol/network-switcher';
+import { useEnvironment } from '@vegaprotocol/environment';
import { createConnectors } from '../../lib/web3-connectors';
interface Web3ContainerProps {
@@ -18,14 +18,15 @@ interface Web3ContainerProps {
export const Web3Container = ({ children }: Web3ContainerProps) => {
const [dialogOpen, setDialogOpen] = useState(false);
const { config, loading, error } = useEthereumConfig();
- const { ETHEREUM_PROVIDER_URL, ETHEREUM_CHAIN_ID } = useEnvironment();
- const Connectors = useMemo(
- () => createConnectors(ETHEREUM_PROVIDER_URL, ETHEREUM_CHAIN_ID),
- [ETHEREUM_CHAIN_ID, ETHEREUM_PROVIDER_URL]
- );
+ const { ETHEREUM_PROVIDER_URL } = useEnvironment();
+ const Connectors = useMemo(() => {
+ if (config?.chain_id) {
+ return createConnectors(ETHEREUM_PROVIDER_URL, Number(config?.chain_id));
+ }
+ }, [config?.chain_id, ETHEREUM_PROVIDER_URL]);
return (
- {config ? (
+ {Connectors && config && (
{
desiredChainId={Number(config.chain_id)}
/>
- ) : null}
+ )}
);
};
diff --git a/apps/trading/pages/_app.page.tsx b/apps/trading/pages/_app.page.tsx
index 45d4a20af..38aaf4014 100644
--- a/apps/trading/pages/_app.page.tsx
+++ b/apps/trading/pages/_app.page.tsx
@@ -1,5 +1,6 @@
import type { AppProps } from 'next/app';
import Head from 'next/head';
+import { useRouter } from 'next/router';
import { Navbar } from '../components/navbar';
import { t, ThemeContext, useThemeSwitcher } from '@vegaprotocol/react-helpers';
import {
@@ -7,29 +8,27 @@ import {
VegaManageDialog,
VegaWalletProvider,
} from '@vegaprotocol/wallet';
-import { NetworkSwitcherDialog } from '@vegaprotocol/network-switcher';
+import {
+ useEnvironment,
+ EnvironmentProvider,
+ NetworkSwitcherDialog,
+} from '@vegaprotocol/environment';
import { Connectors } from '../lib/vega-connectors';
-import { useMemo } from 'react';
-import { createClient } from '../lib/apollo-client';
import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
-import { ApolloProvider } from '@apollo/client';
import { AppLoader } from '../components/app-loader';
import { VegaWalletConnectButton } from '../components/vega-wallet-connect-button';
import './styles.css';
import { useGlobalStore } from '../stores';
-import { ENV } from '../lib/config/env';
-import { EnvironmentProvider } from '@vegaprotocol/network-switcher';
-import { useEnvironment } from '@vegaprotocol/network-switcher';
function AppBody({ Component, pageProps }: AppProps) {
+ const { push } = useRouter();
const store = useGlobalStore();
const { VEGA_NETWORKS } = useEnvironment();
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const [theme, toggleTheme] = useThemeSwitcher();
+ const [, toggleTheme] = useThemeSwitcher();
return (
-
-
+
+
@@ -62,54 +61,46 @@ function AppBody({ Component, pageProps }: AppProps) {
setDialogOpen={(open) => store.setVegaNetworkSwitcherDialog(open)}
onConnect={({ network }) => {
if (VEGA_NETWORKS[network]) {
- window.location.href = VEGA_NETWORKS[network];
+ push(VEGA_NETWORKS[network] ?? '');
}
}}
/>
-
-
+
+
);
}
function VegaTradingApp(props: AppProps) {
const [theme] = useThemeSwitcher();
- const client = useMemo(() => createClient(ENV.vegaUrl), []);
return (
-
-
-
-
-
- {t('Welcome to Vega trading!')}
-
-
- {['1', 'true'].includes(
- process.env['NX_USE_ENV_OVERRIDES'] || ''
- ) ? (
- /* eslint-disable-next-line @next/next/no-sync-scripts */
-
- ) : null}
-
-
-
-
-
+
+
+
+ {t('Welcome to Vega trading!')}
+
+
+ {['1', 'true'].includes(
+ process.env['NX_USE_ENV_OVERRIDES'] || ''
+ ) ? (
+ /* eslint-disable-next-line @next/next/no-sync-scripts */
+
+ ) : null}
+
+
+
);
diff --git a/apps/trading/pages/portfolio/deposit/deposit-container.tsx b/apps/trading/pages/portfolio/deposit/deposit-container.tsx
index a4aaa68db..98f34c2e9 100644
--- a/apps/trading/pages/portfolio/deposit/deposit-container.tsx
+++ b/apps/trading/pages/portfolio/deposit/deposit-container.tsx
@@ -3,7 +3,7 @@ import { PageQueryContainer } from '../../../components/page-query-container';
import type { DepositPage } from './__generated__/DepositPage';
import { DepositManager } from '@vegaprotocol/deposits';
import { t } from '@vegaprotocol/react-helpers';
-import { useEnvironment } from '@vegaprotocol/network-switcher';
+import { useEnvironment } from '@vegaprotocol/environment';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { ASSET_FRAGMENT } from '../../../lib/query-fragments';
diff --git a/libs/network-switcher/.babelrc b/libs/environment/.babelrc
similarity index 100%
rename from libs/network-switcher/.babelrc
rename to libs/environment/.babelrc
diff --git a/libs/network-switcher/.eslintrc.json b/libs/environment/.eslintrc.json
similarity index 100%
rename from libs/network-switcher/.eslintrc.json
rename to libs/environment/.eslintrc.json
diff --git a/libs/environment/README.md b/libs/environment/README.md
new file mode 100644
index 000000000..32ad9c42f
--- /dev/null
+++ b/libs/environment/README.md
@@ -0,0 +1,19 @@
+# environment
+
+This library was generated with [Nx](https://nx.dev).
+
+## Prerequisites
+
+The environment variables needed to be present for any app consuming this library.
+
+`NX_VEGA_ENV` is the name of the environment.
+
+`NX_VEGA_REST` is the REST endpoint for the environment.
+
+`NX_VEGA_URL` OR `NX_VEGA_CONFIG_URL` - either the network configuration url or a url to a node to directly connect to
+
+For examples, see Block Explorer's .env files [here](../../apps/explorer)
+
+## Running unit tests
+
+Run `nx test environment` to execute the unit tests via [Jest](https://jestjs.io).
diff --git a/libs/network-switcher/jest.config.js b/libs/environment/jest.config.js
similarity index 63%
rename from libs/network-switcher/jest.config.js
rename to libs/environment/jest.config.js
index b3eaa4301..5894ddbc0 100644
--- a/libs/network-switcher/jest.config.js
+++ b/libs/environment/jest.config.js
@@ -1,9 +1,9 @@
module.exports = {
- displayName: 'network-switcher',
+ displayName: 'environment',
preset: '../../jest.preset.js',
transform: {
'^.+\\.[tj]sx?$': 'babel-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
- coverageDirectory: '../../coverage/libs/network-switcher',
+ coverageDirectory: '../../coverage/libs/environment',
};
diff --git a/libs/environment/package.json b/libs/environment/package.json
new file mode 100644
index 000000000..a7463c152
--- /dev/null
+++ b/libs/environment/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "@vegaprotocol/environment",
+ "version": "0.0.1"
+}
diff --git a/libs/network-switcher/project.json b/libs/environment/project.json
similarity index 55%
rename from libs/network-switcher/project.json
rename to libs/environment/project.json
index 6047531b7..eb0d2d34a 100644
--- a/libs/network-switcher/project.json
+++ b/libs/environment/project.json
@@ -1,6 +1,6 @@
{
- "root": "libs/network-switcher",
- "sourceRoot": "libs/network-switcher/src",
+ "root": "libs/environment",
+ "sourceRoot": "libs/environment/src",
"projectType": "library",
"tags": [],
"targets": {
@@ -8,16 +8,16 @@
"executor": "@nrwl/web:rollup",
"outputs": ["{options.outputPath}"],
"options": {
- "outputPath": "dist/libs/network-switcher",
- "tsConfig": "libs/network-switcher/tsconfig.lib.json",
- "project": "libs/network-switcher/package.json",
- "entryFile": "libs/network-switcher/src/index.ts",
+ "outputPath": "dist/libs/environment",
+ "tsConfig": "libs/environment/tsconfig.lib.json",
+ "project": "libs/environment/package.json",
+ "entryFile": "libs/environment/src/index.ts",
"external": ["react/jsx-runtime"],
"rollupConfig": "@nrwl/react/plugins/bundle-rollup",
"compiler": "babel",
"assets": [
{
- "glob": "libs/network-switcher/README.md",
+ "glob": "libs/environment/README.md",
"input": ".",
"output": "."
}
@@ -28,14 +28,14 @@
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
- "lintFilePatterns": ["libs/network-switcher/**/*.{ts,tsx,js,jsx}"]
+ "lintFilePatterns": ["libs/environment/**/*.{ts,tsx,js,jsx}"]
}
},
"test": {
"executor": "@nrwl/jest:jest",
- "outputs": ["coverage/libs/network-switcher"],
+ "outputs": ["coverage/libs/environment"],
"options": {
- "jestConfig": "libs/network-switcher/jest.config.js",
+ "jestConfig": "libs/environment/jest.config.js",
"passWithNoTests": true
}
}
diff --git a/libs/network-switcher/src/components/index.ts b/libs/environment/src/components/index.ts
similarity index 69%
rename from libs/network-switcher/src/components/index.ts
rename to libs/environment/src/components/index.ts
index a62c7028f..ec29fafd4 100644
--- a/libs/network-switcher/src/components/index.ts
+++ b/libs/environment/src/components/index.ts
@@ -1,2 +1,3 @@
+export * from './network-loader';
export * from './network-switcher';
export * from './network-switcher-dialog';
diff --git a/libs/environment/src/components/network-loader/index.ts b/libs/environment/src/components/network-loader/index.ts
new file mode 100644
index 000000000..32e02ef13
--- /dev/null
+++ b/libs/environment/src/components/network-loader/index.ts
@@ -0,0 +1 @@
+export * from './network-loader';
diff --git a/libs/environment/src/components/network-loader/network-loader.tsx b/libs/environment/src/components/network-loader/network-loader.tsx
new file mode 100644
index 000000000..304f6a90e
--- /dev/null
+++ b/libs/environment/src/components/network-loader/network-loader.tsx
@@ -0,0 +1,162 @@
+import { useState, useEffect, useMemo } from 'react';
+import type { ReactNode } from 'react';
+import type { ApolloClient } from '@apollo/client';
+import { ApolloProvider } from '@apollo/client';
+import {
+ Callout,
+ Intent,
+ Button,
+ Icon,
+ Loader,
+} from '@vegaprotocol/ui-toolkit';
+import { t } from '@vegaprotocol/react-helpers';
+import { useEnvironment } from '../../hooks';
+import type { ConfigStatus } from '../../types';
+
+type MessageComponentProps = {
+ children: ReactNode;
+};
+
+const StatusMessage = ({ children }: MessageComponentProps) => (
+
+ {children}
+
+);
+
+type ErrorComponentProps = MessageComponentProps & {
+ children?: ReactNode;
+ showTryAgain?: boolean;
+};
+
+const Error = ({ children, showTryAgain }: ErrorComponentProps) => (
+
+
{children}
+ {showTryAgain && (
+
window.location.reload()}
+ >
+ {t('Try again')}
+
+ )}
+
+);
+
+type StatusComponentProps = {
+ status: ConfigStatus;
+ children?: ReactNode;
+};
+
+const StatusComponent = ({ status, children }: StatusComponentProps) => {
+ switch (status) {
+ case 'error-loading-config':
+ return (
+
+ {t('There was an error fetching the network configuration.')}
+
+ }
+ />
+ );
+ case 'error-validating-config':
+ return (
+
+ {t('The network configuration for the app is invalid.')}
+
+ }
+ />
+ );
+ case 'error-loading-node':
+ return (
+ {t('Failed to connect to a data node.')}
+ }
+ />
+ );
+ case 'idle':
+ case 'loading-config':
+ return (
+ <>
+ {children}
+
+
+ {t('Loading configuration...')}
+
+ >
+ );
+ case 'loading-node':
+ return (
+ <>
+ {children}
+
+
+ {t('Finding a node...')}
+
+ >
+ );
+ case 'success':
+ return (
+ <>
+ {children}
+
+
+ {t("You're connected!")}
+
+ >
+ );
+ }
+};
+
+type NetworkLoaderProps = {
+ children?: ReactNode;
+ skeleton?: ReactNode;
+ createClient: (url: string) => ApolloClient;
+};
+
+export function NetworkLoader({
+ skeleton,
+ children,
+ createClient,
+}: NetworkLoaderProps) {
+ // this is to prevent an error rendering callouts on the server side
+ const [canShowCallout, setShowCallout] = useState(false);
+ const { configStatus, VEGA_URL } = useEnvironment();
+
+ const client = useMemo(() => {
+ if (VEGA_URL) {
+ return createClient(VEGA_URL);
+ }
+ return undefined;
+ }, [VEGA_URL, createClient]);
+
+ useEffect(() => {
+ setShowCallout(true);
+ }, []);
+
+ if (!client) {
+ return canShowCallout ? (
+
+ {skeleton}
+
+ ) : null;
+ }
+
+ return {children} ;
+}
diff --git a/libs/network-switcher/src/components/network-switcher-dialog/index.tsx b/libs/environment/src/components/network-switcher-dialog/index.tsx
similarity index 100%
rename from libs/network-switcher/src/components/network-switcher-dialog/index.tsx
rename to libs/environment/src/components/network-switcher-dialog/index.tsx
diff --git a/libs/network-switcher/src/components/network-switcher-dialog/network-switcher-dialog.tsx b/libs/environment/src/components/network-switcher-dialog/network-switcher-dialog.tsx
similarity index 100%
rename from libs/network-switcher/src/components/network-switcher-dialog/network-switcher-dialog.tsx
rename to libs/environment/src/components/network-switcher-dialog/network-switcher-dialog.tsx
diff --git a/libs/network-switcher/src/components/network-switcher/index.tsx b/libs/environment/src/components/network-switcher/index.tsx
similarity index 100%
rename from libs/network-switcher/src/components/network-switcher/index.tsx
rename to libs/environment/src/components/network-switcher/index.tsx
diff --git a/libs/network-switcher/src/components/network-switcher/network-switcher.tsx b/libs/environment/src/components/network-switcher/network-switcher.tsx
similarity index 95%
rename from libs/network-switcher/src/components/network-switcher/network-switcher.tsx
rename to libs/environment/src/components/network-switcher/network-switcher.tsx
index cff092bef..e87415c27 100644
--- a/libs/network-switcher/src/components/network-switcher/network-switcher.tsx
+++ b/libs/environment/src/components/network-switcher/network-switcher.tsx
@@ -1,8 +1,8 @@
import { useForm, Controller } from 'react-hook-form';
import { Button, Select } from '@vegaprotocol/ui-toolkit';
-import type { Networks } from '@vegaprotocol/react-helpers';
import { t } from '@vegaprotocol/react-helpers';
import { useEnvironment } from '../../hooks';
+import type { Networks } from '../../types';
type NetworkState = {
network: Networks;
diff --git a/libs/network-switcher/src/hooks/index.ts b/libs/environment/src/hooks/index.ts
similarity index 100%
rename from libs/network-switcher/src/hooks/index.ts
rename to libs/environment/src/hooks/index.ts
diff --git a/libs/environment/src/hooks/use-config.spec.tsx b/libs/environment/src/hooks/use-config.spec.tsx
new file mode 100644
index 000000000..ed9a54d0c
--- /dev/null
+++ b/libs/environment/src/hooks/use-config.spec.tsx
@@ -0,0 +1,311 @@
+import { renderHook } from '@testing-library/react-hooks';
+import type { EnvironmentWithOptionalUrl } from './use-config';
+import { useConfig, LOCAL_STORAGE_NETWORK_KEY } from './use-config';
+import { Networks } from '../types';
+
+type HostMapping = Record;
+
+const mockHostsMap: HostMapping = {
+ 'https://host1.com': 300,
+ 'https://host2.com': 500,
+ 'https://host3.com': 100,
+ 'https://host4.com': 650,
+};
+
+const hostList = Object.keys(mockHostsMap);
+
+const mockEnvironment: EnvironmentWithOptionalUrl = {
+ VEGA_ENV: Networks.TESTNET,
+ VEGA_CONFIG_URL: 'https://vega.url/config.json',
+ VEGA_NETWORKS: {},
+ ETHEREUM_PROVIDER_URL: 'https://ethereum.provider',
+ ETHERSCAN_URL: 'https://etherscan.url',
+};
+
+function setupFetch(configUrl: string, hostMap: HostMapping) {
+ const hostUrls = Object.keys(hostMap);
+ return (url: RequestInfo) => {
+ if (url === configUrl) {
+ return Promise.resolve({
+ ok: true,
+ json: () => Promise.resolve({ hosts: hostUrls }),
+ } as Response);
+ }
+
+ if (hostUrls.includes(url as string)) {
+ const value = hostMap[url as string];
+ return new Promise((resolve, reject) => {
+ if (typeof value === 'number') {
+ setTimeout(() => {
+ resolve({ ok: true } as Response);
+ }, value);
+ } else {
+ reject(value);
+ }
+ });
+ }
+
+ return Promise.resolve({
+ ok: true,
+ } as Response);
+ };
+}
+
+// eslint-disable-next-line @typescript-eslint/no-empty-function
+const noop = () => {};
+
+global.fetch = jest.fn();
+
+const mockUpdate = jest.fn();
+
+beforeEach(() => {
+ jest.useFakeTimers();
+ mockUpdate.mockClear();
+ window.localStorage.clear();
+
+ // @ts-ignore typescript doesn't recognise the mocked instance
+ global.fetch.mockReset();
+ // @ts-ignore typescript doesn't recognise the mocked instance
+ global.fetch.mockImplementation(
+ setupFetch(mockEnvironment.VEGA_CONFIG_URL ?? '', mockHostsMap)
+ );
+});
+
+afterAll(() => {
+ // @ts-ignore: typescript doesn't recognise the mocked fetch instance
+ fetch.mockRestore();
+});
+
+describe('useConfig hook', () => {
+ it('has an initial success state when the environment already has a URL', async () => {
+ const mockEnvWithUrl = {
+ ...mockEnvironment,
+ VEGA_URL: 'https://some.url/query',
+ };
+ const { result } = renderHook(() => useConfig(mockEnvWithUrl, mockUpdate));
+
+ expect(fetch).not.toHaveBeenCalled();
+ expect(mockUpdate).not.toHaveBeenCalled();
+ expect(result.current.status).toBe('success');
+ });
+
+ it('updates the environment with a host url from the network configuration', async () => {
+ const allowedStatuses = [
+ 'idle',
+ 'loading-config',
+ 'loading-node',
+ 'success',
+ ];
+
+ const { result, waitForNextUpdate } = renderHook(() =>
+ useConfig(mockEnvironment, mockUpdate)
+ );
+
+ await waitForNextUpdate();
+ jest.runAllTimers();
+ await waitForNextUpdate();
+
+ expect(result.current.status).toBe('success');
+ result.all.forEach((state) => {
+ expect(allowedStatuses).toContain('status' in state && state.status);
+ });
+
+ // fetches config
+ expect(fetch).toHaveBeenCalledWith(mockEnvironment.VEGA_CONFIG_URL);
+ // calls each node
+ hostList.forEach((url) => {
+ expect(fetch).toHaveBeenCalledWith(url);
+ });
+
+ // updates the environment
+ expect(hostList).toContain(mockUpdate.mock.calls[0][0]({}).VEGA_URL);
+ });
+
+ it('uses the host from the configuration which responds first', async () => {
+ const shortestResponseTime = Object.values(mockHostsMap).sort()[0];
+ const expectedHost = hostList.find((url: keyof typeof mockHostsMap) => {
+ return mockHostsMap[url] === shortestResponseTime;
+ });
+
+ const { result, waitForNextUpdate } = renderHook(() =>
+ useConfig(mockEnvironment, mockUpdate)
+ );
+
+ await waitForNextUpdate();
+ jest.runAllTimers();
+ await waitForNextUpdate();
+
+ expect(result.current.status).toBe('success');
+ expect(mockUpdate.mock.calls[0][0]({}).VEGA_URL).toBe(expectedHost);
+ });
+
+ it('ignores failing hosts and uses one which returns a success response', async () => {
+ const mockHostsMapScoped = {
+ 'https://host1.com': 350,
+ 'https://host2.com': new Error('Server error'),
+ 'https://host3.com': 230,
+ 'https://host4.com': new Error('Server error'),
+ };
+
+ // @ts-ignore typescript doesn't recognise the mocked instance
+ global.fetch.mockImplementation(
+ setupFetch(mockEnvironment.VEGA_CONFIG_URL ?? '', mockHostsMapScoped)
+ );
+
+ const { result, waitForNextUpdate } = renderHook(() =>
+ useConfig(mockEnvironment, mockUpdate)
+ );
+
+ await waitForNextUpdate();
+ jest.runAllTimers();
+ await waitForNextUpdate();
+
+ expect(result.current.status).toBe('success');
+ expect(mockUpdate.mock.calls[0][0]({}).VEGA_URL).toBe('https://host3.com');
+ });
+
+ it('returns the correct error status for when the config cannot be accessed', async () => {
+ // @ts-ignore typescript doesn't recognise the mocked instance
+ global.fetch.mockImplementation((url: RequestInfo) => {
+ if (url === mockEnvironment.VEGA_CONFIG_URL) {
+ return Promise.reject(new Error('Server error'));
+ }
+ return Promise.resolve({ ok: true } as Response);
+ });
+
+ const { result, waitForNextUpdate } = renderHook(() =>
+ useConfig(mockEnvironment, mockUpdate)
+ );
+
+ await waitForNextUpdate();
+
+ expect(result.current.status).toBe('error-loading-config');
+ expect(mockUpdate).not.toHaveBeenCalled();
+ });
+
+ it('returns the correct error status for when the config is not valid', async () => {
+ // @ts-ignore typescript doesn't recognise the mocked instance
+ global.fetch.mockImplementation((url: RequestInfo) => {
+ if (url === mockEnvironment.VEGA_CONFIG_URL) {
+ return Promise.resolve({
+ ok: true,
+ json: () => Promise.resolve({ some: 'data' }),
+ });
+ }
+ return Promise.resolve({ ok: true } as Response);
+ });
+
+ const { result, waitForNextUpdate } = renderHook(() =>
+ useConfig(mockEnvironment, mockUpdate)
+ );
+
+ await waitForNextUpdate();
+
+ expect(result.current.status).toBe('error-validating-config');
+ expect(mockUpdate).not.toHaveBeenCalled();
+ });
+
+ it('returns the correct error status for when no hosts can be accessed', async () => {
+ const mockHostsMapScoped = {
+ 'https://host1.com': new Error('Server error'),
+ 'https://host2.com': new Error('Server error'),
+ 'https://host3.com': new Error('Server error'),
+ 'https://host4.com': new Error('Server error'),
+ };
+
+ // @ts-ignore typescript doesn't recognise the mocked instance
+ global.fetch.mockImplementation(
+ setupFetch(mockEnvironment.VEGA_CONFIG_URL ?? '', mockHostsMapScoped)
+ );
+
+ const { result, waitForNextUpdate } = renderHook(() =>
+ useConfig(mockEnvironment, mockUpdate)
+ );
+
+ await waitForNextUpdate();
+
+ expect(result.current.status).toBe('error-loading-node');
+ expect(mockUpdate).not.toHaveBeenCalled();
+ });
+
+ it('caches the list of networks', async () => {
+ const run1 = renderHook(() => useConfig(mockEnvironment, mockUpdate));
+
+ await run1.waitForNextUpdate();
+ jest.runAllTimers();
+ await run1.waitForNextUpdate();
+
+ expect(run1.result.current.status).toBe('success');
+ expect(fetch).toHaveBeenCalledWith(mockEnvironment.VEGA_CONFIG_URL);
+
+ // @ts-ignore typescript doesn't recognise the mocked instance
+ fetch.mockClear();
+
+ const run2 = renderHook(() => useConfig(mockEnvironment, mockUpdate));
+
+ jest.runAllTimers();
+ await run2.waitForNextUpdate();
+
+ expect(run2.result.current.status).toBe('success');
+ expect(fetch).not.toHaveBeenCalledWith(mockEnvironment.VEGA_CONFIG_URL);
+ });
+
+ it('caches the list of networks between runs', async () => {
+ const run1 = renderHook(() => useConfig(mockEnvironment, mockUpdate));
+
+ await run1.waitForNextUpdate();
+ jest.runAllTimers();
+ await run1.waitForNextUpdate();
+
+ expect(run1.result.current.status).toBe('success');
+ expect(fetch).toHaveBeenCalledWith(mockEnvironment.VEGA_CONFIG_URL);
+
+ // @ts-ignore typescript doesn't recognise the mocked instance
+ fetch.mockClear();
+
+ const run2 = renderHook(() => useConfig(mockEnvironment, mockUpdate));
+
+ jest.runAllTimers();
+ await run2.waitForNextUpdate();
+
+ expect(run2.result.current.status).toBe('success');
+ expect(fetch).not.toHaveBeenCalledWith(mockEnvironment.VEGA_CONFIG_URL);
+ });
+
+ it('refetches the network configuration and resets the cache when malformed data found in the storage', async () => {
+ window.localStorage.setItem(LOCAL_STORAGE_NETWORK_KEY, '{not:{valid:{json');
+ const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(noop);
+
+ const run1 = renderHook(() => useConfig(mockEnvironment, mockUpdate));
+
+ await run1.waitForNextUpdate();
+ jest.runAllTimers();
+ await run1.waitForNextUpdate();
+
+ expect(run1.result.current.status).toBe('success');
+ expect(fetch).toHaveBeenCalledWith(mockEnvironment.VEGA_CONFIG_URL);
+ expect(consoleWarnSpy).toHaveBeenCalled();
+
+ consoleWarnSpy.mockRestore();
+ });
+
+ it('refetches the network configuration and resets the cache when invalid data found in the storage', async () => {
+ window.localStorage.setItem(
+ LOCAL_STORAGE_NETWORK_KEY,
+ JSON.stringify({ invalid: 'data' })
+ );
+ const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(noop);
+
+ const run1 = renderHook(() => useConfig(mockEnvironment, mockUpdate));
+
+ await run1.waitForNextUpdate();
+ jest.runAllTimers();
+ await run1.waitForNextUpdate();
+
+ expect(run1.result.current.status).toBe('success');
+ expect(fetch).toHaveBeenCalledWith(mockEnvironment.VEGA_CONFIG_URL);
+ expect(consoleSpy).toHaveBeenCalled();
+
+ consoleSpy.mockRestore();
+ });
+});
diff --git a/libs/environment/src/hooks/use-config.tsx b/libs/environment/src/hooks/use-config.tsx
new file mode 100644
index 000000000..205a882da
--- /dev/null
+++ b/libs/environment/src/hooks/use-config.tsx
@@ -0,0 +1,122 @@
+import type { Dispatch, SetStateAction } from 'react';
+import { useState, useEffect } from 'react';
+import { LocalStorage } from '@vegaprotocol/react-helpers';
+import type { Environment, Configuration, ConfigStatus } from '../types';
+import { validateConfiguration } from '../utils/validate-configuration';
+import { promiseRaceToSuccess } from '../utils/promise-race-success';
+
+export const LOCAL_STORAGE_NETWORK_KEY = 'vegaNetworkConfig';
+
+export type EnvironmentWithOptionalUrl = Partial &
+ Omit;
+
+const requestToNode = async (url: string, index: number): Promise => {
+ const response = await fetch(url);
+ if (!response.ok) {
+ throw new Error(`Failed connecting to node: ${url}.`);
+ }
+ return index;
+};
+
+const getCachedConfig = () => {
+ const value = LocalStorage.getItem(LOCAL_STORAGE_NETWORK_KEY);
+
+ if (value) {
+ try {
+ const config = JSON.parse(value) as Configuration;
+ const hasError = validateConfiguration(config);
+
+ if (hasError) {
+ throw new Error('Invalid configuration found in the storage.');
+ }
+
+ return config;
+ } catch (err) {
+ LocalStorage.removeItem(LOCAL_STORAGE_NETWORK_KEY);
+ console.warn(
+ 'Malformed data found for network configuration. Removed and continuing...'
+ );
+ }
+ }
+
+ return undefined;
+};
+
+export const useConfig = (
+ environment: EnvironmentWithOptionalUrl,
+ updateEnvironment: Dispatch>
+) => {
+ const [config, setConfig] = useState(
+ getCachedConfig()
+ );
+ const [status, setStatus] = useState(
+ !environment.VEGA_URL ? 'idle' : 'success'
+ );
+
+ useEffect(() => {
+ if (!config && status === 'idle') {
+ (async () => {
+ setStatus('loading-config');
+ try {
+ const response = await fetch(environment.VEGA_CONFIG_URL ?? '');
+ const configData: Configuration = await response.json();
+
+ if (validateConfiguration(configData)) {
+ setStatus('error-validating-config');
+ return;
+ }
+
+ setConfig({ hosts: configData.hosts });
+ LocalStorage.setItem(
+ LOCAL_STORAGE_NETWORK_KEY,
+ JSON.stringify({ hosts: configData.hosts })
+ );
+ } catch (err) {
+ setStatus('error-loading-config');
+ }
+ })();
+ }
+ // load config only once per runtime
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [environment.VEGA_CONFIG_URL, !!config, status, setStatus, setConfig]);
+
+ useEffect(() => {
+ if (
+ config &&
+ !['loading-node', 'success', 'error-loading-node'].includes(status)
+ ) {
+ (async () => {
+ setStatus('loading-node');
+
+ // if there's only one configured node to choose from, set is as the env url
+ if (config.hosts.length === 1) {
+ setStatus('success');
+ updateEnvironment((prevEnvironment) => ({
+ ...prevEnvironment,
+ VEGA_URL: config.hosts[0],
+ }));
+ return;
+ }
+
+ // when there are multiple possible hosts, set the env url to the node which responds first
+ try {
+ const requests = config.hosts.map(requestToNode);
+ const index = await promiseRaceToSuccess(requests);
+ setStatus('success');
+ updateEnvironment((prevEnvironment) => ({
+ ...prevEnvironment,
+ VEGA_URL: config.hosts[index],
+ }));
+ } catch (err) {
+ setStatus('error-loading-node');
+ }
+ })();
+ }
+ // load config only once per runtime
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [status, !!config, setStatus, updateEnvironment]);
+
+ return {
+ status,
+ };
+};
diff --git a/libs/environment/src/hooks/use-environment.spec.tsx b/libs/environment/src/hooks/use-environment.spec.tsx
new file mode 100644
index 000000000..af42f19a5
--- /dev/null
+++ b/libs/environment/src/hooks/use-environment.spec.tsx
@@ -0,0 +1,228 @@
+import type { ComponentProps } from 'react';
+import { renderHook } from '@testing-library/react-hooks';
+import type { EnvironmentState } from './use-environment';
+import { useEnvironment, EnvironmentProvider } from './use-environment';
+import { Networks } from '../types';
+
+const MockWrapper = (props: ComponentProps) => {
+ return ;
+};
+
+const MOCK_HOST = 'https://vega.host/query';
+
+global.fetch = jest.fn();
+
+// eslint-disable-next-line @typescript-eslint/no-empty-function
+const noop = () => {};
+
+const mockFetch = (url: RequestInfo) => {
+ if (url === mockEnvironmentState.VEGA_CONFIG_URL) {
+ return Promise.resolve({
+ ok: true,
+ json: () =>
+ Promise.resolve({
+ hosts: [MOCK_HOST],
+ }),
+ } as Response);
+ }
+ return Promise.resolve({ ok: true } as Response);
+};
+
+const mockEnvironmentState: EnvironmentState = {
+ configStatus: 'success',
+ VEGA_URL: 'https://vega.xyz',
+ VEGA_ENV: Networks.TESTNET,
+ VEGA_CONFIG_URL: 'https://vega.xyz/testnet-config.json',
+ VEGA_NETWORKS: {
+ TESTNET: 'https://testnet.url',
+ STAGNET: 'https://stagnet.url',
+ MAINNET: 'https://mainnet.url',
+ },
+ ETHEREUM_PROVIDER_URL: 'https://ether.provider',
+ ETHERSCAN_URL: 'https://etherscan.url',
+};
+
+beforeEach(() => {
+ // @ts-ignore typscript doesn't recognise the mock implementation
+ global.fetch.mockReset();
+ // @ts-ignore typscript doesn't recognise the mock implementation
+ global.fetch.mockImplementation(mockFetch);
+
+ window.localStorage.clear();
+
+ process.env['NX_VEGA_URL'] = mockEnvironmentState.VEGA_URL;
+ process.env['NX_VEGA_ENV'] = mockEnvironmentState.VEGA_ENV;
+ process.env['NX_VEGA_CONFIG_URL'] = mockEnvironmentState.VEGA_CONFIG_URL;
+ process.env['NX_ETHEREUM_PROVIDER_URL'] =
+ mockEnvironmentState.ETHEREUM_PROVIDER_URL;
+ process.env['NX_ETHERSCAN_URL'] = mockEnvironmentState.ETHERSCAN_URL;
+ process.env['NX_VEGA_NETWORKS'] = JSON.stringify(
+ mockEnvironmentState.VEGA_NETWORKS
+ );
+});
+
+afterAll(() => {
+ // @ts-ignore: typescript doesn't recognise the mocked fetch instance
+ fetch.mockRestore();
+ window.localStorage.clear();
+
+ delete process.env['NX_VEGA_URL'];
+ delete process.env['NX_VEGA_ENV'];
+ delete process.env['NX_VEGA_CONFIG_URL'];
+ delete process.env['NX_ETHEREUM_PROVIDER_URL'];
+ delete process.env['NX_ETHERSCAN_URL'];
+ delete process.env['NX_VEGA_NETWORKS'];
+});
+
+describe('useEnvironment hook', () => {
+ it('transforms and exposes values from the environment', () => {
+ const { result } = renderHook(() => useEnvironment(), {
+ wrapper: MockWrapper,
+ });
+ expect(result.error).toBe(undefined);
+ expect(result.current).toEqual(mockEnvironmentState);
+ });
+
+ it('allows for the VEGA_CONFIG_URL to be missing when there is a VEGA_URL present', () => {
+ delete process.env['NX_VEGA_CONFIG_URL'];
+ const { result } = renderHook(() => useEnvironment(), {
+ wrapper: MockWrapper,
+ });
+ expect(result.error).toBe(undefined);
+ expect(result.current).toEqual({
+ ...mockEnvironmentState,
+ VEGA_CONFIG_URL: undefined,
+ });
+ });
+
+ it('allows for the VEGA_URL to be missing when there is a VEGA_CONFIG_URL present', async () => {
+ delete process.env['NX_VEGA_URL'];
+ const { result, waitForNextUpdate } = renderHook(() => useEnvironment(), {
+ wrapper: MockWrapper,
+ });
+ await waitForNextUpdate();
+
+ expect(result.error).toBe(undefined);
+ expect(result.current).toEqual({
+ ...mockEnvironmentState,
+ VEGA_URL: MOCK_HOST,
+ });
+ });
+
+ it('allows for the VEGA_NETWORKS to be missing from the environment', () => {
+ delete process.env['NX_VEGA_NETWORKS'];
+ const { result } = renderHook(() => useEnvironment(), {
+ wrapper: MockWrapper,
+ });
+ expect(result.error).toBe(undefined);
+ expect(result.current).toEqual({
+ ...mockEnvironmentState,
+ VEGA_NETWORKS: {},
+ });
+ });
+
+ it('throws a validation error when NX_VEGA_ENV is not found in the environment', async () => {
+ delete process.env['NX_VEGA_ENV'];
+ const { result } = renderHook(() => useEnvironment(), {
+ wrapper: MockWrapper,
+ });
+ expect(result.error?.message).toContain(
+ `NX_VEGA_ENV is invalid, received "undefined" instead of: 'CUSTOM' | 'TESTNET' | 'STAGNET' | 'STAGNET2' | 'DEVNET' | 'MAINNET'`
+ );
+ });
+
+ it('throws a validation error when VEGA_ENV is not a valid network', () => {
+ process.env['NX_VEGA_ENV'] = 'SOMETHING';
+ const { result } = renderHook(() => useEnvironment(), {
+ wrapper: MockWrapper,
+ });
+ expect(result.error).not.toContain(
+ `NX_VEGA_ENV is invalid, received "SOMETHING" instead of: CUSTOM | TESTNET | STAGNET | STAGNET2 | DEVNET | MAINNET`
+ );
+ });
+
+ it('when VEGA_NETWORKS is not a valid json, prints a warning and continues without using the value from it', () => {
+ const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(noop);
+ process.env['NX_VEGA_NETWORKS'] = '{not:{valid:json';
+ const { result } = renderHook(() => useEnvironment(), {
+ wrapper: MockWrapper,
+ });
+ expect(result.error).toBe(undefined);
+ expect(result.current).toEqual({
+ ...mockEnvironmentState,
+ VEGA_NETWORKS: {},
+ });
+
+ expect(consoleWarnSpy).toHaveBeenCalled();
+ consoleWarnSpy.mockRestore();
+ });
+
+ it('throws a validation error when VEGA_NETWORKS is has an invalid network as a key', () => {
+ process.env['NX_VEGA_NETWORKS'] = JSON.stringify({
+ NOT_A_NETWORK: 'https://somewhere.url',
+ });
+ const { result } = renderHook(() => useEnvironment(), {
+ wrapper: MockWrapper,
+ });
+ expect(result.error?.message).toContain(
+ `All keys in NX_VEGA_NETWORKS must represent a valid environment: CUSTOM | TESTNET | STAGNET | STAGNET2 | DEVNET | MAINNET`
+ );
+ });
+
+ it('throws a validation error when both VEGA_URL and VEGA_CONFIG_URL are missing in the environment', () => {
+ delete process.env['NX_VEGA_URL'];
+ delete process.env['NX_VEGA_CONFIG_URL'];
+ const { result } = renderHook(() => useEnvironment(), {
+ wrapper: MockWrapper,
+ });
+ expect(result.error?.message).toContain(
+ `Must provide either NX_VEGA_CONFIG_URL or NX_VEGA_URL in the environment.`
+ );
+ });
+
+ it.each`
+ env | etherscanUrl | providerUrl
+ ${Networks.DEVNET} | ${'https://ropsten.etherscan.io'} | ${'https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8'}
+ ${Networks.TESTNET} | ${'https://ropsten.etherscan.io'} | ${'https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8'}
+ ${Networks.STAGNET} | ${'https://ropsten.etherscan.io'} | ${'https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8'}
+ ${Networks.STAGNET2} | ${'https://ropsten.etherscan.io'} | ${'https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8'}
+ ${Networks.MAINNET} | ${'https://etherscan.io'} | ${'https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8'}
+ `(
+ 'uses correct default ethereum connection variables in $env',
+ async ({ env, etherscanUrl, providerUrl }) => {
+ process.env['NX_VEGA_ENV'] = env;
+ delete process.env['NX_ETHEREUM_PROVIDER_URL'];
+ delete process.env['NX_ETHERSCAN_URL'];
+ const { result } = renderHook(() => useEnvironment(), {
+ wrapper: MockWrapper,
+ });
+ expect(result.error).toBe(undefined);
+ expect(result.current).toEqual({
+ ...mockEnvironmentState,
+ VEGA_ENV: env,
+ ETHEREUM_PROVIDER_URL: providerUrl,
+ ETHERSCAN_URL: etherscanUrl,
+ });
+ }
+ );
+
+ it('throws a validation error when NX_ETHERSCAN_URL is not a valid url', async () => {
+ process.env['NX_ETHERSCAN_URL'] = 'invalid-url';
+ const { result } = renderHook(() => useEnvironment(), {
+ wrapper: MockWrapper,
+ });
+ expect(result.error?.message).toContain(
+ `The NX_ETHERSCAN_URL environment variable must be a valid url`
+ );
+ });
+
+ it('throws a validation error when NX_ETHEREUM_PROVIDER_URL is not a valid url', async () => {
+ process.env['NX_ETHEREUM_PROVIDER_URL'] = 'invalid-url';
+ const { result } = renderHook(() => useEnvironment(), {
+ wrapper: MockWrapper,
+ });
+ expect(result.error?.message).toContain(
+ `The NX_ETHEREUM_PROVIDER_URL environment variable must be a valid url`
+ );
+ });
+});
diff --git a/libs/environment/src/hooks/use-environment.tsx b/libs/environment/src/hooks/use-environment.tsx
new file mode 100644
index 000000000..c9a6d7f4d
--- /dev/null
+++ b/libs/environment/src/hooks/use-environment.tsx
@@ -0,0 +1,50 @@
+import type { ReactNode } from 'react';
+import { useState, createContext, useContext } from 'react';
+
+import { useConfig } from './use-config';
+import { compileEnvironment } from '../utils/compile-environment';
+import { validateEnvironment } from '../utils/validate-environment';
+import type { Environment, RawEnvironment, ConfigStatus } from '../types';
+
+type EnvironmentProviderProps = {
+ definitions?: Partial;
+ children?: ReactNode;
+};
+
+export type EnvironmentState = Environment & {
+ configStatus: ConfigStatus;
+};
+
+const EnvironmentContext = createContext({} as EnvironmentState);
+
+export const EnvironmentProvider = ({
+ definitions,
+ children,
+}: EnvironmentProviderProps) => {
+ const [environment, updateEnvironment] = useState(
+ compileEnvironment(definitions)
+ );
+ const { status: configStatus } = useConfig(environment, updateEnvironment);
+
+ const errorMessage = validateEnvironment(environment);
+
+ if (errorMessage) {
+ throw new Error(errorMessage);
+ }
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useEnvironment = () => {
+ const context = useContext(EnvironmentContext);
+ if (context === undefined) {
+ throw new Error(
+ 'Error running "useEnvironment". No context found, make sure your component is wrapped in an .'
+ );
+ }
+ return context;
+};
diff --git a/libs/network-switcher/src/index.ts b/libs/environment/src/index.ts
similarity index 69%
rename from libs/network-switcher/src/index.ts
rename to libs/environment/src/index.ts
index 171999fdb..e036a6e53 100644
--- a/libs/network-switcher/src/index.ts
+++ b/libs/environment/src/index.ts
@@ -3,3 +3,6 @@ export * from './components';
// Hooks
export * from './hooks';
+
+// Types
+export * from './types';
diff --git a/libs/environment/src/types.ts b/libs/environment/src/types.ts
new file mode 100644
index 000000000..58a29a3db
--- /dev/null
+++ b/libs/environment/src/types.ts
@@ -0,0 +1,27 @@
+import type z from 'zod';
+
+import type { configSchema } from './utils/validate-configuration';
+import type { envSchema } from './utils/validate-environment';
+import { Networks, ENV_KEYS } from './utils/validate-environment';
+
+export { ENV_KEYS, Networks };
+
+export type Environment = z.infer & {
+ // provide this manually, zod fails to compile the correct type fot VEGA_NETWORKS
+ VEGA_NETWORKS: Partial>;
+};
+
+export type EnvKey = keyof Environment;
+
+export type RawEnvironment = Record;
+
+export type Configuration = z.infer;
+
+export type ConfigStatus =
+ | 'idle'
+ | 'success'
+ | 'loading-config'
+ | 'loading-node'
+ | 'error-loading-config'
+ | 'error-validating-config'
+ | 'error-loading-node';
diff --git a/libs/environment/src/utils/compile-environment.ts b/libs/environment/src/utils/compile-environment.ts
new file mode 100644
index 000000000..9bbaa95b8
--- /dev/null
+++ b/libs/environment/src/utils/compile-environment.ts
@@ -0,0 +1,102 @@
+import type { RawEnvironment, EnvKey, Environment } from '../types';
+import { Networks, ENV_KEYS } from '../types';
+
+declare global {
+ interface Window {
+ _env_?: Record;
+ }
+}
+
+const isBrowser = typeof window !== 'undefined';
+
+const getDefaultEtherumProviderUrl = (env: Networks) => {
+ return env === Networks.MAINNET
+ ? 'https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8'
+ : 'https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8';
+};
+
+const getDefaultEtherscanUrl = (env: Networks) => {
+ return env === Networks.MAINNET
+ ? 'https://etherscan.io'
+ : 'https://ropsten.etherscan.io';
+};
+
+const transformValue = (key: EnvKey, value?: string) => {
+ switch (key) {
+ case 'VEGA_ENV':
+ return value as Networks;
+ case 'VEGA_NETWORKS': {
+ if (value) {
+ try {
+ return JSON.parse(value);
+ } catch (e) {
+ console.warn(
+ 'Error parsing the "NX_VEGA_NETWORKS" environment variable. Make sure it has a valid JSON format.'
+ );
+ return {};
+ }
+ }
+ return {};
+ }
+ default:
+ return value;
+ }
+};
+
+const getBundledEnvironmentValue = (key: EnvKey) => {
+ switch (key) {
+ // need to have these hardcoded so on build time they can be replaced with the relevant environment variable
+ case 'VEGA_URL':
+ return process.env['NX_VEGA_URL'];
+ case 'VEGA_ENV':
+ return process.env['NX_VEGA_ENV'];
+ case 'VEGA_CONFIG_URL':
+ return process.env['NX_VEGA_CONFIG_URL'];
+ case 'ETHEREUM_PROVIDER_URL':
+ return process.env['NX_ETHEREUM_PROVIDER_URL'];
+ case 'ETHERSCAN_URL':
+ return process.env['NX_ETHERSCAN_URL'];
+ case 'VEGA_NETWORKS':
+ return process.env['NX_VEGA_NETWORKS'];
+ }
+};
+
+const getValue = (key: EnvKey, definitions: Partial = {}) => {
+ if (!isBrowser) {
+ return transformValue(
+ key,
+ definitions[key] ?? getBundledEnvironmentValue(key)
+ );
+ }
+ return transformValue(
+ key,
+ definitions[key] ?? window._env_?.[key] ?? getBundledEnvironmentValue(key)
+ );
+};
+
+export const compileEnvironment = (
+ definitions?: Partial
+): Environment => {
+ const environment = ENV_KEYS.reduce((acc, key) => {
+ const value = getValue(key, definitions);
+
+ if (value) {
+ return {
+ ...acc,
+ [key]: value,
+ };
+ }
+
+ return acc;
+ }, {} as Environment);
+
+ return {
+ // @ts-ignore enable using default object props
+ ETHERSCAN_URL: getDefaultEtherscanUrl(environment['VEGA_ENV']),
+ // @ts-ignore enable using default object props
+ ETHEREUM_PROVIDER_URL: getDefaultEtherumProviderUrl(
+ environment['VEGA_ENV']
+ ),
+ ...environment,
+ };
+};
diff --git a/libs/environment/src/utils/compile-errors.ts b/libs/environment/src/utils/compile-errors.ts
new file mode 100644
index 000000000..fe74e4d3d
--- /dev/null
+++ b/libs/environment/src/utils/compile-errors.ts
@@ -0,0 +1,19 @@
+import type { ZodIssue } from 'zod';
+import { ZodError } from 'zod';
+
+export const compileErrors = (
+ headline: string,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ error: any,
+ compileIssue?: (issue: ZodIssue) => string
+) => {
+ if (error instanceof ZodError) {
+ return error.issues.reduce((acc, issue) => {
+ return (
+ acc + `\n - ${compileIssue ? compileIssue(issue) : issue.message}`
+ );
+ }, `${headline}:`);
+ }
+
+ return `${headline}${error?.message ? `: ${error.message}` : ''}`;
+};
diff --git a/libs/environment/src/utils/promise-race-success.ts b/libs/environment/src/utils/promise-race-success.ts
new file mode 100644
index 000000000..54aa34511
--- /dev/null
+++ b/libs/environment/src/utils/promise-race-success.ts
@@ -0,0 +1,22 @@
+export function promiseRaceToSuccess(requests: Array>) {
+ return new Promise((resolve, reject) => {
+ let hasResolved = false;
+ const failures = [];
+
+ requests.forEach((req) => {
+ req
+ .then((res) => {
+ if (!hasResolved) {
+ resolve(res);
+ hasResolved = true;
+ }
+ })
+ .catch((err) => {
+ failures.push(err);
+ if (failures.length === requests.length) {
+ reject(err);
+ }
+ });
+ });
+ });
+}
diff --git a/libs/environment/src/utils/validate-configuration.ts b/libs/environment/src/utils/validate-configuration.ts
new file mode 100644
index 000000000..9a551c73a
--- /dev/null
+++ b/libs/environment/src/utils/validate-configuration.ts
@@ -0,0 +1,19 @@
+import z from 'zod';
+import type { Configuration } from '../types';
+import { compileErrors } from './compile-errors';
+
+export const configSchema = z.object({
+ hosts: z.array(z.string()),
+});
+
+export const validateConfiguration = (
+ config: Configuration
+): string | undefined => {
+ try {
+ configSchema.parse(config);
+ return undefined;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ } catch (err: any) {
+ return compileErrors('Error processing the vega app configuration', err);
+ }
+};
diff --git a/libs/environment/src/utils/validate-environment.ts b/libs/environment/src/utils/validate-environment.ts
new file mode 100644
index 000000000..b739e248f
--- /dev/null
+++ b/libs/environment/src/utils/validate-environment.ts
@@ -0,0 +1,84 @@
+import type { ZodIssue } from 'zod';
+import z from 'zod';
+import type { Environment } from '../types';
+import { compileErrors } from './compile-errors';
+
+export enum Networks {
+ CUSTOM = 'CUSTOM',
+ TESTNET = 'TESTNET',
+ STAGNET = 'STAGNET',
+ STAGNET2 = 'STAGNET2',
+ DEVNET = 'DEVNET',
+ MAINNET = 'MAINNET',
+}
+
+const schemaObject = {
+ VEGA_URL: z.optional(z.string()),
+ VEGA_CONFIG_URL: z.optional(z.string()),
+ ETHEREUM_PROVIDER_URL: z.string().url({
+ message:
+ 'The NX_ETHEREUM_PROVIDER_URL environment variable must be a valid url',
+ }),
+ ETHERSCAN_URL: z.string().url({
+ message: 'The NX_ETHERSCAN_URL environment variable must be a valid url',
+ }),
+ VEGA_ENV: z.nativeEnum(Networks),
+ VEGA_NETWORKS: z
+ .object(
+ Object.keys(Networks).reduce(
+ (acc, env) => ({
+ ...acc,
+ [env]: z.optional(z.string()),
+ }),
+ {}
+ )
+ )
+ .strict({
+ message: `All keys in NX_VEGA_NETWORKS must represent a valid environment: ${Object.keys(
+ Networks
+ ).join(' | ')}`,
+ }),
+};
+
+export const ENV_KEYS = Object.keys(schemaObject) as Array<
+ keyof typeof schemaObject
+>;
+
+const compileIssue = (issue: ZodIssue) => {
+ switch (issue.code) {
+ case 'invalid_type':
+ return `NX_${issue.path[0]} is invalid, received "${issue.received}" instead of: ${issue.expected}`;
+ case 'invalid_enum_value':
+ return `NX_${issue.path[0]} is invalid, received "${
+ issue.received
+ }" instead of: ${issue.options.join(' | ')}`;
+ default:
+ return issue.message;
+ }
+};
+
+export const envSchema = z.object(schemaObject).refine(
+ (data) => {
+ return !(!data.VEGA_URL && !data.VEGA_CONFIG_URL);
+ },
+ {
+ message:
+ 'Must provide either NX_VEGA_CONFIG_URL or NX_VEGA_URL in the environment.',
+ }
+);
+
+export const validateEnvironment = (
+ environment: Environment
+): string | undefined => {
+ try {
+ envSchema.parse(environment);
+ return undefined;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ } catch (err: any) {
+ return compileErrors(
+ 'Error processing the vega app environment',
+ err,
+ compileIssue
+ );
+ }
+};
diff --git a/libs/network-switcher/tsconfig.json b/libs/environment/tsconfig.json
similarity index 100%
rename from libs/network-switcher/tsconfig.json
rename to libs/environment/tsconfig.json
diff --git a/libs/network-switcher/tsconfig.lib.json b/libs/environment/tsconfig.lib.json
similarity index 100%
rename from libs/network-switcher/tsconfig.lib.json
rename to libs/environment/tsconfig.lib.json
diff --git a/libs/network-switcher/tsconfig.spec.json b/libs/environment/tsconfig.spec.json
similarity index 100%
rename from libs/network-switcher/tsconfig.spec.json
rename to libs/environment/tsconfig.spec.json
diff --git a/libs/network-switcher/README.md b/libs/network-switcher/README.md
deleted file mode 100644
index 7053a1c52..000000000
--- a/libs/network-switcher/README.md
+++ /dev/null
@@ -1,17 +0,0 @@
-# network-switcher
-
-This library was generated with [Nx](https://nx.dev).
-
-## Prerequisites
-
-Two environment variables need to be present for any app consuming this library.
-
-`NX_VEGA_ENV` is the name of the environment.
-
-`NX_VEGA_REST` is the REST endpoint for the environment.
-
-For examples, see Block Explorer's .env files [here](../../apps/explorer)
-
-## Running unit tests
-
-Run `nx test network-switcher` to execute the unit tests via [Jest](https://jestjs.io).
diff --git a/libs/network-switcher/package.json b/libs/network-switcher/package.json
deleted file mode 100644
index fd7f0c0ea..000000000
--- a/libs/network-switcher/package.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "@vegaprotocol/network-switcher",
- "version": "0.0.1"
-}
diff --git a/libs/network-switcher/src/hooks/use-environment.tsx b/libs/network-switcher/src/hooks/use-environment.tsx
deleted file mode 100644
index 48b1b32d6..000000000
--- a/libs/network-switcher/src/hooks/use-environment.tsx
+++ /dev/null
@@ -1,183 +0,0 @@
-import type { Networks } from '@vegaprotocol/react-helpers';
-import type { ReactNode } from 'react';
-import { useState } from 'react';
-import { createContext, useContext } from 'react';
-
-const isBrowser = typeof window !== 'undefined';
-
-declare global {
- interface Window {
- _env_?: Record;
- }
-}
-interface VegaContracts {
- claimAddress: string;
- lockedAddress: string;
-}
-
-export const ContractAddresses: { [key in Networks]: VegaContracts } = {
- DEVNET: {
- claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994',
- lockedAddress: '0x0',
- },
- STAGNET: {
- claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error
- lockedAddress: '0x0', // TODO not deployed to this env
- },
- STAGNET2: {
- claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error
- lockedAddress: '0x0', // TODO not deployed to this env
- },
- TESTNET: {
- claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error
- lockedAddress: '0x0', // TODO not deployed to this env
- },
- MAINNET: {
- claimAddress: '0x0ee1fb382caf98e86e97e51f9f42f8b4654020f3',
- lockedAddress: '0x78344c7305d73a7a0ac3c94cd9960f4449a1814e',
- },
-};
-
-type EnvironmentProviderProps = {
- definitions?: Partial;
- children?: ReactNode;
-};
-
-export const ENV_KEYS = [
- 'VEGA_URL',
- 'VEGA_ENV',
- 'VEGA_NETWORKS',
- 'ETHEREUM_CHAIN_ID',
- 'ETHEREUM_PROVIDER_URL',
- 'ETHERSCAN_URL',
-] as const;
-
-type EnvKey = typeof ENV_KEYS[number];
-
-type RawEnvironment = Record;
-
-export type Environment = {
- VEGA_URL: string;
- VEGA_ENV: Networks;
- VEGA_NETWORKS: Record;
- ETHEREUM_CHAIN_ID: number;
- ETHEREUM_PROVIDER_URL: string;
- ETHERSCAN_URL: string;
- ADDRESSES: VegaContracts;
-};
-
-type EnvironmentState = Environment;
-
-const getBundledEnvironmentValue = (key: EnvKey) => {
- switch (key) {
- // need to have these hardcoded so on build time we can insert sensible defaults
- case 'VEGA_URL':
- return process.env['NX_VEGA_URL'];
- case 'VEGA_ENV':
- return process.env['NX_VEGA_ENV'];
- case 'ETHEREUM_CHAIN_ID':
- return process.env['NX_ETHEREUM_CHAIN_ID'];
- case 'ETHEREUM_PROVIDER_URL':
- return process.env['NX_ETHEREUM_PROVIDER_URL'];
- case 'ETHERSCAN_URL':
- return process.env['NX_ETHERSCAN_URL'];
- case 'VEGA_NETWORKS':
- return process.env['NX_VEGA_NETWORKS'];
- }
-};
-
-const transformValue = (key: EnvKey, value?: string) => {
- switch (key) {
- case 'VEGA_ENV':
- return value as Networks;
- case 'ETHEREUM_CHAIN_ID':
- return value && Number(value);
- case 'VEGA_NETWORKS': {
- if (value) {
- try {
- return JSON.parse(value);
- } catch (e) {
- console.warn(
- 'Error parsing the "NX_VEGA_NETWORKS" environment variable. Make sure it has a valid JSON format.'
- );
- return undefined;
- }
- }
- return undefined;
- }
- default:
- return value;
- }
-};
-
-const getValue = (key: EnvKey, definitions: Partial = {}) => {
- if (!isBrowser) {
- return transformValue(
- key,
- definitions[key] ?? getBundledEnvironmentValue(key)
- );
- }
- return transformValue(
- key,
- window._env_?.[key] ?? definitions[key] ?? getBundledEnvironmentValue(key)
- );
-};
-
-const compileEnvironment = (
- definitions?: Partial
-): Environment => {
- const environment = ENV_KEYS.reduce(
- (acc, key) => ({
- ...acc,
- [key]: getValue(key, definitions),
- }),
- {} as Environment
- );
-
- return {
- ...environment,
- ADDRESSES: ContractAddresses[environment['VEGA_ENV']],
- VEGA_NETWORKS: {
- [environment.VEGA_ENV]: isBrowser
- ? window.location.href
- : environment.VEGA_NETWORKS[environment.VEGA_ENV],
- ...environment.VEGA_NETWORKS,
- },
- };
-};
-
-const EnvironmentContext = createContext({} as EnvironmentState);
-
-export const EnvironmentProvider = ({
- definitions,
- children,
-}: EnvironmentProviderProps) => {
- const [environment] = useState(compileEnvironment(definitions));
-
- const missingKeys = Object.keys(environment)
- .filter((key) => typeof environment[key as EnvKey] === undefined)
- .map((key) => `"${key}"`)
- .join(', ');
-
- if (missingKeys.length) {
- throw new Error(
- `Error setting up the app environment. The following variables are missing from your environment: ${missingKeys}.`
- );
- }
-
- return (
-
- {children}
-
- );
-};
-
-export const useEnvironment = () => {
- const context = useContext(EnvironmentContext);
- if (context === undefined) {
- throw new Error(
- 'Error running "useEnvironment". No context found, make sure your component is wrapped in an .'
- );
- }
- return context;
-};
diff --git a/libs/react-helpers/src/index.ts b/libs/react-helpers/src/index.ts
index 1ed3710ae..87c27d7b9 100644
--- a/libs/react-helpers/src/index.ts
+++ b/libs/react-helpers/src/index.ts
@@ -1,6 +1,5 @@
export * from './hooks';
export * from './lib/context';
-export * from './lib/environment';
export * from './lib/format';
export * from './lib/generic-data-provider';
export * from './lib/grid';
diff --git a/libs/react-helpers/src/lib/environment.ts b/libs/react-helpers/src/lib/environment.ts
deleted file mode 100644
index a7565ef91..000000000
--- a/libs/react-helpers/src/lib/environment.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export enum Networks {
- TESTNET = 'TESTNET',
- STAGNET = 'STAGNET',
- STAGNET2 = 'STAGNET2',
- DEVNET = 'DEVNET',
- MAINNET = 'MAINNET',
-}
diff --git a/libs/ui-toolkit/src/components/async-renderer/async-renderer.tsx b/libs/ui-toolkit/src/components/async-renderer/async-renderer.tsx
index d234e1ffe..16ef721e4 100644
--- a/libs/ui-toolkit/src/components/async-renderer/async-renderer.tsx
+++ b/libs/ui-toolkit/src/components/async-renderer/async-renderer.tsx
@@ -1,12 +1,12 @@
import { Splash } from '../splash';
-import type { ReactElement, ReactNode } from 'react';
+import type { ReactNode } from 'react';
import { t } from '@vegaprotocol/react-helpers';
interface AsyncRendererProps {
loading: boolean;
error: Error | undefined | null;
data: T | undefined;
- children?: ReactElement | null;
+ children?: ReactNode | null;
render?: (data: T) => ReactNode;
}
diff --git a/libs/ui-toolkit/src/components/dialog/dialog.tsx b/libs/ui-toolkit/src/components/dialog/dialog.tsx
index 275707150..e7c7309ca 100644
--- a/libs/ui-toolkit/src/components/dialog/dialog.tsx
+++ b/libs/ui-toolkit/src/components/dialog/dialog.tsx
@@ -8,7 +8,7 @@ import { Icon } from '../icon';
interface DialogProps {
children: ReactNode;
open: boolean;
- onChange: (isOpen: boolean) => void;
+ onChange?: (isOpen: boolean) => void;
title?: string;
intent?: Intent;
titleClassNames?: string;
@@ -33,7 +33,7 @@ export function Dialog({
contentClassNames
);
return (
- onChange(x)}>
+ onChange?.(x)}>
;
+const Template: Story = (args) => ;
export const Default = Template.bind({});
Default.args = {};
diff --git a/libs/web3/src/lib/transaction-dialog/dialog-rows.tsx b/libs/web3/src/lib/transaction-dialog/dialog-rows.tsx
index 82dbfdd28..31548a0ba 100644
--- a/libs/web3/src/lib/transaction-dialog/dialog-rows.tsx
+++ b/libs/web3/src/lib/transaction-dialog/dialog-rows.tsx
@@ -1,6 +1,6 @@
import { t } from '@vegaprotocol/react-helpers';
import { Link } from '@vegaprotocol/ui-toolkit';
-import { useEnvironment } from '@vegaprotocol/network-switcher';
+import { useEnvironment } from '@vegaprotocol/environment';
import { EthTxStatus } from '../use-ethereum-transaction';
const ACTIVE_CLASSES = 'text-black dark:text-white';
diff --git a/libs/withdraws/src/lib/withdraw-dialog.tsx b/libs/withdraws/src/lib/withdraw-dialog.tsx
index f7e1fc4fd..1e1188283 100644
--- a/libs/withdraws/src/lib/withdraw-dialog.tsx
+++ b/libs/withdraws/src/lib/withdraw-dialog.tsx
@@ -1,5 +1,5 @@
import { Link, Dialog, Icon, Intent, Loader } from '@vegaprotocol/ui-toolkit';
-import { useEnvironment } from '@vegaprotocol/network-switcher';
+import { useEnvironment } from '@vegaprotocol/environment';
import type { VegaTxState } from '@vegaprotocol/wallet';
import { VegaTxStatus } from '@vegaprotocol/wallet';
import type { ReactNode } from 'react';
diff --git a/libs/withdraws/src/lib/withdrawals-table.tsx b/libs/withdraws/src/lib/withdrawals-table.tsx
index 53b7e63cd..d5b363418 100644
--- a/libs/withdraws/src/lib/withdrawals-table.tsx
+++ b/libs/withdraws/src/lib/withdrawals-table.tsx
@@ -11,7 +11,7 @@ import {
} from '@vegaprotocol/react-helpers';
import { WithdrawalStatus } from '@vegaprotocol/types';
import { Link, AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
-import { useEnvironment } from '@vegaprotocol/network-switcher';
+import { useEnvironment } from '@vegaprotocol/environment';
import { TransactionDialog } from '@vegaprotocol/web3';
import { useCompleteWithdraw } from './use-complete-withdraw';
import type { Withdrawals_party_withdrawals } from './__generated__/Withdrawals';
diff --git a/package.json b/package.json
index 6968cf920..478fd665a 100644
--- a/package.json
+++ b/package.json
@@ -73,6 +73,7 @@
"tslib": "^2.0.0",
"uuid": "^8.3.2",
"web-vitals": "^2.1.4",
+ "zod": "^3.17.3",
"zustand": "^4.0.0-rc.1"
},
"devDependencies": {
diff --git a/tsconfig.base.json b/tsconfig.base.json
index 5654dfc9a..9c8bdb87a 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -21,10 +21,10 @@
"@vegaprotocol/cypress": ["libs/cypress/src/index.ts"],
"@vegaprotocol/deal-ticket": ["libs/deal-ticket/src/index.ts"],
"@vegaprotocol/deposits": ["libs/deposits/src/index.ts"],
+ "@vegaprotocol/environment": ["libs/environment/src/index.ts"],
"@vegaprotocol/market-depth": ["libs/market-depth/src/index.ts"],
"@vegaprotocol/market-list": ["libs/market-list/src/index.ts"],
"@vegaprotocol/network-stats": ["libs/network-stats/src/index.ts"],
- "@vegaprotocol/network-switcher": ["libs/network-switcher/src/index.ts"],
"@vegaprotocol/order-list": ["libs/order-list/src/index.ts"],
"@vegaprotocol/positions": ["libs/positions/src/index.ts"],
"@vegaprotocol/react-helpers": ["libs/react-helpers/src/index.ts"],
diff --git a/workspace.json b/workspace.json
index 4836649a8..5d672ae34 100644
--- a/workspace.json
+++ b/workspace.json
@@ -6,12 +6,12 @@
"cypress": "libs/cypress",
"deal-ticket": "libs/deal-ticket",
"deposits": "libs/deposits",
+ "environment": "libs/environment",
"explorer": "apps/explorer",
"explorer-e2e": "apps/explorer-e2e",
"market-depth": "libs/market-depth",
"market-list": "libs/market-list",
"network-stats": "libs/network-stats",
- "network-switcher": "libs/network-switcher",
"order-list": "libs/order-list",
"positions": "libs/positions",
"react-helpers": "libs/react-helpers",
diff --git a/yarn.lock b/yarn.lock
index c01f593da..bc2d28778 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -22669,6 +22669,11 @@ zen-observable@0.8.15, zen-observable@^0.8.0:
resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15"
integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==
+zod@^3.17.3:
+ version "3.17.3"
+ resolved "https://registry.yarnpkg.com/zod/-/zod-3.17.3.tgz#86abbc670ff0063a4588d85a4dcc917d6e4af2ba"
+ integrity sha512-4oKP5zvG6GGbMlqBkI5FESOAweldEhSOZ6LI6cG+JzUT7ofj1ZOC0PJudpQOpT1iqOFpYYtX5Pw0+o403y4bcg==
+
zustand@^4.0.0-beta.2:
version "4.0.0-beta.3"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.0.0-beta.3.tgz#16dc82b48b65ed61fe2bae5dea4501f49bd450c7"