Compare commits
	
		
			9 Commits
		
	
	
		
			develop
			...
			fix/5759-i
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 002738af70 | ||
|  | b0bbae4c2e | ||
|  | 635445f23b | ||
|  | 10e30b9f70 | ||
|  | 46c6e27aec | ||
|  | 4bd73bfc0f | ||
|  | 15aad9db62 | ||
|  | 46308b381f | ||
|  | 938ad08a60 | 
| @ -1,63 +1,126 @@ | ||||
| import type { InMemoryCacheConfig } from '@apollo/client'; | ||||
| import { | ||||
|   AppFailure, | ||||
|   AppLoader, | ||||
|   NetworkLoader, | ||||
|   NodeFailure, | ||||
|   NodeGuard, | ||||
|   useEnvironment, | ||||
|   useNodeSwitcherStore, | ||||
| } from '@vegaprotocol/environment'; | ||||
| import { type ReactNode } from 'react'; | ||||
| import { useEffect, type ReactNode, useState } from 'react'; | ||||
| import { Web3Provider } from './web3-provider'; | ||||
| import { useT } from '../../lib/use-t'; | ||||
| import { DataLoader } from './data-loader'; | ||||
| import { WalletProvider } from '@vegaprotocol/wallet-react'; | ||||
| import { useVegaWalletConfig } from '../../lib/hooks/use-vega-wallet-config'; | ||||
| import { Trans } from 'react-i18next'; | ||||
| import { Button, Loader, Splash, VLogo } from '@vegaprotocol/ui-toolkit'; | ||||
| 
 | ||||
