diff --git a/getWebpackConfig.js b/getWebpackConfig.js index 67a63bfe..f6430d55 100644 --- a/getWebpackConfig.js +++ b/getWebpackConfig.js @@ -158,9 +158,6 @@ function getWebpackConfig({ apps = [], config = {}, envVars = {}, defineVars = { assert(templatePath, '"templatePath" missing in config') assert(logoPath, '"logoPath" missing in config') - // Log the apps - console.log('apps', apps) - // Generate one entry point per app const entryPoints = apps.reduce((acc, app) => { const { name } = app diff --git a/package.json b/package.json index b54880ff..19435815 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cowprotocol/explorer", - "version": "2.24.0", + "version": "2.25.0", "description": "", "main": "src/index.js", "sideEffects": false, @@ -46,9 +46,9 @@ "author": "", "dependencies": { "@apollo/client": "^3.1.5", - "@cowprotocol/app-data": "v0.1.0", + "@cowprotocol/app-data": "^1.0.2", "@cowprotocol/contracts": "1.3.1", - "@cowprotocol/cow-sdk": "^2.0.6", + "@cowprotocol/cow-sdk": "^2.2.1", "@fortawesome/fontawesome-svg-core": "^6.1.2", "@fortawesome/free-regular-svg-icons": "^6.1.2", "@fortawesome/free-solid-svg-icons": "^6.1.2", diff --git a/src/api/operator/types.ts b/src/api/operator/types.ts index 0878cff3..503c90da 100644 --- a/src/api/operator/types.ts +++ b/src/api/operator/types.ts @@ -18,7 +18,7 @@ export type RawOrder = EnrichedOrder */ export type Order = Pick< RawOrder, - 'owner' | 'uid' | 'appData' | 'kind' | 'partiallyFillable' | 'signature' | 'class' + 'owner' | 'uid' | 'appData' | 'kind' | 'partiallyFillable' | 'signature' | 'class' | 'fullAppData' > & { receiver: string txHash?: string diff --git a/src/apps/explorer/components/TransactionsTableWidget/index.tsx b/src/apps/explorer/components/TransactionsTableWidget/index.tsx index 8f680eb8..b8d261ca 100644 --- a/src/apps/explorer/components/TransactionsTableWidget/index.tsx +++ b/src/apps/explorer/components/TransactionsTableWidget/index.tsx @@ -1,22 +1,22 @@ -import React, { useState, useEffect, useCallback } from 'react' +import React, { useCallback, useEffect, useState } from 'react' import { faListUl, faProjectDiagram } from '@fortawesome/free-solid-svg-icons' -import { useHistory } from 'react-router-dom' -import { useQuery } from 'hooks/useQuery' +import { useQuery, useUpdateQueryString } from 'hooks/useQuery' import { BlockchainNetwork, TransactionsTableContext } from './context/TransactionsTableContext' import { useGetTxOrders, useTxOrderExplorerLink } from 'hooks/useGetOrders' import RedirectToSearch from 'components/RedirectToSearch' import { RedirectToNetwork, useNetworkId } from 'state/network' import { Order } from 'api/operator' import { TransactionsTableWithData } from 'apps/explorer/components/TransactionsTableWidget/TransactionsTableWithData' -import { TabItemInterface, TabIcon } from 'components/common/Tabs/Tabs' +import { TabIcon, TabItemInterface } from 'components/common/Tabs/Tabs' import ExplorerTabs from '../common/ExplorerTabs/ExplorerTabs' -import { TitleAddress, FlexContainer, Title } from 'apps/explorer/pages/styled' +import { FlexContainer, Title, TitleAddress } from 'apps/explorer/pages/styled' import { BlockExplorerLink } from 'components/common/BlockExplorerLink' import { ConnectionStatus } from 'components/ConnectionStatus' import { Notification } from 'components/Notification' import { TransactionBatchGraph } from 'apps/explorer/components/TransanctionBatchGraph' import CowLoading from 'components/common/CowLoading' +import { TAB_QUERY_PARAM_KEY } from 'apps/explorer/const' interface Props { txHash: string @@ -33,7 +33,7 @@ const DEFAULT_TAB = TabView[1] function useQueryViewParams(): { tab: string } { const query = useQuery() - return { tab: query.get('tab')?.toUpperCase() || DEFAULT_TAB } // if URL param empty will be used DEFAULT + return { tab: query.get(TAB_QUERY_PARAM_KEY)?.toUpperCase() || DEFAULT_TAB } // if URL param empty will be used DEFAULT } const tabItems = (orders: Order[] | undefined, networkId: BlockchainNetwork, txHash: string): TabItemInterface[] => { @@ -61,7 +61,7 @@ export const TransactionsTableWidget: React.FC = ({ txHash }) => { const isZeroOrders = !!(orders && orders.length === 0) const notGpv2ExplorerData = useTxOrderExplorerLink(txHash, isZeroOrders) - const history = useHistory() + const updateQueryString = useUpdateQueryString() // Avoid redirecting until another network is searched again useEffect(() => { @@ -81,9 +81,10 @@ export const TransactionsTableWidget: React.FC = ({ txHash }) => { setTabViewSelected(TabView[newTabViewName]) }, []) - useEffect(() => { - history.replace({ search: `?tab=${TabView[tabViewSelected].toLowerCase()}` }) - }, [history, tabViewSelected]) + useEffect( + () => updateQueryString(TAB_QUERY_PARAM_KEY, TabView[tabViewSelected].toLowerCase()), + [tabViewSelected, updateQueryString], + ) if (errorTxPresentInNetworkId && networkId != errorTxPresentInNetworkId) { return diff --git a/src/apps/explorer/components/TransanctionBatchGraph/hooks.ts b/src/apps/explorer/components/TransanctionBatchGraph/hooks.ts index d4e1fcd0..76b16c1b 100644 --- a/src/apps/explorer/components/TransanctionBatchGraph/hooks.ts +++ b/src/apps/explorer/components/TransanctionBatchGraph/hooks.ts @@ -18,8 +18,7 @@ import { PopperInstance, ViewType, } from 'apps/explorer/components/TransanctionBatchGraph/types' -import { useQuery } from 'hooks/useQuery' -import { useHistory } from 'react-router-dom' +import { useQuery, useUpdateQueryString } from 'hooks/useQuery' import { Order } from 'api/operator' import { useTransactionData } from 'hooks/useTransactionData' import { @@ -205,18 +204,9 @@ function useQueryViewParams(): { visualization: string } { } function useUpdateVisQuery(): (vis: string) => void { - const query = useQuery() - const history = useHistory() - - // TODO: this is causing one extra re-render as the query is being updated when history is updated - // TODO: make it not depend on query - return useCallback( - (vis: string) => { - query.set(VISUALIZATION_PARAM_NAME, vis) - history.replace({ search: query.toString() }) - }, - [history, query], - ) + const updateQueryString = useUpdateQueryString() + + return useCallback((vis: string) => updateQueryString(VISUALIZATION_PARAM_NAME, vis), [updateQueryString]) } export function useTxBatchData( diff --git a/src/apps/explorer/const.ts b/src/apps/explorer/const.ts index d19d2e3f..09bfcd79 100644 --- a/src/apps/explorer/const.ts +++ b/src/apps/explorer/const.ts @@ -53,3 +53,5 @@ export const SPECIAL_ADDRESSES = { '0xa03be496e67ec29bc62f01a428683d7f9c204930': 'Solver Rewards Safe', '0xca771eda0c70aa7d053ab1b25004559b918fe662': 'CoW DAO', } + +export const TAB_QUERY_PARAM_KEY = 'tab' diff --git a/src/apps/explorer/pages/AppData/EncodePage.tsx b/src/apps/explorer/pages/AppData/EncodePage.tsx index 7c416be5..793f75fc 100644 --- a/src/apps/explorer/pages/AppData/EncodePage.tsx +++ b/src/apps/explorer/pages/AppData/EncodePage.tsx @@ -1,27 +1,22 @@ import React, { useCallback, useEffect, useState } from 'react' import Form, { FormValidation } from '@rjsf/core' import { JSONSchema7 } from 'json-schema' -import { IpfsHashInfo } from '@cowprotocol/app-data' -import { DEFAULT_IPFS_READ_URI } from 'const' +import { IpfsHashInfo, stringifyDeterministic } from '@cowprotocol/app-data' + import { RowWithCopyButton } from 'components/common/RowWithCopyButton' -import Spinner from 'components/common/Spinner' + import AppDataWrapper from 'components/common/AppDataWrapper' -import { Notification } from 'components/Notification' + import { INITIAL_FORM_VALUES, - INVALID_IPFS_CREDENTIALS, getSchema, transformErrors, handleErrors, - handleFormatData, - ipfsSchema, uiSchema, - ipfsUiSchema, CustomField, FormProps, } from './config' -import { TabData, TabView } from '.' -import { IpfsWrapper } from './styled' +import { TabData } from '.' import { metadataApiSDK } from 'cowSdk' type EncodeProps = { @@ -29,13 +24,19 @@ type EncodeProps = { setTabData: React.Dispatch> handleTabChange: (tabId: number) => void } +type FullAppData = { fullAppData: string; fullAppDataPrettified: string; isValidAppData: boolean } -const EncodePage: React.FC = ({ tabData, setTabData, handleTabChange }) => { +const EncodePage: React.FC = ({ tabData, setTabData /* handleTabChange */ }) => { const { encode } = tabData const [schema, setSchema] = useState(encode.options.schema ?? {}) const [appDataForm, setAppDataForm] = useState(encode.formData) + const [{ fullAppData, fullAppDataPrettified, isValidAppData }, setFullAppData] = useState({ + fullAppData: '', + fullAppDataPrettified: '', + isValidAppData: false, + }) const [disabledAppData, setDisabledAppData] = useState(encode.options.disabledAppData ?? true) - const [disabledIPFS, setDisabledIPFS] = useState(encode.options.disabledIPFS ?? true) + const [disabledIPFS /* setDisabledIPFS*/] = useState(encode.options.disabledIPFS ?? true) const [invalidFormDataAttempted, setInvalidFormDataAttempted] = useState<{ appData: boolean; ipfs: boolean }>( encode.options.invalidFormDataAttempted ?? { appData: false, @@ -44,15 +45,12 @@ const EncodePage: React.FC = ({ tabData, setTabData, handleTabChang ) const [isLoading, setIsLoading] = useState(encode.options.isLoading ?? false) const [ipfsHashInfo, setIpfsHashInfo] = useState(encode.options.ipfsHashInfo) - const [ipfsCredentials, setIpfsCredentials] = useState<{ pinataApiKey?: string; pinataApiSecret?: string }>( + const [ipfsCredentials /* setIpfsCredentials */] = useState<{ pinataApiKey?: string; pinataApiSecret?: string }>( encode.options.ipfsCredentials ?? {}, ) const [isDocUploaded, setIsDocUploaded] = useState(encode.options.isDocUploaded ?? false) const [error, setError] = useState(encode.options.error) const formRef = React.useRef>(null) - const ipfsFormRef = React.useRef>(null) - - const isDisabled = !appDataForm.metadata?.orderClass?.orderClass || disabledAppData useEffect(() => { const fetchSchema = async (): Promise => { @@ -98,6 +96,34 @@ const EncodePage: React.FC = ({ tabData, setTabData, handleTabChang setTabData, ]) + useEffect(() => { + _toFullAppData(appDataForm).then(setFullAppData) + }, [appDataForm]) + + useEffect(() => { + setIsLoading(true) + + // Get the fullAppData (dermenistic stringify JSON) + _toFullAppData(appDataForm) + .then((fullAppData) => { + // Update the fullAppData + setFullAppData(fullAppData) + + // Get the IPFS hash + return metadataApiSDK.appDataToCid(fullAppData.fullAppData) + }) + // Update CID + .then(setIpfsHashInfo) + .catch((e) => { + console.error('Error updating the IPFS Hash info (CID, hex)', e) + setError(e.message) + }) + .finally(() => { + setIsLoading(false) + toggleInvalid({ appData: true }) + }) + }, [appDataForm]) + const toggleInvalid = (data: { [key: string]: boolean }): void => { setInvalidFormDataAttempted((prevState) => ({ ...prevState, ...data })) } @@ -107,10 +133,10 @@ const EncodePage: React.FC = ({ tabData, setTabData, handleTabChang [], ) - const handleIPFSErrors = useCallback( - (_: FormProps, errors: FormValidation): FormValidation => handleErrors(ipfsFormRef, errors, setDisabledIPFS), - [], - ) + // const handleIPFSErrors = useCallback( + // (_: FormProps, errors: FormValidation): FormValidation => handleErrors(ipfsFormRef, errors, setDisabledIPFS), + // [], + // ) const handleOnChange = useCallback( ({ formData }: FormProps): void => { @@ -127,62 +153,53 @@ const EncodePage: React.FC = ({ tabData, setTabData, handleTabChang resetFormFields('appData') setError(undefined) } - if (JSON.stringify(handleFormatData(formData)) !== JSON.stringify(INITIAL_FORM_VALUES)) { - setDisabledAppData(false) - } }, [ipfsHashInfo], ) - const onSubmit = useCallback(async ({ formData }: FormProps): Promise => { - setIsLoading(true) - try { - const hashInfo = await metadataApiSDK.calculateAppDataHash(handleFormatData(formData)) - setIpfsHashInfo(hashInfo) - } catch (e) { - setError(e.message) - } finally { - setIsLoading(false) - toggleInvalid({ appData: true }) - } - }, []) + // const handleIPFSOnChange = useCallback(({ formData: ipfsData }: FormProps): void => { + // setIpfsCredentials(ipfsData) + // if (JSON.stringify(ipfsData) !== JSON.stringify({})) { + // setDisabledIPFS(false) + // } + // }, []) - const handleIPFSOnChange = useCallback(({ formData: ipfsData }: FormProps): void => { - setIpfsCredentials(ipfsData) - if (JSON.stringify(ipfsData) !== JSON.stringify({})) { - setDisabledIPFS(false) - } - }, []) - - const onUploadToIPFS = useCallback( - async ({ formData }: FormProps): Promise => { - if (!ipfsHashInfo) return - setIsLoading(true) - try { - await metadataApiSDK.uploadMetadataDocToIpfs(handleFormatData(appDataForm), formData) - setIsDocUploaded(true) - } catch (e) { - if (INVALID_IPFS_CREDENTIALS.includes(e.message)) { - e.message = 'Invalid API keys provided.' - } - setError(e.message) - setIsDocUploaded(false) - } finally { - setIsLoading(false) - toggleInvalid({ ipfs: true }) - } - }, - [appDataForm, ipfsHashInfo], - ) + // const onUploadToIPFS = useCallback( + // async ({ formData }: FormProps): Promise => { + // if (!ipfsHashInfo) return + // setIsLoading(true) + // try { + // await metadataApiSDK.uploadMetadataDocToIpfsLegacy(handleFormatData(appDataForm), formData) + // setIsDocUploaded(true) + // } catch (e) { + // if (INVALID_IPFS_CREDENTIALS.includes(e.message)) { + // e.message = 'Invalid API keys provided.' + // } + // setError(e.message) + // setIsDocUploaded(false) + // } finally { + // setIsLoading(false) + // toggleInvalid({ ipfs: true }) + // } + // }, + // [appDataForm, ipfsHashInfo], + // ) return ( <>

- The appData is an optional field part of CoW Protocol orders. It allows users/dapps/wallets - to attach meta-information to orders. This is useful for giving context to your orders, like crediting the - order to a specific UI, adding affiliate information, or even signalling your order should be treated in a - special way. + The{' '} + + AppData hex + + is an optional field part of CoW Protocol orders. It allows users/dapps/wallets to attach meta-information to + orders. This is useful for giving context to your orders, like crediting the order to a specific UI, adding + affiliate information, or even signalling your order should be treated in a special way.

This field is the hexadecimal digest of an IPFS document’s CID of a JSON file.

@@ -211,45 +228,98 @@ const EncodePage: React.FC = ({ tabData, setTabData, handleTabChang transformErrors={transformErrors} ref={formRef} autoComplete="off" - onSubmit={onSubmit} onError={(): void => toggleInvalid({ appData: true })} schema={schema} uiSchema={uiSchema} > - + <>

+

💅 AppData prettified

This is the generated and prettified file based on the input you provided on the form.

- {JSON.stringify(handleFormatData(appDataForm), null, 2)} - } - /> +

This content is for illustration purposes, see below.

+ + {fullAppData && ( + <> +

ℹ️ AppData string

+

+ This is the actual content that is hashed using keccak-256 to get the{' '} + AppData hex. +

+

+ This UI formats the provided info using a{' '} + + deterministic JSON formatter + {' '} + , this way the same content yields always the same AppData hex. +

+ +

Note: Don’t forget to upload this file to IPFS!

+ + )} {ipfsHashInfo && ( <> -

AppData Hash

+

🐮 AppData hex

- This is the corresponding hash of the above document. Use this in your orders appData{' '} - field. + This is the keccak-256 hash of the above document represented in hexadecimal format. +

+

+ Use this in your{' '} + + CoW Orders appData field + {' '} + .

+

🌍 IPFS CiD

+

+ This is the{' '} + + IPFS CID + + . +

+

+ This CID is derived from the AppData hex ( + + see here how + + ). You can see how this AppData hex is encoded, using the{' '} + + CID Inspector + + . +

+

+ What this means is that you can derived the IPFS CID from on-chain CoW Orders, and download the JSON + from IPFS network to see the meta-information of that order. +

+ -

Note: Don’t forget to upload this file to IPFS!

)}
-
+ {/*
{ipfsHashInfo && ( <> @@ -313,10 +383,10 @@ const EncodePage: React.FC = ({ tabData, setTabData, handleTabChang <> - {ipfsHashInfo?.cidV0} + + {ipfsHashInfo?.cid} } /> @@ -332,9 +402,46 @@ const EncodePage: React.FC = ({ tabData, setTabData, handleTabChang {error && !isDocUploaded && ( )} -
+
+ */} ) } +function JsonContent({ content, isError }: { content: string; isError: boolean }): JSX.Element { + return ( + <> + {content}} + /> + {isError && The AppData content is not valid, check the errors in the input form.} + + ) +} + +async function _toFullAppData(formData: FormProps): Promise { + const doc = await metadataApiSDK.generateAppDataDoc(formData) + + // Cleanup doc (remove undefined props since it messess up with the validation) + const metadata = doc.metadata + Object.keys(metadata).forEach((key) => { + const meta = metadata[key] + Object.keys(meta).forEach((k) => { + if (!meta[k]) { + delete meta[k] + } + }) + if (!meta || Object.keys(meta).length === 0) { + delete metadata[key] + } + }) + + return { + fullAppData: await stringifyDeterministic(doc), // deterministic string + fullAppDataPrettified: JSON.stringify(doc, null, 2), // prettified string + isValidAppData: await metadataApiSDK.validateAppDataDoc(doc).then((result) => result.success), + } +} + export default EncodePage diff --git a/src/apps/explorer/pages/AppData/config.tsx b/src/apps/explorer/pages/AppData/config.tsx index 08f27dd7..b865d11c 100644 --- a/src/apps/explorer/pages/AppData/config.tsx +++ b/src/apps/explorer/pages/AppData/config.tsx @@ -1,8 +1,9 @@ import React, { RefObject } from 'react' import Form, { AjvError, FieldProps, FormValidation } from '@rjsf/core' -import { LATEST_APP_DATA_VERSION, getAppDataSchema } from '@cowprotocol/app-data' +import { LATEST_APP_DATA_VERSION } from '@cowprotocol/app-data' import { JSONSchema7 } from 'json-schema' import { HelpTooltip } from 'components/Tooltip' +import { metadataApiSDK } from 'cowSdk' const ERROR_MESSAGES = { REQUIRED: 'Required field.', @@ -24,65 +25,24 @@ export const INVALID_IPFS_CREDENTIALS = [ export type FormProps = Record export const getSchema = async (): Promise => { - const latestSchema = (await getAppDataSchema(LATEST_APP_DATA_VERSION)) as JSONSchema7 + const latestSchema = (await metadataApiSDK.getAppDataSchema(LATEST_APP_DATA_VERSION)) as JSONSchema7 deleteAllPropertiesByName(latestSchema, 'examples') deleteAllPropertiesByName(latestSchema, '$id') return formatSchema(latestSchema) } -const setDependencies = (formattedSchema: JSONSchema7, field: string, dependencies: { [key: string]: any }): void => { - if (formattedSchema?.properties?.metadata['properties'][field]) { - const requiredFields = formattedSchema.properties.metadata['properties'][field].required - deletePropertyPath(formattedSchema, `properties.metadata.properties.${field}.required`) - - const properties = formattedSchema.properties.metadata['properties'][field].properties - const [fieldKey] = Object.keys(dependencies) - formattedSchema.properties.metadata['properties'][field].properties = { - [fieldKey]: { type: 'boolean', title: 'Enable/Disable' }, - } - dependencies[fieldKey].oneOf[0] = { - properties: { - ...dependencies[fieldKey].oneOf[0].properties, - ...properties, - }, - required: requiredFields, - } - formattedSchema.properties.metadata['properties'][field].dependencies = dependencies - } -} - const formatSchema = (schema: JSONSchema7): JSONSchema7 => { const formattedSchema = structuredClone(schema) - setDependencies(formattedSchema, 'quote', quoteDependencies) - setDependencies(formattedSchema, 'referrer', referrerDependencies) - return formattedSchema } -export const handleFormatData = (formData: FormProps): any => { - if (!formData.metadata || !Object.keys(formData.metadata).length) return formData - const formattedData = structuredClone(formData) - const isReferrerEnabled = formattedData.metadata.referrer.enableReferrer - const isQuoteEnabled = formattedData.metadata.quote.enableQuote - - deletePropertyPath(formattedData, 'metadata.referrer.enableReferrer') - deletePropertyPath(formattedData, 'metadata.quote.enableQuote') - - if (!isReferrerEnabled) { - deletePropertyPath(formattedData, 'metadata.referrer') - } - if (!isQuoteEnabled) { - deletePropertyPath(formattedData, 'metadata.quote') - } - - return formattedData -} - export const transformErrors = (errors: AjvError[]): AjvError[] => { return errors.reduce((errorsList, error) => { if (error.name === 'required') { - error.message = ERROR_MESSAGES.REQUIRED + // Disable the non-required fields (it validates the required fields on un-required fields) + // error.message = ERROR_MESSAGES.REQUIRED + return errorsList } else { if (error.property === '.metadata.referrer.address') { error.message = ERROR_MESSAGES.INVALID_ADDRESS @@ -150,36 +110,6 @@ export const deletePropertyPath = (obj: any, path: any): void => { } } -const quoteDependencies = { - enableQuote: { - oneOf: [ - { - properties: { - enableQuote: { - const: true, - }, - }, - required: [], - }, - ], - }, -} - -const referrerDependencies = { - enableReferrer: { - oneOf: [ - { - properties: { - enableReferrer: { - const: true, - }, - }, - required: [], - }, - ], - }, -} - export const ipfsSchema: JSONSchema7 = { type: 'object', required: ['pinataApiKey', 'pinataApiSecret'], @@ -205,7 +135,7 @@ export const decodeAppDataSchema: JSONSchema7 = { properties: { appData: { type: 'string', - title: 'AppData', + title: 'AppData Hex', pattern: '^0x[a-fA-F0-9]{64}', }, }, @@ -253,20 +183,12 @@ export const uiSchema = { }, metadata: { referrer: { - version: { - 'ui:field': 'cField', - tooltip: 'The schema will be versioned using Semantic Versioning.', - }, address: { 'ui:field': 'cField', tooltip: 'Add a valid address to enable referrer.', }, }, quote: { - version: { - 'ui:field': 'cField', - tooltip: 'The schema will be versioned using Semantic Versioning.', - }, slippageBips: { 'ui:field': 'cField', tooltip: 'Set the slippage in BIPS (e.g. "0.3").', diff --git a/src/apps/explorer/pages/AppData/index.tsx b/src/apps/explorer/pages/AppData/index.tsx index a403b87c..75cdbae4 100644 --- a/src/apps/explorer/pages/AppData/index.tsx +++ b/src/apps/explorer/pages/AppData/index.tsx @@ -1,8 +1,7 @@ -import React, { useCallback, useState, useEffect } from 'react' -import { useHistory } from 'react-router-dom' +import React, { useCallback, useEffect, useState } from 'react' import { Helmet } from 'react-helmet' import { faCode, faListUl } from '@fortawesome/free-solid-svg-icons' -import { useQuery } from 'hooks/useQuery' +import { useQuery, useUpdateQueryString } from 'hooks/useQuery' import EncodePage from './EncodePage' import DecodePage from './DecodePage' import TabIcon from 'components/common/Tabs/TabIcon' @@ -12,7 +11,7 @@ import { ContentCard as Content, Title } from 'apps/explorer/pages/styled' import { FormProps } from './config' import { StyledExplorerTabs, Wrapper } from './styled' -import { APP_TITLE } from 'apps/explorer/const' +import { APP_TITLE, TAB_QUERY_PARAM_KEY } from 'apps/explorer/const' export enum TabView { ENCODE = 1, @@ -28,7 +27,7 @@ const DEFAULT_TAB = TabView[1] function useQueryViewParams(): { tab: string } { const query = useQuery() - return { tab: query.get('tab')?.toUpperCase() || DEFAULT_TAB } // if URL param empty will be used DEFAULT + return { tab: query.get(TAB_QUERY_PARAM_KEY)?.toUpperCase() || DEFAULT_TAB } // if URL param empty will be used DEFAULT } const tabItems = ( @@ -51,13 +50,13 @@ const tabItems = ( } const AppDataPage: React.FC = () => { - const history = useHistory() const { tab } = useQueryViewParams() const [tabData, setTabData] = useState({ encode: { formData: {}, options: {} }, decode: { formData: {}, options: {} }, }) const [tabViewSelected, setTabViewSelected] = useState(TabView[tab] || TabView[DEFAULT_TAB]) // use DEFAULT when URL param is outside the enum + const updateQueryString = useUpdateQueryString() const onChangeTab = useCallback((tabId: number) => { const newTabViewName = TabView[tabId] @@ -65,9 +64,10 @@ const AppDataPage: React.FC = () => { setTabViewSelected(TabView[newTabViewName]) }, []) - useEffect(() => { - history.replace({ search: `?tab=${TabView[tabViewSelected].toLowerCase()}` }) - }, [history, tabViewSelected]) + useEffect( + () => updateQueryString(TAB_QUERY_PARAM_KEY, TabView[tabViewSelected].toLowerCase()), + [tabViewSelected, updateQueryString], + ) return ( diff --git a/src/apps/explorer/pages/AppData/styled.ts b/src/apps/explorer/pages/AppData/styled.ts index 27f10394..3042fc9a 100644 --- a/src/apps/explorer/pages/AppData/styled.ts +++ b/src/apps/explorer/pages/AppData/styled.ts @@ -3,6 +3,7 @@ import { ContentCard as Content, Wrapper as WrapperTemplate } from 'apps/explore import { media } from 'theme/styles/media' import AppDataWrapper from 'components/common/AppDataWrapper' import ExplorerTabs from 'apps/explorer/components/common/ExplorerTabs/ExplorerTabs' +import { transparentize } from 'polished' export const StyledExplorerTabs = styled(ExplorerTabs)` margin: 1.6rem auto 0; @@ -71,6 +72,10 @@ export const Wrapper = styled(WrapperTemplate)` ${media.mobile} { max-width: none; } + + &.error { + background: ${({ theme }): string => transparentize(0.8, theme.red1)}; + } } .hidden-content { padding: 0 1rem; @@ -83,7 +88,7 @@ export const Wrapper = styled(WrapperTemplate)` } .appData-hash { margin: 0 0 1rem 0; - max-width: 54rem; + max-width: 55rem; border: 1px solid ${({ theme }): string => theme.tableRowBorder}; padding: 0.75rem; background: ${({ theme }): string => theme.tableRowBorder}; @@ -136,8 +141,9 @@ export const Wrapper = styled(WrapperTemplate)` top: 4rem; width: 60rem; } - h4 { - margin: 1rem 0 0.75rem 0; + h2 { + margin: 2rem 0 2rem 0; + font-size: 2rem; } } } @@ -306,6 +312,10 @@ export const Wrapper = styled(WrapperTemplate)` } } } + + span.error { + color: ${(props): string => props.theme.red1}; + } ` export const IpfsWrapper = styled.div` display: flex; diff --git a/src/components/AppData/DecodeAppData.tsx b/src/components/AppData/DecodeAppData.tsx index 08e37cad..7f041616 100644 --- a/src/components/AppData/DecodeAppData.tsx +++ b/src/components/AppData/DecodeAppData.tsx @@ -5,16 +5,40 @@ import { RowWithCopyButton } from 'components/common/RowWithCopyButton' import { Notification } from 'components/Notification' import Spinner from 'components/common/Spinner' import { DEFAULT_IPFS_READ_URI, IPFS_INVALID_APP_IDS } from 'const' -import { getCidHashFromAppData, getDecodedAppData } from 'hooks/useAppData' +import { appDataHexToCid, fetchDocFromAppDataHex } from 'hooks/useAppData' import useSafeState from 'hooks/useSafeState' type Props = { appData: string + fullAppData?: string showExpanded?: boolean } +async function _getDecodedAppData( + appData: string, + isLegacyAppDataHex: boolean, + fullAppData?: string, +): Promise<{ decodedAppData?: void | AnyAppDataDocVersion; isError: boolean }> { + // If the full appData is available, we try to parse it as JSON + if (fullAppData) { + try { + const decodedAppData = JSON.parse(fullAppData) + return { decodedAppData, isError: false } + } catch (error) { + console.error('Error parsing fullAppData from the API', { fullAppData }, error) + } + } + + if (IPFS_INVALID_APP_IDS.includes(appData.toString())) { + return { isError: true } + } + + const decodedAppData = await fetchDocFromAppDataHex(appData.toString(), isLegacyAppDataHex) + return { isError: false, decodedAppData } +} + const DecodeAppData = (props: Props): JSX.Element => { - const { appData, showExpanded = false } = props + const { appData, showExpanded = false, fullAppData } = props const [appDataLoading, setAppDataLoading] = useSafeState(false) const [appDataError, setAppDataError] = useSafeState(false) const [decodedAppData, setDecodedAppData] = useSafeState(undefined) @@ -22,18 +46,21 @@ const DecodeAppData = (props: Props): JSX.Element => { const [showDecodedAppData, setShowDecodedAppData] = useSafeState(showExpanded) + // Old AppData use a different way to derive the CID (we know is old if fullAppData is not available) + const isLegacyAppDataHex = fullAppData === undefined + useEffect(() => { const fetchIPFS = async (): Promise => { try { - const decodedAppDataHex = await getCidHashFromAppData(appData.toString()) - setIpfsUri(`${DEFAULT_IPFS_READ_URI}/${decodedAppDataHex}`) + const cid = await appDataHexToCid(appData.toString(), isLegacyAppDataHex) + setIpfsUri(`${DEFAULT_IPFS_READ_URI}/${cid}`) } catch { setAppDataError(true) } } fetchIPFS() - }, [appData, setAppDataError, setIpfsUri]) + }, [appData, setAppDataError, setIpfsUri, isLegacyAppDataHex]) const handleDecodedAppData = useCallback( async (isOpen?: boolean): Promise => { @@ -41,14 +68,15 @@ const DecodeAppData = (props: Props): JSX.Element => { setShowDecodedAppData(!showDecodedAppData) } if (decodedAppData) return - if (IPFS_INVALID_APP_IDS.includes(appData.toString())) { - setAppDataError(true) - return - } + setAppDataLoading(true) try { - const decodedAppData = await getDecodedAppData(appData.toString()) - setDecodedAppData(decodedAppData) + const { isError, decodedAppData } = await _getDecodedAppData(appData, isLegacyAppDataHex, fullAppData) + if (isError) { + setAppDataError(true) + } else { + setDecodedAppData(decodedAppData) + } } catch (e) { setDecodedAppData(undefined) setAppDataError(true) @@ -58,12 +86,14 @@ const DecodeAppData = (props: Props): JSX.Element => { }, [ appData, + fullAppData, decodedAppData, setAppDataError, setAppDataLoading, setDecodedAppData, setShowDecodedAppData, showDecodedAppData, + isLegacyAppDataHex, ], ) @@ -100,7 +130,7 @@ const DecodeAppData = (props: Props): JSX.Element => {
{appDataError ? ( {appData} - ) : ( + ) : isLegacyAppDataHex ? ( { } /> + ) : ( + // TODO: Remove this, and leave just the LINK after the backend uploads the IPFS documents + // https://cowservices.slack.com/archives/C0375NV72SC/p1689618027267289 + appData )} => handleDecodedAppData(false)}> {showDecodedAppData ? '[-] Show less' : '[+] Show more'} diff --git a/src/components/common/SurplusComponent/index.tsx b/src/components/common/SurplusComponent/index.tsx index eeabc155..862149ac 100644 --- a/src/components/common/SurplusComponent/index.tsx +++ b/src/components/common/SurplusComponent/index.tsx @@ -5,6 +5,7 @@ import { TokenErc20 } from '@gnosis.pm/dex-js' import { TokenAmount } from 'components/token/TokenAmount' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { IconDefinition } from '@fortawesome/free-solid-svg-icons' +import { MAX_SURPLUS_PERCENTAGE } from 'const' const IconWrapper = styled(FontAwesomeIcon)` padding: 0 0.5rem 0 0; @@ -31,6 +32,11 @@ export const Amount = styled.span<{ showHiddenSection: boolean; strechHiddenSect `} ` +const Wrapper = styled.span` + display: flex; + align-items: center; +` + export type SurplusComponentProps = { surplus: Surplus | null token: TokenErc20 | null @@ -49,13 +55,19 @@ export const SurplusComponent: React.FC = (props) => { const { percentage, amount } = surplus + const showPercentage = percentage.lt(MAX_SURPLUS_PERCENTAGE) + + const amountDisplay = ( + + + + ) + return ( - + {icon && } - {formatPercentage(percentage)} - - - - + {showPercentage ? formatPercentage(percentage) : amountDisplay} + {showPercentage && amountDisplay} + ) } diff --git a/src/components/orders/DetailsTable/index.tsx b/src/components/orders/DetailsTable/index.tsx index 4d757dcc..76e3fc1f 100644 --- a/src/components/orders/DetailsTable/index.tsx +++ b/src/components/orders/DetailsTable/index.tsx @@ -24,6 +24,7 @@ import { GasFeeDisplay } from 'components/orders/GasFeeDisplay' import { sendEvent } from 'components/analytics' import { LinkWithPrefixNetwork } from 'components/common/LinkWithPrefixNetwork' import DecodeAppData from 'components/AppData/DecodeAppData' +import { TAB_QUERY_PARAM_KEY } from 'apps/explorer/const' const Table = styled(SimpleTable)` > tbody > tr { @@ -194,6 +195,7 @@ export function DetailsTable(props: Props): JSX.Element | null { buyToken, sellToken, appData, + fullAppData, } = order if (!buyToken || !sellToken) { @@ -260,7 +262,7 @@ export function DetailsTable(props: Props): JSX.Element | null { onCopy={(): void => onCopy('settlementTx')} contentsToDisplay={{txHash}} /> - + View batch graph @@ -357,7 +359,7 @@ export function DetailsTable(props: Props): JSX.Element | null { {showFillsButton && ( - + View fills @@ -385,7 +387,7 @@ export function DetailsTable(props: Props): JSX.Element | null { AppData - + diff --git a/src/components/orders/OrderDetails/index.tsx b/src/components/orders/OrderDetails/index.tsx index e0660ea0..b63b6a44 100644 --- a/src/components/orders/OrderDetails/index.tsx +++ b/src/components/orders/OrderDetails/index.tsx @@ -1,5 +1,4 @@ import React, { useCallback, useEffect, useState } from 'react' -import { useHistory } from 'react-router-dom' import styled from 'styled-components' import { Order, Trade } from 'api/operator' @@ -8,7 +7,7 @@ import { Errors } from 'types' import { formatPercentage } from 'utils' import { FillsTableContext } from './context/FillsTableContext' import { media } from 'theme/styles/media' -import { useQuery } from 'hooks/useQuery' +import { useQuery, useUpdateQueryString } from 'hooks/useQuery' import { DetailsTable } from 'components/orders/DetailsTable' import { RowWithCopyButton } from 'components/common/RowWithCopyButton' import RedirectToSearch from 'components/RedirectToSearch' @@ -22,6 +21,7 @@ import { useTable } from 'apps/explorer/components/TokensTableWidget/useTable' import ExplorerTabs from 'apps/explorer/components/common/ExplorerTabs/ExplorerTabs' import { FillsTableWithData } from './FillsTableWithData' +import { TAB_QUERY_PARAM_KEY } from 'apps/explorer/const' const TitleUid = styled(RowWithCopyButton)` color: ${({ theme }): string => theme.grey}; @@ -74,7 +74,7 @@ const DEFAULT_TAB = TabView[1] function useQueryViewParams(): { tab: string } { const query = useQuery() - return { tab: query.get('tab')?.toUpperCase() || DEFAULT_TAB } // if URL param empty will be used DEFAULT + return { tab: query.get(TAB_QUERY_PARAM_KEY)?.toUpperCase() || DEFAULT_TAB } // if URL param empty will be used DEFAULT } const tabItems = ( @@ -166,7 +166,7 @@ export const OrderDetails: React.FC = (props) => { const invertPrice = useCallback((): void => setIsPriceInverted((prev) => !prev), []) const [redirectTo, setRedirectTo] = useState(false) - const history = useHistory() + const updateQueryString = useUpdateQueryString() tableState['hasNextPage'] = tableState.pageOffset + tableState.pageSize < trades.length tableState['totalResults'] = trades.length @@ -195,9 +195,10 @@ export const OrderDetails: React.FC = (props) => { setTabViewSelected(TabView[newTabViewName]) }, []) - useEffect(() => { - history.replace({ search: `?tab=${TabView[tabViewSelected].toLowerCase()}` }) - }, [history, tabViewSelected]) + useEffect( + () => updateQueryString(TAB_QUERY_PARAM_KEY, TabView[tabViewSelected].toLowerCase()), + [tabViewSelected, updateQueryString], + ) if (redirectTo) { return diff --git a/src/const.ts b/src/const.ts index d32acbb7..216e14b6 100644 --- a/src/const.ts +++ b/src/const.ts @@ -1,7 +1,8 @@ import BigNumber from 'bignumber.js' import BN from 'bn.js' -import { TokenErc20, UNLIMITED_ORDER_AMOUNT, BATCH_TIME } from '@gnosis.pm/dex-js' +import { BATCH_TIME, TokenErc20, UNLIMITED_ORDER_AMOUNT } from '@gnosis.pm/dex-js' import { SupportedChainId } from '@cowprotocol/cow-sdk' + export { UNLIMITED_ORDER_AMOUNT, FEE_DENOMINATOR, @@ -196,3 +197,10 @@ export const IPFS_INVALID_APP_IDS = [ '0xf6a005bde820da47fdbb19bc07e56782b9ccec403a6899484cf502090627af8a', '0x00000000000000000000000055662e225a3376759c24331a9aed764f8f0c9fbb', ] + +/** + * The maximum percentage of the surplus that can be used for the surplus + * Values above this will not be displayed. + * Instead, the Surplus amount will be used + */ +export const MAX_SURPLUS_PERCENTAGE = '1000' diff --git a/src/hooks/useAppData.ts b/src/hooks/useAppData.ts index ffb44660..83988140 100644 --- a/src/hooks/useAppData.ts +++ b/src/hooks/useAppData.ts @@ -6,6 +6,7 @@ import { DEFAULT_IPFS_READ_URI } from 'const' export const useAppData = ( appDataHash: string, + isLegacyAppDataHex: boolean, ): { isLoading: boolean; appDataDoc: AnyAppDataDocVersion | void | undefined } => { const network = useNetworkId() || undefined const [isLoading, setLoading] = useState(false) @@ -14,7 +15,7 @@ export const useAppData = ( async function getAppDataDoc(): Promise { setLoading(true) try { - const decodedAppData = await getDecodedAppData(appDataHash) + const decodedAppData = await fetchDocFromAppDataHex(appDataHash, isLegacyAppDataHex) setAppDataDoc(decodedAppData) } catch (e) { const msg = `Failed to fetch appData document` @@ -25,15 +26,20 @@ export const useAppData = ( } } getAppDataDoc() - }, [appDataHash, network]) + }, [appDataHash, network, isLegacyAppDataHex]) return { isLoading, appDataDoc } } -export const getDecodedAppData = (appDataHash: string): Promise => { - return metadataApiSDK.decodeAppData(appDataHash, DEFAULT_IPFS_READ_URI) +export const fetchDocFromAppDataHex = ( + appDataHex: string, + isLegacyAppDataHex: boolean, +): Promise => { + const method = isLegacyAppDataHex ? 'fetchDocFromAppDataHexLegacy' : 'fetchDocFromAppDataHex' + return metadataApiSDK[method](appDataHex, DEFAULT_IPFS_READ_URI) } -export const getCidHashFromAppData = (appDataHash: string): Promise => { - return metadataApiSDK.appDataHexToCid(appDataHash) +export const appDataHexToCid = (appDataHash: string, isLegacyAppDataHex: boolean): Promise => { + const method = isLegacyAppDataHex ? 'appDataHexToCidLegacy' : 'appDataHexToCid' + return metadataApiSDK[method](appDataHash) } diff --git a/src/hooks/useQuery.ts b/src/hooks/useQuery.ts index 72b3e2f1..72f16def 100644 --- a/src/hooks/useQuery.ts +++ b/src/hooks/useQuery.ts @@ -1,6 +1,7 @@ import { useLocation } from 'react-router' -import { useMemo } from 'react' -import { sanitizeInput, checkDateOrValidBatchTime } from 'utils' +import { useCallback, useMemo } from 'react' +import { checkDateOrValidBatchTime, sanitizeInput } from 'utils' +import { useHistory } from 'react-router-dom' export function useQuery(): URLSearchParams { const { search } = useLocation() @@ -32,3 +33,16 @@ export function useQueryTradeParams(): { validUntil: checkDateOrValidBatchTime(query.get('expires')), } } + +export function useUpdateQueryString(): (key: string, value: string) => void { + const query = useQuery() + const history = useHistory() + + return useCallback( + (key: string, value: string) => { + query.set(key, value) + history.replace({ search: query.toString() }) + }, + [history, query], + ) +} diff --git a/src/utils/format.ts b/src/utils/format.ts index 3b3c8733..29887e29 100644 --- a/src/utils/format.ts +++ b/src/utils/format.ts @@ -2,23 +2,23 @@ import BigNumber from 'bignumber.js' import { parseBytes32String } from '@ethersproject/strings' import { arrayify } from 'ethers/lib/utils' -import { TokenErc20, formatSmart, safeTokenName } from '@gnosis.pm/dex-js' +import { formatSmart, safeTokenName, TokenErc20 } from '@gnosis.pm/dex-js' import { - ONE_HUNDRED_BIG_NUMBER, BATCH_TIME_IN_MS, DEFAULT_DECIMALS, + MINIMUM_ATOM_VALUE, ONE_BIG_NUMBER, + ONE_HUNDRED_BIG_NUMBER, TEN_BIG_NUMBER, - MINIMUM_ATOM_VALUE, } from 'const' import { - MIDDLE_PRECISION_DECIMALS, HIGH_PRECISION_DECIMALS, HIGH_PRECISION_SMALL_LIMIT, + MIDDLE_PRECISION_DECIMALS, NO_ADJUSTMENT_NEEDED_PRECISION, } from 'apps/explorer/const' -import { FormatAmountPrecision, batchIdToDate } from 'utils' +import { batchIdToDate, FormatAmountPrecision } from 'utils' export { formatSmart, @@ -156,7 +156,15 @@ export function formatPercentage(percentage: BigNumber): string { if (displayPercentage.gt('99.99') && displayPercentage.lt('100')) { return '>99.99%' } - return displayPercentage.decimalPlaces(2, BigNumber.ROUND_FLOOR).toString(10) + '%' + + return ( + formatSmart({ + amount: displayPercentage.decimalPlaces(2, BigNumber.ROUND_DOWN).toString(), + precision: 0, + thousandSeparator: false, + decimals: 2, + }) + '%' + ) } /** diff --git a/yarn.lock b/yarn.lock index 8d5b01bb..fbff9dff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11,9 +11,9 @@ "@jridgewell/trace-mapping" "^0.3.9" "@apollo/client@^3.1.5": - version "3.7.15" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.7.15.tgz#59dbeb5d64479f8ce0af321d8c0bf6df1d873e2d" - integrity sha512-pLScjo4GAQRWKWyEg2J3FlQr9fbUAuADT0EI2+JlLf2BjaU9I7WUaZVD9w+0qNPE8BZqs53MRQq0OCm1QCW+eg== + version "3.7.16" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.7.16.tgz#418cb23566a6d52e9e22d34484167149269efd40" + integrity sha512-rdhoc7baSD7ZzcjavEpYN8gZJle1KhjEKj4SJeMgBpcnO4as7oXUVU4LtFpotzZdFlo57qaLrNzfvppSTsKvZQ== dependencies: "@graphql-typed-document-node/core" "^3.1.1" "@wry/context" "^0.7.0" @@ -53,10 +53,10 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.7.tgz#61caffb60776e49a57ba61a88f02bedd8714f6bc" integrity sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA== -"@babel/compat-data@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.5.tgz#b1f6c86a02d85d2dd3368a2b67c09add8cd0c255" - integrity sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA== +"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" + integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== "@babel/core@7.12.9": version "7.12.9" @@ -81,30 +81,30 @@ source-map "^0.5.0" "@babel/core@>=7.9.0", "@babel/core@^7.1.0", "@babel/core@^7.12.10", "@babel/core@^7.7.5", "@babel/core@^7.9.4": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.5.tgz#d67d9747ecf26ee7ecd3ebae1ee22225fe902a89" - integrity sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg== + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.9.tgz#bd96492c68822198f33e8a256061da3cf391f58f" + integrity sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w== dependencies: "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.22.5" - "@babel/generator" "^7.22.5" - "@babel/helper-compilation-targets" "^7.22.5" - "@babel/helper-module-transforms" "^7.22.5" - "@babel/helpers" "^7.22.5" - "@babel/parser" "^7.22.5" + "@babel/generator" "^7.22.9" + "@babel/helper-compilation-targets" "^7.22.9" + "@babel/helper-module-transforms" "^7.22.9" + "@babel/helpers" "^7.22.6" + "@babel/parser" "^7.22.7" "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.5" + "@babel/traverse" "^7.22.8" "@babel/types" "^7.22.5" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.2" - semver "^6.3.0" + semver "^6.3.1" -"@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.22.5", "@babel/generator@^7.4.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.5.tgz#1e7bf768688acfb05cf30b2369ef855e82d984f7" - integrity sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA== +"@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.22.7", "@babel/generator@^7.22.9", "@babel/generator@^7.4.0": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.9.tgz#572ecfa7a31002fa1de2a9d91621fd895da8493d" + integrity sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw== dependencies: "@babel/types" "^7.22.5" "@jridgewell/gen-mapping" "^0.3.2" @@ -140,16 +140,16 @@ "@babel/helper-explode-assignable-expression" "^7.18.6" "@babel/types" "^7.18.9" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.5", "@babel/helper-compilation-targets@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.5.tgz#fc7319fc54c5e2fa14b2909cf3c5fd3046813e02" - integrity sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw== +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.5", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz#f9d0a7aaaa7cd32a3f31c9316a69f5a9bcacb892" + integrity sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw== dependencies: - "@babel/compat-data" "^7.22.5" + "@babel/compat-data" "^7.22.9" "@babel/helper-validator-option" "^7.22.5" - browserslist "^4.21.3" + browserslist "^4.21.9" lru-cache "^5.1.1" - semver "^6.3.0" + semver "^6.3.1" "@babel/helper-create-class-features-plugin@^7.14.5", "@babel/helper-create-class-features-plugin@^7.15.0": version "7.15.0" @@ -224,17 +224,16 @@ resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-define-polyfill-provider@^0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.0.tgz#487053f103110f25b9755c5980e031e93ced24d8" - integrity sha512-RnanLx5ETe6aybRi1cO/edaRH+bNYWaryCEmjDDYyNr4wnSzyOp8T0dWipmqVHKEY3AbVKUom50AKSlj1zmKbg== +"@babel/helper-define-polyfill-provider@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.1.tgz#af1429c4a83ac316a6a8c2cc8ff45cb5d2998d3a" + integrity sha512-kX4oXixDxG197yhX+J3Wp+NpL2wuCFjWQAr6yX2jtCnflK9ulMI51ULFGIrWiX1jGfvAxdHp+XQCcP2bZGPs9A== dependencies: - "@babel/helper-compilation-targets" "^7.17.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-plugin-utils" "^7.22.5" debug "^4.1.1" lodash.debounce "^4.0.8" resolve "^1.14.2" - semver "^6.1.2" "@babel/helper-environment-visitor@^7.18.9": version "7.18.9" @@ -335,19 +334,16 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.5", "@babel/helper-module-transforms@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz#0f65daa0716961b6e96b164034e737f60a80d2ef" - integrity sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw== +"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.5", "@babel/helper-module-transforms@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz#92dfcb1fbbb2bc62529024f72d942a8c97142129" + integrity sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ== dependencies: "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-module-imports" "^7.22.5" "@babel/helper-simple-access" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" "@babel/helper-validator-identifier" "^7.22.5" - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.5" - "@babel/types" "^7.22.5" "@babel/helper-optimise-call-expression@^7.14.5": version "7.14.5" @@ -440,10 +436,10 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-split-export-declaration@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz#88cf11050edb95ed08d596f7a044462189127a08" - integrity sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ== +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== dependencies: "@babel/types" "^7.22.5" @@ -482,13 +478,13 @@ "@babel/traverse" "^7.20.5" "@babel/types" "^7.20.5" -"@babel/helpers@^7.12.5", "@babel/helpers@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.5.tgz#74bb4373eb390d1ceed74a15ef97767e63120820" - integrity sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q== +"@babel/helpers@^7.12.5", "@babel/helpers@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.6.tgz#8e61d3395a4f0c5a8060f309fb008200969b5ecd" + integrity sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA== dependencies: "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.5" + "@babel/traverse" "^7.22.6" "@babel/types" "^7.22.5" "@babel/highlight@^7.10.4": @@ -509,10 +505,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.22.5", "@babel/parser@^7.4.3": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.5.tgz#721fd042f3ce1896238cf1b341c77eb7dee7dbea" - integrity sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q== +"@babel/parser@^7.1.0", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.22.5", "@babel/parser@^7.22.7", "@babel/parser@^7.4.3": + version "7.22.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae" + integrity sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" @@ -1088,16 +1084,16 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-runtime@^7.5.5", "@babel/plugin-transform-runtime@^7.8.3": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.5.tgz#ca975fb5e260044473c8142e1b18b567d33c2a3b" - integrity sha512-bg4Wxd1FWeFx3daHFTWk1pkSWK/AyQuiyAoeZAOkAOUBjnZPH6KT7eMxouV47tQ6hl6ax2zyAWBdWZXbrvXlaw== + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.9.tgz#a87b11e170cbbfb018e6a2bf91f5c6e533b9e027" + integrity sha512-9KjBH61AGJetCPYp/IEyLEp47SyybZb0nDRpBvmtEkm+rUIwxdlKpyNHI1TmsGkeuLclJdleQHRZ8XLBnnh8CQ== dependencies: "@babel/helper-module-imports" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" - babel-plugin-polyfill-corejs2 "^0.4.3" - babel-plugin-polyfill-corejs3 "^0.8.1" - babel-plugin-polyfill-regenerator "^0.5.0" - semver "^6.3.0" + babel-plugin-polyfill-corejs2 "^0.4.4" + babel-plugin-polyfill-corejs3 "^0.8.2" + babel-plugin-polyfill-regenerator "^0.5.1" + semver "^6.3.1" "@babel/plugin-transform-shorthand-properties@^7.12.1", "@babel/plugin-transform-shorthand-properties@^7.18.6": version "7.18.6" @@ -1309,18 +1305,18 @@ "@babel/parser" "^7.22.5" "@babel/types" "^7.22.5" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.1.6", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.15.0", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.22.5", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.5.tgz#44bd276690db6f4940fdb84e1cb4abd2f729ccd1" - integrity sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ== +"@babel/traverse@^7.1.0", "@babel/traverse@^7.1.6", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.15.0", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.22.6", "@babel/traverse@^7.22.8", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.5": + version "7.22.8" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.8.tgz#4d4451d31bc34efeae01eac222b514a77aa4000e" + integrity sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw== dependencies: "@babel/code-frame" "^7.22.5" - "@babel/generator" "^7.22.5" + "@babel/generator" "^7.22.7" "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-function-name" "^7.22.5" "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.5" - "@babel/parser" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.22.7" "@babel/types" "^7.22.5" debug "^4.1.0" globals "^11.1.0" @@ -1357,14 +1353,15 @@ resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== -"@cowprotocol/app-data@v0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@cowprotocol/app-data/-/app-data-0.1.0.tgz#99a5ac52dcb21044fa11b81cb5a90db2c544e4a6" - integrity sha512-E7Fk0LjtCp/TCyEtcudJpY+RqCfdPjlDIkqsLjjGeNd6TeRlZGmZhYXZGT1E4QV75PiMHufdd3WMkTwd7Aufdw== +"@cowprotocol/app-data@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@cowprotocol/app-data/-/app-data-1.0.2.tgz#e559ed892265b84d926a5c84ce6add14e597bb57" + integrity sha512-tf4KGK+moHEAjgmOQ8E7MaRHM/rscbzFxsLE/Q+bLXNYcrcwmFDn/VjahkO7bMWhDQj4whmdgw8rbHlPo7zjdw== dependencies: ajv "^8.11.0" cross-fetch "^3.1.5" ipfs-only-hash "^4.0.0" + json-stringify-deterministic "^1.0.8" multiformats "^9.6.4" "@cowprotocol/contracts@1.3.1": @@ -1377,10 +1374,10 @@ resolved "https://registry.yarnpkg.com/@cowprotocol/contracts/-/contracts-1.4.0.tgz#e93e5f25aac76feeaa348fa57231903274676247" integrity sha512-XLs3SlPmXD4lbiWIO7mxxuCn1eE5isuO6EUlE1cj17HqN/wukDAN0xXYPx6umOH/XdjGS33miMiPHELEyY9siw== -"@cowprotocol/cow-sdk@^2.0.6": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@cowprotocol/cow-sdk/-/cow-sdk-2.1.0.tgz#b66adf0c4f6d9e6690762a1c3e169db5ba8feff1" - integrity sha512-/2bM58kddP1um9COO5XNPl88gOmnzAPr8fK6EGsdPkPuAQZtCfOWGvqajTtM+shixA21ZaATcSnNL57s5Zd8qw== +"@cowprotocol/cow-sdk@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@cowprotocol/cow-sdk/-/cow-sdk-2.2.1.tgz#496c7caf3a17e8873e8e4bb4cdbd54b2f966ac1e" + integrity sha512-EjvVnfHsM9a/f60msa2MebDQTF7137cIMr/fuCb4K0AdUAsk5NqTrr6fVrp+YMyVrIj9OZ/zhaJUxDfGcwza3w== dependencies: "@cowprotocol/contracts" "^1.4.0" "@ethersproject/abstract-signer" "^5.7.0" @@ -2833,6 +2830,11 @@ resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" integrity sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw== +"@nicolo-ribaudo/semver-v6@^6.3.3": + version "6.3.3" + resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz#ea6d23ade78a325f7a52750aab1526b02b628c29" + integrity sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -4138,9 +4140,9 @@ "@types/cytoscape" "*" "@types/cytoscape@*", "@types/cytoscape@^3.19.4": - version "3.19.9" - resolved "https://registry.yarnpkg.com/@types/cytoscape/-/cytoscape-3.19.9.tgz#d697971d4a33eacc6ae7a7a158035b5c60c064a2" - integrity sha512-oqCx0ZGiBO0UESbjgq052vjDAy2X53lZpMrWqiweMpvVwKw/2IiYDdzPFK6+f4tMfdv9YKEM9raO5bAZc3UYBg== + version "3.19.10" + resolved "https://registry.yarnpkg.com/@types/cytoscape/-/cytoscape-3.19.10.tgz#f4540749d68cd3db6f89da5197f7ec2a2ca516ee" + integrity sha512-PLsKQcsUd05nz4PYyulIhjkLnlq9oD2WYpswrWOjoqtFZEuuBje0f9fi2zTG5/yfTf5+Gpllf/MPcFmfDzZ24w== "@types/enzyme-adapter-react-16@^1.0.5": version "1.0.6" @@ -5138,7 +5140,7 @@ address@^1.1.2: aes-js@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" - integrity sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0= + integrity sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw== aes-js@^3.1.1: version "3.1.2" @@ -5805,14 +5807,14 @@ babel-plugin-polyfill-corejs2@^0.3.3: "@babel/helper-define-polyfill-provider" "^0.3.3" semver "^6.1.1" -babel-plugin-polyfill-corejs2@^0.4.3: - version "0.4.3" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.3.tgz#75044d90ba5043a5fb559ac98496f62f3eb668fd" - integrity sha512-bM3gHc337Dta490gg+/AseNB9L4YLHxq1nGKZZSHbhXv4aTYU2MD2cjza1Ru4S6975YLTaL1K8uJf6ukJhhmtw== +babel-plugin-polyfill-corejs2@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.4.tgz#9f9a0e1cd9d645cc246a5e094db5c3aa913ccd2b" + integrity sha512-9WeK9snM1BfxB38goUEv2FLnA6ja07UMfazFHzCXUb3NyDZAwfXvQiURQ6guTTMeHcOsdknULm1PDhs4uWtKyA== dependencies: - "@babel/compat-data" "^7.17.7" - "@babel/helper-define-polyfill-provider" "^0.4.0" - semver "^6.1.1" + "@babel/compat-data" "^7.22.6" + "@babel/helper-define-polyfill-provider" "^0.4.1" + "@nicolo-ribaudo/semver-v6" "^6.3.3" babel-plugin-polyfill-corejs3@^0.1.0: version "0.1.7" @@ -5830,13 +5832,13 @@ babel-plugin-polyfill-corejs3@^0.6.0: "@babel/helper-define-polyfill-provider" "^0.3.3" core-js-compat "^3.25.1" -babel-plugin-polyfill-corejs3@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.1.tgz#39248263c38191f0d226f928d666e6db1b4b3a8a" - integrity sha512-ikFrZITKg1xH6pLND8zT14UPgjKHiGLqex7rGEZCH2EvhsneJaJPemmpQaIZV5AL03II+lXylw3UmddDK8RU5Q== +babel-plugin-polyfill-corejs3@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.2.tgz#d406c5738d298cd9c66f64a94cf8d5904ce4cc5e" + integrity sha512-Cid+Jv1BrY9ReW9lIfNlNpsI53N+FN7gE+f73zLAUbr9C52W4gKLWSByx47pfDJsEysojKArqOtOKZSVIIUTuQ== dependencies: - "@babel/helper-define-polyfill-provider" "^0.4.0" - core-js-compat "^3.30.1" + "@babel/helper-define-polyfill-provider" "^0.4.1" + core-js-compat "^3.31.0" babel-plugin-polyfill-regenerator@^0.4.1: version "0.4.1" @@ -5845,12 +5847,12 @@ babel-plugin-polyfill-regenerator@^0.4.1: dependencies: "@babel/helper-define-polyfill-provider" "^0.3.3" -babel-plugin-polyfill-regenerator@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.0.tgz#e7344d88d9ef18a3c47ded99362ae4a757609380" - integrity sha512-hDJtKjMLVa7Z+LwnTCxoDLQj6wdc+B8dun7ayF2fYieI6OzfuvcLMB32ihJZ4UhCBwNYGl5bg/x/P9cMdnkc2g== +babel-plugin-polyfill-regenerator@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.1.tgz#ace7a5eced6dff7d5060c335c52064778216afd3" + integrity sha512-L8OyySuI6OSQ5hFy9O+7zFjyr4WhAfRjLIOkhQGYl+emwJkd/S4XXT1JpfrgR1jrQ1NcGiOh+yAdGlF8pnC3Jw== dependencies: - "@babel/helper-define-polyfill-provider" "^0.4.0" + "@babel/helper-define-polyfill-provider" "^0.4.1" babel-plugin-react-docgen@^4.2.1: version "4.2.1" @@ -6334,7 +6336,7 @@ browserslist@^4.12.0: escalade "^3.1.1" node-releases "^1.1.73" -browserslist@^4.14.5, browserslist@^4.18.1, browserslist@^4.21.3, browserslist@^4.21.4: +browserslist@^4.14.5, browserslist@^4.18.1, browserslist@^4.21.4: version "4.21.4" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== @@ -6344,13 +6346,13 @@ browserslist@^4.14.5, browserslist@^4.18.1, browserslist@^4.21.3, browserslist@^ node-releases "^2.0.6" update-browserslist-db "^1.0.9" -browserslist@^4.21.5: - version "4.21.6" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.6.tgz#18ab9830a5a61806a909a4717f85665792e7f267" - integrity sha512-PF07dKGXKR+/bljJzCB6rAYtHEu21TthLxmJagtQizx+rwiqdRDBO5971Xu1N7MgcMLi4+mr4Cnl76x7O3DHtA== +browserslist@^4.21.9: + version "4.21.9" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.9.tgz#e11bdd3c313d7e2a9e87e8b4b0c7872b13897635" + integrity sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg== dependencies: - caniuse-lite "^1.0.30001489" - electron-to-chromium "^1.4.411" + caniuse-lite "^1.0.30001503" + electron-to-chromium "^1.4.431" node-releases "^2.0.12" update-browserslist-db "^1.0.11" @@ -6692,10 +6694,10 @@ caniuse-lite@^1.0.30001400: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001441.tgz#987437b266260b640a23cd18fbddb509d7f69f3e" integrity sha512-OyxRR4Vof59I3yGWXws6i908EtGbMzVUi3ganaZQHmydk1iwDhRnvaPG2WaR0KcqrDFKrxVZHULT396LEPhXfg== -caniuse-lite@^1.0.30001489: - version "1.0.30001489" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001489.tgz#ca82ee2d4e4dbf2bd2589c9360d3fcc2c7ba3bd8" - integrity sha512-x1mgZEXK8jHIfAxm+xgdpHpk50IN3z3q3zP261/WS+uvePxW8izXuCu6AHz0lkuYTlATDehiZ/tNyYBdSQsOUQ== +caniuse-lite@^1.0.30001503: + version "1.0.30001516" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001516.tgz#621b1be7d85a8843ee7d210fd9d87b52e3daab3a" + integrity sha512-Wmec9pCBY8CWbmI4HsjBeQLqDTqV91nFVR83DnZpYyRnPI1wePDsTg0bGLPC5VU/3OIZV1fmxEea1b+tFKe86g== capture-exit@^2.0.0: version "2.0.0" @@ -7404,12 +7406,12 @@ core-js-compat@^3.25.1, core-js-compat@^3.8.1: dependencies: browserslist "^4.21.4" -core-js-compat@^3.30.1: - version "3.30.2" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.30.2.tgz#83f136e375babdb8c80ad3c22d67c69098c1dd8b" - integrity sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA== +core-js-compat@^3.31.0: + version "3.31.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.31.1.tgz#5084ad1a46858df50ff89ace152441a63ba7aae0" + integrity sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA== dependencies: - browserslist "^4.21.5" + browserslist "^4.21.9" core-js-pure@^3.23.3, core-js-pure@^3.6.5: version "3.27.1" @@ -8429,10 +8431,10 @@ electron-to-chromium@^1.4.251: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== -electron-to-chromium@^1.4.411: - version "1.4.411" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.411.tgz#8cb7787f0442fcb4209590e9951bdb482caa93b2" - integrity sha512-5VXLW4Qw89vM2WTICHua/y8v7fKGDRVa2VPOtBB9IpLvW316B+xd8yD1wTmLPY2ot/00P/qt87xdolj4aG/Lzg== +electron-to-chromium@^1.4.431: + version "1.4.461" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.461.tgz#6b14af66042732bf883ab63a4d82cac8f35eb252" + integrity sha512-1JkvV2sgEGTDXjdsaQCeSwYYuhLRphRpc+g6EHTFELJXEiznLt3/0pZ9JuAOQ5p2rI3YxKTbivtvajirIfhrEQ== elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3: version "6.5.4" @@ -8519,9 +8521,9 @@ enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.1, enhanced-resolve@^4.5.0: tapable "^1.0.0" enhanced-resolve@^5.10.0, enhanced-resolve@^5.7.0: - version "5.14.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.14.1.tgz#de684b6803724477a4af5d74ccae5de52c25f6b3" - integrity sha512-Vklwq2vDKtl0y/vtwjSesgJ5MYS7Etuk5txS8VdKL4AOS1aUlD96zqIfsOSLQsdv3xgMRbtkWM8eG9XDfKUPow== + version "5.15.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" + integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -8929,9 +8931,9 @@ eslint-plugin-react-hooks@^4.1.2: integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== eslint-plugin-react@^7.21.2: - version "7.32.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz#e71f21c7c265ebce01bcbc9d0955170c55571f10" - integrity sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg== + version "7.33.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.33.0.tgz#6c356fb0862fec2cd1b04426c669ea746e9b6eb3" + integrity sha512-qewL/8P34WkY8jAqdQxsiL82pDUeT7nhs8IsuXgfgnsEloKCT4miAV9N9kGtx7/KM9NH/NCGUE7Edt9iGxLXFw== dependencies: array-includes "^3.1.6" array.prototype.flatmap "^1.3.1" @@ -8946,13 +8948,13 @@ eslint-plugin-react@^7.21.2: object.values "^1.1.6" prop-types "^15.8.1" resolve "^2.0.0-next.4" - semver "^6.3.0" + semver "^6.3.1" string.prototype.matchall "^4.0.8" eslint-plugin-storybook@^0.6.8: - version "0.6.12" - resolved "https://registry.yarnpkg.com/eslint-plugin-storybook/-/eslint-plugin-storybook-0.6.12.tgz#7bdb3392bb03bebde40ed19accfd61246e9d6301" - integrity sha512-XbIvrq6hNVG6rpdBr+eBw63QhOMLpZneQVSooEDow8aQCWGCk/5vqtap1yxpVydNfSxi3S/3mBBRLQqKUqQRww== + version "0.6.13" + resolved "https://registry.yarnpkg.com/eslint-plugin-storybook/-/eslint-plugin-storybook-0.6.13.tgz#897a9f6a9bb88c63b02f05850f30c28a9848a3f7" + integrity sha512-smd+CS0WH1jBqUEJ3znGS7DU4ayBE9z6lkQAK2yrSUv1+rq8BT/tiI5C/rKE7rmiqiAfojtNYZRhzo5HrulccQ== dependencies: "@storybook/csf" "^0.0.1" "@typescript-eslint/utils" "^5.45.0" @@ -10851,9 +10853,9 @@ graphql-tag@^2.12.6: tslib "^2.1.0" graphql@^16.5.0: - version "16.6.0" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.6.0.tgz#c2dcffa4649db149f6282af726c8c83f1c7c5fdb" - integrity sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw== + version "16.7.1" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.7.1.tgz#11475b74a7bff2aefd4691df52a0eca0abd9b642" + integrity sha512-DRYR9tf+UGU0KOsMcKAlXeFfX89UiiIZ0dRU3mR0yJfu6OjZqUcp68NnFLnqQU5RexygFoDy1EW+ccOYcPfmHg== growly@^1.3.0: version "1.3.0" @@ -13226,6 +13228,11 @@ json-stable-stringify@^1.0.1: dependencies: jsonify "~0.0.0" +json-stringify-deterministic@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/json-stringify-deterministic/-/json-stringify-deterministic-1.0.8.tgz#675eaf26f18ebac0197c5e4d2d4dcc2ab3750948" + integrity sha512-rkJID3qeigo3VCrEcxX1333fTBBxW89YrdYcZexMnL/WdB8u0zctyG63e/DpahRJyrWCDhh7IQhiR7u2XEDQ4Q== + json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -15822,17 +15829,17 @@ playwright-cli@^0.180.0: resolved "https://registry.yarnpkg.com/playwright-cli/-/playwright-cli-0.180.0.tgz#5e95a517732b12f0f70e88e4a60bfbd622149b2f" integrity sha512-T2mBkdIwSNz8K+wRYpZgPDWzS3pUjSomi+9W6ybg/FSTQvxceGg2n8upo4YGFkBrH4PyOcRTi+si+B7O6yRa+g== -playwright-core@1.35.0: - version "1.35.0" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.35.0.tgz#b7871b742b4a5c8714b7fa2f570c280a061cb414" - integrity sha512-muMXyPmIx/2DPrCHOD1H1ePT01o7OdKxKj2ebmCAYvqhUy+Y1bpal7B0rdoxros7YrXI294JT/DWw2LqyiqTPA== +playwright-core@1.36.1: + version "1.36.1" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.36.1.tgz#f5f275d70548768ca892583519c89b237a381c77" + integrity sha512-7+tmPuMcEW4xeCL9cp9KxmYpQYHKkyjwoXRnoeTowaeNat8PoBMk/HwCYhqkH2fRkshfKEOiVus/IhID2Pg8kg== playwright@^1.24.2: - version "1.35.0" - resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.35.0.tgz#4e3b3ea2495d6fd671700f77b2f97b3adedf80f1" - integrity sha512-xhFhsoBmKPQfj3dM+HbIiFVlqRCZp2rwdJd/QFd9YBuidabo3TkVv0iuxPQ4vZoMwtSI7qzjY93f5ohdC97hww== + version "1.36.1" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.36.1.tgz#bd01541048b4ea54bcf1989e61e0129f34d2df71" + integrity sha512-2ZqHpD0U0COKR8bqR3W5IkyIAAM0mT9FgGJB9xWCI1qAUkqLxJskA1ueeQOTH2Qfz3+oxdwwf2EzdOX+RkZmmQ== dependencies: - playwright-core "1.35.0" + playwright-core "1.36.1" please-upgrade-node@^3.2.0: version "3.2.0" @@ -17556,10 +17563,10 @@ semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^ dependencies: lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@~5.4.1: version "5.4.1"