| const Failure = ({ reason }: { reason?: ReactNode }) => { | ||||
|   const t = useT(); | ||||
|   const setNodeSwitcher = useNodeSwitcherStore((store) => store.setDialogOpen); | ||||
|   return ( | ||||
|     <Splash> | ||||
|       <div className="border border-vega-red m-10 mx-auto w-4/5 max-w-3xl rounded-lg overflow-hidden animate-shake"> | ||||
|         <div className="bg-vega-red text-white px-2 py-2 flex gap-1 items-center font-alpha calt uppercase"> | ||||
|           <VLogo className="h-4" /> | ||||
|           <span className="text-lg">{t('Failed to initialize the app')}</span> | ||||
|         </div> | ||||
|         <div className="p-4 text-left text-sm"> | ||||
|           <p className="mb-4">{reason}</p> | ||||
|           <div className="text-center"> | ||||
|             <Button className="border-2" onClick={() => setNodeSwitcher(true)}> | ||||
|               {t('Change node')} | ||||
|             </Button> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </Splash> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| const Loading = () => { | ||||
|   const [showSlowNotification, setShowSlowNotification] = useState(false); | ||||
|   const t = useT(); | ||||
|   const setNodeSwitcher = useNodeSwitcherStore((store) => store.setDialogOpen); | ||||
|   useEffect(() => { | ||||
|     const to = setTimeout(() => { | ||||
|       if (!showSlowNotification) setShowSlowNotification(true); | ||||
|     }, 5000); | ||||
|     return () => { | ||||
|       clearTimeout(to); | ||||
|     }; | ||||
|   }, [showSlowNotification]); | ||||
|   return ( | ||||
|     <Splash> | ||||
|       <div className="border border-transparent m-10 mx-auto w-4/5 max-w-3xl rounded-lg overflow-hidden"> | ||||
|         <div className="mt-11 p-4 text-left text-sm"> | ||||
|           <Loader /> | ||||
|           {showSlowNotification && ( | ||||
|             <> | ||||
|               <p className="mt-4 text-center"> | ||||
|                 {t( | ||||
|                   "It looks like you're connection is slow, try switching to another node." | ||||
|                 )} | ||||
|               </p> | ||||
|               <div className="mt-4 text-center"> | ||||
|                 <Button | ||||
|                   className="border-2" | ||||
|                   onClick={() => setNodeSwitcher(true)} | ||||
|                 > | ||||
|                   {t('Change node')} | ||||
|                 </Button> | ||||
|               </div> | ||||
|             </> | ||||
|           )} | ||||
|         </div> | ||||
|       </div> | ||||
|     </Splash> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| export const Bootstrapper = ({ children }: { children: ReactNode }) => { | ||||
|   const t = useT(); | ||||
|   const { error, VEGA_URL } = useEnvironment(); | ||||
| 
 | ||||
|   const { error, VEGA_URL } = useEnvironment((state) => ({ | ||||
|     error: state.error, | ||||
|     VEGA_URL: state.VEGA_URL, | ||||
|   })); | ||||
|   const config = useVegaWalletConfig(); | ||||
| 
 | ||||
|   if (!config) { | ||||
|     return <AppLoader />; | ||||
|   } | ||||
| 
 | ||||
|   const ERR_DATA_LOADER = ( | ||||
|     <Trans | ||||
|       i18nKey="It appears that the connection to the node <0>{{VEGA_URL}}</0> does not return necessary data, try switching to another node." | ||||
|       components={[ | ||||
|         <span key="vega" className="text-muted"> | ||||
|           {VEGA_URL} | ||||
|         </span>, | ||||
|       ]} | ||||
|       values={{ | ||||
|         VEGA_URL, | ||||
|       }} | ||||
|     /> | ||||
|   ); | ||||
| 
 | ||||
|   return ( | ||||
|     <NetworkLoader | ||||
|       cache={cacheConfig} | ||||
|       skeleton={<AppLoader />} | ||||
|       failure={ | ||||
|         <AppFailure title={t('Could not initialize app')} error={error} /> | ||||
|       } | ||||
|       skeleton={<Loading />} | ||||
|       failure={<Failure reason={error} />} | ||||
|     > | ||||
|       <NodeGuard | ||||
|         skeleton={<AppLoader />} | ||||
|         failure={ | ||||
|           <NodeFailure | ||||
|             title={t('Node: {{VEGA_URL}} is unsuitable', { VEGA_URL })} | ||||
|           /> | ||||
|         } | ||||
|       <DataLoader | ||||
|         skeleton={<Loading />} | ||||
|         failure={<Failure reason={ERR_DATA_LOADER} />} | ||||
|       > | ||||
|         <DataLoader | ||||
|           skeleton={<AppLoader />} | ||||
|           failure={ | ||||
|             <AppFailure | ||||
|               title={t('Could not load market data or asset data')} | ||||
|               error={error} | ||||
|             /> | ||||
|           } | ||||
|         <Web3Provider | ||||
|           skeleton={<Loading />} | ||||
|           failure={<Failure reason={t('Could not configure web3 provider')} />} | ||||
|         > | ||||
|           <Web3Provider | ||||
|             skeleton={<AppLoader />} | ||||
|             failure={ | ||||
|               <AppFailure title={t('Could not configure web3 provider')} /> | ||||
|             } | ||||
|           > | ||||
|             <WalletProvider config={config}>{children}</WalletProvider> | ||||
|           </Web3Provider> | ||||
|         </DataLoader> | ||||
|       </NodeGuard> | ||||
|           <WalletProvider config={config}>{children}</WalletProvider> | ||||
|         </Web3Provider> | ||||
|       </DataLoader> | ||||
|     </NetworkLoader> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| @ -1,16 +0,0 @@ | ||||
| import { Splash } from '@vegaprotocol/ui-toolkit'; | ||||
| 
 | ||||
| export const AppFailure = ({ | ||||
|   title, | ||||
|   error, | ||||
| }: { | ||||
|   title: string; | ||||
|   error?: string | null; | ||||
| }) => { | ||||
|   return ( | ||||
|     <Splash> | ||||
|       <p className="mb-4 text-xl">{title}</p> | ||||
|       {error && <p className="mb-8 text-sm">{error}</p>} | ||||
|     </Splash> | ||||
|   ); | ||||
| }; | ||||
| @ -1 +0,0 @@ | ||||
| export * from './app-failure'; | ||||
| @ -1,4 +1,3 @@ | ||||
| export * from './app-failure'; | ||||
| export * from './app-loader'; | ||||
| export * from './network-loader'; | ||||
| export * from './network-switcher'; | ||||
|  | ||||
| @ -40,6 +40,16 @@ const mockStatsQuery = ( | ||||
|         vegaTime: new Date().toISOString(), | ||||
|         chainId: 'test-chain-id', | ||||
|       }, | ||||
|       networkParametersConnection: { | ||||
|         edges: [ | ||||
|           { | ||||
|             node: { | ||||
|               key: 'a', | ||||
|               value: '1', | ||||
|             }, | ||||
|           }, | ||||
|         ], | ||||
|       }, | ||||
|     }, | ||||
|   }, | ||||
| }); | ||||
| @ -335,6 +345,16 @@ describe('RowData', () => { | ||||
|               vegaTime: new Date().toISOString(), | ||||
|               chainId: 'test-chain-id', | ||||
|             }, | ||||
|             networkParametersConnection: { | ||||
|               edges: [ | ||||
|                 { | ||||
|                   node: { | ||||
|                     key: 'a', | ||||
|                     value: '1', | ||||
|                   }, | ||||
|                 }, | ||||
|               ], | ||||
|             }, | ||||
|           }, | ||||
|         }, | ||||
|       }; | ||||
|  | ||||
| @ -1,4 +1,9 @@ | ||||
| import { TradingRadio } from '@vegaprotocol/ui-toolkit'; | ||||
| import { | ||||
|   CopyWithTooltip, | ||||
|   TradingRadio, | ||||
|   VegaIcon, | ||||
|   VegaIconNames, | ||||
| } from '@vegaprotocol/ui-toolkit'; | ||||
| import { useEffect, useState } from 'react'; | ||||
| import { CUSTOM_NODE_KEY } from '../../types'; | ||||
| import { | ||||
| @ -127,8 +132,17 @@ export const RowData = ({ | ||||
|   return ( | ||||
|     <> | ||||
|       {id !== CUSTOM_NODE_KEY && ( | ||||
|         <div className="break-all" data-testid="node"> | ||||
|         <div className="break-all flex gap-2" data-testid="node"> | ||||
|           <TradingRadio id={`node-url-${id}`} value={url} label={url} /> | ||||
|           {url.length > 0 && url !== 'custom' && ( | ||||
|             <span className="text-muted"> | ||||
|               <CopyWithTooltip text={url}> | ||||
|                 <button> | ||||
|                   <VegaIcon name={VegaIconNames.COPY} /> | ||||
|                 </button> | ||||
|               </CopyWithTooltip> | ||||
|             </span> | ||||
|           )} | ||||
|         </div> | ||||
|       )} | ||||
|       <LayoutCell | ||||
|  | ||||
| @ -39,6 +39,19 @@ export const getMockStatisticsResult = ( | ||||
|     blockHeight: '11', | ||||
|     vegaTime: new Date().toISOString(), | ||||
|   }, | ||||
|   networkParametersConnection: { | ||||
|     __typename: 'NetworkParametersConnection', | ||||
|     edges: [ | ||||
|       { | ||||
|         __typename: 'NetworkParameterEdge', | ||||
|         node: { | ||||
|           __typename: 'NetworkParameter', | ||||
|           key: 'a', | ||||
|           value: '1', | ||||
|         }, | ||||
|       }, | ||||
|     ], | ||||
|   }, | ||||
| }); | ||||
| 
 | ||||
| export const getMockQueryResult = (env: Networks): NodeCheckQuery => ({ | ||||
| @ -48,6 +61,19 @@ export const getMockQueryResult = (env: Networks): NodeCheckQuery => ({ | ||||
|     blockHeight: '11', | ||||
|     vegaTime: new Date().toISOString(), | ||||
|   }, | ||||
|   networkParametersConnection: { | ||||
|     __typename: 'NetworkParametersConnection', | ||||
|     edges: [ | ||||
|       { | ||||
|         __typename: 'NetworkParameterEdge', | ||||
|         node: { | ||||
|           __typename: 'NetworkParameter', | ||||
|           key: 'a', | ||||
|           value: '1', | ||||
|         }, | ||||
|       }, | ||||
|     ], | ||||
|   }, | ||||
| }); | ||||
| 
 | ||||
| function getHandler<T>( | ||||
|  | ||||
| @ -34,6 +34,16 @@ const createDefaultMockClient = () => { | ||||
|             blockHeight: '100', | ||||
|             vegaTime: new Date().toISOString(), | ||||
|           }, | ||||
|           networkParametersConnection: { | ||||
|             edges: [ | ||||
|               { | ||||
|                 node: { | ||||
|                   key: 'something', | ||||
|                   value: 123, | ||||
|                 }, | ||||
|               }, | ||||
|             ], | ||||
|           }, | ||||
|         }, | ||||
|       }), | ||||
|     subscribe: () => ({ | ||||
| @ -183,6 +193,16 @@ describe('useEnvironment', () => { | ||||
|                   blockHeight: '100', | ||||
|                   vegaTime: new Date(1).toISOString(), | ||||
|                 }, | ||||
|                 networkParametersConnection: { | ||||
|                   edges: [ | ||||
|                     { | ||||
|                       node: { | ||||
|                         key: 'something', | ||||
|                         value: 123, | ||||
|                       }, | ||||
|                     }, | ||||
|                   ], | ||||
|                 }, | ||||
|               }, | ||||
|             }); | ||||
|           }, wait); | ||||
| @ -244,6 +264,16 @@ describe('useEnvironment', () => { | ||||
|               blockHeight: '100', | ||||
|               vegaTime: new Date().toISOString(), | ||||
|             }, | ||||
|             networkParametersConnection: { | ||||
|               edges: [ | ||||
|                 { | ||||
|                   node: { | ||||
|                     key: 'something', | ||||
|                     value: 123, | ||||
|                   }, | ||||
|                 }, | ||||
|               ], | ||||
|             }, | ||||
|           }, | ||||
|         }), | ||||
|       subscribe: () => ({ | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import { parse as tomlParse } from 'toml'; | ||||
| import { isValidUrl, LocalStorage } from '@vegaprotocol/utils'; | ||||
| import { LocalStorage, isValidUrl } from '@vegaprotocol/utils'; | ||||
| import { useEffect } from 'react'; | ||||
| import { create } from 'zustand'; | ||||
| import { createClient } from '@vegaprotocol/apollo-client'; | ||||
| @ -22,6 +22,7 @@ import uniq from 'lodash/uniq'; | ||||
| import orderBy from 'lodash/orderBy'; | ||||
| import first from 'lodash/first'; | ||||
| import { canMeasureResponseTime, measureResponseTime } from '../utils/time'; | ||||
| import compact from 'lodash/compact'; | ||||
| 
 | ||||
| type Client = ReturnType<typeof createClient>; | ||||
| type ClientCollection = { | ||||
| @ -70,10 +71,19 @@ const fetchConfig = async (url?: string) => { | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Find a suitable node by running a test query and test | ||||
|  * Find a suitable nodes by running a test query and test | ||||
|  * subscription, against a list of clients, first to resolve wins | ||||
|  */ | ||||
| const findNode = async (clients: ClientCollection): Promise<string | null> => { | ||||
| const findHealthyNodes = async (nodes: string[]) => { | ||||
|   const clients: ClientCollection = {}; | ||||
|   nodes.forEach((url) => { | ||||
|     clients[url] = createClient({ | ||||
|       url, | ||||
|       cacheConfig: undefined, | ||||
|       retry: false, | ||||
|       connectToDevTools: false, | ||||
|     }); | ||||
|   }); | ||||
|   const tests = Object.entries(clients).map((args) => testNode(...args)); | ||||
|   try { | ||||
|     const nodes = await Promise.all(tests); | ||||
| @ -93,11 +103,10 @@ const findNode = async (clients: ClientCollection): Promise<string | null> => { | ||||
|       ['desc', 'desc', 'asc'] | ||||
|     ); | ||||
| 
 | ||||
|     const best = first(ordered); | ||||
|     return best ? best.url : null; | ||||
|     return ordered; | ||||
|   } catch (err) { | ||||
|     // All tests rejected, no suitable node found
 | ||||
|     return null; | ||||
|     return []; | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| @ -142,6 +151,15 @@ const testQuery = ( | ||||
|       }) | ||||
|       .then((result) => { | ||||
|         if (result && !result.error) { | ||||
|           const netParams = compact( | ||||
|             result.data.networkParametersConnection.edges?.map((n) => n?.node) | ||||
|           ); | ||||
|           if (netParams.length === 0) { | ||||
|             // any node that doesn't return the network parameters is considered
 | ||||
|             // failed
 | ||||
|             resolve(false); | ||||
|             return; | ||||
|           } | ||||
|           const res = { | ||||
|             blockHeight: Number(result.data.statistics.blockHeight), | ||||
|             vegaTime: new Date(result.data.statistics.vegaTime), | ||||
| @ -600,32 +618,34 @@ export const useEnvironment = create<EnvStore>()((set, get) => ({ | ||||
|     } | ||||
| 
 | ||||
|     const state = get(); | ||||
|     const storedUrl = LocalStorage.getItem(STORAGE_KEY); | ||||
| 
 | ||||
|     let storedUrl = LocalStorage.getItem(STORAGE_KEY); | ||||
|     if (!isValidUrl(storedUrl)) { | ||||
|       // remove invalid data from local storage
 | ||||
|       LocalStorage.removeItem(STORAGE_KEY); | ||||
|       storedUrl = null; | ||||
|     } | ||||
| 
 | ||||
|     let nodes: string[] | undefined; | ||||
|     try { | ||||
|       nodes = await fetchConfig(state.VEGA_CONFIG_URL); | ||||
|       const enrichedNodes = uniq( | ||||
|         [...nodes, state.VEGA_URL, storedUrl].filter(Boolean) as string[] | ||||
|       nodes = uniq( | ||||
|         compact([ | ||||
|           // url from local storage
 | ||||
|           storedUrl, | ||||
|           // url from state (if set via env var)
 | ||||
|           state.VEGA_URL, | ||||
|           // urls from network configuration
 | ||||
|           ...(await fetchConfig(state.VEGA_CONFIG_URL)), | ||||
|         ]) | ||||
|       ); | ||||
|       set({ nodes: enrichedNodes }); | ||||
|       set({ nodes }); | ||||
|     } catch (err) { | ||||
|       console.warn(`Could not fetch node config from ${state.VEGA_CONFIG_URL}`); | ||||
|     } | ||||
| 
 | ||||
|     // Node url found in localStorage, if its valid attempt to connect
 | ||||
|     if (storedUrl) { | ||||
|       if (isValidUrl(storedUrl)) { | ||||
|         set({ VEGA_URL: storedUrl, status: 'success' }); | ||||
|         return; | ||||
|       } else { | ||||
|         LocalStorage.removeItem(STORAGE_KEY); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     // VEGA_URL env var is set and is a valid url no need to proceed
 | ||||
|     if (state.VEGA_URL) { | ||||
|       set({ status: 'success' }); | ||||
|     // skip picking up the best node if VEGA_URL env variable is set
 | ||||
|     if (state.VEGA_URL && isValidUrl(state.VEGA_URL)) { | ||||
|       state.setUrl(state.VEGA_URL); | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
| @ -639,37 +659,35 @@ export const useEnvironment = create<EnvStore>()((set, get) => ({ | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     // Create a map of node urls to client instances
 | ||||
|     const clients: ClientCollection = {}; | ||||
|     nodes.forEach((url) => { | ||||
|       clients[url] = createClient({ | ||||
|         url, | ||||
|         cacheConfig: undefined, | ||||
|         retry: false, | ||||
|         connectToDevTools: false, | ||||
|       }); | ||||
|     const healthyNodes = await findHealthyNodes(nodes); | ||||
| 
 | ||||
|     // A requested node is a node to which the app was previously connected
 | ||||
|     // or the one set via env variable.
 | ||||
|     const requestedNodeUrl = storedUrl || state.VEGA_URL; | ||||
| 
 | ||||
|     const bestNode = first(healthyNodes); | ||||
|     const requestedNode = healthyNodes.find( | ||||
|       (n) => requestedNodeUrl && n.url === requestedNodeUrl | ||||
|     ); | ||||
|     if (!requestedNode) { | ||||
|       // remove unhealthy node url from local storage
 | ||||
|       LocalStorage.removeItem(STORAGE_KEY); | ||||
|     } | ||||
|     // A node's url (VEGA_URL) is either the requested node (previously
 | ||||
|     // connected or taken form env variable) or the currently best available
 | ||||
|     // node.
 | ||||
|     const url = requestedNode?.url || bestNode?.url; | ||||
| 
 | ||||
|     if (url != null) { | ||||
|       state.setUrl(url); | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     set({ | ||||
|       status: 'failed', | ||||
|       error: 'No suitable node found', | ||||
|     }); | ||||
| 
 | ||||
|     // Find a suitable node to connect to by attempting a query and a
 | ||||
|     // subscription, first to fulfill both will be the resulting url.
 | ||||
|     const url = await findNode(clients); | ||||
| 
 | ||||
|     if (url !== null) { | ||||
|       set({ | ||||
|         status: 'success', | ||||
|         VEGA_URL: url, | ||||
|       }); | ||||
|       LocalStorage.setItem(STORAGE_KEY, url); | ||||
|     } | ||||
|     // Every node failed either to make a query or retrieve data from
 | ||||
|     // a subscription
 | ||||
|     else { | ||||
|       set({ | ||||
|         status: 'failed', | ||||
|         error: 'No node found', | ||||
|       }); | ||||
|       console.warn('No suitable vega node was found'); | ||||
|     } | ||||
|     console.warn('No suitable node was found'); | ||||
|   }, | ||||
| })); | ||||
| 
 | ||||
|  | ||||
| @ -28,6 +28,16 @@ const createStatsMock = ( | ||||
|           blockHeight: blockHeight.toString(), | ||||
|           vegaTime: '12345', | ||||
|         }, | ||||
|         networkParametersConnection: { | ||||
|           edges: [ | ||||
|             { | ||||
|               node: { | ||||
|                 key: 'a', | ||||
|                 value: '1', | ||||
|               }, | ||||
|             }, | ||||
|           ], | ||||
|         }, | ||||
|       }, | ||||
|     }, | ||||
|   }; | ||||
|  | ||||
| @ -1,9 +1,19 @@ | ||||
| query NodeCheck { | ||||
|   # statistics needed to get the most recent node in terms of block height | ||||
|   statistics { | ||||
|     chainId | ||||
|     blockHeight | ||||
|     vegaTime | ||||
|   } | ||||
|   # net params needed to filter out the nodes that are not suitable | ||||
|   networkParametersConnection { | ||||
|     edges { | ||||
|       node { | ||||
|         key | ||||
|         value | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| subscription NodeCheckTimeUpdate { | ||||
|  | ||||
| @ -6,7 +6,7 @@ const defaultOptions = {} as const; | ||||
| export type NodeCheckQueryVariables = Types.Exact<{ [key: string]: never; }>; | ||||
| 
 | ||||
| 
 | ||||
| export type NodeCheckQuery = { __typename?: 'Query', statistics: { __typename?: 'Statistics', chainId: string, blockHeight: string, vegaTime: any } }; | ||||
| export type NodeCheckQuery = { __typename?: 'Query', statistics: { __typename?: 'Statistics', chainId: string, blockHeight: string, vegaTime: any }, networkParametersConnection: { __typename?: 'NetworkParametersConnection', edges?: Array<{ __typename?: 'NetworkParameterEdge', node: { __typename?: 'NetworkParameter', key: string, value: string } } | null> | null } }; | ||||
| 
 | ||||
| export type NodeCheckTimeUpdateSubscriptionVariables = Types.Exact<{ [key: string]: never; }>; | ||||
| 
 | ||||
| @ -21,6 +21,14 @@ export const NodeCheckDocument = gql` | ||||
|     blockHeight | ||||
|     vegaTime | ||||
|   } | ||||
|   networkParametersConnection { | ||||
|     edges { | ||||
|       node { | ||||
|         key | ||||
|         value | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|     `;
 | ||||
| 
 | ||||
|  | ||||
| @ -12,6 +12,19 @@ export const statisticsQuery = ( | ||||
|       blockHeight: '11', | ||||
|       vegaTime: new Date().toISOString(), | ||||
|     }, | ||||
|     networkParametersConnection: { | ||||
|       __typename: 'NetworkParametersConnection', | ||||
|       edges: [ | ||||
|         { | ||||
|           __typename: 'NetworkParameterEdge', | ||||
|           node: { | ||||
|             __typename: 'NetworkParameter', | ||||
|             key: 'a', | ||||
|             value: '1', | ||||
|           }, | ||||
|         }, | ||||
|       ], | ||||
|     }, | ||||
|   }; | ||||
| 
 | ||||
|   return merge(defaultResult, override); | ||||
|  | ||||
| @ -485,5 +485,7 @@ | ||||
|   "See all the live games on the cards below. Every on-chain game is community funded and designed. <0>Find out how to create one</0>.": "See all the live games on the cards below. Every on-chain game is community funded and designed. <0>Find out how to create one</0>.", | ||||
|   "Teams can earn rewards if they meet the goals set in the on-chain trading competitions. Track your earned rewards here, and see which teams are top of the leaderboard this month.": "Teams can earn rewards if they meet the goals set in the on-chain trading competitions. Track your earned rewards here, and see which teams are top of the leaderboard this month.", | ||||
|   "Currently no active games on the network.": "Currently no active games on the network.", | ||||
|   "Currently no active teams on the network.": "Currently no active teams on the network." | ||||
|   "Currently no active teams on the network.": "Currently no active teams on the network.", | ||||
|   "It looks like you're connection is slow, try switching to another node.": "It looks like you're connection is slow, try switching to another node.", | ||||
|   "It appears that the connection to the node <0>{{VEGA_URL}}</0> does not return necessary data, try switching to another node.": "It appears that the connection to the node <0>{{VEGA_URL}}</0> does not return necessary data, try switching to another node." | ||||
| } | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| export const isValidUrl = (url?: string) => { | ||||
| export const isValidUrl = (url?: string | null) => { | ||||
|   if (!url) return false; | ||||
|   try { | ||||
|     new URL(url); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user