From 77bf0dd567a0f044ab3e0f3bd9f0cf6ea644547b Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Tue, 6 Jun 2023 13:13:28 +0600 Subject: [PATCH 01/67] refactor: simplify ParsedOrder interface (#2596) * refactor: simplify ParsedOrder interface * fix: fix ParsedOrder namings --- src/common/hooks/useGetSurplusFiatValue.ts | 3 +- .../useMultipleOrdersCancellation/index.ts | 4 +- .../useMultipleOrdersCancellation/state.ts | 7 +-- .../useCancelMultipleOrders.ts | 8 +-- src/common/pure/ExecutedSummary/index.tsx | 5 +- src/common/utils/isOrderCancellable.ts | 16 +++++- .../utils/isOrderOffChainCancellable.ts | 6 +- .../TransactionExecutedContent/index.tsx | 6 +- .../orders/hooks/useTokensFromOrders.ts | 10 ---- src/modules/orders/index.ts | 1 - .../containers/OrdersReceiptModal/hooks.ts | 24 ++------ .../containers/OrdersReceiptModal/index.tsx | 4 +- .../hooks/useOrdersTableList.ts | 10 ++-- .../hooks/useValidatePageUrlParams.ts | 8 +-- .../containers/OrdersTableWidget/index.tsx | 34 ++++++++---- .../ordersTable/pure/OrderStatusBox/index.tsx | 6 +- .../OrdersTableContainer/OrderRow/index.tsx | 9 +-- .../pure/OrdersTableContainer/OrdersTable.tsx | 10 ++-- .../OrdersTableContainer/index.cosmos.tsx | 8 +-- .../pure/OrdersTableContainer/types.ts | 13 ++--- .../utils/getOrderParams.ts | 5 +- .../pure/ReceiptModal/FeeField.tsx | 5 +- .../pure/ReceiptModal/FilledField.tsx | 6 +- .../pure/ReceiptModal/SurplusField.tsx | 3 +- .../ordersTable/pure/ReceiptModal/index.tsx | 8 +-- .../ordersTable/state/orderReceiptAtom.ts | 6 +- .../swap/containers/EthFlowStepper/index.tsx | 4 +- src/utils/getExecutedSummaryData.ts | 13 +++-- src/utils/orderUtils/getFilledAmounts.ts | 14 +---- src/utils/orderUtils/getSellAmountWithFee.ts | 4 +- src/utils/orderUtils/ordersSorter.ts | 2 +- src/utils/orderUtils/parseOrder.ts | 55 +++++++++++++++---- 32 files changed, 171 insertions(+), 146 deletions(-) delete mode 100644 src/modules/orders/hooks/useTokensFromOrders.ts diff --git a/src/common/hooks/useGetSurplusFiatValue.ts b/src/common/hooks/useGetSurplusFiatValue.ts index 635b723894..f0c02fcb14 100644 --- a/src/common/hooks/useGetSurplusFiatValue.ts +++ b/src/common/hooks/useGetSurplusFiatValue.ts @@ -7,6 +7,7 @@ import { useCoingeckoUsdValue } from 'legacy/hooks/useStablecoinPrice' import { Order } from 'legacy/state/orders/actions' import { getExecutedSummaryData } from 'utils/getExecutedSummaryData' +import { ParsedOrder } from 'utils/orderUtils/parseOrder' type Output = { surplusFiatValue: CurrencyAmount | null @@ -15,7 +16,7 @@ type Output = { showFiatValue: boolean } -export function useGetSurplusData(order: Order | undefined): Output { +export function useGetSurplusData(order: Order | ParsedOrder | undefined): Output { const { surplusAmount, surplusToken } = useMemo(() => { const output: { surplusToken?: Token; surplusAmount?: CurrencyAmount } = {} diff --git a/src/common/hooks/useMultipleOrdersCancellation/index.ts b/src/common/hooks/useMultipleOrdersCancellation/index.ts index cfb84f1a93..c7e138ffb8 100644 --- a/src/common/hooks/useMultipleOrdersCancellation/index.ts +++ b/src/common/hooks/useMultipleOrdersCancellation/index.ts @@ -3,16 +3,16 @@ import { useCallback } from 'react' import { useOpenModal } from 'legacy/state/application/hooks' import { ApplicationModal } from 'legacy/state/application/reducer' -import { Order } from 'legacy/state/orders/actions' import { updateOrdersToCancelAtom } from 'common/hooks/useMultipleOrdersCancellation/state' +import { CancellableOrder } from 'common/utils/isOrderCancellable' export function useMultipleOrdersCancellation() { const setOrdersToCancel = useUpdateAtom(updateOrdersToCancelAtom) const openModal = useOpenModal(ApplicationModal.MULTIPLE_CANCELLATION) return useCallback( - (orders: Order[]) => { + (orders: CancellableOrder[]) => { setOrdersToCancel(orders) openModal() }, diff --git a/src/common/hooks/useMultipleOrdersCancellation/state.ts b/src/common/hooks/useMultipleOrdersCancellation/state.ts index 108fbd396d..b952802988 100644 --- a/src/common/hooks/useMultipleOrdersCancellation/state.ts +++ b/src/common/hooks/useMultipleOrdersCancellation/state.ts @@ -1,12 +1,11 @@ import { atom } from 'jotai' -import { Order } from 'legacy/state/orders/actions' - +import { CancellableOrder } from 'common/utils/isOrderCancellable' import { isOrderOffChainCancellable } from 'common/utils/isOrderOffChainCancellable' -export const ordersToCancelAtom = atom([]) +export const ordersToCancelAtom = atom([]) -export const updateOrdersToCancelAtom = atom(null, (get, set, nextState: Order[]) => { +export const updateOrdersToCancelAtom = atom(null, (get, set, nextState: CancellableOrder[]) => { set(ordersToCancelAtom, () => { return nextState.filter(isOrderOffChainCancellable) }) diff --git a/src/common/hooks/useMultipleOrdersCancellation/useCancelMultipleOrders.ts b/src/common/hooks/useMultipleOrdersCancellation/useCancelMultipleOrders.ts index 0d82512170..20a14be3d1 100644 --- a/src/common/hooks/useMultipleOrdersCancellation/useCancelMultipleOrders.ts +++ b/src/common/hooks/useMultipleOrdersCancellation/useCancelMultipleOrders.ts @@ -5,18 +5,16 @@ import { useWeb3React } from '@web3-react/core' import { orderBookApi } from 'cowSdk' -import { Order } from 'legacy/state/orders/actions' - import { useWalletInfo } from 'modules/wallet' -import { isOrderCancellable } from 'common/utils/isOrderCancellable' +import { CancellableOrder, isOrderCancellable } from 'common/utils/isOrderCancellable' -export function useCancelMultipleOrders(): (orders: Order[]) => Promise { +export function useCancelMultipleOrders(): (orders: CancellableOrder[]) => Promise { const { provider } = useWeb3React() const { chainId } = useWalletInfo() return useCallback( - async (ordersToCancel: Order[]) => { + async (ordersToCancel: CancellableOrder[]) => { const notCancellableOrders = ordersToCancel.filter((order) => !isOrderCancellable(order)) const signer = provider?.getSigner() diff --git a/src/common/pure/ExecutedSummary/index.tsx b/src/common/pure/ExecutedSummary/index.tsx index 1179690b00..1b1536b39d 100644 --- a/src/common/pure/ExecutedSummary/index.tsx +++ b/src/common/pure/ExecutedSummary/index.tsx @@ -1,11 +1,10 @@ -import { Order } from 'legacy/state/orders/actions' - import { useGetSurplusData } from 'common/hooks/useGetSurplusFiatValue' import { getExecutedSummaryData } from 'utils/getExecutedSummaryData' +import { ParsedOrder } from 'utils/orderUtils/parseOrder' import * as styledEl from './styled' -export function ExecutedSummary({ order }: { order: Order }) { +export function ExecutedSummary({ order }: { order: ParsedOrder }) { const { formattedFilledAmount, formattedSwappedAmount } = getExecutedSummaryData(order) const { surplusFiatValue, showFiatValue, surplusToken, surplusAmount } = useGetSurplusData(order) diff --git a/src/common/utils/isOrderCancellable.ts b/src/common/utils/isOrderCancellable.ts index 3ceecd78bf..ffea261f1c 100644 --- a/src/common/utils/isOrderCancellable.ts +++ b/src/common/utils/isOrderCancellable.ts @@ -1,11 +1,21 @@ -import { Order, OrderStatus } from 'legacy/state/orders/actions' +import { Token } from '@uniswap/sdk-core' + +import { OrderStatus } from 'legacy/state/orders/actions' + +export interface CancellableOrder { + id: string + inputToken: Token + isCancelling?: boolean + cancellationHash?: string + status: OrderStatus +} // 1. An order can be cancelled when the order is CREATING or PENDING // 2. It cannot be cancelled if there's a cancellationHash already or a cancellation in progress -export function isOrderCancellable(order: Order): boolean { +export function isOrderCancellable(order: CancellableOrder): boolean { if (order.isCancelling || order.cancellationHash) { return false } - return [OrderStatus.CREATING, OrderStatus.PENDING].includes(order?.status) + return [OrderStatus.CREATING, OrderStatus.PENDING].includes(order.status) } diff --git a/src/common/utils/isOrderOffChainCancellable.ts b/src/common/utils/isOrderOffChainCancellable.ts index 2ecc1ab8ec..edd7870f24 100644 --- a/src/common/utils/isOrderOffChainCancellable.ts +++ b/src/common/utils/isOrderOffChainCancellable.ts @@ -1,9 +1,7 @@ -import { Order } from 'legacy/state/orders/actions' - import { getIsEthFlowOrder } from 'modules/swap/containers/EthFlowStepper' -import { isOrderCancellable } from 'common/utils/isOrderCancellable' +import { CancellableOrder, isOrderCancellable } from 'common/utils/isOrderCancellable' -export function isOrderOffChainCancellable(order: Order): boolean { +export function isOrderOffChainCancellable(order: CancellableOrder): boolean { return !getIsEthFlowOrder(order) && isOrderCancellable(order) } diff --git a/src/legacy/components/TransactionExecutedContent/index.tsx b/src/legacy/components/TransactionExecutedContent/index.tsx index 5492ca1500..5f351f8b59 100644 --- a/src/legacy/components/TransactionExecutedContent/index.tsx +++ b/src/legacy/components/TransactionExecutedContent/index.tsx @@ -6,6 +6,7 @@ import { Order } from 'legacy/state/orders/actions' import { useGetSurplusData } from 'common/hooks/useGetSurplusFiatValue' import { getExecutedSummaryData } from 'utils/getExecutedSummaryData' +import { parseOrder } from 'utils/orderUtils/parseOrder' import * as styledEl from './styled' @@ -18,8 +19,9 @@ export function TransactionExecutedContent({ chainId: SupportedChainId hash?: string }) { - const { formattedFilledAmount, formattedSwappedAmount } = getExecutedSummaryData(order) - const { surplusFiatValue, showFiatValue, surplusToken, surplusAmount } = useGetSurplusData(order) + const parsedOrder = parseOrder(order) + const { formattedFilledAmount, formattedSwappedAmount } = getExecutedSummaryData(parsedOrder) + const { surplusFiatValue, showFiatValue, surplusToken, surplusAmount } = useGetSurplusData(parsedOrder) return ( diff --git a/src/modules/orders/hooks/useTokensFromOrders.ts b/src/modules/orders/hooks/useTokensFromOrders.ts deleted file mode 100644 index 7ab38c40b3..0000000000 --- a/src/modules/orders/hooks/useTokensFromOrders.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { useMemo } from 'react' - -import { Order } from 'modules/orders' - -export function useInputTokensFromOrders(orders: Order[]) { - return useMemo(() => { - return orders.map((order) => order.inputToken) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [orders.map((order) => order.sellToken).join('')]) -} diff --git a/src/modules/orders/index.ts b/src/modules/orders/index.ts index f967c9dffe..c9f6f047dc 100644 --- a/src/modules/orders/index.ts +++ b/src/modules/orders/index.ts @@ -1,2 +1 @@ export * from './types' -export * from './hooks/useTokensFromOrders' diff --git a/src/modules/ordersTable/containers/OrdersReceiptModal/hooks.ts b/src/modules/ordersTable/containers/OrdersReceiptModal/hooks.ts index 0e7bf0fa90..e87670b5b4 100644 --- a/src/modules/ordersTable/containers/OrdersReceiptModal/hooks.ts +++ b/src/modules/ordersTable/containers/OrdersReceiptModal/hooks.ts @@ -1,34 +1,22 @@ import { useUpdateAtom, useAtomValue } from 'jotai/utils' -import { useCallback, useMemo } from 'react' +import { useCallback } from 'react' -import { UID } from '@cowprotocol/cow-sdk' - -import { useOrdersTableList } from 'modules/ordersTable/containers/OrdersTableWidget/hooks/useOrdersTableList' import { updateReceiptAtom, receiptAtom } from 'modules/ordersTable/state/orderReceiptAtom' import { ParsedOrder } from 'utils/orderUtils/parseOrder' export function useCloseReceiptModal() { const updateReceiptState = useUpdateAtom(updateReceiptAtom) - return useCallback(() => updateReceiptState({ orderId: null }), [updateReceiptState]) + return useCallback(() => updateReceiptState({ order: null }), [updateReceiptState]) } -export function useSelectReceiptOrder(): (orderId: UID) => void { +export function useSelectReceiptOrder(): (order: ParsedOrder) => void { const updateReceiptState = useUpdateAtom(updateReceiptAtom) - return useCallback((orderId: UID) => updateReceiptState({ orderId }), [updateReceiptState]) + return useCallback((order: ParsedOrder) => updateReceiptState({ order }), [updateReceiptState]) } export function useSelectedOrder(): ParsedOrder | null { - const { orderId } = useAtomValue(receiptAtom) - const orders = useOrdersTableList() - - return useMemo(() => { - if (!orderId || !orders) { - return null - } - - const allOrders = Object.values(orders).flat() + const { order } = useAtomValue(receiptAtom) - return allOrders.find(({ id }) => id === orderId) || null - }, [orderId, orders]) + return order } diff --git a/src/modules/ordersTable/containers/OrdersReceiptModal/index.tsx b/src/modules/ordersTable/containers/OrdersReceiptModal/index.tsx index d320be55cb..e82d3125b9 100644 --- a/src/modules/ordersTable/containers/OrdersReceiptModal/index.tsx +++ b/src/modules/ordersTable/containers/OrdersReceiptModal/index.tsx @@ -27,8 +27,8 @@ export function OrdersReceiptModal(props: OrdersReceiptModalProps) { return null } - const { inputToken, outputToken, buyAmount, sellAmount, executedBuyAmount, executedSellAmount } = order - + const { inputToken, outputToken, buyAmount, sellAmount } = order + const { executedBuyAmount, executedSellAmount } = order.executionData // Sell and buy amounts const sellAmountCurrency = CurrencyAmount.fromRawAmount(inputToken, sellAmount.toString()) const buyAmountCurrency = CurrencyAmount.fromRawAmount(outputToken, buyAmount.toString()) diff --git a/src/modules/ordersTable/containers/OrdersTableWidget/hooks/useOrdersTableList.ts b/src/modules/ordersTable/containers/OrdersTableWidget/hooks/useOrdersTableList.ts index 328e94d7d8..0c3bf5567b 100644 --- a/src/modules/ordersTable/containers/OrdersTableWidget/hooks/useOrdersTableList.ts +++ b/src/modules/ordersTable/containers/OrdersTableWidget/hooks/useOrdersTableList.ts @@ -1,7 +1,6 @@ import { useCallback, useMemo } from 'react' import { Order, PENDING_STATES } from 'legacy/state/orders/actions' -import { useOrders } from 'legacy/state/orders/hooks' import { useWalletInfo } from 'modules/wallet' @@ -15,9 +14,8 @@ export interface OrdersTableList { const ORDERS_LIMIT = 100 -export function useOrdersTableList(): OrdersTableList { - const { chainId, account } = useWalletInfo() - const allNonEmptyOrders = useOrders({ chainId }) +export function useOrdersTableList(allOrders: Order[]): OrdersTableList { + const { account } = useWalletInfo() const accountLowerCase = account?.toLowerCase() const ordersFilter = useCallback( @@ -26,8 +24,8 @@ export function useOrdersTableList(): OrdersTableList { ) const allSortedOrders = useMemo(() => { - return allNonEmptyOrders.filter(ordersFilter).map(parseOrder).sort(ordersSorter) - }, [allNonEmptyOrders, ordersFilter]) + return allOrders.filter(ordersFilter).map(parseOrder).sort(ordersSorter) + }, [allOrders, ordersFilter]) return useMemo(() => { const { pending, history } = allSortedOrders.reduce( diff --git a/src/modules/ordersTable/containers/OrdersTableWidget/hooks/useValidatePageUrlParams.ts b/src/modules/ordersTable/containers/OrdersTableWidget/hooks/useValidatePageUrlParams.ts index 87f83c477a..66585c4b1a 100644 --- a/src/modules/ordersTable/containers/OrdersTableWidget/hooks/useValidatePageUrlParams.ts +++ b/src/modules/ordersTable/containers/OrdersTableWidget/hooks/useValidatePageUrlParams.ts @@ -2,18 +2,16 @@ import { useLayoutEffect } from 'react' import { useLocation, useNavigate } from 'react-router-dom' -import { Order } from 'legacy/state/orders/actions' - import { ORDERS_TABLE_PAGE_SIZE } from '../../../const/tabs' import { buildOrdersTableUrl, parseOrdersTableUrl } from '../../../utils/buildOrdersTableUrl' // Reset page params if they are invalid -export function useValidatePageUrlParams(orders: Order[], currentTabId: string, currentPageNumber: number) { +export function useValidatePageUrlParams(ordersLength: number, currentTabId: string, currentPageNumber: number) { const location = useLocation() const navigate = useNavigate() useLayoutEffect(() => { - const pagesCount = Math.ceil(orders.length / ORDERS_TABLE_PAGE_SIZE) + const pagesCount = Math.ceil(ordersLength / ORDERS_TABLE_PAGE_SIZE) const params = parseOrdersTableUrl(location.search) const shouldResetPageNumber = @@ -31,5 +29,5 @@ export function useValidatePageUrlParams(orders: Order[], currentTabId: string, { replace: true } ) } - }, [navigate, location, currentTabId, currentPageNumber, orders.length]) + }, [navigate, location, currentTabId, currentPageNumber, ordersLength]) } diff --git a/src/modules/ordersTable/containers/OrdersTableWidget/index.tsx b/src/modules/ordersTable/containers/OrdersTableWidget/index.tsx index 2dd4282a6d..7a101ea651 100644 --- a/src/modules/ordersTable/containers/OrdersTableWidget/index.tsx +++ b/src/modules/ordersTable/containers/OrdersTableWidget/index.tsx @@ -5,9 +5,8 @@ import { useLocation, useNavigate } from 'react-router-dom' import styled from 'styled-components/macro' import { GP_VAULT_RELAYER } from 'legacy/constants' -import { Order } from 'legacy/state/orders/actions' +import { useOrders } from 'legacy/state/orders/hooks' -import { useInputTokensFromOrders } from 'modules/orders' import { pendingOrdersPricesAtom } from 'modules/orders/state/pendingOrdersPricesAtom' import { useGetSpotPrice } from 'modules/orders/state/spotPricesAtom' import { ORDERS_TABLE_TABS, OPEN_TAB } from 'modules/ordersTable/const/tabs' @@ -21,6 +20,7 @@ import { useWalletDetails, useWalletInfo } from 'modules/wallet' import { useCancelOrder } from 'common/hooks/useCancelOrder' import { ordersToCancelAtom, updateOrdersToCancelAtom } from 'common/hooks/useMultipleOrdersCancellation/state' +import { CancellableOrder } from 'common/utils/isOrderCancellable' import { ParsedOrder } from 'utils/orderUtils/parseOrder' import { OrdersTableList, useOrdersTableList } from './hooks/useOrdersTableList' @@ -32,7 +32,7 @@ function getOrdersListByIndex(ordersList: OrdersTableList, id: string): ParsedOr return id === OPEN_TAB.id ? ordersList.pending : ordersList.history } -function toggleOrderInCancellationList(state: Order[], order: Order): Order[] { +function toggleOrderInCancellationList(state: CancellableOrder[], order: CancellableOrder): CancellableOrder[] { const isOrderIncluded = state.find((item) => item.id === order.id) if (isOrderIncluded) { @@ -47,11 +47,12 @@ const ContentWrapper = styled.div` ` export function OrdersTableWidget() { + const { chainId, account } = useWalletInfo() const location = useLocation() const navigate = useNavigate() - const ordersList = useOrdersTableList() - const { chainId, account } = useWalletInfo() - const getShowCancellationModal = useCancelOrder() + const allOrders = useOrders({ chainId }) + const ordersList = useOrdersTableList(allOrders) + const cancelOrder = useCancelOrder() const { allowsOffchainSigning } = useWalletDetails() const pendingOrdersPrices = useAtomValue(pendingOrdersPricesAtom) const ordersToCancel = useAtomValue(ordersToCancelAtom) @@ -83,25 +84,38 @@ export function OrdersTableWidget() { const isOpenOrdersTab = useMemo(() => OPEN_TAB.id === currentTabId, [currentTabId]) // Get tokens from pending orders (only if the OPEN orders tab is opened) - const tokens = useInputTokensFromOrders(isOpenOrdersTab ? ordersList.pending : []) + const tokens = useMemo(() => { + const pendingOrders = isOpenOrdersTab ? ordersList.pending : [] + + return pendingOrders.map((order) => order.inputToken) + }, [isOpenOrdersTab, ordersList.pending]) // Get effective balance const balancesAndAllowances = useBalancesAndAllowances({ account, spender, tokens }) const toggleOrdersForCancellation = useCallback( - (orders: Order[]) => { + (orders: ParsedOrder[]) => { updateOrdersToCancel(orders) }, [updateOrdersToCancel] ) const toggleOrderForCancellation = useCallback( - (order: Order) => { + (order: ParsedOrder) => { updateOrdersToCancel(toggleOrderInCancellationList(ordersToCancel, order)) }, [ordersToCancel, updateOrdersToCancel] ) + const getShowCancellationModal = useCallback( + (order: ParsedOrder) => { + const rawOrder = allOrders.find((item) => item.id === order.id) + + return rawOrder ? cancelOrder(rawOrder) : null + }, + [allOrders, cancelOrder] + ) + const orderActions: LimitOrderActions = { getShowCancellationModal, selectReceiptOrder, @@ -114,7 +128,7 @@ export function OrdersTableWidget() { navigate(buildOrdersTableUrl(location, { pageNumber: currentPageNumber, tabId: currentTabId }), { replace: true }) }, []) // eslint-disable-line react-hooks/exhaustive-deps - useValidatePageUrlParams(orders, currentTabId, currentPageNumber) + useValidatePageUrlParams(orders.length, currentTabId, currentPageNumber) return ( <> diff --git a/src/modules/ordersTable/pure/OrderStatusBox/index.tsx b/src/modules/ordersTable/pure/OrderStatusBox/index.tsx index b12a2aadd3..ce0df2e3af 100644 --- a/src/modules/ordersTable/pure/OrderStatusBox/index.tsx +++ b/src/modules/ordersTable/pure/OrderStatusBox/index.tsx @@ -71,7 +71,7 @@ export function OrderStatusBox({ order, widthAuto, withWarning, onClick }: Order return ( - {formattedPercentage}% - + {filledPercentDisplay}% + {/* Status label */} diff --git a/src/modules/ordersTable/pure/OrdersTableContainer/OrdersTable.tsx b/src/modules/ordersTable/pure/OrdersTableContainer/OrdersTable.tsx index 0bff42f7d3..fca133bd5b 100644 --- a/src/modules/ordersTable/pure/OrdersTableContainer/OrdersTable.tsx +++ b/src/modules/ordersTable/pure/OrdersTableContainer/OrdersTable.tsx @@ -12,7 +12,6 @@ import styled from 'styled-components/macro' import iconOrderExecution from 'legacy/assets/cow-swap/orderExecution.svg' import { QuestionWrapper } from 'legacy/components/QuestionHelper' import QuestionHelper from 'legacy/components/QuestionHelper' -import { Order } from 'legacy/state/orders/actions' import { PendingOrdersPrices } from 'modules/orders/state/pendingOrdersPricesAtom' import { SpotPricesKeyParams } from 'modules/orders/state/spotPricesAtom' @@ -28,6 +27,7 @@ import { BalancesAndAllowances } from 'modules/tokens' import { OrderExecutionStatusList, RateTooltipHeader } from 'common/pure/OrderExecutionStatusList' import { InvertRateControl } from 'common/pure/RateInfo' +import { CancellableOrder } from 'common/utils/isOrderCancellable' import { isOrderOffChainCancellable } from 'common/utils/isOrderOffChainCancellable' import { ordersTableFeatures } from 'constants/featureFlags' import { ordersSorter } from 'utils/orderUtils/ordersSorter' @@ -193,7 +193,7 @@ export interface OrdersTableProps { chainId: SupportedChainId | undefined pendingOrdersPrices: PendingOrdersPrices orders: ParsedOrder[] - selectedOrders: Order[] + selectedOrders: CancellableOrder[] balancesAndAllowances: BalancesAndAllowances getSpotPrice: (params: SpotPricesKeyParams) => Price | null orderActions: LimitOrderActions @@ -389,14 +389,14 @@ export function OrdersTable({ order={order} spotPrice={getSpotPrice({ chainId: chainId as SupportedChainId, - sellTokenAddress: order.sellToken, - buyTokenAddress: order.buyToken, + sellTokenAddress: order.inputToken.address, + buyTokenAddress: order.outputToken.address, })} prices={pendingOrdersPrices[order.id]} orderParams={getOrderParams(chainId, balancesAndAllowances, order)} isRateInverted={isRateInverted} orderActions={orderActions} - onClick={() => orderActions.selectReceiptOrder(order.id)} + onClick={() => orderActions.selectReceiptOrder(order)} /> ))} diff --git a/src/modules/ordersTable/pure/OrdersTableContainer/index.cosmos.tsx b/src/modules/ordersTable/pure/OrdersTableContainer/index.cosmos.tsx index 187d3a8101..99e05de110 100644 --- a/src/modules/ordersTable/pure/OrdersTableContainer/index.cosmos.tsx +++ b/src/modules/ordersTable/pure/OrdersTableContainer/index.cosmos.tsx @@ -1,8 +1,8 @@ -import { UID } from '@cowprotocol/cow-sdk' - import { LimitOrderActions } from 'modules/ordersTable/pure/OrdersTableContainer/types' import { BalancesAndAllowances } from 'modules/tokens' +import { ParsedOrder } from 'utils/orderUtils/parseOrder' + import { ordersMock } from './orders.mock' import { OrdersTableContainer } from './index' @@ -37,8 +37,8 @@ const orderActions: LimitOrderActions = { } return null }, - selectReceiptOrder(orderUid: UID) { - console.log('selectReceiptOrder', orderUid) + selectReceiptOrder(order: ParsedOrder) { + console.log('selectReceiptOrder', order) }, toggleOrderForCancellation(order) { console.log('toggleOrderForCancellation', order) diff --git a/src/modules/ordersTable/pure/OrdersTableContainer/types.ts b/src/modules/ordersTable/pure/OrdersTableContainer/types.ts index 9ac9117a90..72a438a2c3 100644 --- a/src/modules/ordersTable/pure/OrdersTableContainer/types.ts +++ b/src/modules/ordersTable/pure/OrdersTableContainer/types.ts @@ -1,12 +1,9 @@ -import { UID } from '@cowprotocol/cow-sdk' - -import { Order } from 'legacy/state/orders/actions' - import { UseCancelOrderReturn } from 'common/hooks/useCancelOrder' +import { ParsedOrder } from 'utils/orderUtils/parseOrder' export interface LimitOrderActions { - getShowCancellationModal: (order: Order) => UseCancelOrderReturn - selectReceiptOrder(orderId: UID): void - toggleOrderForCancellation(order: Order): void - toggleOrdersForCancellation(orders: Order[]): void + getShowCancellationModal: (order: ParsedOrder) => UseCancelOrderReturn + selectReceiptOrder(order: ParsedOrder): void + toggleOrderForCancellation(order: ParsedOrder): void + toggleOrdersForCancellation(orders: ParsedOrder[]): void } diff --git a/src/modules/ordersTable/pure/OrdersTableContainer/utils/getOrderParams.ts b/src/modules/ordersTable/pure/OrdersTableContainer/utils/getOrderParams.ts index 6d2aa58f96..ff97c09181 100644 --- a/src/modules/ordersTable/pure/OrdersTableContainer/utils/getOrderParams.ts +++ b/src/modules/ordersTable/pure/OrdersTableContainer/utils/getOrderParams.ts @@ -1,12 +1,11 @@ import { SupportedChainId } from '@cowprotocol/cow-sdk' import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core' -import { Order } from 'legacy/state/orders/actions' - import { BalancesAndAllowances } from 'modules/tokens' import { RateInfoParams } from 'common/pure/RateInfo' import { isEnoughAmount } from 'utils/isEnoughAmount' +import { ParsedOrder } from 'utils/orderUtils/parseOrder' export interface OrderParams { chainId: SupportedChainId | undefined @@ -22,7 +21,7 @@ const PERCENTAGE_FOR_PARTIAL_FILLS = new Percent(5, 10000) // 0.05% export function getOrderParams( chainId: SupportedChainId | undefined, balancesAndAllowances: BalancesAndAllowances, - order: Order + order: ParsedOrder ): OrderParams { const sellAmount = CurrencyAmount.fromRawAmount(order.inputToken, order.sellAmount.toString()) const buyAmount = CurrencyAmount.fromRawAmount(order.outputToken, order.buyAmount.toString()) diff --git a/src/modules/ordersTable/pure/ReceiptModal/FeeField.tsx b/src/modules/ordersTable/pure/ReceiptModal/FeeField.tsx index 68c463a37e..7786024f61 100644 --- a/src/modules/ordersTable/pure/ReceiptModal/FeeField.tsx +++ b/src/modules/ordersTable/pure/ReceiptModal/FeeField.tsx @@ -8,9 +8,10 @@ import * as styledEl from './styled' export type Props = { order: ParsedOrder } export function FeeField({ order }: Props): JSX.Element | null { - const { executedFeeAmount, executedSurplusFee, inputToken, sellToken } = order + const { inputToken } = order + const { executedFeeAmount, executedSurplusFee } = order.executionData - if (!sellToken) return + if (!inputToken) return // TODO: use the value from SDK const totalFee = CurrencyAmount.fromRawAmount(inputToken, (executedSurplusFee ?? executedFeeAmount) || 0) diff --git a/src/modules/ordersTable/pure/ReceiptModal/FilledField.tsx b/src/modules/ordersTable/pure/ReceiptModal/FilledField.tsx index 73a2cba721..b9ac819eb3 100644 --- a/src/modules/ordersTable/pure/ReceiptModal/FilledField.tsx +++ b/src/modules/ordersTable/pure/ReceiptModal/FilledField.tsx @@ -11,7 +11,9 @@ interface Props { } export function FilledField({ order }: Props) { - const { filledPercentage, formattedPercentage, fullyFilled } = order + const { + executionData: { filledPercentage, filledPercentDisplay, fullyFilled }, + } = order const { action, mainAmount, formattedFilledAmount, formattedSwappedAmount } = getFilledAmounts(order) const touched = filledPercentage?.gt(0) @@ -19,7 +21,7 @@ export function FilledField({ order }: Props) { return ( - + diff --git a/src/modules/ordersTable/pure/ReceiptModal/SurplusField.tsx b/src/modules/ordersTable/pure/ReceiptModal/SurplusField.tsx index 5be423be58..7cd1495a1b 100644 --- a/src/modules/ordersTable/pure/ReceiptModal/SurplusField.tsx +++ b/src/modules/ordersTable/pure/ReceiptModal/SurplusField.tsx @@ -11,7 +11,8 @@ interface Props { } export function SurplusField({ order }: Props) { - const { kind, inputToken, outputToken, surplusAmount, surplusPercentage } = order + const { kind, inputToken, outputToken } = order + const { surplusAmount, surplusPercentage } = order.executionData const surplusToken = kind === OrderKind.SELL ? outputToken : inputToken diff --git a/src/modules/ordersTable/pure/ReceiptModal/index.tsx b/src/modules/ordersTable/pure/ReceiptModal/index.tsx index 94d8878663..928ba9def9 100644 --- a/src/modules/ordersTable/pure/ReceiptModal/index.tsx +++ b/src/modules/ordersTable/pure/ReceiptModal/index.tsx @@ -131,7 +131,7 @@ export function ReceiptModal({ - + @@ -145,10 +145,10 @@ export function ReceiptModal({ - {order.activityId && ( + {order.executionData.activityId && ( <> - - + + )} diff --git a/src/modules/ordersTable/state/orderReceiptAtom.ts b/src/modules/ordersTable/state/orderReceiptAtom.ts index 573d3677e4..0784f76dd8 100644 --- a/src/modules/ordersTable/state/orderReceiptAtom.ts +++ b/src/modules/ordersTable/state/orderReceiptAtom.ts @@ -1,11 +1,13 @@ import { atom } from 'jotai' +import { ParsedOrder } from 'utils/orderUtils/parseOrder' + export interface ReceiptState { - orderId: string | null + order: ParsedOrder | null } export const receiptAtom = atom({ - orderId: null, + order: null, }) export const updateReceiptAtom = atom(null, (get, set, nextState: Partial) => { diff --git a/src/modules/swap/containers/EthFlowStepper/index.tsx b/src/modules/swap/containers/EthFlowStepper/index.tsx index a6447a61e6..47ff95c0f7 100644 --- a/src/modules/swap/containers/EthFlowStepper/index.tsx +++ b/src/modules/swap/containers/EthFlowStepper/index.tsx @@ -1,3 +1,5 @@ +import { Token } from '@uniswap/sdk-core' + import { NATIVE_CURRENCY_BUY_ADDRESS } from 'legacy/constants' import { useAllTransactions } from 'legacy/state/enhancedTransactions/hooks' import { EnhancedTransactionDetails } from 'legacy/state/enhancedTransactions/reducer' @@ -117,6 +119,6 @@ function didRefundFail(order: Order): boolean | undefined { } // TODO: move this somewhere else? -export function getIsEthFlowOrder(order: Order | undefined): boolean { +export function getIsEthFlowOrder(order: { inputToken: Token } | undefined): boolean { return order?.inputToken.address === NATIVE_CURRENCY_BUY_ADDRESS } diff --git a/src/utils/getExecutedSummaryData.ts b/src/utils/getExecutedSummaryData.ts index 387ecec2d4..2f4ca2f423 100644 --- a/src/utils/getExecutedSummaryData.ts +++ b/src/utils/getExecutedSummaryData.ts @@ -5,12 +5,17 @@ import { Order } from 'legacy/state/orders/actions' import { getFilledAmounts } from 'utils/orderUtils/getFilledAmounts' -import { parseOrder } from './orderUtils/parseOrder' +import { ParsedOrder, parseOrder } from './orderUtils/parseOrder' -export function getExecutedSummaryData(order: Order) { - const parsedOrder = parseOrder(order) +function isParsedOrder(order: Order | ParsedOrder): order is ParsedOrder { + return !!(order as ParsedOrder).executionData +} + +export function getExecutedSummaryData(order: Order | ParsedOrder) { + const parsedOrder = isParsedOrder(order) ? order : parseOrder(order) - const { inputToken, outputToken, surplusAmount: amount, surplusPercentage: percentage } = parsedOrder + const { inputToken, outputToken } = parsedOrder + const { surplusAmount: amount, surplusPercentage: percentage } = parsedOrder.executionData const parsedInputToken = new Token( inputToken.chainId, diff --git a/src/utils/orderUtils/getFilledAmounts.ts b/src/utils/orderUtils/getFilledAmounts.ts index a129cf8fd2..05a848c871 100644 --- a/src/utils/orderUtils/getFilledAmounts.ts +++ b/src/utils/orderUtils/getFilledAmounts.ts @@ -13,18 +13,8 @@ function legacyBigNumberToCurrencyAmount(currency: Token, value: BigNumber | und } export function getFilledAmounts(order: ParsedOrder) { - const { - inputToken, - outputToken, - kind, - feeAmount, - executedBuyAmount, - executedSellAmount, - executedFeeAmount, - filledAmount, - sellAmount, - buyAmount, - } = order + const { inputToken, outputToken, kind, feeAmount, sellAmount, buyAmount } = order + const { executedBuyAmount, executedSellAmount, executedFeeAmount, filledAmount } = order.executionData let mainToken: Token let mainAmount: CurrencyAmount diff --git a/src/utils/orderUtils/getSellAmountWithFee.ts b/src/utils/orderUtils/getSellAmountWithFee.ts index dd6fe54c9d..bab9434b16 100644 --- a/src/utils/orderUtils/getSellAmountWithFee.ts +++ b/src/utils/orderUtils/getSellAmountWithFee.ts @@ -1,8 +1,8 @@ import { CurrencyAmount, Token } from '@uniswap/sdk-core' -import { Order } from 'legacy/state/orders/actions' +import { ParsedOrder } from './parseOrder' -export function getSellAmountWithFee(order: Order): CurrencyAmount { +export function getSellAmountWithFee(order: ParsedOrder): CurrencyAmount { const feeAmountParsed = CurrencyAmount.fromRawAmount(order.inputToken, order.feeAmount.toString()) const sellAmountParsed = CurrencyAmount.fromRawAmount(order.inputToken, order.sellAmount.toString()) return sellAmountParsed.add(feeAmountParsed) diff --git a/src/utils/orderUtils/ordersSorter.ts b/src/utils/orderUtils/ordersSorter.ts index df7460cda1..883153280d 100644 --- a/src/utils/orderUtils/ordersSorter.ts +++ b/src/utils/orderUtils/ordersSorter.ts @@ -1,3 +1,3 @@ import { ParsedOrder } from './parseOrder' -export const ordersSorter = (a: ParsedOrder, b: ParsedOrder) => Date.parse(b.creationTime) - Date.parse(a.creationTime) +export const ordersSorter = (a: ParsedOrder, b: ParsedOrder) => b.creationTime.getTime() - a.creationTime.getTime() diff --git a/src/utils/orderUtils/parseOrder.ts b/src/utils/orderUtils/parseOrder.ts index d6254ea6bb..8a249c25af 100644 --- a/src/utils/orderUtils/parseOrder.ts +++ b/src/utils/orderUtils/parseOrder.ts @@ -1,4 +1,5 @@ -import { Currency, CurrencyAmount, Price } from '@uniswap/sdk-core' +import { OrderClass, OrderKind } from '@cowprotocol/cow-sdk' +import { Currency, CurrencyAmount, Price, Token } from '@uniswap/sdk-core' import BigNumber from 'bignumber.js' import JSBI from 'jsbi' @@ -11,10 +12,9 @@ import { getOrderSurplus } from './getOrderSurplus' import { isOrderFilled } from './isOrderFilled' import { isPartiallyFilled } from './isPartiallyFilled' -export interface ParsedOrder extends Order { +export interface ParsedOrderExecutionData { executedBuyAmount: JSBI executedSellAmount: JSBI - expirationTime: Date fullyFilled: boolean partiallyFilled: boolean filledAmount: BigNumber @@ -23,13 +23,30 @@ export interface ParsedOrder extends Order { surplusPercentage: BigNumber executedFeeAmount: string | undefined executedSurplusFee: string | null - formattedPercentage: number - parsedCreationTime: Date + filledPercentDisplay: number executedPrice: Price | null activityId: string | undefined activityTitle: string } +export interface ParsedOrder { + id: string + isCancelling: boolean | undefined + inputToken: Token + outputToken: Token + kind: OrderKind + sellAmount: string + buyAmount: string + feeAmount: string + class: OrderClass + status: OrderStatus + partiallyFillable: boolean + creationTime: Date + expirationTime: Date + + executionData: ParsedOrderExecutionData +} + export const parseOrder = (order: Order): ParsedOrder => { const { amount: filledAmount, percentage: filledPercentage } = getOrderFilledAmount(order) const { amount: surplusAmount, percentage: surplusPercentage } = getOrderSurplus(order) @@ -37,10 +54,10 @@ export const parseOrder = (order: Order): ParsedOrder => { const expirationTime = new Date(Number(order.validTo) * 1000) const executedFeeAmount = order.apiAdditionalInfo?.executedFeeAmount const executedSurplusFee = order.apiAdditionalInfo?.executedSurplusFee || null - const parsedCreationTime = new Date(order.creationTime) + const creationTime = new Date(order.creationTime) const fullyFilled = isOrderFilled(order) const partiallyFilled = isPartiallyFilled(order) - const formattedPercentage = filledPercentage.times(100).decimalPlaces(2).toNumber() + const filledPercentDisplay = filledPercentage.times(100).decimalPlaces(2).toNumber() const executedPrice = JSBI.greaterThan(executedBuyAmount, JSBI.BigInt(0)) ? new Price({ baseAmount: CurrencyAmount.fromRawAmount(order.inputToken, executedSellAmount), @@ -54,23 +71,37 @@ export const parseOrder = (order: Order): ParsedOrder => { const activityId = showCreationTxLink ? order.orderCreationHash : order.id const activityTitle = showCreationTxLink ? 'Creation transaction' : 'Order ID' - return { - ...order, - expirationTime, + const executionData: ParsedOrderExecutionData = { executedBuyAmount, executedSellAmount, filledAmount, filledPercentage, - formattedPercentage, + filledPercentDisplay, surplusAmount, surplusPercentage, executedFeeAmount, executedSurplusFee, executedPrice, - parsedCreationTime, fullyFilled, partiallyFilled, activityId, activityTitle, } + + return { + id: order.id, + isCancelling: order.isCancelling, + inputToken: order.inputToken, + outputToken: order.outputToken, + kind: order.kind, + sellAmount: order.sellAmount, + buyAmount: order.buyAmount, + feeAmount: order.feeAmount, + class: order.class, + status: order.status, + partiallyFillable: order.partiallyFillable, + creationTime, + expirationTime, + executionData, + } } From 9493f111f88ad8020013ba57afd9ee87ca52cb2a Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Tue, 6 Jun 2023 14:00:39 +0600 Subject: [PATCH 02/67] feat(trade-form-validation): service to validate generic trade widget behaviour (#2590) --- .../integration/limit-orders.test.ts | 2 +- .../TradeApprove/useTradeApproveState.ts | 4 +- src/common/hooks/useApproveState.ts | 6 +- .../pure/TradeLoadingButton/index.cosmos.tsx | 0 .../pure/TradeLoadingButton/index.tsx | 0 src/legacy/hooks/useTokenAllowance.ts | 8 +- .../containers/AdvancedOrdersWidget/index.tsx | 3 +- .../containers/LimitOrdersWarnings/index.tsx | 32 ++- .../containers/TradeButtons/index.tsx | 97 ++++----- .../limitOrdersTradeButtonsMap.tsx | 200 +---------------- .../hooks/useLimitOrdersFormState.ts | 202 +++--------------- src/modules/limitOrders/index.ts | 1 + src/modules/swap/pure/SwapButtons/styled.tsx | 2 +- src/modules/trade/index.ts | 1 + .../hooks/useGetTradeFormValidation.ts | 15 ++ .../hooks/useTradeFormValidationContext.ts | 56 +++++ src/modules/tradeFormValidation/index.ts | 5 + .../pure/TradeFormBlankButton/index.tsx | 65 ++++++ .../pure/TradeFormButtons/index.tsx | 46 ++++ .../pure/TradeFormButtons/tradeButtonsMap.tsx | 146 +++++++++++++ .../services/validateTradeForm.ts | 94 ++++++++ .../state/tradeFormValidationAtom.ts | 13 ++ .../state/tradeFormValidationContextAtom.ts | 5 + src/modules/tradeFormValidation/types.ts | 68 ++++++ .../updaters/TradeFormValidationUpdater.ts | 22 ++ .../tradeQuote/hooks/useTradeQuotePolling.ts | 4 +- src/pages/LimitOrders/index.tsx | 4 + 27 files changed, 652 insertions(+), 449 deletions(-) rename src/{modules/trade => common}/pure/TradeLoadingButton/index.cosmos.tsx (100%) rename src/{modules/trade => common}/pure/TradeLoadingButton/index.tsx (100%) create mode 100644 src/modules/tradeFormValidation/hooks/useGetTradeFormValidation.ts create mode 100644 src/modules/tradeFormValidation/hooks/useTradeFormValidationContext.ts create mode 100644 src/modules/tradeFormValidation/index.ts create mode 100644 src/modules/tradeFormValidation/pure/TradeFormBlankButton/index.tsx create mode 100644 src/modules/tradeFormValidation/pure/TradeFormButtons/index.tsx create mode 100644 src/modules/tradeFormValidation/pure/TradeFormButtons/tradeButtonsMap.tsx create mode 100644 src/modules/tradeFormValidation/services/validateTradeForm.ts create mode 100644 src/modules/tradeFormValidation/state/tradeFormValidationAtom.ts create mode 100644 src/modules/tradeFormValidation/state/tradeFormValidationContextAtom.ts create mode 100644 src/modules/tradeFormValidation/types.ts create mode 100644 src/modules/tradeFormValidation/updaters/TradeFormValidationUpdater.ts diff --git a/cypress-custom/integration/limit-orders.test.ts b/cypress-custom/integration/limit-orders.test.ts index f5d93148c2..fc5fb137ef 100644 --- a/cypress-custom/integration/limit-orders.test.ts +++ b/cypress-custom/integration/limit-orders.test.ts @@ -13,7 +13,7 @@ describe('Limit orders', () => { cy.get('#input-currency-input .token-amount-input').type(inputAmount.toString()) cy.get('#rate-limit-amount-input').clear().type(rate.toString(), { force: true }) - cy.get('#review-limit-order-btn').click() + cy.get('#do-trade-button').click() cy.get('#output-currency-input .token-amount-input').should('have.value', outputAmount.toString()) cy.get('#trade-confirmation #input-currency-preview .token-amount-input').should( diff --git a/src/common/containers/TradeApprove/useTradeApproveState.ts b/src/common/containers/TradeApprove/useTradeApproveState.ts index 780385d8cb..99ddf1469a 100644 --- a/src/common/containers/TradeApprove/useTradeApproveState.ts +++ b/src/common/containers/TradeApprove/useTradeApproveState.ts @@ -1,11 +1,13 @@ import { Currency, CurrencyAmount } from '@uniswap/sdk-core' +import { Nullish } from 'types' + import { ApprovalState } from 'legacy/hooks/useApproveCallback' import { useApproveState } from 'common/hooks/useApproveState' import { useTradeSpenderAddress } from 'common/hooks/useTradeSpenderAddress' -export function useTradeApproveState(amountToApprove: CurrencyAmount | null): ApprovalState { +export function useTradeApproveState(amountToApprove: Nullish>): ApprovalState { const spender = useTradeSpenderAddress() return useApproveState(amountToApprove, spender) diff --git a/src/common/hooks/useApproveState.ts b/src/common/hooks/useApproveState.ts index 5ee9883605..0fded7d3a5 100644 --- a/src/common/hooks/useApproveState.ts +++ b/src/common/hooks/useApproveState.ts @@ -2,6 +2,8 @@ import { useMemo } from 'react' import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core' +import { Nullish } from 'types' + import { ApprovalState } from 'legacy/hooks/useApproveCallback' import usePrevious from 'legacy/hooks/usePrevious' import { useTokenAllowance } from 'legacy/hooks/useTokenAllowance' @@ -11,7 +13,7 @@ import { useWalletInfo } from 'modules/wallet' import { useSafeMemo } from 'common/hooks/useSafeMemo' -function getCurrencyToApprove(amountToApprove: CurrencyAmount | null): Token | undefined { +function getCurrencyToApprove(amountToApprove: Nullish>): Token | undefined { if (!amountToApprove) return undefined if (amountToApprove.currency.isNative) return amountToApprove.currency.wrapped @@ -19,7 +21,7 @@ function getCurrencyToApprove(amountToApprove: CurrencyAmount | null): return amountToApprove.currency } -export function useApproveState(amountToApprove: CurrencyAmount | null, spender?: string): ApprovalState { +export function useApproveState(amountToApprove: Nullish>, spender?: string): ApprovalState { const { account } = useWalletInfo() const token = getCurrencyToApprove(amountToApprove) const currentAllowance = useTokenAllowance(token, account ?? undefined, spender) diff --git a/src/modules/trade/pure/TradeLoadingButton/index.cosmos.tsx b/src/common/pure/TradeLoadingButton/index.cosmos.tsx similarity index 100% rename from src/modules/trade/pure/TradeLoadingButton/index.cosmos.tsx rename to src/common/pure/TradeLoadingButton/index.cosmos.tsx diff --git a/src/modules/trade/pure/TradeLoadingButton/index.tsx b/src/common/pure/TradeLoadingButton/index.tsx similarity index 100% rename from src/modules/trade/pure/TradeLoadingButton/index.tsx rename to src/common/pure/TradeLoadingButton/index.tsx diff --git a/src/legacy/hooks/useTokenAllowance.ts b/src/legacy/hooks/useTokenAllowance.ts index a8db22cb0f..673d35f939 100644 --- a/src/legacy/hooks/useTokenAllowance.ts +++ b/src/legacy/hooks/useTokenAllowance.ts @@ -2,11 +2,17 @@ import { useMemo } from 'react' import { CurrencyAmount, Token } from '@uniswap/sdk-core' +import { Nullish } from 'types' + import { useSingleCallResult } from 'lib/hooks/multicall' import { useTokenContract } from './useContract' -export function useTokenAllowance(token?: Token, owner?: string, spender?: string): CurrencyAmount | undefined { +export function useTokenAllowance( + token: Nullish, + owner?: string, + spender?: string +): CurrencyAmount | undefined { const contract = useTokenContract(token?.address, false) const inputs = useMemo(() => [owner, spender], [owner, spender]) diff --git a/src/modules/advancedOrders/containers/AdvancedOrdersWidget/index.tsx b/src/modules/advancedOrders/containers/AdvancedOrdersWidget/index.tsx index 7c8a0feeeb..ae4af32d5c 100644 --- a/src/modules/advancedOrders/containers/AdvancedOrdersWidget/index.tsx +++ b/src/modules/advancedOrders/containers/AdvancedOrdersWidget/index.tsx @@ -12,11 +12,10 @@ import { import { useSetupTradeState, TradeWidget, TradeWidgetSlots } from 'modules/trade' import { useTradeQuote, useSetTradeQuoteParams } from 'modules/tradeQuote' import { TwapFormWidget } from 'modules/twap' +import { partsStateAtom } from 'modules/twap/state/partsStateAtom' import { CurrencyInfo } from 'common/pure/CurrencyInputPanel/types' -import { partsStateAtom } from '../../../twap/state/partsStateAtom' - export function AdvancedOrdersWidget() { useSetupTradeState() useFillAdvancedOrdersDerivedState() diff --git a/src/modules/limitOrders/containers/LimitOrdersWarnings/index.tsx b/src/modules/limitOrders/containers/LimitOrdersWarnings/index.tsx index 8babc7cfe9..4729b9a17c 100644 --- a/src/modules/limitOrders/containers/LimitOrdersWarnings/index.tsx +++ b/src/modules/limitOrders/containers/LimitOrdersWarnings/index.tsx @@ -10,7 +10,7 @@ import { Nullish } from 'types' import { PriceImpact } from 'legacy/hooks/usePriceImpact' import { useLimitOrdersDerivedState } from 'modules/limitOrders/hooks/useLimitOrdersDerivedState' -import { LimitOrdersFormState, useLimitOrdersFormState } from 'modules/limitOrders/hooks/useLimitOrdersFormState' +import { useLimitOrdersFormState } from 'modules/limitOrders/hooks/useLimitOrdersFormState' import { useRateImpact } from 'modules/limitOrders/hooks/useRateImpact' import { limitOrdersSettingsAtom } from 'modules/limitOrders/state/limitOrdersSettingsAtom' import { @@ -18,6 +18,8 @@ import { updateLimitOrdersWarningsAtom, } from 'modules/limitOrders/state/limitOrdersWarningsAtom' import { NoImpactWarning } from 'modules/trade/pure/NoImpactWarning' +import { TradeFormValidation, useGetTradeFormValidation } from 'modules/tradeFormValidation' +import { useTradeQuote } from 'modules/tradeQuote' import { useIsSafeViaWc, useWalletInfo } from 'modules/wallet' import { useShouldZeroApprove } from 'common/hooks/useShouldZeroApprove' @@ -33,10 +35,7 @@ import { calculatePercentageInRelationToReference } from 'utils/orderUtils/calcu import { RateImpactWarning } from '../../pure/RateImpactWarning' -const FORM_STATES_TO_SHOW_BUNDLE_BANNER = [ - LimitOrdersFormState.ExpertApproveAndSwap, - LimitOrdersFormState.ApproveAndSwap, -] +const FORM_STATES_TO_SHOW_BUNDLE_BANNER = [TradeFormValidation.ExpertApproveAndSwap, TradeFormValidation.ApproveAndSwap] export interface LimitOrdersWarningsProps { priceImpact: PriceImpact @@ -59,26 +58,39 @@ export function LimitOrdersWarnings(props: LimitOrdersWarningsProps) { const updateLimitOrdersWarnings = useSetAtom(updateLimitOrdersWarningsAtom) const { expertMode } = useAtomValue(limitOrdersSettingsAtom) - const formState = useLimitOrdersFormState() + const localFormValidation = useLimitOrdersFormState() + const primaryFormValidation = useGetTradeFormValidation() const rateImpact = useRateImpact() const { chainId, account } = useWalletInfo() const { slippageAdjustedSellAmount, inputCurrency, inputCurrencyAmount, outputCurrency, outputCurrencyAmount } = useLimitOrdersDerivedState() + const tradeQuote = useTradeQuote() + const canTrade = localFormValidation === null && primaryFormValidation === null && !tradeQuote.error const showPriceImpactWarning = - !!chainId && !expertMode && !!account && !!priceImpact.error && formState === LimitOrdersFormState.CanTrade + canTrade && !tradeQuote.isLoading && !!chainId && !expertMode && !!account && !!priceImpact.error + const showRateImpactWarning = + canTrade && + !tradeQuote.isLoading && + inputCurrency && + !isFractionFalsy(inputCurrencyAmount) && + !isFractionFalsy(outputCurrencyAmount) const feePercentage = calculatePercentageInRelationToReference({ value: feeAmount, reference: inputCurrencyAmount }) const showHighFeeWarning = feePercentage?.greaterThan(HIGH_FEE_WARNING_PERCENTAGE) - const showApprovalBundlingBanner = !isConfirmScreen && FORM_STATES_TO_SHOW_BUNDLE_BANNER.includes(formState) + const showApprovalBundlingBanner = + !isConfirmScreen && primaryFormValidation && FORM_STATES_TO_SHOW_BUNDLE_BANNER.includes(primaryFormValidation) const shouldZeroApprove = useShouldZeroApprove(slippageAdjustedSellAmount) const showZeroApprovalWarning = shouldZeroApprove && outputCurrency !== null // Show warning only when output currency is also present. const isSafeViaWc = useIsSafeViaWc() const showSafeWcBundlingBanner = - !isConfirmScreen && !showApprovalBundlingBanner && isSafeViaWc && formState === LimitOrdersFormState.NotApproved + !isConfirmScreen && + !showApprovalBundlingBanner && + isSafeViaWc && + primaryFormValidation === TradeFormValidation.ApproveRequired const isVisible = showPriceImpactWarning || @@ -120,7 +132,7 @@ export function LimitOrdersWarnings(props: LimitOrdersWarningsProps) { acceptCallback={onAcceptPriceImpact} /> )} - {inputCurrency && !isFractionFalsy(inputCurrencyAmount) && !isFractionFalsy(outputCurrencyAmount) && ( + {showRateImpactWarning && ( { - if (settingsState.expertMode) { - handleTrade() - } else { - tradeConfirmActions.onOpen() - } - }, [settingsState.expertMode, handleTrade, tradeConfirmActions]) + const isExpertMode = settingsState.expertMode - const buttonFactory = limitOrdersTradeButtonsMap[formState] + const tradeFormButtonContext: TradeFormButtonContext = { + defaultText: 'Limit order', + derivedState: tradeState, + doTrade: handleTrade, + quote, + isSupportedWallet, + confirmTrade() { + tradeConfirmActions.onOpen() + }, + connectWallet() { + toggleWalletModal() + }, + } - const isButtonDisabled = (typeof buttonFactory !== 'function' && buttonFactory.disabled) || !warningsAccepted - const showWarnings = !!(inputCurrency && outputCurrency && isSwapUnsupported) + // Display local form validation errors only when there are no primary errors + if (!primaryFormValidation && localFormValidation) { + const buttonFactory = limitOrdersTradeButtonsMap[localFormValidation] - const Button = - typeof buttonFactory === 'function' ? ( - buttonFactory({ tradeState, toggleWalletModal, quote, wrapUnwrapParams, doTrade }) + return typeof buttonFactory === 'function' ? ( + buttonFactory() ) : ( - + {buttonFactory.text} - + ) + } return ( - <> - {Button} - {showWarnings && ( - - - - )} - + ) } diff --git a/src/modules/limitOrders/containers/TradeButtons/limitOrdersTradeButtonsMap.tsx b/src/modules/limitOrders/containers/TradeButtons/limitOrdersTradeButtonsMap.tsx index 7d7d211bea..5cae823352 100644 --- a/src/modules/limitOrders/containers/TradeButtons/limitOrdersTradeButtonsMap.tsx +++ b/src/modules/limitOrders/containers/TradeButtons/limitOrdersTradeButtonsMap.tsx @@ -1,219 +1,29 @@ import React from 'react' -import { Trans } from '@lingui/macro' - -import { ButtonPrimary } from 'legacy/components/Button' -import { TransactionConfirmationModal } from 'legacy/components/TransactionConfirmationModal' -import { WrapUnwrapCallback } from 'legacy/hooks/useWrapCallback' -import { ButtonSize } from 'legacy/theme/enum' - -import { LimitOrdersDerivedState } from 'modules/limitOrders' -import { TransactionConfirmState } from 'modules/swap/state/transactionConfirmAtom' -import { TradeLoadingButton } from 'modules/trade/pure/TradeLoadingButton' -import { TradeQuoteState } from 'modules/tradeQuote' - -import { GpQuoteErrorCodes } from 'api/gnosisProtocol/errors/QuoteError' -import { TradeApproveButton } from 'common/containers/TradeApprove/TradeApproveButton' -import { TokenSymbol } from 'common/pure/TokenSymbol' +import { TradeLoadingButton } from 'common/pure/TradeLoadingButton' import { LimitOrdersFormState } from '../../hooks/useLimitOrdersFormState' -export interface WrapUnwrapParams { - isNativeIn: boolean - wrapUnwrapCallback: WrapUnwrapCallback | null - transactionConfirmState: TransactionConfirmState - closeModals(): void - showTransactionConfirmationModal: boolean -} - -export interface TradeButtonsParams { - tradeState: LimitOrdersDerivedState - quote: TradeQuoteState - toggleWalletModal: () => void - wrapUnwrapParams: WrapUnwrapParams - doTrade: () => void -} - interface ButtonConfig { - disabled: boolean - text: string + text: JSX.Element | string id?: string } interface ButtonCallback { - (params: TradeButtonsParams): JSX.Element | null -} - -export function SwapButton({ - children, - disabled, - onClick, - id, -}: { - children: React.ReactNode - disabled: boolean - onClick?: () => void - id?: string -}) { - return ( - - {children} - - ) -} - -const quoteErrorTexts: { [key in GpQuoteErrorCodes]: string } = { - [GpQuoteErrorCodes.UNHANDLED_ERROR]: 'Error loading price. Try again later.', - [GpQuoteErrorCodes.TransferEthToContract]: - 'Buying native currency with smart contract wallets is not currently supported', - [GpQuoteErrorCodes.UnsupportedToken]: 'Unsupported token', - [GpQuoteErrorCodes.InsufficientLiquidity]: 'Insufficient liquidity for this trade.', - [GpQuoteErrorCodes.FeeExceedsFrom]: 'Sell amount is too small', - [GpQuoteErrorCodes.ZeroPrice]: 'Invalid price. Try increasing input/output amount.', + (): JSX.Element | null } export const limitOrdersTradeButtonsMap: { [key in LimitOrdersFormState]: ButtonConfig | ButtonCallback } = { - [LimitOrdersFormState.Loading]: () => ( - - - - ), - [LimitOrdersFormState.CanTrade]: { - disabled: false, - text: 'Review limit order', - id: 'review-limit-order-btn', - }, - [LimitOrdersFormState.ApproveAndSwap]: ({ tradeState: { inputCurrency }, doTrade }: TradeButtonsParams) => { - const token = inputCurrency?.wrapped - const symbol = - - return ( - - Review (Approve {symbol} & Limit order) - - ) - }, - [LimitOrdersFormState.ExpertApproveAndSwap]: ({ tradeState: { inputCurrency }, doTrade }: TradeButtonsParams) => { - const token = inputCurrency?.wrapped - const symbol = - - return ( - - Confirm (Approve {symbol} & Limit order) - - ) - }, - [LimitOrdersFormState.ExpertCanTrade]: { - disabled: false, - text: 'Place limit order', - id: 'place-limit-order-btn', - }, - [LimitOrdersFormState.SwapIsUnsupported]: { - disabled: true, - text: 'Unsupported token', - }, - [LimitOrdersFormState.WalletIsUnsupported]: { - disabled: true, - text: 'Wallet Unsupported', - }, - [LimitOrdersFormState.ReadonlyGnosisSafeUser]: { - disabled: true, - text: 'Read Only', - }, - [LimitOrdersFormState.NeedToSelectToken]: { - disabled: true, - text: 'Select a token', - }, - [LimitOrdersFormState.AmountIsNotSet]: { - disabled: true, - text: 'Enter an amount', + [LimitOrdersFormState.RateLoading]: { + text: , }, [LimitOrdersFormState.PriceIsNotSet]: { - disabled: true, text: 'Enter a price', }, - [LimitOrdersFormState.InvalidRecipient]: { - disabled: true, - text: 'Enter a valid recipient', - }, - [LimitOrdersFormState.CantLoadBalances]: { - disabled: true, - text: "Couldn't load balances", - }, [LimitOrdersFormState.ZeroPrice]: { - disabled: true, text: 'Invalid price. Try increasing input/output amount.', }, [LimitOrdersFormState.FeeExceedsFrom]: { - disabled: true, text: 'Sell amount is too small', }, - [LimitOrdersFormState.QuoteError]: ({ quote }: TradeButtonsParams) => { - const defaultError = quoteErrorTexts[GpQuoteErrorCodes.UNHANDLED_ERROR] - - return ( - - {(quote.error && quoteErrorTexts[quote.error.type]) || defaultError} - - ) - }, - [LimitOrdersFormState.InsufficientBalance]: ({ tradeState }: TradeButtonsParams) => { - return ( - - Insufficient {} balance - - ) - }, - [LimitOrdersFormState.WalletIsNotConnected]: ({ toggleWalletModal }: TradeButtonsParams) => { - return ( - - Connect Wallet - - ) - }, - [LimitOrdersFormState.NotApproved]: ({ tradeState }: TradeButtonsParams) => { - const amountToApprove = tradeState.inputCurrencyAmount - - return ( - <> - {!!amountToApprove && ( - - - Review limit order - - - )} - - ) - }, - [LimitOrdersFormState.WrapUnwrap]: ({ wrapUnwrapParams }: TradeButtonsParams) => { - const { - isNativeIn, - wrapUnwrapCallback, - transactionConfirmState: { pendingText, operationType }, - closeModals, - showTransactionConfirmationModal, - } = wrapUnwrapParams - - return ( - <> - wrapUnwrapCallback?.({ useModals: true })}> - {isNativeIn ? 'Wrap' : 'Unwrap'} - - - - ) - }, } diff --git a/src/modules/limitOrders/hooks/useLimitOrdersFormState.ts b/src/modules/limitOrders/hooks/useLimitOrdersFormState.ts index 3dc29ce5ba..fd814225ef 100644 --- a/src/modules/limitOrders/hooks/useLimitOrdersFormState.ts +++ b/src/modules/limitOrders/hooks/useLimitOrdersFormState.ts @@ -1,166 +1,39 @@ import { useAtomValue } from 'jotai/utils' -import { Currency, CurrencyAmount, Fraction, Token } from '@uniswap/sdk-core' +import { Currency, CurrencyAmount, Fraction } from '@uniswap/sdk-core' -import { GP_VAULT_RELAYER } from 'legacy/constants' -import { ApprovalState } from 'legacy/hooks/useApproveCallback' -import useENSAddress from 'legacy/hooks/useENSAddress' -import { useTokenAllowance } from 'legacy/hooks/useTokenAllowance' -import { isAddress } from 'legacy/utils' - -import { LimitOrdersDerivedState } from 'modules/limitOrders' -import { limitOrdersSettingsAtom, LimitOrdersSettingsState } from 'modules/limitOrders/state/limitOrdersSettingsAtom' import { limitRateAtom } from 'modules/limitOrders/state/limitRateAtom' -import { isUnsupportedTokenInQuote } from 'modules/limitOrders/utils/isUnsupportedTokenInQuote' -import { useIsWrapOrUnwrap } from 'modules/trade/hooks/useIsWrapOrUnwrap' -import { TradeQuoteState, useTradeQuote } from 'modules/tradeQuote' -import { useGnosisSafeInfo, useWalletDetails, useWalletInfo } from 'modules/wallet' +import { useTradeQuote } from 'modules/tradeQuote' -import { useTradeApproveState } from 'common/containers/TradeApprove/useTradeApproveState' -import { useIsTxBundlingEnabled } from 'common/hooks/useIsTxBundlingEnabled' import { useSafeMemo } from 'common/hooks/useSafeMemo' +import { isFractionFalsy } from 'utils/isFractionFalsy' import { useLimitOrdersDerivedState } from './useLimitOrdersDerivedState' export enum LimitOrdersFormState { - NotApproved = 'NotApproved', - CanTrade = 'CanTrade', - ExpertCanTrade = 'ExpertCanTrade', - Loading = 'Loading', - SwapIsUnsupported = 'SwapIsUnsupported', - WrapUnwrap = 'WrapUnwrap', - InvalidRecipient = 'InvalidRecipient', - WalletIsUnsupported = 'WalletIsUnsupported', - WalletIsNotConnected = 'WalletIsNotConnected', - ReadonlyGnosisSafeUser = 'ReadonlyGnosisSafeUser', - NeedToSelectToken = 'NeedToSelectToken', - AmountIsNotSet = 'AmountIsNotSet', + RateLoading = 'RateLoading', + FeeExceedsFrom = 'FeeExceedsFrom', PriceIsNotSet = 'PriceIsNotSet', - InsufficientBalance = 'InsufficientBalance', - CantLoadBalances = 'CantLoadBalances', - QuoteError = 'QuoteError', ZeroPrice = 'ZeroPrice', - FeeExceedsFrom = 'FeeExceedsFrom', - ApproveAndSwap = 'ApproveAndSwap', - ExpertApproveAndSwap = 'ExpertApproveAndSwap', } interface LimitOrdersFormParams { - account: string | undefined - isSwapUnsupported: boolean - isSupportedWallet: boolean - isReadonlyGnosisSafeUser: boolean - isTxBundlingEnabled: boolean - currentAllowance: CurrencyAmount | undefined - approvalState: ApprovalState - tradeState: LimitOrdersDerivedState - settingsState: LimitOrdersSettingsState - recipientEnsAddress: string | null - quote: TradeQuoteState | null activeRate: Fraction | null - isWrapOrUnwrap: boolean isRateLoading: boolean - buyAmount: CurrencyAmount | null + feeAmount: CurrencyAmount | null sellAmount: CurrencyAmount | null + buyAmount: CurrencyAmount | null } -function getLimitOrdersFormState(params: LimitOrdersFormParams): LimitOrdersFormState { - const { - account, - isReadonlyGnosisSafeUser, - isTxBundlingEnabled, - isSupportedWallet, - currentAllowance, - approvalState, - tradeState, - settingsState, - recipientEnsAddress, - quote, - isWrapOrUnwrap, - activeRate, - isRateLoading, - sellAmount, - buyAmount, - isSwapUnsupported, - } = params - - const { inputCurrency, outputCurrency, inputCurrencyAmount, outputCurrencyAmount, inputCurrencyBalance, recipient } = - tradeState - - const inputAmountIsNotSet = !inputCurrencyAmount || inputCurrencyAmount.equalTo(0) - const outputAmountIsNotSet = !outputCurrencyAmount || outputCurrencyAmount.equalTo(0) - const feeAmount = - quote?.response?.quote?.feeAmount && sellAmount - ? CurrencyAmount.fromRawAmount(sellAmount.currency, quote?.response?.quote?.feeAmount) - : null - const approvalRequired = approvalState === ApprovalState.NOT_APPROVED || approvalState === ApprovalState.PENDING +function getLimitOrdersFormState(params: LimitOrdersFormParams): LimitOrdersFormState | null { + const { activeRate, isRateLoading, feeAmount, sellAmount, buyAmount } = params - if (!inputCurrency || !outputCurrency) { - return LimitOrdersFormState.NeedToSelectToken + if (isFractionFalsy(activeRate)) { + return LimitOrdersFormState.PriceIsNotSet } - if (recipient && !recipientEnsAddress && !isAddress(recipient)) { - return LimitOrdersFormState.InvalidRecipient - } - - if (!isWrapOrUnwrap && quote?.error) { - return LimitOrdersFormState.QuoteError - } - - if (isSwapUnsupported) { - return LimitOrdersFormState.SwapIsUnsupported - } - - if (!account) { - return LimitOrdersFormState.WalletIsNotConnected - } - - if (isWrapOrUnwrap) { - if (inputAmountIsNotSet && outputAmountIsNotSet) { - return LimitOrdersFormState.AmountIsNotSet - } - } else { - if (inputAmountIsNotSet || outputAmountIsNotSet) { - if (!activeRate || activeRate.equalTo(0)) { - return LimitOrdersFormState.PriceIsNotSet - } - - return LimitOrdersFormState.AmountIsNotSet - } - } - - if (!isSupportedWallet) { - return LimitOrdersFormState.WalletIsUnsupported - } - - if (isReadonlyGnosisSafeUser) { - return LimitOrdersFormState.ReadonlyGnosisSafeUser - } - - if (!inputCurrencyBalance) { - return LimitOrdersFormState.CantLoadBalances - } - - if (inputCurrencyAmount && inputCurrencyBalance.lessThan(inputCurrencyAmount)) { - return LimitOrdersFormState.InsufficientBalance - } - - if (!isWrapOrUnwrap && (isRateLoading || !currentAllowance || !quote)) { - return LimitOrdersFormState.Loading - } - - if (isWrapOrUnwrap) { - return LimitOrdersFormState.WrapUnwrap - } - - if (approvalRequired) { - if (isTxBundlingEnabled) { - if (settingsState.expertMode) { - return LimitOrdersFormState.ExpertApproveAndSwap - } - return LimitOrdersFormState.ApproveAndSwap - } - return LimitOrdersFormState.NotApproved + if (isRateLoading) { + return LimitOrdersFormState.RateLoading } if ( @@ -174,53 +47,26 @@ function getLimitOrdersFormState(params: LimitOrdersFormParams): LimitOrdersForm return LimitOrdersFormState.FeeExceedsFrom } - if (settingsState.expertMode) { - return LimitOrdersFormState.ExpertCanTrade - } - - return LimitOrdersFormState.CanTrade + return null } -export function useLimitOrdersFormState(): LimitOrdersFormState { - const { chainId, account } = useWalletInfo() - const tradeState = useLimitOrdersDerivedState() - const settingsState = useAtomValue(limitOrdersSettingsAtom) - const { isSupportedWallet } = useWalletDetails() - const gnosisSafeInfo = useGnosisSafeInfo() - const isReadonlyGnosisSafeUser = gnosisSafeInfo?.isReadOnly || false - const isTxBundlingEnabled = useIsTxBundlingEnabled() +export function useLimitOrdersFormState(): LimitOrdersFormState | null { + const { inputCurrencyAmount, outputCurrencyAmount } = useLimitOrdersDerivedState() const quote = useTradeQuote() const { activeRate, isLoading } = useAtomValue(limitRateAtom) - const isWrapOrUnwrap = useIsWrapOrUnwrap() - - const { inputCurrency, recipient } = tradeState - const sellAmount = tradeState.inputCurrencyAmount - const buyAmount = tradeState.outputCurrencyAmount - const sellToken = inputCurrency?.isToken ? inputCurrency : undefined - const spender = chainId ? GP_VAULT_RELAYER[chainId] : undefined - const currentAllowance = useTokenAllowance(sellToken, account ?? undefined, spender) - const approvalState = useTradeApproveState(sellAmount) - const isSwapUnsupported = isUnsupportedTokenInQuote(quote) - const { address: recipientEnsAddress } = useENSAddress(recipient) + const feeRawAmount = quote?.response?.quote?.feeAmount + const feeAmount = + feeRawAmount && inputCurrencyAmount + ? CurrencyAmount.fromRawAmount(inputCurrencyAmount.currency, feeRawAmount) + : null const params: LimitOrdersFormParams = { - account, - isReadonlyGnosisSafeUser, - isTxBundlingEnabled, - isSwapUnsupported, - isSupportedWallet, - approvalState, - currentAllowance, - tradeState, - settingsState, - recipientEnsAddress, - quote, activeRate, - isWrapOrUnwrap, + feeAmount, isRateLoading: isLoading, - buyAmount, - sellAmount, + sellAmount: inputCurrencyAmount, + buyAmount: outputCurrencyAmount, } return useSafeMemo(() => { diff --git a/src/modules/limitOrders/index.ts b/src/modules/limitOrders/index.ts index 70bb9f075e..6c9cf0c532 100644 --- a/src/modules/limitOrders/index.ts +++ b/src/modules/limitOrders/index.ts @@ -4,3 +4,4 @@ export * from './updaters/QuoteObserverUpdater' export * from './updaters/InitialPriceUpdater' export * from './updaters/ExecutionPriceUpdater' export * from './state/limitOrdersRawStateAtom' +export * from './state/limitOrdersSettingsAtom' diff --git a/src/modules/swap/pure/SwapButtons/styled.tsx b/src/modules/swap/pure/SwapButtons/styled.tsx index f52bcd371e..b650ca0ec7 100644 --- a/src/modules/swap/pure/SwapButtons/styled.tsx +++ b/src/modules/swap/pure/SwapButtons/styled.tsx @@ -4,7 +4,7 @@ import { ButtonError } from 'legacy/components/Button' import { RowBetween } from 'legacy/components/Row' import { ButtonSize } from 'legacy/theme/enum' -import { TradeLoadingButton } from 'modules/trade/pure/TradeLoadingButton' +import { TradeLoadingButton } from 'common/pure/TradeLoadingButton' export interface SwapButtonBoxProps { showLoading?: boolean diff --git a/src/modules/trade/index.ts b/src/modules/trade/index.ts index 8099952643..881ffef03d 100644 --- a/src/modules/trade/index.ts +++ b/src/modules/trade/index.ts @@ -4,3 +4,4 @@ export * from './pure/TradeConfirmation' export * from './hooks/useTradeConfirmActions' export * from './hooks/useTradeTypeInfo' export * from './hooks/setupTradeState/useSetupTradeState' +export * from './types/TradeDerivedState' diff --git a/src/modules/tradeFormValidation/hooks/useGetTradeFormValidation.ts b/src/modules/tradeFormValidation/hooks/useGetTradeFormValidation.ts new file mode 100644 index 0000000000..04c2cb7275 --- /dev/null +++ b/src/modules/tradeFormValidation/hooks/useGetTradeFormValidation.ts @@ -0,0 +1,15 @@ +import { useAtomValue } from 'jotai' + +import useDebounce from 'legacy/hooks/useDebounce' + +import { tradeFormValidationAtom } from '../state/tradeFormValidationAtom' +import { TradeFormValidation } from '../types' + +const TRADE_FORM_VALIDATION_DEBOUNCE = 200 + +export function useGetTradeFormValidation(): TradeFormValidation | null { + const validation = useAtomValue(tradeFormValidationAtom) + + // Just in case, to avoid blinking of the trade form button + return useDebounce(validation, TRADE_FORM_VALIDATION_DEBOUNCE) +} diff --git a/src/modules/tradeFormValidation/hooks/useTradeFormValidationContext.ts b/src/modules/tradeFormValidation/hooks/useTradeFormValidationContext.ts new file mode 100644 index 0000000000..f4ad4ad11f --- /dev/null +++ b/src/modules/tradeFormValidation/hooks/useTradeFormValidationContext.ts @@ -0,0 +1,56 @@ +import { useMemo } from 'react' + +import useENSAddress from 'legacy/hooks/useENSAddress' +import { useIsTradeUnsupported } from 'legacy/state/lists/hooks' + +import { isUnsupportedTokenInQuote } from 'modules/limitOrders/utils/isUnsupportedTokenInQuote' +import { useDerivedTradeState } from 'modules/trade/hooks/useDerivedTradeState' +import { useIsWrapOrUnwrap } from 'modules/trade/hooks/useIsWrapOrUnwrap' +import { useTradeQuote } from 'modules/tradeQuote' +import { useGnosisSafeInfo, useWalletDetails, useWalletInfo } from 'modules/wallet' + +import { useTradeApproveState } from 'common/containers/TradeApprove' +import { useIsTxBundlingEnabled } from 'common/hooks/useIsTxBundlingEnabled' + +import { TradeFormValidationCommonContext } from '../types' + +export function useTradeFormValidationContext(): TradeFormValidationCommonContext | null { + const { account } = useWalletInfo() + const { state: derivedTradeState } = useDerivedTradeState() + const tradeQuote = useTradeQuote() + + const { inputCurrency, outputCurrency, slippageAdjustedSellAmount, recipient } = derivedTradeState || {} + const approvalState = useTradeApproveState(slippageAdjustedSellAmount) + const { address: recipientEnsAddress } = useENSAddress(recipient) + const isSwapUnsupported = + useIsTradeUnsupported(inputCurrency, outputCurrency) || isUnsupportedTokenInQuote(tradeQuote) + + const isTxBundlingEnabled = useIsTxBundlingEnabled() + const isWrapUnwrap = useIsWrapOrUnwrap() + const { isSupportedWallet } = useWalletDetails() + const gnosisSafeInfo = useGnosisSafeInfo() + + const isSafeReadonlyUser = gnosisSafeInfo?.isReadOnly || false + + const commonContext = { + account, + isWrapUnwrap, + isTxBundlingEnabled, + isSupportedWallet, + isSwapUnsupported, + isSafeReadonlyUser, + recipientEnsAddress, + approvalState, + tradeQuote, + } + + return useMemo(() => { + if (!derivedTradeState) return null + + return { + ...commonContext, + derivedTradeState, + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [...Object.values(commonContext), derivedTradeState]) +} diff --git a/src/modules/tradeFormValidation/index.ts b/src/modules/tradeFormValidation/index.ts new file mode 100644 index 0000000000..ecf9e5dbf6 --- /dev/null +++ b/src/modules/tradeFormValidation/index.ts @@ -0,0 +1,5 @@ +export * from './updaters/TradeFormValidationUpdater' +export * from './hooks/useGetTradeFormValidation' +export * from './pure/TradeFormButtons' +export * from './pure/TradeFormBlankButton' +export * from './types' diff --git a/src/modules/tradeFormValidation/pure/TradeFormBlankButton/index.tsx b/src/modules/tradeFormValidation/pure/TradeFormBlankButton/index.tsx new file mode 100644 index 0000000000..efda5ba520 --- /dev/null +++ b/src/modules/tradeFormValidation/pure/TradeFormBlankButton/index.tsx @@ -0,0 +1,65 @@ +import React, { useEffect, useRef, useState } from 'react' + +import { Trans } from '@lingui/macro' +import { lighten, transparentize } from 'polished' +import styled from 'styled-components/macro' + +const LONG_TEXT_LENGTH = 20 + +const ActionButton = styled.button<{ hasLongText$: boolean }>` + display: flex; + align-items: center; + justify-content: center; + background: ${({ theme }) => theme.bg2}; + color: ${({ theme }) => theme.white}; + font-size: ${({ hasLongText$ }) => (hasLongText$ ? '16px' : '18px')}; + font-weight: 600; + border-radius: 16px; + cursor: pointer; + min-height: 58px; + text-align: center; + transition: background 0.2s ease-in-out, color 0.2s ease-in-out; + border: none; + outline: none; + + &:hover { + background: ${({ theme }) => lighten(0.08, theme.bg2)}; + } + + &:disabled { + background-color: ${({ theme }) => theme.grey1}; + color: ${({ theme }) => transparentize(0.4, theme.text1)}; + background-image: none; + border: 0; + cursor: auto; + animation: none; + transform: none; + } +` + +export interface TradeFormPrimaryButtonProps { + children: JSX.Element | string + disabled?: boolean + id?: string + + onClick?(): void +} + +export function TradeFormBlankButton({ onClick, children, disabled, id }: TradeFormPrimaryButtonProps) { + const ref = useRef(null) + const [hasLongText, setHasLongText] = useState(false) + + useEffect(() => { + if (!ref?.current) return + + const text = ref.current.innerText + + setHasLongText(text.length > LONG_TEXT_LENGTH) + }, [children]) + + return ( + + {children} + + ) +} diff --git a/src/modules/tradeFormValidation/pure/TradeFormButtons/index.tsx b/src/modules/tradeFormValidation/pure/TradeFormButtons/index.tsx new file mode 100644 index 0000000000..eb63f6e25a --- /dev/null +++ b/src/modules/tradeFormValidation/pure/TradeFormButtons/index.tsx @@ -0,0 +1,46 @@ +import React from 'react' + +import { Trans } from '@lingui/macro' + +import { tradeButtonsMap } from './tradeButtonsMap' + +import { TradeFormButtonContext, TradeFormValidation } from '../../types' +import { TradeFormBlankButton } from '../TradeFormBlankButton' + +export interface TradeFormButtonsProps { + validation: TradeFormValidation | null + context: TradeFormButtonContext + doTradeText: string + confirmText: string + isExpertMode: boolean + isDisabled?: boolean +} + +export function TradeFormButtons(props: TradeFormButtonsProps) { + const { validation, context, isExpertMode, isDisabled, doTradeText, confirmText } = props + + // When there are no validation errors + if (!validation) { + return ( + (isExpertMode ? context.doTrade() : context.confirmTrade())} + > + {isExpertMode ? doTradeText : confirmText} + + ) + } + + const buttonFactory = tradeButtonsMap[validation] + + if (typeof buttonFactory === 'function') { + return buttonFactory(context) + } + + return ( + + {buttonFactory.text} + + ) +} diff --git a/src/modules/tradeFormValidation/pure/TradeFormButtons/tradeButtonsMap.tsx b/src/modules/tradeFormValidation/pure/TradeFormButtons/tradeButtonsMap.tsx new file mode 100644 index 0000000000..7d2184f314 --- /dev/null +++ b/src/modules/tradeFormValidation/pure/TradeFormButtons/tradeButtonsMap.tsx @@ -0,0 +1,146 @@ +import React from 'react' + +import { Trans } from '@lingui/macro' +import styled from 'styled-components/macro' + +import { CompatibilityIssuesWarning } from 'modules/trade/pure/CompatibilityIssuesWarning' + +import { GpQuoteErrorCodes } from 'api/gnosisProtocol/errors/QuoteError' +import { TradeApproveButton } from 'common/containers/TradeApprove' +import { TokenSymbol } from 'common/pure/TokenSymbol' +import { TradeLoadingButton } from 'common/pure/TradeLoadingButton' + +import { TradeFormButtonContext, TradeFormValidation } from '../../types' +import { TradeFormBlankButton } from '../TradeFormBlankButton' + +interface ButtonErrorConfig { + text: JSX.Element | string + id?: string +} + +interface ButtonCallback { + (context: TradeFormButtonContext): JSX.Element | null +} + +const CompatibilityIssuesWarningWrapper = styled.div` + margin-top: -10px; +` + +const quoteErrorTexts: Record = { + [GpQuoteErrorCodes.UNHANDLED_ERROR]: 'Error loading price. Try again later.', + [GpQuoteErrorCodes.TransferEthToContract]: + 'Buying native currency with smart contract wallets is not currently supported', + [GpQuoteErrorCodes.UnsupportedToken]: 'Unsupported token', + [GpQuoteErrorCodes.InsufficientLiquidity]: 'Insufficient liquidity for this trade.', + [GpQuoteErrorCodes.FeeExceedsFrom]: 'Sell amount is too small', + [GpQuoteErrorCodes.ZeroPrice]: 'Invalid price. Try increasing input/output amount.', +} + +export const tradeButtonsMap: Record = { + [TradeFormValidation.WrapUnwrapAmountNotSet]: { + text: 'Enter an amount', + }, + // TODO: implement + [TradeFormValidation.WrapUnwrapFlow]: { + text: 'Wrap or unwrap', + }, + [TradeFormValidation.CurrencyNotSet]: { + text: 'Select a token', + }, + [TradeFormValidation.InputAmountNotSet]: { + text: 'Enter an amount', + }, + [TradeFormValidation.RecipientInvalid]: { + text: 'Enter a valid recipient', + }, + [TradeFormValidation.CurrencyNotSupported]: (context) => { + const { derivedState, isSupportedWallet } = context + const { inputCurrency, outputCurrency } = derivedState + + return inputCurrency && outputCurrency ? ( + <> + + Unsupported token + + + + + + ) : null + }, + [TradeFormValidation.QuoteErrors]: (context) => { + const { quote } = context + const defaultError = quoteErrorTexts[GpQuoteErrorCodes.UNHANDLED_ERROR] + + return ( + + {(quote.error && quoteErrorTexts[quote.error.type]) || defaultError} + + ) + }, + [TradeFormValidation.WalletNotConnected]: (context) => { + return ( + + Connect Wallet + + ) + }, + [TradeFormValidation.WalletNotSupported]: { + text: 'Wallet Unsupported', + }, + [TradeFormValidation.SafeReadonlyUser]: { + text: 'Read Only', + }, + [TradeFormValidation.QuoteLoading]: { + text: , + }, + [TradeFormValidation.BalancesNotLoaded]: { + text: "Couldn't load balances", + }, + [TradeFormValidation.BalanceInsufficient]: (context) => { + return ( + + Insufficient {} balance + + ) + }, + [TradeFormValidation.ExpertApproveAndSwap]: (context) => { + const tokenToApprove = context.derivedState.slippageAdjustedSellAmount?.currency.wrapped + + return ( + + + Confirm (Approve {} and {context.defaultText}) + + + ) + }, + [TradeFormValidation.ApproveAndSwap]: (context) => { + const tokenToApprove = context.derivedState.slippageAdjustedSellAmount?.currency.wrapped + + return ( + + + Approve {} and {context.defaultText} + + + ) + }, + [TradeFormValidation.ApproveRequired]: (context) => { + const amountToApprove = context.derivedState.slippageAdjustedSellAmount + + if (!amountToApprove) return null + + return ( + + + {context.defaultText} + + + ) + }, +} diff --git a/src/modules/tradeFormValidation/services/validateTradeForm.ts b/src/modules/tradeFormValidation/services/validateTradeForm.ts new file mode 100644 index 0000000000..ab47164060 --- /dev/null +++ b/src/modules/tradeFormValidation/services/validateTradeForm.ts @@ -0,0 +1,94 @@ +import { ApprovalState } from 'legacy/hooks/useApproveCallback' +import { isAddress } from 'legacy/utils' + +import { isFractionFalsy } from 'utils/isFractionFalsy' + +import { TradeFormValidation, TradeFormValidationContext } from '../types' + +export function validateTradeForm(context: TradeFormValidationContext): TradeFormValidation | null { + const { + derivedTradeState, + approvalState, + isTxBundlingEnabled, + isWrapUnwrap, + isExpertMode, + isSupportedWallet, + isSafeReadonlyUser, + isSwapUnsupported, + recipientEnsAddress, + tradeQuote, + account, + } = context + + const { inputCurrency, outputCurrency, inputCurrencyAmount, inputCurrencyBalance, outputCurrencyAmount, recipient } = + derivedTradeState + + const approvalRequired = approvalState === ApprovalState.NOT_APPROVED || approvalState === ApprovalState.PENDING + + const inputAmountIsNotSet = !inputCurrencyAmount || isFractionFalsy(inputCurrencyAmount) + const outputAmountIsNotSet = isFractionFalsy(outputCurrencyAmount) + + if (isWrapUnwrap) { + if (inputAmountIsNotSet || outputAmountIsNotSet) { + return TradeFormValidation.WrapUnwrapAmountNotSet + } + + return TradeFormValidation.WrapUnwrapFlow + } + + if (!inputCurrency || !outputCurrency) { + return TradeFormValidation.CurrencyNotSet + } + + if (inputAmountIsNotSet) { + return TradeFormValidation.InputAmountNotSet + } + + if (recipient && !recipientEnsAddress && !isAddress(recipient)) { + return TradeFormValidation.RecipientInvalid + } + + if (isSwapUnsupported) { + return TradeFormValidation.CurrencyNotSupported + } + + if (tradeQuote.error) { + return TradeFormValidation.QuoteErrors + } + + if (!account) { + return TradeFormValidation.WalletNotConnected + } + + if (!isSupportedWallet) { + return TradeFormValidation.WalletNotSupported + } + + if (isSafeReadonlyUser) { + return TradeFormValidation.SafeReadonlyUser + } + + if (!tradeQuote.response) { + return TradeFormValidation.QuoteLoading + } + + if (!inputCurrencyBalance) { + return TradeFormValidation.BalancesNotLoaded + } + + if (inputCurrencyBalance.lessThan(inputCurrencyAmount)) { + return TradeFormValidation.BalanceInsufficient + } + + if (approvalRequired) { + if (isTxBundlingEnabled) { + if (isExpertMode) { + return TradeFormValidation.ExpertApproveAndSwap + } + return TradeFormValidation.ApproveAndSwap + } + return TradeFormValidation.ApproveRequired + } + + return null +} diff --git a/src/modules/tradeFormValidation/state/tradeFormValidationAtom.ts b/src/modules/tradeFormValidation/state/tradeFormValidationAtom.ts new file mode 100644 index 0000000000..de7529f3e3 --- /dev/null +++ b/src/modules/tradeFormValidation/state/tradeFormValidationAtom.ts @@ -0,0 +1,13 @@ +import { atom } from 'jotai' + +import { tradeFormValidationContextAtom } from './tradeFormValidationContextAtom' + +import { validateTradeForm } from '../services/validateTradeForm' + +export const tradeFormValidationAtom = atom((get) => { + const context = get(tradeFormValidationContextAtom) + + if (!context) return null + + return validateTradeForm(context) +}) diff --git a/src/modules/tradeFormValidation/state/tradeFormValidationContextAtom.ts b/src/modules/tradeFormValidation/state/tradeFormValidationContextAtom.ts new file mode 100644 index 0000000000..f422632a8b --- /dev/null +++ b/src/modules/tradeFormValidation/state/tradeFormValidationContextAtom.ts @@ -0,0 +1,5 @@ +import { atom } from 'jotai' + +import { TradeFormValidationContext } from '../types' + +export const tradeFormValidationContextAtom = atom(null) diff --git a/src/modules/tradeFormValidation/types.ts b/src/modules/tradeFormValidation/types.ts new file mode 100644 index 0000000000..4d1af1bc8e --- /dev/null +++ b/src/modules/tradeFormValidation/types.ts @@ -0,0 +1,68 @@ +import { ApprovalState } from 'legacy/hooks/useApproveCallback' + +import { TradeDerivedState } from 'modules/trade' +import { TradeQuoteState } from 'modules/tradeQuote' + +export enum TradeFormValidation { + // Wrap/unwrap + WrapUnwrapAmountNotSet, + WrapUnwrapFlow, + + // Quote request params + CurrencyNotSet, + InputAmountNotSet, + RecipientInvalid, + + // Quote errors + QuoteErrors, + CurrencyNotSupported, + + // Wallet + WalletNotConnected, + WalletNotSupported, + SafeReadonlyUser, + + // Quote loading indicator + QuoteLoading, + + // Balances + BalancesNotLoaded, + BalanceInsufficient, + + // Approve + ExpertApproveAndSwap, + ApproveAndSwap, + ApproveRequired, +} + +export interface TradeFormValidationLocalContext { + isExpertMode: boolean +} + +export interface TradeFormValidationCommonContext { + account: string | undefined + derivedTradeState: TradeDerivedState + approvalState: ApprovalState + tradeQuote: TradeQuoteState + recipientEnsAddress: string | null + isWrapUnwrap: boolean + isTxBundlingEnabled: boolean + isSupportedWallet: boolean + isSwapUnsupported: boolean + isSafeReadonlyUser: boolean +} + +export interface TradeFormValidationContext extends TradeFormValidationLocalContext, TradeFormValidationCommonContext {} + +export interface TradeFormButtonContext { + defaultText: string + derivedState: TradeDerivedState + quote: TradeQuoteState + isSupportedWallet: boolean + + doTrade(): void + + confirmTrade(): void + + connectWallet(): void +} diff --git a/src/modules/tradeFormValidation/updaters/TradeFormValidationUpdater.ts b/src/modules/tradeFormValidation/updaters/TradeFormValidationUpdater.ts new file mode 100644 index 0000000000..6d7a826999 --- /dev/null +++ b/src/modules/tradeFormValidation/updaters/TradeFormValidationUpdater.ts @@ -0,0 +1,22 @@ +import { useUpdateAtom } from 'jotai/utils' +import { useEffect, useMemo } from 'react' + +import { useTradeFormValidationContext } from '../hooks/useTradeFormValidationContext' +import { tradeFormValidationContextAtom } from '../state/tradeFormValidationContextAtom' + +export function TradeFormValidationUpdater({ isExpertMode }: { isExpertMode: boolean }) { + const updateContext = useUpdateAtom(tradeFormValidationContextAtom) + const localContext = useMemo(() => ({ isExpertMode }), [isExpertMode]) + const commonContext = useTradeFormValidationContext() + + useEffect(() => { + if (!commonContext) return + + updateContext({ + ...commonContext, + ...localContext, + }) + }, [commonContext, localContext, updateContext]) + + return null +} diff --git a/src/modules/tradeQuote/hooks/useTradeQuotePolling.ts b/src/modules/tradeQuote/hooks/useTradeQuotePolling.ts index 2cbacc507e..25a5ba8256 100644 --- a/src/modules/tradeQuote/hooks/useTradeQuotePolling.ts +++ b/src/modules/tradeQuote/hooks/useTradeQuotePolling.ts @@ -33,7 +33,7 @@ export function useTradeQuotePolling() { } const fetchQuote = () => { - updateQuoteState({ isLoading: true }) + updateQuoteState({ isLoading: true, error: null }) getQuoteOnlyResolveLast(quoteParams) .then((response) => { @@ -43,7 +43,7 @@ export function useTradeQuotePolling() { return } - updateQuoteState({ response: data, isLoading: false }) + updateQuoteState({ response: data, isLoading: false, error: null }) }) .catch((error: GpQuoteError) => { console.log('[useGetQuote]:: fetchQuote error', error) diff --git a/src/pages/LimitOrders/index.tsx b/src/pages/LimitOrders/index.tsx index 7432537606..1dca417d72 100644 --- a/src/pages/LimitOrders/index.tsx +++ b/src/pages/LimitOrders/index.tsx @@ -6,18 +6,22 @@ import { InitialPriceUpdater, ExecutionPriceUpdater, limitOrdersRawStateAtom, + limitOrdersSettingsAtom, } from 'modules/limitOrders' import { OrdersTableWidget } from 'modules/ordersTable' import * as styledEl from 'modules/trade/pure/TradePageLayout' +import { TradeFormValidationUpdater } from 'modules/tradeFormValidation' export default function LimitOrderPage() { const { isUnlocked } = useAtomValue(limitOrdersRawStateAtom) + const { expertMode } = useAtomValue(limitOrdersSettingsAtom) return ( <> + From 5d77169b38cc074cf5f6ad093426d8969847e111 Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Tue, 6 Jun 2023 14:04:22 +0600 Subject: [PATCH 03/67] [2] refactor(trade): add wrap native token flow to TradeWidget (#2591) --- src/legacy/hooks/useWrapCallback.ts | 112 +++++++----------- .../containers/TradeButtons/index.tsx | 4 +- .../swap/hooks/useSwapButtonContext.ts | 42 ++++++- .../trade/containers/TradeWidget/index.tsx | 3 + .../containers/WrapNativeModal/index.tsx | 64 ++++++++++ .../trade/hooks/useSwitchTokensPlaces.ts | 20 ++-- src/modules/trade/hooks/useWrapNativeFlow.ts | 68 +++++++++++ src/modules/trade/index.ts | 1 + .../trade/state/wrapNativeStateAtom.ts | 9 ++ .../pure/TradeFormButtons/tradeButtonsMap.tsx | 11 +- src/modules/tradeFormValidation/types.ts | 3 +- 11 files changed, 245 insertions(+), 92 deletions(-) create mode 100644 src/modules/trade/containers/WrapNativeModal/index.tsx create mode 100644 src/modules/trade/hooks/useWrapNativeFlow.ts create mode 100644 src/modules/trade/state/wrapNativeStateAtom.ts diff --git a/src/legacy/hooks/useWrapCallback.ts b/src/legacy/hooks/useWrapCallback.ts index 4b2f7da2c5..5a1d65894a 100644 --- a/src/legacy/hooks/useWrapCallback.ts +++ b/src/legacy/hooks/useWrapCallback.ts @@ -1,9 +1,10 @@ +import { SupportedChainId } from '@cowprotocol/cow-sdk' import { BigNumber } from '@ethersproject/bignumber' import { Contract } from '@ethersproject/contracts' import { TransactionResponse } from '@ethersproject/providers' import { Currency, CurrencyAmount } from '@uniswap/sdk-core' -import { t } from '@lingui/macro' +import { Nullish } from 'types' import { wrapAnalytics } from 'legacy/components/analytics' import { ConfirmOperationType } from 'legacy/components/TransactionConfirmationModal' @@ -12,18 +13,14 @@ import { RADIX_HEX } from 'legacy/constants' import { useWETHContract } from 'legacy/hooks/useContract' import { useCloseModals } from 'legacy/state/application/hooks' import { useTransactionAdder } from 'legacy/state/enhancedTransactions/hooks' -import { useDerivedSwapInfo } from 'legacy/state/swap/hooks' import { calculateGasMargin } from 'legacy/utils/calculateGasMargin' import { getChainCurrencySymbols } from 'legacy/utils/gnosis_chain/hack' import { useTransactionConfirmModal } from 'modules/swap/hooks/useTransactionConfirmModal' -import useCurrencyBalance from 'modules/tokens/hooks/useCurrencyBalance' -import { useIsNativeIn } from 'modules/trade/hooks/useIsNativeInOrOut' import { useNativeTokenContext } from 'modules/trade/hooks/useNativeTokenContext' import { useWalletInfo } from 'modules/wallet' import { formatTokenAmount } from 'utils/amountFormat' -import { formatSymbol } from 'utils/format' import { isRejectRequestProviderError } from '../utils/misc' @@ -46,27 +43,21 @@ type TransactionAdder = ReturnType export type OpenSwapConfirmModalCallback = (message: string, operationType: ConfirmOperationType) => void -export interface WrapUnwrapContext { - wrapType: WrapType - wethContract: Contract - amountHex: string +export interface WrapDescription { confirmationMessage: string operationMessage: string summary: string +} + +export interface WrapUnwrapContext { + chainId: SupportedChainId + wethContract: Contract + amount: CurrencyAmount addTransaction: TransactionAdder closeModals: () => void openTransactionConfirmationModal: OpenSwapConfirmModalCallback } -export function useHasEnoughWrappedBalanceForSwap(inputAmount?: CurrencyAmount): boolean { - const { currencies } = useDerivedSwapInfo() - const { account } = useWalletInfo() - const wrappedBalance = useCurrencyBalance(account ?? undefined, currencies.INPUT?.wrapped) - - // is an native currency trade but wrapped token has enough balance - return !!(wrappedBalance && inputAmount && !wrappedBalance.lessThan(inputAmount)) -} - export function useWrapType(): WrapType { const { isNativeIn, isNativeOut, isWrappedIn, isWrappedOut } = useNativeTokenContext() @@ -81,35 +72,11 @@ export function useWrapType(): WrapType { return isNativeIn ? WrapType.WRAP : WrapType.UNWRAP } -export function useWrapUnwrapError(wrapType: WrapType, inputAmount?: CurrencyAmount): string | undefined { - const { chainId, account } = useWalletInfo() - const { currencies } = useDerivedSwapInfo() - const { native, wrapped } = getChainCurrencySymbols(chainId) - const balance = useCurrencyBalance(account ?? undefined, currencies.INPUT) - - const symbol = wrapType === WrapType.WRAP ? native : wrapped - // Check if user has enough balance for wrap/unwrap - const sufficientBalance = !!(inputAmount && balance && !balance.lessThan(inputAmount)) - const isZero = balance && !inputAmount - - if (isZero) { - return t`Enter an amount` - } - - return !sufficientBalance ? t`Insufficient ${formatSymbol(symbol)} balance` : undefined -} - -export function useWrapUnwrapContext( - inputAmount: CurrencyAmount | null | undefined -): WrapUnwrapContext | null { +export function useWrapUnwrapContext(inputAmount: Nullish>): WrapUnwrapContext | null { const { chainId } = useWalletInfo() const closeModals = useCloseModals() const wethContract = useWETHContract() - const { native, wrapped } = getChainCurrencySymbols(chainId) - const isNativeIn = useIsNativeIn() const addTransaction = useTransactionAdder() - const wrapType = isNativeIn ? WrapType.WRAP : WrapType.UNWRAP - const isWrap = isNativeIn const openTxConfirmationModal = useTransactionConfirmModal() if (!wethContract || !chainId || !inputAmount) { @@ -119,23 +86,13 @@ export function useWrapUnwrapContext( const openTransactionConfirmationModal = (pendingText: string, operationType: ConfirmOperationType) => { openTxConfirmationModal({ operationType, pendingText }) } - const amountHex = `0x${inputAmount.quotient.toString(RADIX_HEX)}` - const operationType = isWrap ? ConfirmOperationType.WRAP_ETHER : ConfirmOperationType.UNWRAP_WETH - const baseSummarySuffix = isWrap ? `${native} to ${wrapped}` : `${wrapped} to ${native}` - const baseSummary = `${formatTokenAmount(inputAmount)} ${baseSummarySuffix}` - const summary = `${isWrap ? 'Wrap' : 'Unwrap'} ${baseSummary}` - const confirmationMessage = `${isWrap ? 'Wrapping' : 'Unwrapping'} ${baseSummary}` - const operationMessage = getOperationMessage(operationType, chainId) return { - wrapType, - closeModals, + chainId, wethContract, - amountHex, - summary, - confirmationMessage, - operationMessage, + amount: inputAmount, addTransaction, + closeModals, openTransactionConfirmationModal, } } @@ -159,26 +116,19 @@ export async function wrapUnwrapCallback( context: WrapUnwrapContext, params: WrapUnwrapCallbackParams = { useModals: true } ): Promise { - const { useModals } = params - const { - wrapType, - openTransactionConfirmationModal, - confirmationMessage, - operationMessage, - summary, - wethContract, - amountHex, - addTransaction, - closeModals, - } = context - const isWrap = wrapType === WrapType.WRAP - const operationType = isWrap ? ConfirmOperationType.WRAP_ETHER : ConfirmOperationType.UNWRAP_WETH + const { chainId, amount, wethContract, addTransaction, openTransactionConfirmationModal, closeModals } = context + const isNativeIn = amount.currency.isNative + const amountHex = `0x${amount.quotient.toString(RADIX_HEX)}` + const operationType = isNativeIn ? ConfirmOperationType.WRAP_ETHER : ConfirmOperationType.UNWRAP_WETH + + const useModals = params.useModals + const { confirmationMessage, operationMessage, summary } = getWrapDescription(chainId, isNativeIn, amount) try { useModals && openTransactionConfirmationModal(confirmationMessage, operationType) wrapAnalytics('Send', operationMessage) - const wrapUnwrap = isWrap ? wrapContractCall : unwrapContractCall + const wrapUnwrap = isNativeIn ? wrapContractCall : unwrapContractCall const txReceipt = await wrapUnwrap(wethContract, amountHex) wrapAnalytics('Sign', operationMessage) @@ -207,6 +157,26 @@ export async function wrapUnwrapCallback( } } +function getWrapDescription( + chainId: SupportedChainId, + isWrap: boolean, + inputAmount: CurrencyAmount +): WrapDescription { + const { native, wrapped } = getChainCurrencySymbols(chainId) + const operationType = isWrap ? ConfirmOperationType.WRAP_ETHER : ConfirmOperationType.UNWRAP_WETH + const baseSummarySuffix = isWrap ? `${native} to ${wrapped}` : `${wrapped} to ${native}` + const baseSummary = `${formatTokenAmount(inputAmount)} ${baseSummarySuffix}` + const summary = `${isWrap ? 'Wrap' : 'Unwrap'} ${baseSummary}` + const confirmationMessage = `${isWrap ? 'Wrapping' : 'Unwrapping'} ${baseSummary}` + const operationMessage = getOperationMessage(operationType, chainId) + + return { + summary, + operationMessage, + confirmationMessage, + } +} + async function wrapContractCall(wethContract: Contract, amountHex: string): Promise { const estimatedGas = await wethContract.estimateGas.deposit({ value: amountHex }).catch(_handleGasEstimateError) const gasLimit = calculateGasMargin(estimatedGas) diff --git a/src/modules/limitOrders/containers/TradeButtons/index.tsx b/src/modules/limitOrders/containers/TradeButtons/index.tsx index 05a447b1a4..ac904483a5 100644 --- a/src/modules/limitOrders/containers/TradeButtons/index.tsx +++ b/src/modules/limitOrders/containers/TradeButtons/index.tsx @@ -11,7 +11,7 @@ import { useLimitOrdersDerivedState } from 'modules/limitOrders/hooks/useLimitOr import { useLimitOrdersWarningsAccepted } from 'modules/limitOrders/hooks/useLimitOrdersWarningsAccepted' import { TradeFlowContext } from 'modules/limitOrders/services/types' import { limitOrdersSettingsAtom } from 'modules/limitOrders/state/limitOrdersSettingsAtom' -import { useTradeConfirmActions } from 'modules/trade/hooks/useTradeConfirmActions' +import { useTradeConfirmActions, useWrapNativeFlow } from 'modules/trade' import { TradeFormButtons, useGetTradeFormValidation, @@ -42,6 +42,7 @@ export function TradeButtons(props: TradeButtonsProps) { const warningsAccepted = useLimitOrdersWarningsAccepted(false) const { isSupportedWallet } = useWalletDetails() const tradeConfirmActions = useTradeConfirmActions() + const wrapNativeFlow = useWrapNativeFlow() const handleTrade = useHandleOrderPlacement(tradeContext, priceImpact, settingsState, tradeConfirmActions) @@ -53,6 +54,7 @@ export function TradeButtons(props: TradeButtonsProps) { doTrade: handleTrade, quote, isSupportedWallet, + wrapNativeFlow, confirmTrade() { tradeConfirmActions.onOpen() }, diff --git a/src/modules/swap/hooks/useSwapButtonContext.ts b/src/modules/swap/hooks/useSwapButtonContext.ts index 7b8fc18f0e..7d9868f1c6 100644 --- a/src/modules/swap/hooks/useSwapButtonContext.ts +++ b/src/modules/swap/hooks/useSwapButtonContext.ts @@ -1,16 +1,16 @@ +import { Currency, CurrencyAmount } from '@uniswap/sdk-core' + +import { t } from '@lingui/macro' + import { PriceImpact } from 'legacy/hooks/usePriceImpact' -import { - useHasEnoughWrappedBalanceForSwap, - useWrapCallback, - useWrapType, - useWrapUnwrapError, -} from 'legacy/hooks/useWrapCallback' +import { useWrapCallback, useWrapType, WrapType } from 'legacy/hooks/useWrapCallback' import { useToggleWalletModal } from 'legacy/state/application/hooks' import { useIsTradeUnsupported } from 'legacy/state/lists/hooks' import { useGetQuoteAndStatus, useIsBestQuoteLoading } from 'legacy/state/price/hooks' import { Field } from 'legacy/state/swap/actions' import { useDerivedSwapInfo, useSwapActionHandlers } from 'legacy/state/swap/hooks' import { useExpertModeManager } from 'legacy/state/user/hooks' +import { getChainCurrencySymbols } from 'legacy/utils/gnosis_chain/hack' import { getSwapButtonState } from 'modules/swap/helpers/getSwapButtonState' import { useEthFlowContext } from 'modules/swap/hooks/useEthFlowContext' @@ -19,6 +19,7 @@ import { useSafeBundleApprovalFlowContext } from 'modules/swap/hooks/useSafeBund import { useSwapConfirmManager } from 'modules/swap/hooks/useSwapConfirmManager' import { useSwapFlowContext } from 'modules/swap/hooks/useSwapFlowContext' import { SwapButtonsContext } from 'modules/swap/pure/SwapButtons' +import useCurrencyBalance from 'modules/tokens/hooks/useCurrencyBalance' import { useIsNativeIn } from 'modules/trade/hooks/useIsNativeInOrOut' import { useIsWrappedOut } from 'modules/trade/hooks/useIsWrappedInOrOut' import { useWrappedToken } from 'modules/trade/hooks/useWrappedToken' @@ -28,6 +29,7 @@ import { useTradeApproveState } from 'common/containers/TradeApprove/useTradeApp import { useIsEthFlowBundlingEnabled } from 'common/hooks/useIsEthFlowBundlingEnabled' import { useIsSmartContractWallet } from 'common/hooks/useIsSmartContractWallet' import { useIsTxBundlingEnabled } from 'common/hooks/useIsTxBundlingEnabled' +import { formatSymbol } from 'utils/format' import { useSafeBundleEthFlowContext } from './useSafeBundleEthFlowContext' @@ -139,3 +141,31 @@ export function useSwapButtonContext(input: SwapButtonInput): SwapButtonsContext onCurrencySelection, } } + +// TODO: get rid of it. The validation should be handled by modules/tradeFormValidation +function useWrapUnwrapError(wrapType: WrapType, inputAmount?: CurrencyAmount): string | undefined { + const { chainId, account } = useWalletInfo() + const { currencies } = useDerivedSwapInfo() + const { native, wrapped } = getChainCurrencySymbols(chainId) + const balance = useCurrencyBalance(account ?? undefined, currencies.INPUT) + + const symbol = wrapType === WrapType.WRAP ? native : wrapped + // Check if user has enough balance for wrap/unwrap + const sufficientBalance = !!(inputAmount && balance && !balance.lessThan(inputAmount)) + const isZero = balance && !inputAmount + + if (isZero) { + return t`Enter an amount` + } + + return !sufficientBalance ? t`Insufficient ${formatSymbol(symbol)} balance` : undefined +} + +function useHasEnoughWrappedBalanceForSwap(inputAmount?: CurrencyAmount): boolean { + const { currencies } = useDerivedSwapInfo() + const { account } = useWalletInfo() + const wrappedBalance = useCurrencyBalance(account ?? undefined, currencies.INPUT?.wrapped) + + // is an native currency trade but wrapped token has enough balance + return !!(wrappedBalance && inputAmount && !wrappedBalance.lessThan(inputAmount)) +} diff --git a/src/modules/trade/containers/TradeWidget/index.tsx b/src/modules/trade/containers/TradeWidget/index.tsx index ca6368cecd..36c75529c0 100644 --- a/src/modules/trade/containers/TradeWidget/index.tsx +++ b/src/modules/trade/containers/TradeWidget/index.tsx @@ -19,6 +19,8 @@ import { CurrencyInfo } from 'common/pure/CurrencyInputPanel/types' import * as styledEl from './styled' import { TradeWidgetModals } from './TradeWidgetModals' +import { WrapNativeModal } from '../WrapNativeModal' + export interface TradeWidgetActions { onCurrencySelection: CurrencyInputPanelProps['onCurrencySelection'] onUserInput: CurrencyInputPanelProps['onUserInput'] @@ -99,6 +101,7 @@ export function TradeWidget(props: TradeWidgetProps) { {!disableQuotePolling && } + diff --git a/src/modules/trade/containers/WrapNativeModal/index.tsx b/src/modules/trade/containers/WrapNativeModal/index.tsx new file mode 100644 index 0000000000..8ae0d04fed --- /dev/null +++ b/src/modules/trade/containers/WrapNativeModal/index.tsx @@ -0,0 +1,64 @@ +import { useAtom } from 'jotai' +import { useCallback } from 'react' + +import { useWalletDisplayedAddress } from 'modules/wallet' + +import { useWalletStatusIcon } from 'common/hooks/useWalletStatusIcon' +import { ConfirmationPendingContent } from 'common/pure/ConfirmationPendingContent' +import { GpModal } from 'common/pure/Modal' +import { TokenAmount } from 'common/pure/TokenAmount' +import { TokenSymbol } from 'common/pure/TokenSymbol' + +import { useDerivedTradeState } from '../../hooks/useDerivedTradeState' +import { wrapNativeStateAtom } from '../../state/wrapNativeStateAtom' + +export function WrapNativeModal() { + const [{ isOpen }, setWrapNativeState] = useAtom(wrapNativeStateAtom) + + const derivedState = useDerivedTradeState() + const walletAddress = useWalletDisplayedAddress() + const statusIcon = useWalletStatusIcon() + + const { inputCurrencyAmount, outputCurrency } = derivedState.state || {} + + const handleDismiss = useCallback(() => { + setWrapNativeState({ isOpen: false }) + }, [setWrapNativeState]) + + const inputCurrency = inputCurrencyAmount?.currency + const isNativeIn = !!inputCurrency?.isNative + + const title = isNativeIn ? ( + + Wrapping to{' '} + + + ) : ( + + Unwrapping to{' '} + + + ) + + const description = ( + + Unwrapping
Follow these steps: +
+ ) + + const operationLabel = isNativeIn ? 'wrapping' : 'unwrapping' + + return ( + + + + ) +} diff --git a/src/modules/trade/hooks/useSwitchTokensPlaces.ts b/src/modules/trade/hooks/useSwitchTokensPlaces.ts index 49a6b5c02c..f99e14322b 100644 --- a/src/modules/trade/hooks/useSwitchTokensPlaces.ts +++ b/src/modules/trade/hooks/useSwitchTokensPlaces.ts @@ -25,15 +25,17 @@ export function useSwitchTokensPlaces(stateOverride: Partial { if (!inputCurrencyId || !outputCurrencyId || !updateState) return - if (isWrapOrUnwrap) return - - updateState({ - inputCurrencyId: outputCurrencyId, - outputCurrencyId: inputCurrencyId, - inputCurrencyAmount: FractionUtils.serializeFractionToJSON(outputCurrencyAmount), - outputCurrencyAmount: FractionUtils.serializeFractionToJSON(inputCurrencyAmount), - ...stateOverride, - }) + + if (!isWrapOrUnwrap) { + updateState({ + inputCurrencyId: outputCurrencyId, + outputCurrencyId: inputCurrencyId, + inputCurrencyAmount: FractionUtils.serializeFractionToJSON(outputCurrencyAmount), + outputCurrencyAmount: FractionUtils.serializeFractionToJSON(inputCurrencyAmount), + ...stateOverride, + }) + } + tradeNavigate(chainId, { inputCurrencyId: outputCurrencyId, outputCurrencyId: inputCurrencyId }) }, [ updateState, diff --git a/src/modules/trade/hooks/useWrapNativeFlow.ts b/src/modules/trade/hooks/useWrapNativeFlow.ts new file mode 100644 index 0000000000..c30550825a --- /dev/null +++ b/src/modules/trade/hooks/useWrapNativeFlow.ts @@ -0,0 +1,68 @@ +import { useUpdateAtom } from 'jotai/utils' +import { useCallback } from 'react' + +import { Currency, CurrencyAmount } from '@uniswap/sdk-core' + +import { Nullish } from 'types' + +import { useWETHContract } from 'legacy/hooks/useContract' +import { + wrapUnwrapCallback, + WrapUnwrapCallback, + WrapUnwrapCallbackParams, + WrapUnwrapContext, +} from 'legacy/hooks/useWrapCallback' +import { useTransactionAdder } from 'legacy/state/enhancedTransactions/hooks' + +import { useWalletInfo } from 'modules/wallet' + +import { useDerivedTradeState } from './useDerivedTradeState' + +import { wrapNativeStateAtom } from '../state/wrapNativeStateAtom' + +export function useWrapNativeFlow() { + const setWrapNativeState = useUpdateAtom(wrapNativeStateAtom) + const derivedTradeState = useDerivedTradeState() + const wrapCallback = useWrapNativeCallback(derivedTradeState.state?.inputCurrencyAmount) + + return useCallback(() => { + setWrapNativeState({ isOpen: true }) + wrapCallback?.() + }, [setWrapNativeState, wrapCallback]) +} + +function useWrapNativeContext(amount: Nullish>): WrapUnwrapContext | null { + const { chainId } = useWalletInfo() + const wethContract = useWETHContract() + const addTransaction = useTransactionAdder() + const setWrapNativeState = useUpdateAtom(wrapNativeStateAtom) + + if (!wethContract || !chainId || !amount) { + return null + } + + return { + chainId, + wethContract, + amount, + addTransaction, + closeModals() { + setWrapNativeState({ isOpen: false }) + }, + openTransactionConfirmationModal() { + setWrapNativeState({ isOpen: true }) + }, + } +} + +function useWrapNativeCallback(inputAmount: Nullish>): WrapUnwrapCallback | null { + const context = useWrapNativeContext(inputAmount) + + if (!context) { + return null + } + + return (params?: WrapUnwrapCallbackParams) => { + return wrapUnwrapCallback(context, params) + } +} diff --git a/src/modules/trade/index.ts b/src/modules/trade/index.ts index 881ffef03d..d0a42fe9bc 100644 --- a/src/modules/trade/index.ts +++ b/src/modules/trade/index.ts @@ -4,4 +4,5 @@ export * from './pure/TradeConfirmation' export * from './hooks/useTradeConfirmActions' export * from './hooks/useTradeTypeInfo' export * from './hooks/setupTradeState/useSetupTradeState' +export * from './hooks/useWrapNativeFlow' export * from './types/TradeDerivedState' diff --git a/src/modules/trade/state/wrapNativeStateAtom.ts b/src/modules/trade/state/wrapNativeStateAtom.ts new file mode 100644 index 0000000000..4b4d33fe67 --- /dev/null +++ b/src/modules/trade/state/wrapNativeStateAtom.ts @@ -0,0 +1,9 @@ +import { atom } from 'jotai' + +interface WrapNativeState { + isOpen: boolean +} + +export const wrapNativeStateAtom = atom({ + isOpen: false, +}) diff --git a/src/modules/tradeFormValidation/pure/TradeFormButtons/tradeButtonsMap.tsx b/src/modules/tradeFormValidation/pure/TradeFormButtons/tradeButtonsMap.tsx index 7d2184f314..cc034a60aa 100644 --- a/src/modules/tradeFormValidation/pure/TradeFormButtons/tradeButtonsMap.tsx +++ b/src/modules/tradeFormValidation/pure/TradeFormButtons/tradeButtonsMap.tsx @@ -40,9 +40,14 @@ export const tradeButtonsMap: Record { + const isNativeIn = !!context.derivedState.inputCurrency?.isNative + + return ( + + {isNativeIn ? 'Wrap' : 'Unwrap'} + + ) }, [TradeFormValidation.CurrencyNotSet]: { text: 'Select a token', diff --git a/src/modules/tradeFormValidation/types.ts b/src/modules/tradeFormValidation/types.ts index 4d1af1bc8e..0ef075cf36 100644 --- a/src/modules/tradeFormValidation/types.ts +++ b/src/modules/tradeFormValidation/types.ts @@ -61,8 +61,7 @@ export interface TradeFormButtonContext { isSupportedWallet: boolean doTrade(): void - confirmTrade(): void - connectWallet(): void + wrapNativeFlow(): void } From bccacb60cb9e53c1935da727c0900a5e9f1c4cfc Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Tue, 6 Jun 2023 14:09:44 +0600 Subject: [PATCH 04/67] feat(twap): add generic trade form validation to TWAP (#2592) --- .../containers/TradeButtons/index.tsx | 32 +++----------- .../hooks/useTradeFormButtonContext.ts | 42 ++++++++++++++++++ src/modules/tradeFormValidation/index.ts | 1 + .../twap/containers/ActionButtons/index.tsx | 43 +++++++++++++++++++ .../twap/containers/TwapFormWidget/index.tsx | 16 +------ src/modules/twap/hooks/useTwapFormState.ts | 2 +- .../PrimaryActionButton/getTwapFormState.tsx | 13 +++--- .../twap/pure/PrimaryActionButton/index.tsx | 2 +- src/pages/AdvancedOrders/index.tsx | 4 ++ 9 files changed, 106 insertions(+), 49 deletions(-) create mode 100644 src/modules/tradeFormValidation/hooks/useTradeFormButtonContext.ts create mode 100644 src/modules/twap/containers/ActionButtons/index.tsx diff --git a/src/modules/limitOrders/containers/TradeButtons/index.tsx b/src/modules/limitOrders/containers/TradeButtons/index.tsx index ac904483a5..3396890436 100644 --- a/src/modules/limitOrders/containers/TradeButtons/index.tsx +++ b/src/modules/limitOrders/containers/TradeButtons/index.tsx @@ -4,22 +4,18 @@ import React from 'react' import { Trans } from '@lingui/macro' import { PriceImpact } from 'legacy/hooks/usePriceImpact' -import { useToggleWalletModal } from 'legacy/state/application/hooks' import { useHandleOrderPlacement } from 'modules/limitOrders/hooks/useHandleOrderPlacement' -import { useLimitOrdersDerivedState } from 'modules/limitOrders/hooks/useLimitOrdersDerivedState' import { useLimitOrdersWarningsAccepted } from 'modules/limitOrders/hooks/useLimitOrdersWarningsAccepted' import { TradeFlowContext } from 'modules/limitOrders/services/types' import { limitOrdersSettingsAtom } from 'modules/limitOrders/state/limitOrdersSettingsAtom' -import { useTradeConfirmActions, useWrapNativeFlow } from 'modules/trade' +import { useTradeConfirmActions } from 'modules/trade' import { TradeFormButtons, useGetTradeFormValidation, + useTradeFormButtonContext, TradeFormBlankButton, - TradeFormButtonContext, } from 'modules/tradeFormValidation' -import { useTradeQuote } from 'modules/tradeQuote' -import { useWalletDetails } from 'modules/wallet' import { limitOrdersTradeButtonsMap } from './limitOrdersTradeButtonsMap' @@ -36,32 +32,16 @@ export function TradeButtons(props: TradeButtonsProps) { const settingsState = useAtomValue(limitOrdersSettingsAtom) const localFormValidation = useLimitOrdersFormState() const primaryFormValidation = useGetTradeFormValidation() - const tradeState = useLimitOrdersDerivedState() - const toggleWalletModal = useToggleWalletModal() - const quote = useTradeQuote() const warningsAccepted = useLimitOrdersWarningsAccepted(false) - const { isSupportedWallet } = useWalletDetails() const tradeConfirmActions = useTradeConfirmActions() - const wrapNativeFlow = useWrapNativeFlow() const handleTrade = useHandleOrderPlacement(tradeContext, priceImpact, settingsState, tradeConfirmActions) - + const confirmTrade = tradeConfirmActions.onOpen const isExpertMode = settingsState.expertMode - const tradeFormButtonContext: TradeFormButtonContext = { - defaultText: 'Limit order', - derivedState: tradeState, - doTrade: handleTrade, - quote, - isSupportedWallet, - wrapNativeFlow, - confirmTrade() { - tradeConfirmActions.onOpen() - }, - connectWallet() { - toggleWalletModal() - }, - } + const tradeFormButtonContext = useTradeFormButtonContext('Limit order', { doTrade: handleTrade, confirmTrade }) + + if (!tradeFormButtonContext) return null // Display local form validation errors only when there are no primary errors if (!primaryFormValidation && localFormValidation) { diff --git a/src/modules/tradeFormValidation/hooks/useTradeFormButtonContext.ts b/src/modules/tradeFormValidation/hooks/useTradeFormButtonContext.ts new file mode 100644 index 0000000000..f39622e706 --- /dev/null +++ b/src/modules/tradeFormValidation/hooks/useTradeFormButtonContext.ts @@ -0,0 +1,42 @@ +import { useMemo } from 'react' + +import { useToggleWalletModal } from 'legacy/state/application/hooks' + +import { useWrapNativeFlow } from 'modules/trade' +import { useDerivedTradeState } from 'modules/trade/hooks/useDerivedTradeState' +import { useTradeQuote } from 'modules/tradeQuote' +import { useWalletDetails } from 'modules/wallet' + +import { TradeFormButtonContext } from '../types' + +interface TradeFormCallbacks { + doTrade(): void + confirmTrade(): void +} + +export function useTradeFormButtonContext( + defaultText: string, + callbacks: TradeFormCallbacks +): TradeFormButtonContext | null { + const { state: derivedState } = useDerivedTradeState() + const wrapNativeFlow = useWrapNativeFlow() + const { isSupportedWallet } = useWalletDetails() + const quote = useTradeQuote() + const toggleWalletModal = useToggleWalletModal() + + return useMemo(() => { + if (!derivedState) return null + + return { + defaultText, + derivedState, + quote, + isSupportedWallet, + ...callbacks, + wrapNativeFlow, + connectWallet() { + toggleWalletModal() + }, + } + }, [defaultText, derivedState, quote, isSupportedWallet, callbacks, wrapNativeFlow, toggleWalletModal]) +} diff --git a/src/modules/tradeFormValidation/index.ts b/src/modules/tradeFormValidation/index.ts index ecf9e5dbf6..c76df4d32a 100644 --- a/src/modules/tradeFormValidation/index.ts +++ b/src/modules/tradeFormValidation/index.ts @@ -1,5 +1,6 @@ export * from './updaters/TradeFormValidationUpdater' export * from './hooks/useGetTradeFormValidation' +export * from './hooks/useTradeFormButtonContext' export * from './pure/TradeFormButtons' export * from './pure/TradeFormBlankButton' export * from './types' diff --git a/src/modules/twap/containers/ActionButtons/index.tsx b/src/modules/twap/containers/ActionButtons/index.tsx new file mode 100644 index 0000000000..eedc179172 --- /dev/null +++ b/src/modules/twap/containers/ActionButtons/index.tsx @@ -0,0 +1,43 @@ +import React from 'react' + +import { useTradeConfirmActions } from 'modules/trade' +import { TradeFormButtons, useGetTradeFormValidation } from 'modules/tradeFormValidation' +import { useTradeFormButtonContext } from 'modules/tradeFormValidation' + +import { useSetupFallbackHandler } from '../../hooks/useSetupFallbackHandler' +import { useTwapFormState } from '../../hooks/useTwapFormState' +import { PrimaryActionButton } from '../../pure/PrimaryActionButton' + +export function ActionButtons() { + const setFallbackHandler = useSetupFallbackHandler() + const tradeConfirmActions = useTradeConfirmActions() + const localFormValidation = useTwapFormState() + const primaryFormValidation = useGetTradeFormValidation() + + const primaryActionContext = { + setFallbackHandler, + openConfirmModal: tradeConfirmActions.onOpen, + } + + const confirmTrade = tradeConfirmActions.onOpen + + const tradeFormButtonContext = useTradeFormButtonContext('TWAP order', { doTrade: confirmTrade, confirmTrade }) + + if (!tradeFormButtonContext) return null + + if (!primaryFormValidation && localFormValidation) { + return + } + + return ( + + ) +} diff --git a/src/modules/twap/containers/TwapFormWidget/index.tsx b/src/modules/twap/containers/TwapFormWidget/index.tsx index 0376473097..e7d3837013 100644 --- a/src/modules/twap/containers/TwapFormWidget/index.tsx +++ b/src/modules/twap/containers/TwapFormWidget/index.tsx @@ -7,7 +7,6 @@ import { useAdvancedOrdersRawState, useUpdateAdvancedOrdersRawState, } from 'modules/advancedOrders' -import { useTradeConfirmActions } from 'modules/trade' import { useIsWrapOrUnwrap } from 'modules/trade/hooks/useIsWrapOrUnwrap' import { TradeNumberInput } from 'modules/trade/pure/TradeNumberInput' import { TradeTextBox } from 'modules/trade/pure/TradeTextBox' @@ -18,15 +17,13 @@ import { useRateInfoParams } from 'common/hooks/useRateInfoParams' import * as styledEl from './styled' import { DEFAULT_TWAP_SLIPPAGE, orderDeadlines, defaultNumOfParts } from '../../const' -import { useSetupFallbackHandler } from '../../hooks/useSetupFallbackHandler' -import { useTwapFormState } from '../../hooks/useTwapFormState' import { AmountParts } from '../../pure/AmountParts' import { DeadlineSelector } from '../../pure/DeadlineSelector' -import { PrimaryActionButton } from '../../pure/PrimaryActionButton' import { partsStateAtom } from '../../state/partsStateAtom' import { twapTimeIntervalAtom } from '../../state/twapOrderAtom' import { twapOrdersSettingsAtom, updateTwapOrdersSettingsAtom } from '../../state/twapOrdersSettingsAtom' import { deadlinePartsDisplay } from '../../utils/deadlinePartsDisplay' +import { ActionButtons } from '../ActionButtons' import { TwapConfirmModal } from '../TwapConfirmModal' export function TwapFormWidget() { @@ -42,17 +39,8 @@ export function TwapFormWidget() { const updateSettingsState = useUpdateAtom(updateTwapOrdersSettingsAtom) const isWrapOrUnwrap = useIsWrapOrUnwrap() - const setFallbackHandler = useSetupFallbackHandler() - const tradeConfirmActions = useTradeConfirmActions() - const formState = useTwapFormState() - const rateInfoParams = useRateInfoParams(inputCurrencyAmount, outputCurrencyAmount) - const primaryActionContext = { - setFallbackHandler, - openConfirmModal: tradeConfirmActions.onOpen, - } - const deadlineState = { deadline, customDeadline, @@ -112,7 +100,7 @@ export function TwapFormWidget() { - + ) } diff --git a/src/modules/twap/hooks/useTwapFormState.ts b/src/modules/twap/hooks/useTwapFormState.ts index 0fd0de07bd..32c7b2e0d3 100644 --- a/src/modules/twap/hooks/useTwapFormState.ts +++ b/src/modules/twap/hooks/useTwapFormState.ts @@ -10,7 +10,7 @@ import { getTwapFormState, TwapFormState } from '../pure/PrimaryActionButton/get import { verifyExtensibleFallback } from '../services/verifyExtensibleFallback' import { twapOrderAtom } from '../state/twapOrderAtom' -export function useTwapFormState(): TwapFormState { +export function useTwapFormState(): TwapFormState | null { const isSafeApp = useIsSafeApp() const extensibleFallbackContext = useExtensibleFallbackContext() diff --git a/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx b/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx index b218392c70..0e598b2546 100644 --- a/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx +++ b/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx @@ -7,27 +7,26 @@ export interface TwapFormStateParams { twapOrder: TWAPOrder | null } -// TODO: compose with common TradeFormState export enum TwapFormState { - LOADING, + LOADING_SAFE_INFO, NOT_SAFE, ORDER_NOT_SPECIFIED, // TODO: reveal details NEED_FALLBACK_HANDLER, CAN_CREATE_ORDER, } -export function getTwapFormState(props: TwapFormStateParams): TwapFormState { +export function getTwapFormState(props: TwapFormStateParams): TwapFormState | null { const { isSafeApp, verification, twapOrder } = props if (!isSafeApp) return TwapFormState.NOT_SAFE - if (verification === null) return TwapFormState.LOADING + if (verification === null) return TwapFormState.LOADING_SAFE_INFO if (!twapOrder) return TwapFormState.ORDER_NOT_SPECIFIED - if (verification === ExtensibleFallbackVerification.HAS_DOMAIN_VERIFIER) { - return TwapFormState.CAN_CREATE_ORDER + if (verification !== ExtensibleFallbackVerification.HAS_DOMAIN_VERIFIER) { + return TwapFormState.NEED_FALLBACK_HANDLER } - return TwapFormState.NEED_FALLBACK_HANDLER + return null } diff --git a/src/modules/twap/pure/PrimaryActionButton/index.tsx b/src/modules/twap/pure/PrimaryActionButton/index.tsx index aaa03e4d88..d82a740e12 100644 --- a/src/modules/twap/pure/PrimaryActionButton/index.tsx +++ b/src/modules/twap/pure/PrimaryActionButton/index.tsx @@ -13,7 +13,7 @@ export interface PrimaryActionButtonContext { // TODO: extend with common trade widget states // TODO: set correct buttons text const buttonsMap: Record JSX.Element> = { - [TwapFormState.LOADING]: () => ( + [TwapFormState.LOADING_SAFE_INFO]: () => ( Loading... diff --git a/src/pages/AdvancedOrders/index.tsx b/src/pages/AdvancedOrders/index.tsx index f41721a83a..f948e744fa 100644 --- a/src/pages/AdvancedOrders/index.tsx +++ b/src/pages/AdvancedOrders/index.tsx @@ -1,10 +1,14 @@ import { AdvancedOrdersWidget } from 'modules/advancedOrders' import { OrdersTableWidget } from 'modules/ordersTable' import * as styledEl from 'modules/trade/pure/TradePageLayout' +import { TradeFormValidationUpdater } from 'modules/tradeFormValidation' export default function AdvancedOrdersPage() { return ( <> + {/*TODO: add isExpertMode value*/} + + {/*TODO: add isUnlocked value*/} From a6bb882be15edcb663dbb189c3306c1af4b0d851 Mon Sep 17 00:00:00 2001 From: shoom3301 Date: Tue, 6 Jun 2023 15:39:26 +0600 Subject: [PATCH 05/67] fix: fix kind field in useQuoteParams --- src/modules/tradeQuote/hooks/useQuoteParams.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/modules/tradeQuote/hooks/useQuoteParams.ts b/src/modules/tradeQuote/hooks/useQuoteParams.ts index 511dd85db8..ff634ef633 100644 --- a/src/modules/tradeQuote/hooks/useQuoteParams.ts +++ b/src/modules/tradeQuote/hooks/useQuoteParams.ts @@ -1,8 +1,6 @@ import { useAtomValue } from 'jotai' import { useMemo } from 'react' -import { OrderKind } from '@cowprotocol/cow-sdk' - import { useDerivedTradeState } from 'modules/trade/hooks/useDerivedTradeState' import { useWalletInfo } from 'modules/wallet' @@ -15,7 +13,7 @@ export function useQuoteParams() { const { state } = useDerivedTradeState() const { amount } = useAtomValue(tradeQuoteParamsAtom) - const { inputCurrency, outputCurrency } = state || {} + const { inputCurrency, outputCurrency, orderKind } = state || {} const amountStr = amount?.quotient.toString() @@ -35,10 +33,10 @@ export function useQuoteParams() { amount: amountStr, chainId, receiver: account, - kind: OrderKind.SELL, + kind: orderKind, toDecimals, fromDecimals, isEthFlow: false, } - }, [inputCurrency, outputCurrency, amountStr, account, chainId]) + }, [inputCurrency, outputCurrency, amountStr, account, chainId, orderKind]) } From 23b2a0db189585c66677a7573df5ee29fcd395f4 Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Tue, 6 Jun 2023 18:14:29 +0600 Subject: [PATCH 06/67] chore(develop): release 1.38.1 (#2606) --- CHANGELOG.md | 2 +- public/index.html | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a12077616..5fa077d3cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [1.39.0](https://github.com/cowprotocol/cowswap/compare/v1.38.0...v1.39.0) (2023-05-25) +## [1.38.1](https://github.com/cowprotocol/cowswap/compare/v1.38.0...v1.38.1) (2023-05-25) ### Features diff --git a/public/index.html b/public/index.html index e3d19696f4..ab594caf4a 100644 --- a/public/index.html +++ b/public/index.html @@ -135,5 +135,4 @@
- From 67a15f0c25690ced61135518e30961e17fc1bcf7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 6 Jun 2023 18:18:37 +0600 Subject: [PATCH 07/67] chore(develop): release 1.39.0 (#2607) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fa077d3cf..2a6788120f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,35 @@ # Changelog +## [1.39.0](https://github.com/cowprotocol/cowswap/compare/v1.38.1...v1.39.0) (2023-06-06) + + +### Features + +* add rate info ([#2570](https://github.com/cowprotocol/cowswap/issues/2570)) ([52cfa63](https://github.com/cowprotocol/cowswap/commit/52cfa63c50e1fce0fdb476f7b4509b3774c6a185)) +* clickable fill+status label for order receipt ([#2559](https://github.com/cowprotocol/cowswap/issues/2559)) ([b56954e](https://github.com/cowprotocol/cowswap/commit/b56954e1b2d46a76ac5dac19f309a82523832bc9)) +* get quote for single part in TWAP ([#2580](https://github.com/cowprotocol/cowswap/issues/2580)) ([40b2627](https://github.com/cowprotocol/cowswap/commit/40b2627ce90ff789d77458ba924f4ff837247a30)) +* **limit-orders:** integrate TradeConfirmModal in Limit orders ([#2573](https://github.com/cowprotocol/cowswap/issues/2573)) ([dd76fc2](https://github.com/cowprotocol/cowswap/commit/dd76fc26b602a0b9d6b28bec4ab9a4bc505bdeb8)) +* **safe-eth-flow:** add Safe Eth flow ([#2576](https://github.com/cowprotocol/cowswap/issues/2576)) ([ec4426a](https://github.com/cowprotocol/cowswap/commit/ec4426abe8a1165beb99531f60603fdba3bb9421)) +* show Safe WC banner when selling ETH ([#2588](https://github.com/cowprotocol/cowswap/issues/2588)) ([8107248](https://github.com/cowprotocol/cowswap/commit/810724837298a69a9d110b455d9abf17e4176939)) +* **smart-contract-orders:** hide sc presign orders on start ([#2525](https://github.com/cowprotocol/cowswap/issues/2525)) ([96ea696](https://github.com/cowprotocol/cowswap/commit/96ea6960c91c6a0f9aaea2260399a450fcbf4f2b)) +* swap SELL and BUY fields ([#2567](https://github.com/cowprotocol/cowswap/issues/2567)) ([a9d243e](https://github.com/cowprotocol/cowswap/commit/a9d243e6fe623ebb708934955771f35f120cd952)) +* **trade-form-validation:** service to validate generic trade widget behaviour ([#2590](https://github.com/cowprotocol/cowswap/issues/2590)) ([9493f11](https://github.com/cowprotocol/cowswap/commit/9493f111f88ad8020013ba57afd9ee87ca52cb2a)) +* **trade:** move import token and approval modals to TradeWidget ([#2547](https://github.com/cowprotocol/cowswap/issues/2547)) ([3ce3316](https://github.com/cowprotocol/cowswap/commit/3ce3316c46adecc57c92eb4972dc5db68d0a2bd0)) +* **twap:** add generic trade form validation to TWAP ([#2592](https://github.com/cowprotocol/cowswap/issues/2592)) ([bccacb6](https://github.com/cowprotocol/cowswap/commit/bccacb60cb9e53c1935da727c0900a5e9f1c4cfc)) +* **twap:** add orders table to Advanced orders page ([8de717c](https://github.com/cowprotocol/cowswap/commit/8de717c188c7ba72872196817801c7b192c3bc46)) +* **twap:** integrate TradeConfirmModal in Twap orders ([#2574](https://github.com/cowprotocol/cowswap/issues/2574)) ([771da11](https://github.com/cowprotocol/cowswap/commit/771da1155953c31b94903061e0993de39068858b)) +* **twap:** order creation and fallback verification MVP ([#2533](https://github.com/cowprotocol/cowswap/issues/2533)) ([3dea3d4](https://github.com/cowprotocol/cowswap/commit/3dea3d4fb2da7ac97d4f14297c1b5ed8837b668c)) + + +### Bug Fixes + +* do not trigger expired NPS for hidden orders ([#2554](https://github.com/cowprotocol/cowswap/issues/2554)) ([e806902](https://github.com/cowprotocol/cowswap/commit/e806902bb78dc094a4f5d2a19383de787d5529db)) +* expired order that still shows as pending ([#2558](https://github.com/cowprotocol/cowswap/issues/2558)) ([645c242](https://github.com/cowprotocol/cowswap/commit/645c2426f44eca49abd7d36c47530e3de3af1d0c)) +* fix kind field in useQuoteParams ([a6bb882](https://github.com/cowprotocol/cowswap/commit/a6bb882be15edcb663dbb189c3306c1af4b0d851)) +* **limit-orders:** fix quoting for buy flow ([#2598](https://github.com/cowprotocol/cowswap/issues/2598)) ([76b927f](https://github.com/cowprotocol/cowswap/commit/76b927f4ac9a1a7a5609947123189a92071415b0)) +* make (Trust) wallet icon round on modals ([#2599](https://github.com/cowprotocol/cowswap/issues/2599)) ([dabf88e](https://github.com/cowprotocol/cowswap/commit/dabf88e1af67f872d36846e2e60902dcc7397f61)) +* unsupported token error fix ([d05ae0f](https://github.com/cowprotocol/cowswap/commit/d05ae0f5085685aaf07c78d2f42385522c0e051f)) + ## [1.38.1](https://github.com/cowprotocol/cowswap/compare/v1.38.0...v1.38.1) (2023-05-25) diff --git a/package.json b/package.json index 7adbb34934..6f2920d43a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cowprotocol/cowswap", - "version": "1.38.1", + "version": "1.39.0", "description": "CoW Swap", "main": "index.js", "author": "", From 7e1e798c324ca658d153a49f04a3fa9a08d1a426 Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Thu, 8 Jun 2023 23:44:56 +0600 Subject: [PATCH 08/67] fix(twap): display local form validation first (#2626) --- src/modules/twap/containers/ActionButtons/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/twap/containers/ActionButtons/index.tsx b/src/modules/twap/containers/ActionButtons/index.tsx index eedc179172..0de5f4376e 100644 --- a/src/modules/twap/containers/ActionButtons/index.tsx +++ b/src/modules/twap/containers/ActionButtons/index.tsx @@ -25,7 +25,7 @@ export function ActionButtons() { if (!tradeFormButtonContext) return null - if (!primaryFormValidation && localFormValidation) { + if (localFormValidation) { return } From 15a688e7edecf76f80a7ef60f36a2813e38b7e2a Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Thu, 8 Jun 2023 23:45:50 +0600 Subject: [PATCH 09/67] feat(twap): persist twap orders from Safe and display them in orders table (#2597) --- package.json | 1 + src/abis/ComposableCoW.json | 128 ++++++++++++++++++ src/api/gnosisProtocol/api.ts | 13 +- src/api/gnosisProtocol/hooks.ts | 76 ++++++++++- src/api/gnosisProtocol/index.ts | 2 +- src/api/gnosisSafe/hooks/useSafeSdk.ts | 29 ---- src/cowSdk.ts | 6 +- src/legacy/hooks/useRecentActivity.ts | 2 +- src/legacy/state/orders/hooks.ts | 33 +++-- .../updaters/UnfillableOrdersUpdater.ts | 2 +- src/legacy/utils/environments.ts | 3 +- src/lib/state/index.ts | 13 -- .../containers/AdvancedOrdersWidget/index.tsx | 10 +- .../tokensList/state/tokensListAtom.ts | 8 +- .../tokensList/updaters/TokensListUpdater.ts | 9 +- .../tradeQuote/hooks/useQuoteParams.ts | 8 +- .../hooks/useSetTradeQuoteParams.ts | 4 +- .../tradeQuote/state/tradeQuoteParamsAtom.ts | 4 +- src/modules/twap/const.ts | 5 + .../twap/containers/TwapFormWidget/index.tsx | 12 ++ .../twap/hooks/useFetchTwapOrdersFromSafe.ts | 38 ++++++ .../twap/hooks/useFetchTwapPartOrders.ts | 84 ++++++++++++ .../twap/hooks/useTwapDiscreteOrders.ts | 29 ++++ .../twap/hooks/useTwapOrdersAuthMulticall.ts | 33 +++++ .../hooks/useTwapOrdersTradeableMulticall.ts | 40 ++++++ .../twap/services/fetchTwapOrdersFromSafe.ts | 67 +++++++++ src/modules/twap/state/twapOrdersListAtom.ts | 35 +++++ src/modules/twap/state/twapPartOrdersAtom.ts | 40 ++++++ src/modules/twap/types.ts | 38 ++++++ .../twap/updaters/TwapOrdersUpdater.tsx | 80 +++++++++++ .../twap/utils/buildTwapOrdersItems.ts | 47 +++++++ src/modules/twap/utils/emulateTwapAsOrder.ts | 51 +++++++ .../twap/utils/getConditionalOrderId.ts | 10 ++ src/modules/twap/utils/getTwapOrderStatus.ts | 28 ++++ .../twap/utils/parseTwapOrderStruct.ts | 32 +++++ src/pages/AdvancedOrders/index.tsx | 6 +- src/utils/orderUtils/computeOrderUid.ts | 9 ++ 37 files changed, 942 insertions(+), 93 deletions(-) delete mode 100644 src/api/gnosisSafe/hooks/useSafeSdk.ts delete mode 100644 src/lib/state/index.ts create mode 100644 src/modules/twap/hooks/useFetchTwapOrdersFromSafe.ts create mode 100644 src/modules/twap/hooks/useFetchTwapPartOrders.ts create mode 100644 src/modules/twap/hooks/useTwapDiscreteOrders.ts create mode 100644 src/modules/twap/hooks/useTwapOrdersAuthMulticall.ts create mode 100644 src/modules/twap/hooks/useTwapOrdersTradeableMulticall.ts create mode 100644 src/modules/twap/services/fetchTwapOrdersFromSafe.ts create mode 100644 src/modules/twap/state/twapOrdersListAtom.ts create mode 100644 src/modules/twap/state/twapPartOrdersAtom.ts create mode 100644 src/modules/twap/updaters/TwapOrdersUpdater.tsx create mode 100644 src/modules/twap/utils/buildTwapOrdersItems.ts create mode 100644 src/modules/twap/utils/emulateTwapAsOrder.ts create mode 100644 src/modules/twap/utils/getConditionalOrderId.ts create mode 100644 src/modules/twap/utils/getTwapOrderStatus.ts create mode 100644 src/modules/twap/utils/parseTwapOrderStruct.ts create mode 100644 src/utils/orderUtils/computeOrderUid.ts diff --git a/package.json b/package.json index 8e4483f646..f115069b3a 100644 --- a/package.json +++ b/package.json @@ -134,6 +134,7 @@ "date-fns": "^2.29.3", "ethers": "^5.1.4", "exponential-backoff": "^3.1.1", + "fast-deep-equal": "^3.1.3", "fast-safe-stringify": "^2.0.8", "fortmatic": "^2.2.1", "fraction.js": "^4.2.0", diff --git a/src/abis/ComposableCoW.json b/src/abis/ComposableCoW.json index bc5cfed9ef..f86d84a355 100644 --- a/src/abis/ComposableCoW.json +++ b/src/abis/ComposableCoW.json @@ -33,5 +33,133 @@ "outputs": [], "stateMutability": "nonpayable", "type": "function" + }, + { + "inputs": [ + { "name": "safe", "type": "address" }, + { "name": "singleOrderHash", "type": "bytes32" } + ], + "name": "singleOrders", + "outputs": [{ "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "components": [ + { + "internalType": "contract IConditionalOrder", + "name": "handler", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "staticInput", + "type": "bytes" + } + ], + "internalType": "struct IConditionalOrder.ConditionalOrderParams", + "name": "params", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "offchainInput", + "type": "bytes" + }, + { + "internalType": "bytes32[]", + "name": "proof", + "type": "bytes32[]" + } + ], + "name": "getTradeableOrderWithSignature", + "outputs": [ + { + "components": [ + { + "internalType": "contract IERC20", + "name": "sellToken", + "type": "address" + }, + { + "internalType": "contract IERC20", + "name": "buyToken", + "type": "address" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "buyAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "validTo", + "type": "uint32" + }, + { + "internalType": "bytes32", + "name": "appData", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "feeAmount", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "kind", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "partiallyFillable", + "type": "bool" + }, + { + "internalType": "bytes32", + "name": "sellTokenBalance", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "buyTokenBalance", + "type": "bytes32" + } + ], + "internalType": "struct GPv2Order.Data", + "name": "order", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" } ] diff --git a/src/api/gnosisProtocol/api.ts b/src/api/gnosisProtocol/api.ts index 39be5cfa01..cbc2e2f204 100644 --- a/src/api/gnosisProtocol/api.ts +++ b/src/api/gnosisProtocol/api.ts @@ -1,7 +1,7 @@ import { OrderBookApiError, PriceQuality, SupportedChainId as ChainId } from '@cowprotocol/cow-sdk' import { OrderKind } from '@cowprotocol/cow-sdk' import { OrderQuoteRequest, SigningScheme, OrderQuoteResponse, EnrichedOrder } from '@cowprotocol/cow-sdk' -import { NativePriceResponse, Trade } from '@cowprotocol/cow-sdk' +import { NativePriceResponse, Trade, PartialApiContext, Address } from '@cowprotocol/cow-sdk' import { orderBookApi } from 'cowSdk' @@ -144,8 +144,15 @@ export async function getOrder(chainId: ChainId, orderId: string): Promise { - return orderBookApi.getOrders({ owner, limit, offset }, { chainId }) +export async function getOrders( + params: { + owner: Address + offset?: number + limit?: number + }, + context: PartialApiContext +): Promise { + return orderBookApi.getOrders(params, context) } export async function getTrades(chainId: ChainId, owner: string): Promise { diff --git a/src/api/gnosisProtocol/hooks.ts b/src/api/gnosisProtocol/hooks.ts index 4a843b9129..08c2aa50f8 100644 --- a/src/api/gnosisProtocol/hooks.ts +++ b/src/api/gnosisProtocol/hooks.ts @@ -1,25 +1,89 @@ +import { useAtomValue } from 'jotai' +import { useMemo } from 'react' + import { EnrichedOrder } from '@cowprotocol/cow-sdk' import useSWR from 'swr' import { AMOUNT_OF_ORDERS_TO_FETCH } from 'legacy/constants' +import { isBarnBackendEnv } from 'legacy/utils/environments' import { supportedChainId } from 'legacy/utils/supportedChainId' +import { emulatedTwapOrdersAtom } from 'modules/twap/state/twapOrdersListAtom' +import { TwapPartOrderItem, twapPartOrdersListAtom } from 'modules/twap/state/twapPartOrdersAtom' import { useWalletInfo } from 'modules/wallet' -import { getOrders } from 'api/gnosisProtocol' +import { getOrders } from './api' -export function useGpOrders(account?: string | null, refreshInterval?: number): EnrichedOrder[] | undefined { +/** + * TODO: refactor this hook + * Currently there is a problem with Dependency inversion principle + * This hook depends on 'modules/twap', but it must not + * + * This violation is added because of environment restrictions for TWAP + * Currently, WatchTower creates discrete orders only on PROD environment + * And even if you created a TWAP order not in PROD, we have to load discrete orders from PROD order-book + * + * For non-PROD environment we do two requests: barn and prod + * For PROD environment we do only one: prod. It depends on isBarnBackendEnv + */ +export function useGpOrders(account?: string | null, refreshInterval?: number): EnrichedOrder[] { const { chainId: _chainId } = useWalletInfo() const chainId = supportedChainId(_chainId) + const emulatedTwapOrders = useAtomValue(emulatedTwapOrdersAtom) + const twapParticleOrders = useAtomValue(twapPartOrdersListAtom) + + const requestParams = useMemo(() => { + return account ? { owner: account, limit: AMOUNT_OF_ORDERS_TO_FETCH } : null + }, [account]) + + // Fetch orders for the current environment + const { data: currentEnvOrders } = useSWR( + ['orders', requestParams, chainId], + () => { + if (!chainId || !requestParams) return [] + + return getOrders(requestParams, { chainId }) + }, + { refreshInterval } + ) - const { data } = useSWR( - ['orders', account, chainId], - () => (chainId && account ? getOrders(chainId, account, AMOUNT_OF_ORDERS_TO_FETCH) : []), + // Fetch PROD orders only when current env is not prod + // We need them for TWAP + const { data: loadedProdOrders } = useSWR( + ['prod-orders', requestParams, chainId], + () => { + if (!chainId || !requestParams) return [] + if (!isBarnBackendEnv) return [] + + return getOrders(requestParams, { chainId, env: 'prod' }) + }, { refreshInterval } ) - return data + const prodOrders = useMemo(() => { + return isBarnBackendEnv ? loadedProdOrders : currentEnvOrders + }, [currentEnvOrders, loadedProdOrders]) + + // Take only orders are connected to TWAP orders + const twapRelatedOrders = useMemo(() => { + if (!prodOrders) return [] + + const particleOrdersMap = twapParticleOrders.reduce<{ [uid: string]: TwapPartOrderItem }>((acc, val) => { + acc[val.uid] = val + + return acc + }, {}) + + return prodOrders.filter((order) => { + return particleOrdersMap[order.uid] + }) + }, [twapParticleOrders, prodOrders]) + + // Add only TWAP-related orders to the common list of orders + return useMemo(() => { + return [...(currentEnvOrders || []), ...emulatedTwapOrders, ...twapRelatedOrders] + }, [currentEnvOrders, emulatedTwapOrders, twapRelatedOrders]) } export function useHasOrders(account?: string | null): boolean | undefined { diff --git a/src/api/gnosisProtocol/index.ts b/src/api/gnosisProtocol/index.ts index b42bcaeab9..ede6377c72 100644 --- a/src/api/gnosisProtocol/index.ts +++ b/src/api/gnosisProtocol/index.ts @@ -14,8 +14,8 @@ export const { getProfileData, getQuote = realApi.getQuote, getOrder = realApi.getOrder, - getOrders = realApi.getOrders, getNativePrice = realApi.getNativePrice, getTrades = realApi.getTrades, + getOrders = realApi.getOrders, // functions that only have a mock } = useMock ? { ...mockApi } : { ...realApi } diff --git a/src/api/gnosisSafe/hooks/useSafeSdk.ts b/src/api/gnosisSafe/hooks/useSafeSdk.ts deleted file mode 100644 index 34fb17b264..0000000000 --- a/src/api/gnosisSafe/hooks/useSafeSdk.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { useEffect, useState } from 'react' - -import Safe from '@safe-global/protocol-kit' -import { useWeb3React } from '@web3-react/core' - -import { useIsSafeWallet, useWalletInfo } from 'modules/wallet' - -import { createSafeSdkInstance } from 'api/gnosisSafe' - -export function useSafeSdk(): Safe | null { - const [safeSdk, setSafeSdk] = useState(null) - const { account } = useWalletInfo() - const isSafeWallet = useIsSafeWallet() - const { provider } = useWeb3React() - - useEffect(() => { - async function getInstance(): Promise { - if (isSafeWallet && provider && account) { - setSafeSdk(await createSafeSdkInstance(account, provider)) - } else { - setSafeSdk(null) - } - } - - getInstance() - }, [account, isSafeWallet, provider]) - - return safeSdk -} diff --git a/src/cowSdk.ts b/src/cowSdk.ts index d4df082a14..4ae4b4c415 100644 --- a/src/cowSdk.ts +++ b/src/cowSdk.ts @@ -1,7 +1,9 @@ import { MetadataApi } from '@cowprotocol/app-data' import { OrderBookApi } from '@cowprotocol/cow-sdk' -import { isBarn, isDev, isLocal, isPr } from 'legacy/utils/environments' +import { isBarnBackendEnv } from 'legacy/utils/environments' export const metadataApiSDK = new MetadataApi() -export const orderBookApi = new OrderBookApi({ env: isLocal || isDev || isPr || isBarn ? 'staging' : 'prod' }) +export const orderBookApi = new OrderBookApi({ + env: isBarnBackendEnv ? 'staging' : 'prod', +}) diff --git a/src/legacy/hooks/useRecentActivity.ts b/src/legacy/hooks/useRecentActivity.ts index 5a3b9699d8..d4364d8bfd 100644 --- a/src/legacy/hooks/useRecentActivity.ts +++ b/src/legacy/hooks/useRecentActivity.ts @@ -230,7 +230,7 @@ export function useMultipleActivityDescriptors({ chainId, ids }: UseActivityDesc if (!chainId) return [] return ids.reduce((acc, id) => { - const activity = createActivityDescriptor(txs[id], orders[id]) + const activity = createActivityDescriptor(txs[id], orders?.[id]) if (activity) { acc.push(activity) } diff --git a/src/legacy/state/orders/hooks.ts b/src/legacy/state/orders/hooks.ts index bb3e0d7861..3f48035839 100644 --- a/src/legacy/state/orders/hooks.ts +++ b/src/legacy/state/orders/hooks.ts @@ -1,6 +1,6 @@ import { useCallback, useMemo } from 'react' -import { SupportedChainId as ChainId } from '@cowprotocol/cow-sdk' +import { SupportedChainId } from '@cowprotocol/cow-sdk' import { useDispatch, useSelector } from 'react-redux' @@ -58,11 +58,11 @@ interface AddPendingOrderParams extends GetRemoveOrderParams { interface GetRemoveOrderParams { id: OrderID - chainId: ChainId + chainId: SupportedChainId } type GetOrdersByIdParams = { ids: OrderID[] - chainId?: ChainId + chainId?: SupportedChainId } type GetOrdersParams = Partial> @@ -71,7 +71,7 @@ type SetOrderCancellationHashParams = CancelOrderParams & { hash: string } interface UpdateOrdersBatchParams { ids: OrderID[] - chainId: ChainId + chainId: SupportedChainId } type ExpireOrdersBatchParams = UpdateOrdersBatchParams @@ -155,14 +155,21 @@ export const useOrder = ({ id, chainId }: Partial): Order }) } -function useOrdersStateNetwork(chainId: ChainId | undefined): OrdersStateNetwork | undefined { - return useSelector((state) => { +function useOrdersStateNetwork(chainId: SupportedChainId | undefined): OrdersStateNetwork | undefined { + const ordersState = useSelector((state) => { if (!chainId) { return undefined } - const ordersState = state.orders?.[chainId] || {} - return { ...getDefaultNetworkState(chainId), ...ordersState } + return state.orders?.[chainId] }) + + // Additional memoization to avoid excessive re-renders + // ordersState is a plain object that contains serialized data, so we can stringify it safely + return useMemo(() => { + if (!chainId) return undefined + return { ...getDefaultNetworkState(chainId), ...(ordersState || {}) } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [JSON.stringify(ordersState), chainId]) } export const useOrders = ({ chainId }: GetOrdersParams): Order[] => { @@ -193,16 +200,16 @@ export const useAllOrders = ({ chainId }: GetOrdersParams): PartialOrdersMap => }, [state]) } -type OrdersMap = { +export type OrdersMap = { [id: string]: Order } -export const useOrdersById = ({ chainId, ids }: GetOrdersByIdParams): OrdersMap => { +export const useOrdersById = ({ chainId, ids }: GetOrdersByIdParams): OrdersMap | null => { const allOrders = useAllOrders({ chainId }) return useMemo(() => { if (!allOrders || !ids) { - return {} + return null } return ids.reduce((acc, id) => { @@ -256,7 +263,7 @@ export const useCombinedPendingOrders = ({ chainId }: GetOrdersParams): Order[] * while usePendingOrders aggregates all pending states * @param chainId */ -export const useOnlyPendingOrders = ({ chainId }: GetOrdersParams): Order[] => { +export const useOnlyPendingOrders = (chainId: SupportedChainId | undefined): Order[] => { const state = useSelector( (state) => chainId && state.orders?.[chainId]?.pending ) @@ -329,7 +336,7 @@ export const useAddPendingOrder = (): AddOrderCallback => { } export type UpdateOrderParams = { - chainId: ChainId + chainId: SupportedChainId order: Partial> & Pick } diff --git a/src/legacy/state/orders/updaters/UnfillableOrdersUpdater.ts b/src/legacy/state/orders/updaters/UnfillableOrdersUpdater.ts index 9ae101a335..aa708bb168 100644 --- a/src/legacy/state/orders/updaters/UnfillableOrdersUpdater.ts +++ b/src/legacy/state/orders/updaters/UnfillableOrdersUpdater.ts @@ -93,7 +93,7 @@ export function UnfillableOrdersUpdater(): null { const updatePendingOrderPrices = useUpdateAtom(updatePendingOrderPricesAtom) const isWindowVisible = useIsWindowVisible() - const pending = useOnlyPendingOrders({ chainId }) + const pending = useOnlyPendingOrders(chainId) const setIsOrderUnfillable = useSetIsOrderUnfillable() const strategy = useGetGpPriceStrategy() diff --git a/src/legacy/utils/environments.ts b/src/legacy/utils/environments.ts index 9ef471e3a2..06b58edcc1 100644 --- a/src/legacy/utils/environments.ts +++ b/src/legacy/utils/environments.ts @@ -66,7 +66,8 @@ export const environmentName = (function () { })() const isProdLike = isProd || isEns || isStaging || isBarn +const isBarnBackendEnv = isLocal || isDev || isPr || isBarn registerOnWindow({ environment: environmentName }) -export { isLocal, isDev, isPr, isBarn, isStaging, isProd, isEns, isProdLike } +export { isLocal, isDev, isPr, isBarn, isStaging, isProd, isEns, isProdLike, isBarnBackendEnv } diff --git a/src/lib/state/index.ts b/src/lib/state/index.ts deleted file mode 100644 index 057282bda3..0000000000 --- a/src/lib/state/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { createMulticall } from '@uniswap/redux-multicall' - -import { combineReducers, createStore } from 'redux' - -import price from 'legacy/state/price/reducer' // MOD - -export * from 'lib/state/multicall' - -const multicall = createMulticall() -const reducer = combineReducers({ [multicall.reducerPath]: multicall.reducer, price }) -export const store = createStore(reducer) - -export default store diff --git a/src/modules/advancedOrders/containers/AdvancedOrdersWidget/index.tsx b/src/modules/advancedOrders/containers/AdvancedOrdersWidget/index.tsx index ae4af32d5c..fd62f8456e 100644 --- a/src/modules/advancedOrders/containers/AdvancedOrdersWidget/index.tsx +++ b/src/modules/advancedOrders/containers/AdvancedOrdersWidget/index.tsx @@ -11,12 +11,11 @@ import { } from 'modules/advancedOrders/hooks/useAdvancedOrdersDerivedState' import { useSetupTradeState, TradeWidget, TradeWidgetSlots } from 'modules/trade' import { useTradeQuote, useSetTradeQuoteParams } from 'modules/tradeQuote' -import { TwapFormWidget } from 'modules/twap' import { partsStateAtom } from 'modules/twap/state/partsStateAtom' import { CurrencyInfo } from 'common/pure/CurrencyInputPanel/types' -export function AdvancedOrdersWidget() { +export function AdvancedOrdersWidget({ children }: { children: JSX.Element }) { useSetupTradeState() useFillAdvancedOrdersDerivedState() @@ -60,12 +59,7 @@ export function AdvancedOrdersWidget() { // TODO const slots: TradeWidgetSlots = { settingsWidget:
, - bottomContent: ( - <> - {/*TODO: conditionally display a widget for current advanced order type*/} - - - ), + bottomContent: children, } const params = { diff --git a/src/modules/tokensList/state/tokensListAtom.ts b/src/modules/tokensList/state/tokensListAtom.ts index 239fc331d4..0abca1bf79 100644 --- a/src/modules/tokensList/state/tokensListAtom.ts +++ b/src/modules/tokensList/state/tokensListAtom.ts @@ -17,5 +17,9 @@ export class TokenWithLogo extends Token { } } -export const tokensByAddressAtom = atom<{ [address: string]: TokenWithLogo }>({}) -export const tokensBySymbolAtom = atom<{ [symbol: string]: TokenWithLogo[] }>({}) +export type TokensByAddress = { [address: string]: TokenWithLogo } + +export type TokensBySymbol = { [address: string]: TokenWithLogo[] } + +export const tokensByAddressAtom = atom({}) +export const tokensBySymbolAtom = atom({}) diff --git a/src/modules/tokensList/updaters/TokensListUpdater.ts b/src/modules/tokensList/updaters/TokensListUpdater.ts index 86f0145351..116fd1f563 100644 --- a/src/modules/tokensList/updaters/TokensListUpdater.ts +++ b/src/modules/tokensList/updaters/TokensListUpdater.ts @@ -1,5 +1,5 @@ import { useUpdateAtom } from 'jotai/utils' -import { useEffect } from 'react' +import { useEffect, useMemo } from 'react' import { useTokensListWithDefaults } from 'legacy/state/lists/hooks' @@ -14,11 +14,14 @@ export function TokensListUpdater() { const updateTokensByAddress = useUpdateAtom(tokensByAddressAtom) const updateTokensBySymbol = useUpdateAtom(tokensBySymbolAtom) + // eslint-disable-next-line react-hooks/exhaustive-deps + const allTokensMemo = useMemo(() => allTokens, [JSON.stringify(allTokens)]) + useEffect(() => { const tokensByAddressMap: { [address: string]: TokenWithLogo } = {} const tokensBySymbolMap: { [address: string]: TokenWithLogo[] } = {} - allTokens.forEach((token) => { + allTokensMemo.forEach((token) => { const wrappedToken: TokenWithLogo = new TokenWithLogo( token.logoURI, token.chainId, @@ -46,7 +49,7 @@ export function TokensListUpdater() { updateTokensByAddress(tokensByAddressMap) updateTokensBySymbol(tokensBySymbolMap) - }, [allTokens, updateTokensByAddress, updateTokensBySymbol]) + }, [allTokensMemo, updateTokensByAddress, updateTokensBySymbol]) return null } diff --git a/src/modules/tradeQuote/hooks/useQuoteParams.ts b/src/modules/tradeQuote/hooks/useQuoteParams.ts index 7ceadcdfb8..20558e7733 100644 --- a/src/modules/tradeQuote/hooks/useQuoteParams.ts +++ b/src/modules/tradeQuote/hooks/useQuoteParams.ts @@ -17,10 +17,8 @@ export function useQuoteParams() { const { inputCurrency, outputCurrency, orderKind } = state || {} - const amountStr = amount?.quotient.toString() - return useMemo(() => { - if (!inputCurrency || !outputCurrency || !amountStr) { + if (!inputCurrency || !outputCurrency || !amount) { return } @@ -32,7 +30,7 @@ export function useQuoteParams() { return { sellToken, buyToken, - amount: amountStr, + amount, chainId, receiver: account, kind: orderKind, @@ -40,5 +38,5 @@ export function useQuoteParams() { fromDecimals, isEthFlow: false, } - }, [inputCurrency, outputCurrency, amountStr, account, chainId, orderKind]) + }, [inputCurrency, outputCurrency, amount, account, chainId, orderKind]) } diff --git a/src/modules/tradeQuote/hooks/useSetTradeQuoteParams.ts b/src/modules/tradeQuote/hooks/useSetTradeQuoteParams.ts index 9119b824e7..d5a912b4df 100644 --- a/src/modules/tradeQuote/hooks/useSetTradeQuoteParams.ts +++ b/src/modules/tradeQuote/hooks/useSetTradeQuoteParams.ts @@ -6,10 +6,12 @@ import { Currency, CurrencyAmount } from '@uniswap/sdk-core' import { updateTradeQuoteAtom } from '../state/tradeQuoteAtom' import { tradeQuoteParamsAtom } from '../state/tradeQuoteParamsAtom' -export function useSetTradeQuoteParams(amount: CurrencyAmount | null) { +export function useSetTradeQuoteParams(currencyAmount: CurrencyAmount | null) { const updateTradeQuote = useUpdateAtom(updateTradeQuoteAtom) const updateState = useUpdateAtom(tradeQuoteParamsAtom) + const amount = currencyAmount?.quotient.toString() || null + useEffect(() => { updateTradeQuote({ response: null, error: null }) updateState({ amount }) diff --git a/src/modules/tradeQuote/state/tradeQuoteParamsAtom.ts b/src/modules/tradeQuote/state/tradeQuoteParamsAtom.ts index dd5e54de55..f525dcba77 100644 --- a/src/modules/tradeQuote/state/tradeQuoteParamsAtom.ts +++ b/src/modules/tradeQuote/state/tradeQuoteParamsAtom.ts @@ -1,9 +1,7 @@ import { atom } from 'jotai' -import { Currency, CurrencyAmount } from '@uniswap/sdk-core' - export interface TradeQuoteParamsState { - amount: CurrencyAmount | null + amount: string | null } export const tradeQuoteParamsAtom = atom({ amount: null }) diff --git a/src/modules/twap/const.ts b/src/modules/twap/const.ts index be41985a35..2285da8fb5 100644 --- a/src/modules/twap/const.ts +++ b/src/modules/twap/const.ts @@ -3,6 +3,8 @@ import { Percent } from '@uniswap/sdk-core' import ms from 'ms.macro' +import { TwapOrderStatus } from './types' + export const DEFAULT_TWAP_SLIPPAGE = new Percent(10, 100) // 10% export type OrderDeadline = { label: string; value: number } @@ -29,3 +31,6 @@ export const TWAP_HANDLER_ADDRESS: Record = { 100: 'TODO', 5: '0xa12d770028d7072b80baeb6a1df962374fd13d9a', } + +// TODO: Add filled status +export const TWAP_NOT_PENDING_STATUSES = [TwapOrderStatus.Cancelled, TwapOrderStatus.Expired] diff --git a/src/modules/twap/containers/TwapFormWidget/index.tsx b/src/modules/twap/containers/TwapFormWidget/index.tsx index e7d3837013..617a974a15 100644 --- a/src/modules/twap/containers/TwapFormWidget/index.tsx +++ b/src/modules/twap/containers/TwapFormWidget/index.tsx @@ -7,10 +7,12 @@ import { useAdvancedOrdersRawState, useUpdateAdvancedOrdersRawState, } from 'modules/advancedOrders' +import { useComposableCowContract } from 'modules/advancedOrders/hooks/useComposableCowContract' import { useIsWrapOrUnwrap } from 'modules/trade/hooks/useIsWrapOrUnwrap' import { TradeNumberInput } from 'modules/trade/pure/TradeNumberInput' import { TradeTextBox } from 'modules/trade/pure/TradeTextBox' import { QuoteObserverUpdater } from 'modules/twap/updaters/QuoteObserverUpdater' +import { useIsSafeApp, useWalletInfo } from 'modules/wallet' import { useRateInfoParams } from 'common/hooks/useRateInfoParams' @@ -22,11 +24,14 @@ import { DeadlineSelector } from '../../pure/DeadlineSelector' import { partsStateAtom } from '../../state/partsStateAtom' import { twapTimeIntervalAtom } from '../../state/twapOrderAtom' import { twapOrdersSettingsAtom, updateTwapOrdersSettingsAtom } from '../../state/twapOrdersSettingsAtom' +import { TwapOrdersUpdater } from '../../updaters/TwapOrdersUpdater' import { deadlinePartsDisplay } from '../../utils/deadlinePartsDisplay' import { ActionButtons } from '../ActionButtons' import { TwapConfirmModal } from '../TwapConfirmModal' export function TwapFormWidget() { + const { chainId, account } = useWalletInfo() + const isSafeApp = useIsSafeApp() const { numberOfPartsValue, slippageValue, deadline, customDeadline, isCustomDeadline } = useAtomValue(twapOrdersSettingsAtom) @@ -39,6 +44,8 @@ export function TwapFormWidget() { const updateSettingsState = useUpdateAtom(updateTwapOrdersSettingsAtom) const isWrapOrUnwrap = useIsWrapOrUnwrap() + const composableCowContract = useComposableCowContract() + const rateInfoParams = useRateInfoParams(inputCurrencyAmount, outputCurrencyAmount) const deadlineState = { @@ -47,6 +54,8 @@ export function TwapFormWidget() { isCustomDeadline, } + const shouldLoadTwapOrders = !!(isSafeApp && chainId && account && composableCowContract) + // Reset output amount when num of parts or input amount are changed useEffect(() => { updateRawState({ outputCurrencyAmount: null }) @@ -55,6 +64,9 @@ export function TwapFormWidget() { return ( <> + {shouldLoadTwapOrders && ( + + )} {!isWrapOrUnwrap && ( diff --git a/src/modules/twap/hooks/useFetchTwapOrdersFromSafe.ts b/src/modules/twap/hooks/useFetchTwapOrdersFromSafe.ts new file mode 100644 index 0000000000..b2f18b34aa --- /dev/null +++ b/src/modules/twap/hooks/useFetchTwapOrdersFromSafe.ts @@ -0,0 +1,38 @@ +import { useEffect, useState } from 'react' + +import ms from 'ms.macro' + +import { ComposableCoW } from 'abis/types' +import { useSafeApiKit } from 'api/gnosisSafe/hooks/useSafeApiKit' + +import { fetchTwapOrdersFromSafe, TwapOrdersSafeData } from '../services/fetchTwapOrdersFromSafe' + +const PENDING_TWAP_UPDATE_INTERVAL = ms`10s` + +export function useFetchTwapOrdersFromSafe({ + safeAddress, + composableCowContract, +}: { + safeAddress: string + composableCowContract: ComposableCoW +}): TwapOrdersSafeData[] { + const safeApiKit = useSafeApiKit() + const [ordersSafeData, setOrdersSafeData] = useState([]) + + useEffect(() => { + if (!safeApiKit) return + + // TODO: now it fetches only last N transactions, should take into account the pagination + const persistOrders = () => { + fetchTwapOrdersFromSafe(safeAddress, safeApiKit, composableCowContract).then(setOrdersSafeData) + } + + const interval = setInterval(persistOrders, PENDING_TWAP_UPDATE_INTERVAL) + + persistOrders() + + return () => clearInterval(interval) + }, [safeAddress, safeApiKit, composableCowContract]) + + return ordersSafeData +} diff --git a/src/modules/twap/hooks/useFetchTwapPartOrders.ts b/src/modules/twap/hooks/useFetchTwapPartOrders.ts new file mode 100644 index 0000000000..4c60c7066c --- /dev/null +++ b/src/modules/twap/hooks/useFetchTwapPartOrders.ts @@ -0,0 +1,84 @@ +import { useMemo } from 'react' + +import type { Order } from '@cowprotocol/contracts' +import { OrderParameters, SupportedChainId } from '@cowprotocol/cow-sdk' + +import { useAsyncMemo } from 'use-async-memo' + +import { ComposableCoW } from 'abis/types' +import { computeOrderUid } from 'utils/orderUtils/computeOrderUid' + +import { TradeableOrderWithSignature, useTwapOrdersTradeableMulticall } from './useTwapOrdersTradeableMulticall' + +import { TwapPartOrderItem, TwapPartOrders } from '../state/twapPartOrdersAtom' +import { TwapOrderInfo } from '../types' + +export function useFetchTwapPartOrders( + safeAddress: string, + chainId: SupportedChainId, + composableCowContract: ComposableCoW, + ordersInfo: TwapOrderInfo[] +): TwapPartOrders | null { + const ordersToVerifyParams = useMemo(() => { + return ordersInfo.map((info) => info.safeData.params) + }, [ordersInfo]) + + const ordersTradeableData = useTwapOrdersTradeableMulticall(safeAddress, composableCowContract, ordersToVerifyParams) + + const items = useAsyncMemo( + () => { + if (ordersInfo.length !== ordersTradeableData.length) return null + + const safeAddressLowerCase = safeAddress.toLowerCase() + + return Promise.all( + ordersInfo.map(({ id }, index) => { + const data = ordersTradeableData[index] + return data ? getTwapPartOrderItem(chainId, safeAddressLowerCase, data, id) : Promise.resolve(null) + }) + ) + }, + [chainId, safeAddress, ordersInfo, ordersTradeableData], + null + ) + + return useMemo(() => { + if (!items) return null + + return ordersInfo.reduce((acc, { id }, index) => { + const item = items[index] + + if (item) acc[id] = item + + return acc + }, {} as TwapPartOrders) + }, [ordersInfo, items]) +} + +async function getTwapPartOrderItem( + chainId: SupportedChainId, + safeAddress: string, + data: TradeableOrderWithSignature, + twapOrderId: string +): Promise { + if (!data) return null + + const { order: partOrder, signature } = data + const { sellToken, buyToken, receiver, validTo, appData } = partOrder + const fixedOrder = { + sellToken, + buyToken, + receiver, + validTo, + appData, + sellAmount: partOrder.sellAmount.toString(), + buyAmount: partOrder.buyAmount.toString(), + feeAmount: partOrder.feeAmount.toString(), + kind: 'sell', // TODO: discuss it, smart-contract returns bytes here + partiallyFillable: partOrder.partiallyFillable, + } as Order + + const uid = await computeOrderUid(chainId, safeAddress, fixedOrder) + + return { uid, chainId, safeAddress, twapOrderId, order: fixedOrder as OrderParameters, signature } +} diff --git a/src/modules/twap/hooks/useTwapDiscreteOrders.ts b/src/modules/twap/hooks/useTwapDiscreteOrders.ts new file mode 100644 index 0000000000..1d62408d48 --- /dev/null +++ b/src/modules/twap/hooks/useTwapDiscreteOrders.ts @@ -0,0 +1,29 @@ +import { useAtomValue } from 'jotai' +import { useMemo } from 'react' + +import { Order } from 'legacy/state/orders/actions' +import { useOrdersById } from 'legacy/state/orders/hooks' + +import { useWalletInfo } from '../../wallet' +import { twapPartOrdersListAtom } from '../state/twapPartOrdersAtom' + +export type TwapToDiscreteOrders = { [twapOrderId: string]: Order } + +export function useTwapDiscreteOrders(): TwapToDiscreteOrders | null { + const { chainId } = useWalletInfo() + const partOrders = useAtomValue(twapPartOrdersListAtom) + const ids = useMemo(() => partOrders.map((item) => item.uid), [partOrders]) + const orders = useOrdersById({ chainId, ids }) + + return useMemo(() => { + return partOrders.reduce((acc, item) => { + const order = orders?.[item.uid] + + if (order) { + acc[item.twapOrderId] = order + } + + return acc + }, {}) + }, [partOrders, orders]) +} diff --git a/src/modules/twap/hooks/useTwapOrdersAuthMulticall.ts b/src/modules/twap/hooks/useTwapOrdersAuthMulticall.ts new file mode 100644 index 0000000000..a4a14ba887 --- /dev/null +++ b/src/modules/twap/hooks/useTwapOrdersAuthMulticall.ts @@ -0,0 +1,33 @@ +import { useMemo } from 'react' + +import { ListenerOptionsWithGas } from '@uniswap/redux-multicall' + +import { ComposableCoW } from 'abis/types/ComposableCoW' +import { useSingleContractMultipleData } from 'lib/hooks/multicall' + +import { TwapOrderInfo, TwapOrdersAuthResult } from '../types' + +const DEFAULT_LISTENER_OPTIONS: ListenerOptionsWithGas = { gasRequired: 185_000, blocksPerFetch: 5 } + +export function useTwapOrdersAuthMulticall( + safeAddress: string, + composableCowContract: ComposableCoW, + ordersInfo: TwapOrderInfo[] +): TwapOrdersAuthResult | null { + const input = useMemo(() => { + return ordersInfo.map(({ id }) => [safeAddress, id]) + }, [safeAddress, ordersInfo]) + + const results = useSingleContractMultipleData(composableCowContract, 'singleOrders', input, DEFAULT_LISTENER_OPTIONS) + + return useMemo(() => { + const loadedResults = results.filter((result) => !result.loading) + + if (loadedResults.length !== ordersInfo.length) return null + + return ordersInfo.reduce((acc, val, index) => { + acc[val.id] = !!loadedResults[index].result?.[0] + return acc + }, {} as TwapOrdersAuthResult) + }, [ordersInfo, results]) +} diff --git a/src/modules/twap/hooks/useTwapOrdersTradeableMulticall.ts b/src/modules/twap/hooks/useTwapOrdersTradeableMulticall.ts new file mode 100644 index 0000000000..a9da06ed31 --- /dev/null +++ b/src/modules/twap/hooks/useTwapOrdersTradeableMulticall.ts @@ -0,0 +1,40 @@ +import { useMemo } from 'react' + +import { ListenerOptionsWithGas } from '@uniswap/redux-multicall' + +import { ComposableCoW, GPv2Order } from 'abis/types/ComposableCoW' +import { useSingleContractMultipleData } from 'lib/hooks/multicall' + +import { ConditionalOrderParams } from '../types' + +const DEFAULT_LISTENER_OPTIONS: ListenerOptionsWithGas = { gasRequired: 185_000, blocksPerFetch: 5 } + +export type TradeableOrderWithSignature = + | { + order: GPv2Order.DataStructOutput + signature: string + } + | undefined + +export function useTwapOrdersTradeableMulticall( + safeAddress: string, + composableCowContract: ComposableCoW, + conditionalOrderParams: ConditionalOrderParams[] +): TradeableOrderWithSignature[] { + const input = useMemo(() => { + return conditionalOrderParams.map((params) => { + return [safeAddress, [params.handler, params.salt, params.staticInput], '0x', []] + }) + }, [safeAddress, conditionalOrderParams]) + + const results = useSingleContractMultipleData( + composableCowContract, + 'getTradeableOrderWithSignature', + input, + DEFAULT_LISTENER_OPTIONS + ) + + return useMemo(() => { + return results.filter((result) => !result.loading).map((res) => res.result as TradeableOrderWithSignature) + }, [results]) +} diff --git a/src/modules/twap/services/fetchTwapOrdersFromSafe.ts b/src/modules/twap/services/fetchTwapOrdersFromSafe.ts new file mode 100644 index 0000000000..48356bb848 --- /dev/null +++ b/src/modules/twap/services/fetchTwapOrdersFromSafe.ts @@ -0,0 +1,67 @@ +import type SafeApiKit from '@safe-global/api-kit' +import type { SafeMultisigTransactionResponse } from '@safe-global/safe-core-sdk-types' + +import { isTruthy } from 'legacy/utils/misc' + +import { ComposableCoW } from 'abis/types' + +import { ConditionalOrderParams } from '../types' + +export interface TwapOrdersSafeData { + params: ConditionalOrderParams + submissionDate: string + isExecuted: boolean +} + +// ComposableCoW.create method +const CREATE_COMPOSABLE_ORDER_SELECTOR = '6bfae1ca' + +export async function fetchTwapOrdersFromSafe( + safeAddress: string, + safeApiKit: SafeApiKit, + composableCowContract: ComposableCoW +): Promise { + const allTxs = await safeApiKit.getAllTransactions(safeAddress) + const results = allTxs?.results || [] + + return results + .map((result) => { + if (!result.data || !isSafeMultisigTransactionListResponse(result)) return null + + const selectorIndex = result.data.indexOf(CREATE_COMPOSABLE_ORDER_SELECTOR) + + if (selectorIndex < 0) return null + + const callData = '0x' + result.data.substring(selectorIndex) + + const params = parseConditionalOrderParams(safeAddress, composableCowContract, callData) + + if (!params) return null + + return { + params, + isExecuted: result.isExecuted, + submissionDate: result.submissionDate, + } + }) + .filter(isTruthy) +} + +function isSafeMultisigTransactionListResponse(response: any): response is SafeMultisigTransactionResponse { + return !!response.data && !!response.submissionDate +} + +function parseConditionalOrderParams( + safeAddress: string, + composableCowContract: ComposableCoW, + callData: string +): ConditionalOrderParams | null { + try { + const _result = composableCowContract.interface.decodeFunctionData('create', callData) + const { params } = _result as any as { params: ConditionalOrderParams } + + return { handler: params.handler, salt: params.salt, staticInput: params.staticInput } + } catch (e) { + return null + } +} diff --git a/src/modules/twap/state/twapOrdersListAtom.ts b/src/modules/twap/state/twapOrdersListAtom.ts new file mode 100644 index 0000000000..8206a3aded --- /dev/null +++ b/src/modules/twap/state/twapOrdersListAtom.ts @@ -0,0 +1,35 @@ +import { atom } from 'jotai' +import { atomWithStorage } from 'jotai/utils' + +import deepEqual from 'fast-deep-equal' + +import { tokensByAddressAtom } from 'modules/tokensList/state/tokensListAtom' +import { walletInfoAtom } from 'modules/wallet/api/state' + +import { TwapOrderItem } from '../types' +import { emulateTwapAsOrder } from '../utils/emulateTwapAsOrder' + +export type TwapOrdersList = { [key: string]: TwapOrderItem } + +export const twapOrdersListAtom = atomWithStorage('twap-orders-list:v2', {}) + +export const updateTwapOrdersListAtom = atom(null, (get, set, nextState: TwapOrdersList) => { + const currentState = get(twapOrdersListAtom) + + if (!deepEqual(currentState, nextState)) { + set(twapOrdersListAtom, nextState) + } +}) + +export const emulatedTwapOrdersAtom = atom((get) => { + const { account, chainId } = get(walletInfoAtom) + const tokens = get(tokensByAddressAtom) + const orders = Object.values(get(twapOrdersListAtom)) + const accountLowerCase = account?.toLowerCase() + + if (!accountLowerCase) return [] + + return orders + .filter((order) => order.chainId === chainId && order.safeAddress.toLowerCase() === accountLowerCase) + .map((order) => emulateTwapAsOrder(tokens, order)) +}) diff --git a/src/modules/twap/state/twapPartOrdersAtom.ts b/src/modules/twap/state/twapPartOrdersAtom.ts new file mode 100644 index 0000000000..d26582ed92 --- /dev/null +++ b/src/modules/twap/state/twapPartOrdersAtom.ts @@ -0,0 +1,40 @@ +import { atom } from 'jotai' +import { atomWithStorage } from 'jotai/utils' + +import { OrderParameters, SupportedChainId } from '@cowprotocol/cow-sdk' + +import deepEqual from 'fast-deep-equal' + +import { walletInfoAtom } from 'modules/wallet/api/state' + +export interface TwapPartOrderItem { + uid: string + chainId: SupportedChainId + safeAddress: string + twapOrderId: string + order: OrderParameters + signature: string +} +export type TwapPartOrders = { [twapOrderHash: string]: TwapPartOrderItem } + +export const twapPartOrdersAtom = atomWithStorage('twap-part-orders-list:v1', {}) + +export const updateTwapPartOrdersAtom = atom(null, (get, set, nextState: TwapPartOrders) => { + const currentState = get(twapPartOrdersAtom) + + if (!deepEqual(currentState, nextState)) { + set(twapPartOrdersAtom, nextState) + } +}) + +export const twapPartOrdersListAtom = atom((get) => { + const { account, chainId } = get(walletInfoAtom) + + if (!account || !chainId) return [] + + const accountLowerCase = account.toLowerCase() + + return Object.values(get(twapPartOrdersAtom)).filter( + (order) => order.safeAddress === accountLowerCase && order.chainId === chainId + ) +}) diff --git a/src/modules/twap/types.ts b/src/modules/twap/types.ts index ee2bbc698a..8349aa348f 100644 --- a/src/modules/twap/types.ts +++ b/src/modules/twap/types.ts @@ -1,5 +1,8 @@ +import { SupportedChainId } from '@cowprotocol/cow-sdk' import { CurrencyAmount, Token } from '@uniswap/sdk-core' +import { TwapOrdersSafeData } from './services/fetchTwapOrdersFromSafe' + // Read more: https://github.com/rndlabs/composable-cow#data-structure export interface TWAPOrder { sellAmount: CurrencyAmount @@ -17,8 +20,43 @@ export interface TWAPOrderStruct { receiver: string partSellAmount: string minPartLimit: string + // timeStart t0: number + // numOfParts n: number + // timeInterval t: number span: number } + +export enum TwapOrderStatus { + WaitSigning = 'WaitSigning', + Pending = 'Pending', + Scheduled = 'Scheduled', + Cancelled = 'Cancelled', + Expired = 'Expired', +} + +export interface TwapOrderItem { + order: TWAPOrderStruct + status: TwapOrderStatus + chainId: SupportedChainId + safeAddress: string + hash: string + submissionDate: string +} + +export interface ConditionalOrderParams { + staticInput: string + salt: string + handler: string +} + +export interface TwapOrderInfo { + id: string + orderStruct: TWAPOrderStruct + safeData: TwapOrdersSafeData + isExpired: boolean +} + +export type TwapOrdersAuthResult = { [key: string]: boolean } diff --git a/src/modules/twap/updaters/TwapOrdersUpdater.tsx b/src/modules/twap/updaters/TwapOrdersUpdater.tsx new file mode 100644 index 0000000000..8b3e4de30a --- /dev/null +++ b/src/modules/twap/updaters/TwapOrdersUpdater.tsx @@ -0,0 +1,80 @@ +import { useAtomValue, useUpdateAtom } from 'jotai/utils' +import { useEffect, useMemo } from 'react' + +import { SupportedChainId } from '@cowprotocol/cow-sdk' + +import { ComposableCoW } from 'abis/types' + +import { TWAP_NOT_PENDING_STATUSES } from '../const' +import { useFetchTwapOrdersFromSafe } from '../hooks/useFetchTwapOrdersFromSafe' +import { useFetchTwapPartOrders } from '../hooks/useFetchTwapPartOrders' +import { useTwapDiscreteOrders } from '../hooks/useTwapDiscreteOrders' +import { useTwapOrdersAuthMulticall } from '../hooks/useTwapOrdersAuthMulticall' +import { twapOrdersListAtom, updateTwapOrdersListAtom } from '../state/twapOrdersListAtom' +import { updateTwapPartOrdersAtom } from '../state/twapPartOrdersAtom' +import { TwapOrderInfo } from '../types' +import { buildTwapOrdersItems } from '../utils/buildTwapOrdersItems' +import { getConditionalOrderId } from '../utils/getConditionalOrderId' +import { isTwapOrderExpired } from '../utils/getTwapOrderStatus' +import { parseTwapOrderStruct } from '../utils/parseTwapOrderStruct' + +export function TwapOrdersUpdater(props: { + safeAddress: string + chainId: SupportedChainId + composableCowContract: ComposableCoW +}) { + const { safeAddress, chainId, composableCowContract } = props + + const twapDiscreteOrders = useTwapDiscreteOrders() + const twapOrdersList = useAtomValue(twapOrdersListAtom) + const setTwapOrders = useUpdateAtom(updateTwapOrdersListAtom) + const updateTwapPartOrders = useUpdateAtom(updateTwapPartOrdersAtom) + const ordersSafeData = useFetchTwapOrdersFromSafe(props) + + const allOrdersInfo: TwapOrderInfo[] = useMemo(() => { + return ordersSafeData.map((data) => { + const id = getConditionalOrderId(data.params) + const order = parseTwapOrderStruct(data.params.staticInput) + + return { + id, + orderStruct: order, + safeData: data, + isExpired: isTwapOrderExpired(order), + } + }) + }, [ordersSafeData]) + + // Here we can split all orders in two groups: 1. Not signed + expired, 2. Open + cancelled + const openOrCancelledOrders = useMemo(() => { + return allOrdersInfo.filter((info) => { + const existedOrder = twapOrdersList[info.id] + + if (existedOrder) { + return !TWAP_NOT_PENDING_STATUSES.includes(existedOrder.status) + } + + return !info.isExpired && info.safeData.isExecuted + }) + }, [allOrdersInfo, twapOrdersList]) + + const partOrders = useFetchTwapPartOrders(safeAddress, chainId, composableCowContract, openOrCancelledOrders) + // Here we know which orders are cancelled: if it's auth !== true, then it's cancelled + const ordersAuthResult = useTwapOrdersAuthMulticall(safeAddress, composableCowContract, openOrCancelledOrders) + + useEffect(() => { + if (!ordersAuthResult || !twapDiscreteOrders) return + + const items = buildTwapOrdersItems(chainId, safeAddress, allOrdersInfo, ordersAuthResult, twapDiscreteOrders) + + setTwapOrders(items) + }, [chainId, safeAddress, allOrdersInfo, ordersAuthResult, twapDiscreteOrders, setTwapOrders]) + + useEffect(() => { + if (!partOrders) return + + updateTwapPartOrders(partOrders) + }, [partOrders, updateTwapPartOrders]) + + return null +} diff --git a/src/modules/twap/utils/buildTwapOrdersItems.ts b/src/modules/twap/utils/buildTwapOrdersItems.ts new file mode 100644 index 0000000000..6f95c30d40 --- /dev/null +++ b/src/modules/twap/utils/buildTwapOrdersItems.ts @@ -0,0 +1,47 @@ +import { SupportedChainId } from '@cowprotocol/cow-sdk' + +import { Order } from 'legacy/state/orders/actions' + +import { getTwapOrderStatus } from './getTwapOrderStatus' +import { parseTwapOrderStruct } from './parseTwapOrderStruct' + +import { TwapToDiscreteOrders } from '../hooks/useTwapDiscreteOrders' +import { TwapOrdersSafeData } from '../services/fetchTwapOrdersFromSafe' +import { TwapOrdersList } from '../state/twapOrdersListAtom' +import { TwapOrderItem, TwapOrderInfo, TwapOrdersAuthResult } from '../types' + +export function buildTwapOrdersItems( + chainId: SupportedChainId, + safeAddress: string, + ordersInfo: TwapOrderInfo[], + ordersAuthResult: TwapOrdersAuthResult, + discreteOrders: TwapToDiscreteOrders +): TwapOrdersList { + return ordersInfo.reduce((acc, { safeData, id }) => { + acc[id] = getTwapOrderItem(chainId, safeAddress, safeData, id, ordersAuthResult[id], discreteOrders[id]) + return acc + }, {}) +} + +function getTwapOrderItem( + chainId: SupportedChainId, + safeAddress: string, + safeData: TwapOrdersSafeData, + hash: string, + authorized: boolean, + discreteOrder: Order | undefined +): TwapOrderItem { + const { params, submissionDate, isExecuted } = safeData + + const order = parseTwapOrderStruct(params.staticInput) + const status = getTwapOrderStatus(order, isExecuted, authorized, discreteOrder) + + return { + order, + status, + chainId, + safeAddress, + hash, + submissionDate, + } +} diff --git a/src/modules/twap/utils/emulateTwapAsOrder.ts b/src/modules/twap/utils/emulateTwapAsOrder.ts new file mode 100644 index 0000000000..6f3d7263ab --- /dev/null +++ b/src/modules/twap/utils/emulateTwapAsOrder.ts @@ -0,0 +1,51 @@ +import { EnrichedOrder, OrderClass, OrderKind, SigningScheme, OrderStatus } from '@cowprotocol/cow-sdk' + +import { TokensByAddress } from 'modules/tokensList/state/tokensListAtom' + +import { TwapOrderItem, TwapOrderStatus } from '../types' + +// TODO: add FULFILLED status +const statusMap: Record = { + [TwapOrderStatus.Pending]: OrderStatus.OPEN, + [TwapOrderStatus.Scheduled]: OrderStatus.OPEN, + [TwapOrderStatus.WaitSigning]: OrderStatus.PRESIGNATURE_PENDING, + [TwapOrderStatus.Cancelled]: OrderStatus.CANCELLED, + [TwapOrderStatus.Expired]: OrderStatus.EXPIRED, +} + +export function emulateTwapAsOrder(tokens: TokensByAddress, item: TwapOrderItem): EnrichedOrder { + const { safeAddress, hash, status } = item + const { sellToken, buyToken, partSellAmount, minPartLimit, n, t } = item.order + const numOfParts = BigInt(n) + const sellAmount = BigInt(partSellAmount) * numOfParts + const buyAmount = BigInt(minPartLimit) * numOfParts + + const creationTime = new Date(item.submissionDate) + const expirationTime = new Date(creationTime.getTime() + t * n * 1000) + + return { + signingScheme: SigningScheme.EIP1271, + status: statusMap[status], + sellToken, + buyToken, + validTo: Math.ceil(expirationTime.getTime() / 1000), + uid: hash, + sellAmount: sellAmount.toString(), + buyAmount: buyAmount.toString(), + kind: OrderKind.SELL, + partiallyFillable: true, + creationDate: creationTime.toISOString(), + class: OrderClass.LIMIT, + owner: safeAddress, + // TODO: fulfill data + signature: '', + appData: '', + totalFee: '0', + feeAmount: '0', + executedSellAmount: '0', + executedSellAmountBeforeFees: '0', + executedBuyAmount: '0', + executedFeeAmount: '0', + invalidated: false, + } +} diff --git a/src/modules/twap/utils/getConditionalOrderId.ts b/src/modules/twap/utils/getConditionalOrderId.ts new file mode 100644 index 0000000000..74200135e2 --- /dev/null +++ b/src/modules/twap/utils/getConditionalOrderId.ts @@ -0,0 +1,10 @@ +import { defaultAbiCoder } from '@ethersproject/abi' +import { keccak256 } from '@ethersproject/keccak256' + +import { ConditionalOrderParams } from '../types' + +const CONDITIONAL_ORDER_PARAMS_STRUCT = 'tuple(address handler, bytes32 salt, bytes staticInput)' + +export function getConditionalOrderId(params: ConditionalOrderParams): string { + return keccak256(defaultAbiCoder.encode([CONDITIONAL_ORDER_PARAMS_STRUCT], [params])) +} diff --git a/src/modules/twap/utils/getTwapOrderStatus.ts b/src/modules/twap/utils/getTwapOrderStatus.ts new file mode 100644 index 0000000000..b12d3ef727 --- /dev/null +++ b/src/modules/twap/utils/getTwapOrderStatus.ts @@ -0,0 +1,28 @@ +import { Order, PENDING_STATES } from 'legacy/state/orders/actions' + +import { TwapOrderStatus, TWAPOrderStruct } from '../types' + +export function getTwapOrderStatus( + order: TWAPOrderStruct, + isExecuted: boolean, + auth: boolean, + discreteOrder: Order | undefined +): TwapOrderStatus { + if (isTwapOrderExpired(order)) return TwapOrderStatus.Expired + + if (!isExecuted) return TwapOrderStatus.WaitSigning + + if (!auth) return TwapOrderStatus.Cancelled + + if (discreteOrder && PENDING_STATES.includes(discreteOrder.status)) return TwapOrderStatus.Pending + + return TwapOrderStatus.Scheduled +} + +export function isTwapOrderExpired(order: TWAPOrderStruct): boolean { + const { t0: startTime, n: numOfParts, t: timeInterval } = order + const endTime = startTime + timeInterval * numOfParts + const nowTimestamp = Math.ceil(Date.now() / 1000) + + return nowTimestamp > endTime +} diff --git a/src/modules/twap/utils/parseTwapOrderStruct.ts b/src/modules/twap/utils/parseTwapOrderStruct.ts new file mode 100644 index 0000000000..5f4685258f --- /dev/null +++ b/src/modules/twap/utils/parseTwapOrderStruct.ts @@ -0,0 +1,32 @@ +import { defaultAbiCoder, ParamType } from '@ethersproject/abi' +import { BigNumber } from '@ethersproject/bignumber' + +import { TWAPOrderStruct } from '../types' + +const TWAP_STRUCT_ABI = [ + { name: 'sellToken', type: 'address' }, + { name: 'buyToken', type: 'address' }, + { name: 'receiver', type: 'address' }, + { name: 'partSellAmount', type: 'uint256' }, + { name: 'minPartLimit', type: 'uint256' }, + { name: 't0', type: 'uint256' }, + { name: 'n', type: 'uint256' }, + { name: 't', type: 'uint256' }, + { name: 'span', type: 'uint256' }, +] as ParamType[] + +export function parseTwapOrderStruct(staticInput: string): TWAPOrderStruct { + const rawData = defaultAbiCoder.decode(TWAP_STRUCT_ABI, staticInput) + + return { + sellToken: rawData['sellToken'], + buyToken: rawData['buyToken'], + receiver: rawData['receiver'], + partSellAmount: (rawData['partSellAmount'] as BigNumber).toHexString(), + minPartLimit: (rawData['minPartLimit'] as BigNumber).toHexString(), + t0: (rawData['t0'] as BigNumber).toNumber(), + n: (rawData['n'] as BigNumber).toNumber(), + t: (rawData['t'] as BigNumber).toNumber(), + span: (rawData['span'] as BigNumber).toNumber(), + } +} diff --git a/src/pages/AdvancedOrders/index.tsx b/src/pages/AdvancedOrders/index.tsx index f948e744fa..0a777311fc 100644 --- a/src/pages/AdvancedOrders/index.tsx +++ b/src/pages/AdvancedOrders/index.tsx @@ -2,6 +2,7 @@ import { AdvancedOrdersWidget } from 'modules/advancedOrders' import { OrdersTableWidget } from 'modules/ordersTable' import * as styledEl from 'modules/trade/pure/TradePageLayout' import { TradeFormValidationUpdater } from 'modules/tradeFormValidation' +import { TwapFormWidget } from 'modules/twap' export default function AdvancedOrdersPage() { return ( @@ -12,7 +13,10 @@ export default function AdvancedOrdersPage() { {/*TODO: add isUnlocked value*/} - + + {/*TODO: conditionally display a widget for current advanced order type*/} + + diff --git a/src/utils/orderUtils/computeOrderUid.ts b/src/utils/orderUtils/computeOrderUid.ts new file mode 100644 index 0000000000..ac92236673 --- /dev/null +++ b/src/utils/orderUtils/computeOrderUid.ts @@ -0,0 +1,9 @@ +import type { Order } from '@cowprotocol/contracts' +import { OrderSigningUtils, SupportedChainId } from '@cowprotocol/cow-sdk' + +export async function computeOrderUid(chainId: SupportedChainId, owner: string, order: Order): Promise { + const { computeOrderUid: _computeOrderUid } = await import('@cowprotocol/contracts') + const domain = await OrderSigningUtils.getDomain(chainId) + + return _computeOrderUid(domain, order, owner) +} From d4f841f4523765e3ce97ab36b8b98b4e2d921493 Mon Sep 17 00:00:00 2001 From: Leandro Date: Mon, 12 Jun 2023 03:42:23 -0700 Subject: [PATCH 10/67] feat(twap): twap review order - pt1 (#2629) * feat: start the review twap order confirmation * feat: add limit price * feat: refactor and reuse some components * feat: add min received row * feat: make ExecutionPrice a bit more generic * feat: update LimitPriceRow * feat: implement limit price calculation for TWAP review * refactor: use updateState from useTradeState * refactor: hide horrible button style * refactor: move slippage access to parent component * refactor: simplified MinReceivedRow props * feat: add usd value for min receive amount --------- Co-authored-by: nenadV91 --- src/common/pure/ExecutionPrice/index.tsx | 16 ++++- src/common/pure/RateInfo/index.tsx | 5 +- .../TwapConfirmDetails/LimitPriceRow.tsx | 32 +++++++++ .../TwapConfirmDetails/MinReceivedRow.tsx | 29 ++++++++ .../TwapConfirmDetails/SlippageRow.tsx | 16 +++++ .../containers/TwapConfirmDetails/index.tsx | 71 +++++++++++++++++++ .../containers/TwapConfirmDetails/styled.ts | 22 ++++++ .../containers/TwapConfirmModal/index.tsx | 16 ++++- .../twap/containers/TwapFormWidget/index.tsx | 15 ++-- .../twap/pure/ConfirmDetailsItem/index.tsx | 48 +++++++++++++ .../twap/pure/ConfirmDetailsItem/styled.ts | 23 ++++++ src/modules/twap/state/twapOrderAtom.ts | 13 ++-- .../twap/state/twapOrdersSettingsAtom.ts | 15 +++- 13 files changed, 297 insertions(+), 24 deletions(-) create mode 100644 src/modules/twap/containers/TwapConfirmDetails/LimitPriceRow.tsx create mode 100644 src/modules/twap/containers/TwapConfirmDetails/MinReceivedRow.tsx create mode 100644 src/modules/twap/containers/TwapConfirmDetails/SlippageRow.tsx create mode 100644 src/modules/twap/containers/TwapConfirmDetails/index.tsx create mode 100644 src/modules/twap/containers/TwapConfirmDetails/styled.ts create mode 100644 src/modules/twap/pure/ConfirmDetailsItem/index.tsx create mode 100644 src/modules/twap/pure/ConfirmDetailsItem/styled.ts diff --git a/src/common/pure/ExecutionPrice/index.tsx b/src/common/pure/ExecutionPrice/index.tsx index 13b7f6ae04..82e66cda2f 100644 --- a/src/common/pure/ExecutionPrice/index.tsx +++ b/src/common/pure/ExecutionPrice/index.tsx @@ -2,21 +2,33 @@ import { Currency, Price } from '@uniswap/sdk-core' import { FiatAmount } from 'common/pure/FiatAmount' import { TokenAmount } from 'common/pure/TokenAmount' +import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount' import { useExecutionPriceFiat } from './hooks/useExecutionPriceFiat' export interface ExecutionPriceProps { executionPrice: Price isInverted: boolean + showBaseCurrency?: boolean + separatorSymbol?: string } -export function ExecutionPrice({ executionPrice, isInverted }: ExecutionPriceProps) { +export function ExecutionPrice({ + executionPrice, + isInverted, + showBaseCurrency, + separatorSymbol = '≈', +}: ExecutionPriceProps) { const executionPriceFiat = useExecutionPriceFiat(executionPrice, isInverted) const quoteCurrency = isInverted ? executionPrice?.baseCurrency : executionPrice?.quoteCurrency + const baseCurrency = isInverted ? executionPrice?.quoteCurrency : executionPrice?.baseCurrency + const oneBaseCurrency = tryParseCurrencyAmount('1', baseCurrency) return ( - ≈ + {showBaseCurrency && } + {` ${separatorSymbol} `} + {executionPriceFiat && (  ( diff --git a/src/common/pure/RateInfo/index.tsx b/src/common/pure/RateInfo/index.tsx index 70f24dc1ca..437f63076b 100644 --- a/src/common/pure/RateInfo/index.tsx +++ b/src/common/pure/RateInfo/index.tsx @@ -38,6 +38,7 @@ export interface RateInfoProps { isInvertedState?: [boolean, Dispatch>] rateInfoParams: RateInfoParams opacitySymbol?: boolean + noFiat?: boolean } const Wrapper = styled.div<{ stylized: boolean }>` @@ -132,6 +133,7 @@ export function RateInfo({ prependSymbol = true, isInvertedState, opacitySymbol = false, + noFiat = false, }: RateInfoProps) { const { chainId, inputCurrencyAmount, outputCurrencyAmount, activeRateFiatAmount, invertedActiveRateFiatAmount } = rateInfoParams @@ -150,8 +152,9 @@ export function RateInfo({ }, [currentIsInverted, activeRate]) const fiatAmount = useMemo(() => { + if (noFiat) return null return currentIsInverted ? invertedActiveRateFiatAmount : activeRateFiatAmount - }, [currentIsInverted, activeRateFiatAmount, invertedActiveRateFiatAmount]) + }, [noFiat, currentIsInverted, invertedActiveRateFiatAmount, activeRateFiatAmount]) const rateInputCurrency = useMemo(() => { return currentIsInverted ? outputCurrency : inputCurrency diff --git a/src/modules/twap/containers/TwapConfirmDetails/LimitPriceRow.tsx b/src/modules/twap/containers/TwapConfirmDetails/LimitPriceRow.tsx new file mode 100644 index 0000000000..c3379880ba --- /dev/null +++ b/src/modules/twap/containers/TwapConfirmDetails/LimitPriceRow.tsx @@ -0,0 +1,32 @@ +import { Dispatch, SetStateAction } from 'react' + +import { Currency, Price } from '@uniswap/sdk-core' + +import { Nullish } from 'types' + +import { ConfirmDetailsItem } from 'modules/twap/pure/ConfirmDetailsItem' + +import { ExecutionPrice } from 'common/pure/ExecutionPrice' +import { RateWrapper } from 'common/pure/RateInfo' + +type Props = { + price: Nullish> + isInvertedState: [boolean, Dispatch>] +} + +export function LimitPriceRow(props: Props) { + const { price, isInvertedState } = props + const [isInverted, setIsInverted] = isInvertedState + + return ( + setIsInverted((curr) => !curr)}> + + {price ? ( + + ) : ( + '-' + )} + + + ) +} diff --git a/src/modules/twap/containers/TwapConfirmDetails/MinReceivedRow.tsx b/src/modules/twap/containers/TwapConfirmDetails/MinReceivedRow.tsx new file mode 100644 index 0000000000..db9b51cd40 --- /dev/null +++ b/src/modules/twap/containers/TwapConfirmDetails/MinReceivedRow.tsx @@ -0,0 +1,29 @@ +import { Currency, CurrencyAmount } from '@uniswap/sdk-core' + +import { Nullish } from 'types' + +import { ConfirmDetailsItem } from 'modules/twap/pure/ConfirmDetailsItem' + +import { FiatAmount } from 'common/pure/FiatAmount' +import { TokenAmount } from 'common/pure/TokenAmount' + +type Props = { + amount: Nullish> + usdAmount: Nullish> +} + +export function MinReceivedRow(props: Props) { + const { amount, usdAmount } = props + + return ( + + + {usdAmount && ( + +  ( + ) + + )} + + ) +} diff --git a/src/modules/twap/containers/TwapConfirmDetails/SlippageRow.tsx b/src/modules/twap/containers/TwapConfirmDetails/SlippageRow.tsx new file mode 100644 index 0000000000..ac9ba5ef6b --- /dev/null +++ b/src/modules/twap/containers/TwapConfirmDetails/SlippageRow.tsx @@ -0,0 +1,16 @@ +import { Percent } from '@uniswap/sdk-core' + +import { RowSlippage } from 'modules/swap/containers/Row/RowSlippage' +import { ConfirmDetailsItem } from 'modules/twap/pure/ConfirmDetailsItem' + +export type SlippageRowProps = { + slippage: Percent +} + +export function SlippageRow({ slippage }: SlippageRowProps) { + return ( + + + + ) +} diff --git a/src/modules/twap/containers/TwapConfirmDetails/index.tsx b/src/modules/twap/containers/TwapConfirmDetails/index.tsx new file mode 100644 index 0000000000..0dc12bb9f0 --- /dev/null +++ b/src/modules/twap/containers/TwapConfirmDetails/index.tsx @@ -0,0 +1,71 @@ +import { useAtomValue } from 'jotai' +import { Dispatch, SetStateAction, useMemo } from 'react' + +import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core' + +import { Nullish } from 'types' + +import { ONE_HUNDRED_PERCENT } from 'legacy/constants/misc' +import { useHigherUSDValue } from 'legacy/hooks/useStablecoinPrice' + +import { twapOrderSlippage } from 'modules/twap/state/twapOrdersSettingsAtom' + +import { usePrice } from 'common/hooks/usePrice' +import { useRateInfoParams } from 'common/hooks/useRateInfoParams' +import { CurrencyPreviewInfo } from 'common/pure/CurrencyAmountPreview' + +import { LimitPriceRow } from './LimitPriceRow' +import { MinReceivedRow } from './MinReceivedRow' +import { SlippageRow } from './SlippageRow' +import * as styledEl from './styled' + +type Props = { + inputCurrencyInfo: CurrencyPreviewInfo + outputCurrencyInfo: CurrencyPreviewInfo + isInvertedState: [boolean, Dispatch>] +} + +export function TwapConfirmDetails(props: Props) { + const { inputCurrencyInfo, outputCurrencyInfo, isInvertedState } = props + + const rateInfoParams = useRateInfoParams(inputCurrencyInfo.amount, outputCurrencyInfo.amount) + const allowedSlippage = useAtomValue(twapOrderSlippage) + + // This is the minimum per part + const minReceivedAmountPerPart = useMemo( + () => getSlippageAdjustedBuyAmount(outputCurrencyInfo.amount, allowedSlippage), + [allowedSlippage, outputCurrencyInfo.amount] + ) + const minReceivedUsdAmount = useHigherUSDValue(minReceivedAmountPerPart) + + // Limit price is the same for all parts + const limitPrice = usePrice(inputCurrencyInfo.amount, minReceivedAmountPerPart) + + return ( + + {/* Price */} + + + {/* Slippage */} + + + {/* Limit Price */} + + + {/* Min received */} + + + ) +} + +function getSlippageAdjustedBuyAmount( + buyAmount: Nullish>, + slippage: Percent +): CurrencyAmount | undefined { + return buyAmount?.multiply(ONE_HUNDRED_PERCENT.subtract(slippage)) +} diff --git a/src/modules/twap/containers/TwapConfirmDetails/styled.ts b/src/modules/twap/containers/TwapConfirmDetails/styled.ts new file mode 100644 index 0000000000..ceba27b052 --- /dev/null +++ b/src/modules/twap/containers/TwapConfirmDetails/styled.ts @@ -0,0 +1,22 @@ +import styled from 'styled-components/macro' + +import { RateInfo } from 'common/pure/RateInfo' + +export const Wrapper = styled.div` + padding: 1rem 0; +` + +export const StyledRateInfo = styled(RateInfo)` + font-size: 13px; + font-weight: 500; + margin: 0 auto; +` + +export const ItemWithArrow = styled.div` + display: flex; + align-items: center; + + > svg:first-child { + margin-right: 5px; + } +` diff --git a/src/modules/twap/containers/TwapConfirmModal/index.tsx b/src/modules/twap/containers/TwapConfirmModal/index.tsx index 90bbe26cf3..538e773f1d 100644 --- a/src/modules/twap/containers/TwapConfirmModal/index.tsx +++ b/src/modules/twap/containers/TwapConfirmModal/index.tsx @@ -1,9 +1,10 @@ -import React from 'react' +import { useState } from 'react' import { useAdvancedOrdersDerivedState } from 'modules/advancedOrders' import { TradeConfirmation, TradeConfirmModal, useTradeConfirmActions } from 'modules/trade' import { useCreateTwapOrder } from '../../hooks/useCreateTwapOrder' +import { TwapConfirmDetails } from '../TwapConfirmDetails' export function TwapConfirmModal() { const { @@ -18,6 +19,8 @@ export function TwapConfirmModal() { const tradeConfirmActions = useTradeConfirmActions() const createTwapOrder = useCreateTwapOrder() + const isInvertedState = useState(false) + // TODO: add conditions based on warnings const isConfirmDisabled = false @@ -32,12 +35,14 @@ export function TwapConfirmModal() { amount: inputCurrencyAmount, fiatAmount: inputCurrencyFiatAmount, balance: inputCurrencyBalance, + label: 'Sell amount', } const outputCurrencyInfo = { amount: outputCurrencyAmount, fiatAmount: outputCurrencyFiatAmount, balance: outputCurrencyBalance, + label: 'Estimated receive amount', } return ( @@ -51,7 +56,14 @@ export function TwapConfirmModal() { isConfirmDisabled={isConfirmDisabled} priceImpact={priceImpact} > - <>{/*TODO: display details*/} + <> + <>{/*TODO: display details*/} + + ) diff --git a/src/modules/twap/containers/TwapFormWidget/index.tsx b/src/modules/twap/containers/TwapFormWidget/index.tsx index 617a974a15..28d170f25c 100644 --- a/src/modules/twap/containers/TwapFormWidget/index.tsx +++ b/src/modules/twap/containers/TwapFormWidget/index.tsx @@ -2,13 +2,10 @@ import { useAtomValue } from 'jotai' import { useUpdateAtom } from 'jotai/utils' import { useEffect } from 'react' -import { - useAdvancedOrdersDerivedState, - useAdvancedOrdersRawState, - useUpdateAdvancedOrdersRawState, -} from 'modules/advancedOrders' +import { useAdvancedOrdersDerivedState, useAdvancedOrdersRawState } from 'modules/advancedOrders' import { useComposableCowContract } from 'modules/advancedOrders/hooks/useComposableCowContract' import { useIsWrapOrUnwrap } from 'modules/trade/hooks/useIsWrapOrUnwrap' +import { useTradeState } from 'modules/trade/hooks/useTradeState' import { TradeNumberInput } from 'modules/trade/pure/TradeNumberInput' import { TradeTextBox } from 'modules/trade/pure/TradeTextBox' import { QuoteObserverUpdater } from 'modules/twap/updaters/QuoteObserverUpdater' @@ -18,7 +15,7 @@ import { useRateInfoParams } from 'common/hooks/useRateInfoParams' import * as styledEl from './styled' -import { DEFAULT_TWAP_SLIPPAGE, orderDeadlines, defaultNumOfParts } from '../../const' +import { DEFAULT_TWAP_SLIPPAGE, defaultNumOfParts, orderDeadlines } from '../../const' import { AmountParts } from '../../pure/AmountParts' import { DeadlineSelector } from '../../pure/DeadlineSelector' import { partsStateAtom } from '../../state/partsStateAtom' @@ -37,7 +34,7 @@ export function TwapFormWidget() { const { inputCurrencyAmount, outputCurrencyAmount } = useAdvancedOrdersDerivedState() const { inputCurrencyAmount: rawInputCurrencyAmount } = useAdvancedOrdersRawState() - const updateRawState = useUpdateAdvancedOrdersRawState() + const { updateState } = useTradeState() const partsState = useAtomValue(partsStateAtom) const timeInterval = useAtomValue(twapTimeIntervalAtom) @@ -58,8 +55,8 @@ export function TwapFormWidget() { // Reset output amount when num of parts or input amount are changed useEffect(() => { - updateRawState({ outputCurrencyAmount: null }) - }, [updateRawState, numberOfPartsValue, rawInputCurrencyAmount]) + updateState?.({ outputCurrencyAmount: null }) + }, [updateState, numberOfPartsValue, rawInputCurrencyAmount]) return ( <> diff --git a/src/modules/twap/pure/ConfirmDetailsItem/index.tsx b/src/modules/twap/pure/ConfirmDetailsItem/index.tsx new file mode 100644 index 0000000000..b8cbd8a3d1 --- /dev/null +++ b/src/modules/twap/pure/ConfirmDetailsItem/index.tsx @@ -0,0 +1,48 @@ +import { ReactNode } from 'react' + +import { CornerDownRight } from 'react-feather' + +import { RowFixed } from 'legacy/components/Row' +import { MouseoverTooltipContent } from 'legacy/components/Tooltip' + +import { TextWrapper } from 'modules/swap/pure/Row/styled' +import { StyledInfoIcon } from 'modules/swap/pure/styled' + +import { Wrapper, Row, Content } from './styled' + +type Props = { + children: ReactNode + label?: string + tooltip?: string + withArrow?: boolean + fiatAmount?: string +} + +export function ConfirmDetailsItem(props: Props) { + const { children, label, tooltip, withArrow = true } = props + + return ( + + {withArrow && } + + {label ? ( + <> + + + {label && {label}} + {tooltip && ( + + + + )} + + + {children} + + + ) : ( + children + )} + + ) +} diff --git a/src/modules/twap/pure/ConfirmDetailsItem/styled.ts b/src/modules/twap/pure/ConfirmDetailsItem/styled.ts new file mode 100644 index 0000000000..4de79e3556 --- /dev/null +++ b/src/modules/twap/pure/ConfirmDetailsItem/styled.ts @@ -0,0 +1,23 @@ +import styled from 'styled-components/macro' + +import { StyledRowBetween } from 'modules/swap/pure/Row/styled' + +export const Wrapper = styled.div` + display: flex; + align-items: center; + + > svg:first-child { + margin-right: 5px; + color: ${({ theme }) => theme.text1}; + } +` + +export const Row = styled(StyledRowBetween)` + display: flex; + align-items: center; + justify-content: space-between; +` + +export const Content = styled.div` + margin-left: auto; +` diff --git a/src/modules/twap/state/twapOrderAtom.ts b/src/modules/twap/state/twapOrderAtom.ts index 9ca983f16c..fa72d1e52b 100644 --- a/src/modules/twap/state/twapOrderAtom.ts +++ b/src/modules/twap/state/twapOrderAtom.ts @@ -1,12 +1,11 @@ import { atom } from 'jotai' -import { CurrencyAmount, Percent, Token } from '@uniswap/sdk-core' +import { CurrencyAmount, Token } from '@uniswap/sdk-core' -import { twapOrdersSettingsAtom } from './twapOrdersSettingsAtom' +import { twapOrderSlippage, twapOrdersSettingsAtom } from './twapOrdersSettingsAtom' import { advancedOrdersDerivedStateAtom } from '../../advancedOrders' import { walletInfoAtom } from '../../wallet/api/state' -import { DEFAULT_TWAP_SLIPPAGE } from '../const' import { TWAPOrder } from '../types' import { customDeadlineToSeconds } from '../utils/deadlinePartsDisplay' @@ -19,17 +18,13 @@ export const twapTimeIntervalAtom = atom((get) => { export const twapOrderAtom = atom((get) => { const { account } = get(walletInfoAtom) - const { numberOfPartsValue, slippageValue } = get(twapOrdersSettingsAtom) + const { numberOfPartsValue } = get(twapOrdersSettingsAtom) const timeInterval = get(twapTimeIntervalAtom) const { inputCurrencyAmount, outputCurrencyAmount, recipient } = get(advancedOrdersDerivedStateAtom) + const slippage = get(twapOrderSlippage) if (!inputCurrencyAmount || !outputCurrencyAmount || !account) return null - const slippage = - slippageValue != null - ? // Multiplying on 100 to allow decimals values (e.g 0.05) - new Percent(slippageValue * 100, 10000) - : DEFAULT_TWAP_SLIPPAGE const slippageAmount = outputCurrencyAmount.multiply(slippage) const buyAmountWithSlippage = outputCurrencyAmount.subtract(slippageAmount) diff --git a/src/modules/twap/state/twapOrdersSettingsAtom.ts b/src/modules/twap/state/twapOrdersSettingsAtom.ts index db2c3411ea..6693c56aaf 100644 --- a/src/modules/twap/state/twapOrdersSettingsAtom.ts +++ b/src/modules/twap/state/twapOrdersSettingsAtom.ts @@ -1,9 +1,11 @@ import { atom } from 'jotai' import { atomWithStorage } from 'jotai/utils' +import { Percent } from '@uniswap/sdk-core' + import { Milliseconds } from 'types' -import { defaultNumOfParts, defaultOrderDeadline } from '../const' +import { DEFAULT_TWAP_SLIPPAGE, defaultNumOfParts, defaultOrderDeadline } from '../const' export interface TwapOrdersDeadline { readonly isCustomDeadline: boolean @@ -46,3 +48,14 @@ export const updateTwapOrdersSettingsAtom = atom(null, (get, set, nextState: Par return { ...prevState, ...nextState } }) }) + +export const twapOrderSlippage = atom((get) => { + const { slippageValue } = get(twapOrdersSettingsAtom) + const slippage = + slippageValue != null + ? // Multiplying on 100 to allow decimals values (e.g 0.05) + new Percent(slippageValue * 100, 10000) + : DEFAULT_TWAP_SLIPPAGE + + return slippage +}) From ee9946027b5b20953a4ebae576673d134e2ae32c Mon Sep 17 00:00:00 2001 From: Leandro Date: Mon, 12 Jun 2023 03:47:26 -0700 Subject: [PATCH 11/67] feat(twap): twap review order - pt2 - refactor container (#2630) * refactor: move ConfirmDetailsItem to trade/pure * refactor: move twap/containers/TwapConfirmDetails to trade/containers/TradeBasicConfirmDetails * refactor: move slippage up to TwapConfirmModal * refactor: remove redundant variable --- .../TradeBasicConfirmDetails}/LimitPriceRow.tsx | 2 +- .../TradeBasicConfirmDetails}/MinReceivedRow.tsx | 2 +- .../TradeBasicConfirmDetails}/SlippageRow.tsx | 2 +- .../TradeBasicConfirmDetails}/index.tsx | 15 ++++++--------- .../TradeBasicConfirmDetails}/styled.ts | 0 .../pure/ConfirmDetailsItem/index.tsx | 0 .../pure/ConfirmDetailsItem/styled.ts | 0 .../twap/containers/TwapConfirmModal/index.tsx | 8 ++++++-- src/modules/twap/state/twapOrdersSettingsAtom.ts | 10 ++++------ 9 files changed, 19 insertions(+), 20 deletions(-) rename src/modules/{twap/containers/TwapConfirmDetails => trade/containers/TradeBasicConfirmDetails}/LimitPriceRow.tsx (92%) rename src/modules/{twap/containers/TwapConfirmDetails => trade/containers/TradeBasicConfirmDetails}/MinReceivedRow.tsx (90%) rename src/modules/{twap/containers/TwapConfirmDetails => trade/containers/TradeBasicConfirmDetails}/SlippageRow.tsx (83%) rename src/modules/{twap/containers/TwapConfirmDetails => trade/containers/TradeBasicConfirmDetails}/index.tsx (85%) rename src/modules/{twap/containers/TwapConfirmDetails => trade/containers/TradeBasicConfirmDetails}/styled.ts (100%) rename src/modules/{twap => trade}/pure/ConfirmDetailsItem/index.tsx (100%) rename src/modules/{twap => trade}/pure/ConfirmDetailsItem/styled.ts (100%) diff --git a/src/modules/twap/containers/TwapConfirmDetails/LimitPriceRow.tsx b/src/modules/trade/containers/TradeBasicConfirmDetails/LimitPriceRow.tsx similarity index 92% rename from src/modules/twap/containers/TwapConfirmDetails/LimitPriceRow.tsx rename to src/modules/trade/containers/TradeBasicConfirmDetails/LimitPriceRow.tsx index c3379880ba..11a28bf553 100644 --- a/src/modules/twap/containers/TwapConfirmDetails/LimitPriceRow.tsx +++ b/src/modules/trade/containers/TradeBasicConfirmDetails/LimitPriceRow.tsx @@ -4,7 +4,7 @@ import { Currency, Price } from '@uniswap/sdk-core' import { Nullish } from 'types' -import { ConfirmDetailsItem } from 'modules/twap/pure/ConfirmDetailsItem' +import { ConfirmDetailsItem } from 'modules/trade/pure/ConfirmDetailsItem' import { ExecutionPrice } from 'common/pure/ExecutionPrice' import { RateWrapper } from 'common/pure/RateInfo' diff --git a/src/modules/twap/containers/TwapConfirmDetails/MinReceivedRow.tsx b/src/modules/trade/containers/TradeBasicConfirmDetails/MinReceivedRow.tsx similarity index 90% rename from src/modules/twap/containers/TwapConfirmDetails/MinReceivedRow.tsx rename to src/modules/trade/containers/TradeBasicConfirmDetails/MinReceivedRow.tsx index db9b51cd40..c85fa3e6f2 100644 --- a/src/modules/twap/containers/TwapConfirmDetails/MinReceivedRow.tsx +++ b/src/modules/trade/containers/TradeBasicConfirmDetails/MinReceivedRow.tsx @@ -2,7 +2,7 @@ import { Currency, CurrencyAmount } from '@uniswap/sdk-core' import { Nullish } from 'types' -import { ConfirmDetailsItem } from 'modules/twap/pure/ConfirmDetailsItem' +import { ConfirmDetailsItem } from 'modules/trade/pure/ConfirmDetailsItem' import { FiatAmount } from 'common/pure/FiatAmount' import { TokenAmount } from 'common/pure/TokenAmount' diff --git a/src/modules/twap/containers/TwapConfirmDetails/SlippageRow.tsx b/src/modules/trade/containers/TradeBasicConfirmDetails/SlippageRow.tsx similarity index 83% rename from src/modules/twap/containers/TwapConfirmDetails/SlippageRow.tsx rename to src/modules/trade/containers/TradeBasicConfirmDetails/SlippageRow.tsx index ac9ba5ef6b..815f72f05f 100644 --- a/src/modules/twap/containers/TwapConfirmDetails/SlippageRow.tsx +++ b/src/modules/trade/containers/TradeBasicConfirmDetails/SlippageRow.tsx @@ -1,7 +1,7 @@ import { Percent } from '@uniswap/sdk-core' import { RowSlippage } from 'modules/swap/containers/Row/RowSlippage' -import { ConfirmDetailsItem } from 'modules/twap/pure/ConfirmDetailsItem' +import { ConfirmDetailsItem } from 'modules/trade/pure/ConfirmDetailsItem' export type SlippageRowProps = { slippage: Percent diff --git a/src/modules/twap/containers/TwapConfirmDetails/index.tsx b/src/modules/trade/containers/TradeBasicConfirmDetails/index.tsx similarity index 85% rename from src/modules/twap/containers/TwapConfirmDetails/index.tsx rename to src/modules/trade/containers/TradeBasicConfirmDetails/index.tsx index 0dc12bb9f0..bd618b9947 100644 --- a/src/modules/twap/containers/TwapConfirmDetails/index.tsx +++ b/src/modules/trade/containers/TradeBasicConfirmDetails/index.tsx @@ -1,4 +1,3 @@ -import { useAtomValue } from 'jotai' import { Dispatch, SetStateAction, useMemo } from 'react' import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core' @@ -8,8 +7,6 @@ import { Nullish } from 'types' import { ONE_HUNDRED_PERCENT } from 'legacy/constants/misc' import { useHigherUSDValue } from 'legacy/hooks/useStablecoinPrice' -import { twapOrderSlippage } from 'modules/twap/state/twapOrdersSettingsAtom' - import { usePrice } from 'common/hooks/usePrice' import { useRateInfoParams } from 'common/hooks/useRateInfoParams' import { CurrencyPreviewInfo } from 'common/pure/CurrencyAmountPreview' @@ -23,18 +20,18 @@ type Props = { inputCurrencyInfo: CurrencyPreviewInfo outputCurrencyInfo: CurrencyPreviewInfo isInvertedState: [boolean, Dispatch>] + slippage: Percent } -export function TwapConfirmDetails(props: Props) { - const { inputCurrencyInfo, outputCurrencyInfo, isInvertedState } = props +export function TradeBasicConfirmDetails(props: Props) { + const { inputCurrencyInfo, outputCurrencyInfo, isInvertedState, slippage } = props const rateInfoParams = useRateInfoParams(inputCurrencyInfo.amount, outputCurrencyInfo.amount) - const allowedSlippage = useAtomValue(twapOrderSlippage) // This is the minimum per part const minReceivedAmountPerPart = useMemo( - () => getSlippageAdjustedBuyAmount(outputCurrencyInfo.amount, allowedSlippage), - [allowedSlippage, outputCurrencyInfo.amount] + () => getSlippageAdjustedBuyAmount(outputCurrencyInfo.amount, slippage), + [slippage, outputCurrencyInfo.amount] ) const minReceivedUsdAmount = useHigherUSDValue(minReceivedAmountPerPart) @@ -52,7 +49,7 @@ export function TwapConfirmDetails(props: Props) { /> {/* Slippage */} - + {/* Limit Price */} diff --git a/src/modules/twap/containers/TwapConfirmDetails/styled.ts b/src/modules/trade/containers/TradeBasicConfirmDetails/styled.ts similarity index 100% rename from src/modules/twap/containers/TwapConfirmDetails/styled.ts rename to src/modules/trade/containers/TradeBasicConfirmDetails/styled.ts diff --git a/src/modules/twap/pure/ConfirmDetailsItem/index.tsx b/src/modules/trade/pure/ConfirmDetailsItem/index.tsx similarity index 100% rename from src/modules/twap/pure/ConfirmDetailsItem/index.tsx rename to src/modules/trade/pure/ConfirmDetailsItem/index.tsx diff --git a/src/modules/twap/pure/ConfirmDetailsItem/styled.ts b/src/modules/trade/pure/ConfirmDetailsItem/styled.ts similarity index 100% rename from src/modules/twap/pure/ConfirmDetailsItem/styled.ts rename to src/modules/trade/pure/ConfirmDetailsItem/styled.ts diff --git a/src/modules/twap/containers/TwapConfirmModal/index.tsx b/src/modules/twap/containers/TwapConfirmModal/index.tsx index 538e773f1d..b38eb6d924 100644 --- a/src/modules/twap/containers/TwapConfirmModal/index.tsx +++ b/src/modules/twap/containers/TwapConfirmModal/index.tsx @@ -1,10 +1,12 @@ +import { useAtomValue } from 'jotai' import { useState } from 'react' import { useAdvancedOrdersDerivedState } from 'modules/advancedOrders' import { TradeConfirmation, TradeConfirmModal, useTradeConfirmActions } from 'modules/trade' +import { TradeBasicConfirmDetails } from 'modules/trade/containers/TradeBasicConfirmDetails' import { useCreateTwapOrder } from '../../hooks/useCreateTwapOrder' -import { TwapConfirmDetails } from '../TwapConfirmDetails' +import { twapOrderSlippage } from '../../state/twapOrdersSettingsAtom' export function TwapConfirmModal() { const { @@ -15,6 +17,7 @@ export function TwapConfirmModal() { outputCurrencyFiatAmount, outputCurrencyBalance, } = useAdvancedOrdersDerivedState() + const slippage = useAtomValue(twapOrderSlippage) const tradeConfirmActions = useTradeConfirmActions() const createTwapOrder = useCreateTwapOrder() @@ -58,10 +61,11 @@ export function TwapConfirmModal() { > <> <>{/*TODO: display details*/} - diff --git a/src/modules/twap/state/twapOrdersSettingsAtom.ts b/src/modules/twap/state/twapOrdersSettingsAtom.ts index 6693c56aaf..1faeae10ef 100644 --- a/src/modules/twap/state/twapOrdersSettingsAtom.ts +++ b/src/modules/twap/state/twapOrdersSettingsAtom.ts @@ -51,11 +51,9 @@ export const updateTwapOrdersSettingsAtom = atom(null, (get, set, nextState: Par export const twapOrderSlippage = atom((get) => { const { slippageValue } = get(twapOrdersSettingsAtom) - const slippage = - slippageValue != null - ? // Multiplying on 100 to allow decimals values (e.g 0.05) - new Percent(slippageValue * 100, 10000) - : DEFAULT_TWAP_SLIPPAGE - return slippage + return slippageValue != null + ? // Multiplying on 100 to allow decimals values (e.g 0.05) + new Percent(slippageValue * 100, 10000) + : DEFAULT_TWAP_SLIPPAGE }) From 4165c5a56c0d0ed6ea1ca3ee101c4e31f45e4379 Mon Sep 17 00:00:00 2001 From: Leandro Date: Tue, 13 Jun 2023 02:23:04 -0700 Subject: [PATCH 12/67] feat(twap): twap review order - pt3 - twap fields (#2631) * refactor: moved move logic out of TradeBasicConfirmDetails * refactor: use Nullish on useStablecoinPrice hooks * refactor: extract component ReviewOrderModalAmountRow * feat: add prop to ReviewOrderModalAmountRow * refactor: rename usdAmount to fiatAmount * feat: add TwapConfirmDetails * refactor: use buyAmount with slippage from state * chore: remove comment regarding order amount * refactor: remove MinReceivedRow component in favor of ReviewOrderModalAmountRow --- src/legacy/hooks/useStablecoinPrice/index.ts | 14 +++--- .../MinReceivedRow.tsx | 29 ------------ .../TradeBasicConfirmDetails/index.tsx | 42 +++++++---------- .../trade/pure/ConfirmDetailsItem/index.tsx | 4 +- .../pure/ReviewOrderModalAmountRow/index.tsx | 39 +++++++++++++++ .../TwapConfirmModal/TwapConfirmDetails.tsx | 47 +++++++++++++++++++ .../containers/TwapConfirmModal/index.tsx | 24 ++++++++-- 7 files changed, 133 insertions(+), 66 deletions(-) delete mode 100644 src/modules/trade/containers/TradeBasicConfirmDetails/MinReceivedRow.tsx create mode 100644 src/modules/trade/pure/ReviewOrderModalAmountRow/index.tsx create mode 100644 src/modules/twap/containers/TwapConfirmModal/TwapConfirmDetails.tsx diff --git a/src/legacy/hooks/useStablecoinPrice/index.ts b/src/legacy/hooks/useStablecoinPrice/index.ts index f495fcd721..5d9832c857 100644 --- a/src/legacy/hooks/useStablecoinPrice/index.ts +++ b/src/legacy/hooks/useStablecoinPrice/index.ts @@ -1,10 +1,10 @@ import { useEffect, useMemo, useRef, useState } from 'react' -import { OrderKind } from '@cowprotocol/cow-sdk' -import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { Currency, CurrencyAmount, Price, Token /*, TradeType*/ } from '@uniswap/sdk-core' +import { OrderKind, SupportedChainId } from '@cowprotocol/cow-sdk' +import { Currency, CurrencyAmount, Price, Token } from '@uniswap/sdk-core' import { unstable_batchedUpdates as batchedUpdate } from 'react-dom' +import { Nullish } from 'types' import { DEFAULT_NETWORK_FOR_LISTS } from 'legacy/constants/lists' import { USDC, USDC_MAINNET } from 'legacy/constants/tokens' @@ -122,7 +122,7 @@ export default function useCowUsdPrice(currency?: Currency) { } interface GetPriceQuoteParams { - currencyAmount?: CurrencyAmount + currencyAmount: Nullish> error: Error | null price: Price | null } @@ -144,7 +144,7 @@ function useGetPriceQuote({ price, error, currencyAmount }: GetPriceQuoteParams) * Returns the price in USDC of the input currency from price APIs * @param currencyAmount currency to compute the USDC price of */ -export function useUSDCValue(currencyAmount?: CurrencyAmount) { +export function useUSDCValue(currencyAmount: Nullish>) { const usdcPrice = useCowUsdPrice(currencyAmount?.currency) return useGetPriceQuote({ ...usdcPrice, currencyAmount }) @@ -228,13 +228,13 @@ export function useCoingeckoUsdPrice(currency?: Currency) { return { price, error } } -export function useCoingeckoUsdValue(currencyAmount: CurrencyAmount | undefined) { +export function useCoingeckoUsdValue(currencyAmount: Nullish>) { const coingeckoUsdPrice = useCoingeckoUsdPrice(currencyAmount?.currency) return useGetPriceQuote({ ...coingeckoUsdPrice, currencyAmount }) } -export function useHigherUSDValue(currencyAmount: CurrencyAmount | undefined) { +export function useHigherUSDValue(currencyAmount: Nullish>) { const isWrapOrUnwrap = useIsWrapOrUnwrap() const checkedCurrencyAmount = isWrapOrUnwrap ? undefined : currencyAmount // if iswrap or unwrap use undefined values to not run expensive calculation diff --git a/src/modules/trade/containers/TradeBasicConfirmDetails/MinReceivedRow.tsx b/src/modules/trade/containers/TradeBasicConfirmDetails/MinReceivedRow.tsx deleted file mode 100644 index c85fa3e6f2..0000000000 --- a/src/modules/trade/containers/TradeBasicConfirmDetails/MinReceivedRow.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { Currency, CurrencyAmount } from '@uniswap/sdk-core' - -import { Nullish } from 'types' - -import { ConfirmDetailsItem } from 'modules/trade/pure/ConfirmDetailsItem' - -import { FiatAmount } from 'common/pure/FiatAmount' -import { TokenAmount } from 'common/pure/TokenAmount' - -type Props = { - amount: Nullish> - usdAmount: Nullish> -} - -export function MinReceivedRow(props: Props) { - const { amount, usdAmount } = props - - return ( - - - {usdAmount && ( - -  ( - ) - - )} - - ) -} diff --git a/src/modules/trade/containers/TradeBasicConfirmDetails/index.tsx b/src/modules/trade/containers/TradeBasicConfirmDetails/index.tsx index bd618b9947..1bd92d42a3 100644 --- a/src/modules/trade/containers/TradeBasicConfirmDetails/index.tsx +++ b/src/modules/trade/containers/TradeBasicConfirmDetails/index.tsx @@ -1,42 +1,36 @@ -import { Dispatch, SetStateAction, useMemo } from 'react' +import { Dispatch, SetStateAction } from 'react' import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core' import { Nullish } from 'types' -import { ONE_HUNDRED_PERCENT } from 'legacy/constants/misc' import { useHigherUSDValue } from 'legacy/hooks/useStablecoinPrice' import { usePrice } from 'common/hooks/usePrice' -import { useRateInfoParams } from 'common/hooks/useRateInfoParams' -import { CurrencyPreviewInfo } from 'common/pure/CurrencyAmountPreview' +import { RateInfoParams } from 'common/pure/RateInfo' import { LimitPriceRow } from './LimitPriceRow' -import { MinReceivedRow } from './MinReceivedRow' import { SlippageRow } from './SlippageRow' import * as styledEl from './styled' +import { ReviewOrderModalAmountRow } from '../../pure/ReviewOrderModalAmountRow' + type Props = { - inputCurrencyInfo: CurrencyPreviewInfo - outputCurrencyInfo: CurrencyPreviewInfo + rateInfoParams: RateInfoParams + // TODO: Add maxSendAmount when using component in swap/limit BUY orders + minReceiveAmount: Nullish> isInvertedState: [boolean, Dispatch>] slippage: Percent } export function TradeBasicConfirmDetails(props: Props) { - const { inputCurrencyInfo, outputCurrencyInfo, isInvertedState, slippage } = props - - const rateInfoParams = useRateInfoParams(inputCurrencyInfo.amount, outputCurrencyInfo.amount) + const { rateInfoParams, minReceiveAmount, isInvertedState, slippage } = props + const { inputCurrencyAmount } = rateInfoParams - // This is the minimum per part - const minReceivedAmountPerPart = useMemo( - () => getSlippageAdjustedBuyAmount(outputCurrencyInfo.amount, slippage), - [slippage, outputCurrencyInfo.amount] - ) - const minReceivedUsdAmount = useHigherUSDValue(minReceivedAmountPerPart) + const minReceivedUsdAmount = useHigherUSDValue(minReceiveAmount) // Limit price is the same for all parts - const limitPrice = usePrice(inputCurrencyInfo.amount, minReceivedAmountPerPart) + const limitPrice = usePrice(inputCurrencyAmount, minReceiveAmount) return ( @@ -55,14 +49,12 @@ export function TradeBasicConfirmDetails(props: Props) { {/* Min received */} - + ) } - -function getSlippageAdjustedBuyAmount( - buyAmount: Nullish>, - slippage: Percent -): CurrencyAmount | undefined { - return buyAmount?.multiply(ONE_HUNDRED_PERCENT.subtract(slippage)) -} diff --git a/src/modules/trade/pure/ConfirmDetailsItem/index.tsx b/src/modules/trade/pure/ConfirmDetailsItem/index.tsx index b8cbd8a3d1..6e58e83278 100644 --- a/src/modules/trade/pure/ConfirmDetailsItem/index.tsx +++ b/src/modules/trade/pure/ConfirmDetailsItem/index.tsx @@ -8,12 +8,12 @@ import { MouseoverTooltipContent } from 'legacy/components/Tooltip' import { TextWrapper } from 'modules/swap/pure/Row/styled' import { StyledInfoIcon } from 'modules/swap/pure/styled' -import { Wrapper, Row, Content } from './styled' +import { Content, Row, Wrapper } from './styled' type Props = { children: ReactNode label?: string - tooltip?: string + tooltip?: ReactNode withArrow?: boolean fiatAmount?: string } diff --git a/src/modules/trade/pure/ReviewOrderModalAmountRow/index.tsx b/src/modules/trade/pure/ReviewOrderModalAmountRow/index.tsx new file mode 100644 index 0000000000..08d82fe24f --- /dev/null +++ b/src/modules/trade/pure/ReviewOrderModalAmountRow/index.tsx @@ -0,0 +1,39 @@ +import { ReactNode } from 'react' + +import { Currency, CurrencyAmount } from '@uniswap/sdk-core' + +import { Nullish } from 'types' + +import { FiatAmount } from 'common/pure/FiatAmount' +import { TokenAmount } from 'common/pure/TokenAmount' + +import { ConfirmDetailsItem } from '../ConfirmDetailsItem' + +export type ReviewOrderAmountRowProps = { + amount: Nullish> + fiatAmount?: Nullish> + tooltip?: ReactNode + label: string + isAmountAccurate?: boolean +} + +export function ReviewOrderModalAmountRow({ + amount, + fiatAmount, + tooltip, + label, + isAmountAccurate = true, +}: ReviewOrderAmountRowProps) { + return ( + + {!isAmountAccurate && '≈ '} + + {fiatAmount && ( + +  ( + ) + + )} + + ) +} diff --git a/src/modules/twap/containers/TwapConfirmModal/TwapConfirmDetails.tsx b/src/modules/twap/containers/TwapConfirmModal/TwapConfirmDetails.tsx new file mode 100644 index 0000000000..d1b410e2ae --- /dev/null +++ b/src/modules/twap/containers/TwapConfirmModal/TwapConfirmDetails.tsx @@ -0,0 +1,47 @@ +import { ConfirmDetailsItem } from 'modules/trade/pure/ConfirmDetailsItem' +import { ReviewOrderModalAmountRow } from 'modules/trade/pure/ReviewOrderModalAmountRow' + +import { PartsState } from '../../state/partsStateAtom' + +export type TwapConfirmDetailsProps = { + startTime: number | undefined + partDuration: number | undefined + partsState: PartsState +} + +export function TwapConfirmDetails(_props: TwapConfirmDetailsProps) { + const { startTime, partDuration, partsState } = _props + const { numberOfPartsValue, inputPartAmount, inputFiatAmount, outputFiatAmount, outputPartAmount } = partsState + + const partsSuffix = ` part (1/${numberOfPartsValue})` + const amountLabelSuffix = ' amount per' + partsSuffix + + return ( +
+ TWAP order split in {numberOfPartsValue} equal parts + + + {/*TODO: implement logic for time fields*/} + + {startTime} + + + {partDuration} + + + {partDuration} + +
+ ) +} diff --git a/src/modules/twap/containers/TwapConfirmModal/index.tsx b/src/modules/twap/containers/TwapConfirmModal/index.tsx index b38eb6d924..fa8f954fac 100644 --- a/src/modules/twap/containers/TwapConfirmModal/index.tsx +++ b/src/modules/twap/containers/TwapConfirmModal/index.tsx @@ -5,7 +5,13 @@ import { useAdvancedOrdersDerivedState } from 'modules/advancedOrders' import { TradeConfirmation, TradeConfirmModal, useTradeConfirmActions } from 'modules/trade' import { TradeBasicConfirmDetails } from 'modules/trade/containers/TradeBasicConfirmDetails' +import { useRateInfoParams } from 'common/hooks/useRateInfoParams' + +import { TwapConfirmDetails } from './TwapConfirmDetails' + import { useCreateTwapOrder } from '../../hooks/useCreateTwapOrder' +import { partsStateAtom } from '../../state/partsStateAtom' +import { twapOrderAtom } from '../../state/twapOrderAtom' import { twapOrderSlippage } from '../../state/twapOrdersSettingsAtom' export function TwapConfirmModal() { @@ -17,7 +23,10 @@ export function TwapConfirmModal() { outputCurrencyFiatAmount, outputCurrencyBalance, } = useAdvancedOrdersDerivedState() + // TODO: there's some overlap with what's in each atom + const twapOrder = useAtomValue(twapOrderAtom) const slippage = useAtomValue(twapOrderSlippage) + const partsState = useAtomValue(partsStateAtom) const tradeConfirmActions = useTradeConfirmActions() const createTwapOrder = useCreateTwapOrder() @@ -48,6 +57,11 @@ export function TwapConfirmModal() { label: 'Estimated receive amount', } + const rateInfoParams = useRateInfoParams(inputCurrencyInfo.amount, outputCurrencyInfo.amount) + + // This already takes into account the full order + const minReceivedAmount = twapOrder?.buyAmount + return ( <> - <>{/*TODO: display details*/} + From 45beb4322af0ab6f2c876abed72e7f32d6c9c5be Mon Sep 17 00:00:00 2001 From: Leandro Date: Tue, 13 Jun 2023 02:33:43 -0700 Subject: [PATCH 13/67] feat(favorite-tokens): add EURe as default on Gnosis Chain (#2647) * feat: add EURe token instance * refactor: remove alias ChainId * feat: add EURe to gchain default tokens Also remove duplicated WBTC from the list an re-order favourites --- src/legacy/constants/routing.ts | 7 ++++--- src/legacy/constants/tokens.ts | 15 +++++++++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/legacy/constants/routing.ts b/src/legacy/constants/routing.ts index ac37a31292..07369e0c80 100644 --- a/src/legacy/constants/routing.ts +++ b/src/legacy/constants/routing.ts @@ -8,6 +8,7 @@ import { COW, DAI, ETH2X_FLI, + EURE_GNOSIS_CHAIN, FEI, FRAX, FXS, @@ -92,11 +93,11 @@ export const COMMON_BASES: ChainCurrencyList = { ], [SupportedChainId.GNOSIS_CHAIN]: [ USDC_GNOSIS_CHAIN, - WBTC_GNOSIS_CHAIN, COW[SupportedChainId.GNOSIS_CHAIN], - WBTC_GNOSIS_CHAIN, - WETH_GNOSIS_CHAIN, + EURE_GNOSIS_CHAIN, WRAPPED_NATIVE_CURRENCY[SupportedChainId.GNOSIS_CHAIN], + WETH_GNOSIS_CHAIN, + WBTC_GNOSIS_CHAIN, ], } diff --git a/src/legacy/constants/tokens.ts b/src/legacy/constants/tokens.ts index 201952189b..c3f9d214a7 100644 --- a/src/legacy/constants/tokens.ts +++ b/src/legacy/constants/tokens.ts @@ -1,4 +1,3 @@ -import { SupportedChainId as ChainId } from '@cowprotocol/cow-sdk' import { SupportedChainId } from '@cowprotocol/cow-sdk' import { Currency, Ether, NativeCurrency, Token, WETH9 } from '@uniswap/sdk-core' @@ -209,7 +208,7 @@ function getTrustImage(mainnetAddress: string): string { return `https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/${mainnetAddress}/logo.png` } -const WETH_ADDRESS_MAINNET = WETH9[ChainId.MAINNET].address +const WETH_ADDRESS_MAINNET = WETH9[SupportedChainId.MAINNET].address /** * vCow token @@ -303,13 +302,21 @@ export const GNO: Record = { [SupportedChainId.GOERLI]: GNO_GOERLI, } +export const EURE_GNOSIS_CHAIN = new Token( + SupportedChainId.GNOSIS_CHAIN, + '0xcb444e90d8198415266c6a2724b7900fb12fc56e', + 18, + 'EURe', + 'Monerium EUR emoney' +) + export const ADDRESS_IMAGE_OVERRIDE = { // Goerli [DAI_GOERLI.address]: getTrustImage(DAI.address), [USDC_GOERLI.address]: getTrustImage(USDC_MAINNET.address), [USDT_GOERLI.address]: getTrustImage(USDT.address), [WBTC_GOERLI.address]: getTrustImage(WBTC.address), - [WETH9[ChainId.GOERLI].address]: getTrustImage(WETH_ADDRESS_MAINNET), + [WETH9[SupportedChainId.GOERLI].address]: getTrustImage(WETH_ADDRESS_MAINNET), [V_COW_TOKEN_GOERLI.address]: vCowLogo, [COW_TOKEN_GOERLI.address]: cowLogo, [GNO_GOERLI.address]: gnoLogo, @@ -327,7 +334,7 @@ export const ADDRESS_IMAGE_OVERRIDE = { // Mainnet [V_COW_TOKEN_MAINNET.address]: vCowLogo, [COW_TOKEN_MAINNET.address]: cowLogo, - [WETH9[ChainId.MAINNET].address]: getTrustImage(WETH_ADDRESS_MAINNET), + [WETH9[SupportedChainId.MAINNET].address]: getTrustImage(WETH_ADDRESS_MAINNET), } /** From 41b1204c84e10c26be11edf2c660872bb703dfce Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Tue, 13 Jun 2023 17:41:47 +0600 Subject: [PATCH 14/67] refactor: remove account/Affilate and its deps (#2621) --- src/legacy/hooks/useFetchProfile.ts | 63 ----- .../hooks/useParseReferralQueryParam.ts | 37 --- src/legacy/hooks/useReferralLink.ts | 32 --- src/legacy/state/affiliate/actions.ts | 12 - src/legacy/state/affiliate/hooks.ts | 28 --- src/legacy/state/affiliate/reducer.ts | 36 --- src/legacy/state/affiliate/updater.tsx | 64 ----- src/pages/Account/AddressSelector.tsx | 223 ------------------ src/pages/Account/Affiliate.tsx | 213 ----------------- 9 files changed, 708 deletions(-) delete mode 100644 src/legacy/hooks/useFetchProfile.ts delete mode 100644 src/legacy/hooks/useParseReferralQueryParam.ts delete mode 100644 src/legacy/hooks/useReferralLink.ts delete mode 100644 src/legacy/state/affiliate/actions.ts delete mode 100644 src/legacy/state/affiliate/hooks.ts delete mode 100644 src/legacy/state/affiliate/reducer.ts delete mode 100644 src/legacy/state/affiliate/updater.tsx delete mode 100644 src/pages/Account/AddressSelector.tsx delete mode 100644 src/pages/Account/Affiliate.tsx diff --git a/src/legacy/hooks/useFetchProfile.ts b/src/legacy/hooks/useFetchProfile.ts deleted file mode 100644 index 0c323ae661..0000000000 --- a/src/legacy/hooks/useFetchProfile.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { useEffect, useState } from 'react' - -import useIsWindowVisible from 'legacy/hooks/useIsWindowVisible' - -import { useWalletInfo } from 'modules/wallet' - -import { getProfileData } from 'api/gnosisProtocol' -import { ProfileData } from 'api/gnosisProtocol/api' - -type FetchProfileState = { - profileData: ProfileData | null - error: string - isLoading: boolean -} - -const emptyState: FetchProfileState = { - profileData: null, - error: '', - isLoading: false, -} - -const FETCH_INTERVAL_IN_MINUTES = 5 - -export default function useFetchProfile(): FetchProfileState { - const { account, chainId } = useWalletInfo() - const [profile, setProfile] = useState(emptyState) - const isWindowVisible = useIsWindowVisible() - - useEffect(() => { - if (!isWindowVisible) { - console.debug('[useFetchProfile] No need to fetch profile info') - return - } - - async function fetchAndSetProfileData() { - console.debug('[useFetchProfile] Fetching profile info') - try { - if (chainId && account) { - setProfile((profile) => ({ ...profile, isLoading: true, error: '' })) - const profileData = await getProfileData(chainId, account) - setProfile((profile) => ({ ...profile, isLoading: false, profileData })) - } else { - setProfile(emptyState) - } - } catch (error: any) { - console.error(error) - setProfile((profile) => ({ - ...profile, - isLoading: false, - error: 'There was an error while fetching the profile data', - })) - } - } - - console.debug('[useFetchProfile] No need to fetch profile info') - const intervalId = setInterval(fetchAndSetProfileData, FETCH_INTERVAL_IN_MINUTES * 60_000) - - fetchAndSetProfileData() - return () => clearInterval(intervalId) - }, [account, chainId, isWindowVisible]) - - return profile -} diff --git a/src/legacy/hooks/useParseReferralQueryParam.ts b/src/legacy/hooks/useParseReferralQueryParam.ts deleted file mode 100644 index 3e1a9e3a32..0000000000 --- a/src/legacy/hooks/useParseReferralQueryParam.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { useMemo, useState } from 'react' - -import useENS from 'legacy/hooks/useENS' -import useParsedQueryString from 'legacy/hooks/useParsedQueryString' -import { REFERRAL_QUERY_PARAM } from 'legacy/hooks/useReferralLink' -import { isAddress } from 'legacy/utils' - -type ReferralQueryValue = { - value: string - isValid: boolean -} | null - -/** - * Returns the parsed referral address from the query parameters if its a valid address - */ -export default function useParseReferralQueryParam(): ReferralQueryValue { - const parsedQs = useParsedQueryString() - const referralAddress = parsedQs[REFERRAL_QUERY_PARAM] as string - const result = useENS(referralAddress) - const [loading, setLoading] = useState(isAddress(referralAddress) === false) // this is a hack to force a initial loading state to true in case of referralAddress is a ens name because the useENS hook returns loading as false when initialized - - return useMemo(() => { - if (loading || result.loading || !referralAddress) { - if (result.loading) { - setLoading(false) - } - return null - } - - if (result.address) { - return { value: result.address, isValid: true } - } - - console.warn('Invalid referral address') - return { value: '', isValid: false } - }, [result.loading, result.address, referralAddress, loading]) -} diff --git a/src/legacy/hooks/useReferralLink.ts b/src/legacy/hooks/useReferralLink.ts deleted file mode 100644 index 8f01fbbd01..0000000000 --- a/src/legacy/hooks/useReferralLink.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { useWalletInfo } from 'modules/wallet' - -export const REFERRAL_QUERY_PARAM = 'referral' - -export type ReferralLink = { - link: string - prefix: string - address: string -} - -export function getReferralLink(walletAddress: string): ReferralLink { - const prefix = `${window.location.origin}/#/?${REFERRAL_QUERY_PARAM}=` - - return { - link: prefix + walletAddress, - prefix, - address: walletAddress, - } -} - -/** - * Returns the referral link with the connected wallet address - */ -export default function useReferralLink(): ReferralLink | null { - const { account } = useWalletInfo() - - if (account) { - return getReferralLink(account) - } - - return null -} diff --git a/src/legacy/state/affiliate/actions.ts b/src/legacy/state/affiliate/actions.ts deleted file mode 100644 index 97bac9de10..0000000000 --- a/src/legacy/state/affiliate/actions.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { createAction } from '@reduxjs/toolkit' - -export const updateReferralAddress = createAction<{ - value: string - isValid: boolean -} | null>('affiliate/updateReferralAddress') - -export const setReferralAddressActive = createAction('affiliate/setReferralAddressActive') - -export const updateAddress = createAction('affiliate/updateAddress') - -export const dismissNotification = createAction('affiliate/dismissNotification') diff --git a/src/legacy/state/affiliate/hooks.ts b/src/legacy/state/affiliate/hooks.ts deleted file mode 100644 index b32054f398..0000000000 --- a/src/legacy/state/affiliate/hooks.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { useCallback } from 'react' - -import { setReferralAddressActive, updateReferralAddress } from 'legacy/state/affiliate/actions' -import { useAppDispatch, useAppSelector } from 'legacy/state/hooks' - -export function useReferralAddress() { - return useAppSelector((state: any) => state.affiliate.referralAddress) -} - -export function useAffiliateAddress() { - return useAppSelector((state: any) => state.affiliate.address) -} - -export function useResetReferralAddress() { - const dispatch = useAppDispatch() - - return useCallback(() => dispatch(updateReferralAddress(null)), [dispatch]) -} - -export function useSetReferralAddressActive() { - const dispatch = useAppDispatch() - - return useCallback((isActive: boolean) => dispatch(setReferralAddressActive(isActive)), [dispatch]) -} - -export function useIsNotificationClosed(id?: string): boolean | null { - return useAppSelector((state: any) => (id ? state.affiliate.isNotificationClosed?.[id] ?? false : null)) -} diff --git a/src/legacy/state/affiliate/reducer.ts b/src/legacy/state/affiliate/reducer.ts deleted file mode 100644 index 027e7ac2c7..0000000000 --- a/src/legacy/state/affiliate/reducer.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { createReducer } from '@reduxjs/toolkit' - -import { dismissNotification, setReferralAddressActive, updateAddress, updateReferralAddress } from './actions' - -export interface AffiliateState { - referralAddress?: { - value: string - isValid: boolean - isActive?: boolean - } - isNotificationClosed?: { - [key: string]: boolean - } - address?: string // this can be an ENS name or an address -} - -export const initialState: AffiliateState = {} - -export default createReducer(initialState, (builder) => - builder - .addCase(updateReferralAddress, (state, action) => { - state.referralAddress = action.payload ? { ...state.referralAddress, ...action.payload } : undefined - }) - .addCase(setReferralAddressActive, (state, action) => { - if (state.referralAddress) { - state.referralAddress.isActive = action.payload - } - }) - .addCase(updateAddress, (state, action) => { - state.address = action.payload - }) - .addCase(dismissNotification, (state, action) => { - state.isNotificationClosed = state.isNotificationClosed ?? {} - state.isNotificationClosed[action.payload] = true - }) -) diff --git a/src/legacy/state/affiliate/updater.tsx b/src/legacy/state/affiliate/updater.tsx deleted file mode 100644 index 3a42d48d58..0000000000 --- a/src/legacy/state/affiliate/updater.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { useCallback, useEffect } from 'react' - -import useParseReferralQueryParam from 'legacy/hooks/useParseReferralQueryParam' -import { dismissNotification, updateReferralAddress } from 'legacy/state/affiliate/actions' -import { useReferralAddress } from 'legacy/state/affiliate/hooks' -import { useAppDispatch } from 'legacy/state/hooks' - -import { AffiliateState } from './reducer' - -export function ReferralLinkUpdater() { - const dispatch = useAppDispatch() - const referralAddressParam = useParseReferralQueryParam() - const referralAddress = useReferralAddress() - - useEffect(() => { - if (!referralAddressParam && !referralAddress?.isValid) { - dispatch(updateReferralAddress(null)) - } else if (referralAddressParam) { - dispatch(updateReferralAddress(referralAddressParam)) - } - }, [referralAddressParam, referralAddress?.isValid, dispatch]) - - return null -} - -const AFFILIATE_LS_KEY = 'redux_localstorage_simple_affiliate' - -// This handles cross tab state sync so the Affiliate banner is shown/hidden -export function StorageUpdater() { - const dispatch = useAppDispatch() - - const handler = useCallback( - (e: StorageEvent) => { - if (e.key === AFFILIATE_LS_KEY && e.newValue) { - const parsed = JSON.parse(e.newValue) as AffiliateState - for (const id in parsed.isNotificationClosed) { - dispatch(dismissNotification(id)) - } - - if (parsed.referralAddress) { - dispatch(updateReferralAddress(parsed.referralAddress)) - } - } - }, - [dispatch] - ) - - useEffect(() => { - window.addEventListener('storage', handler) - - return () => window.removeEventListener('storage', handler) - }, [handler]) - - return null -} - -export default function AffiliateUpdaters() { - return ( - <> - - - - ) -} diff --git a/src/pages/Account/AddressSelector.tsx b/src/pages/Account/AddressSelector.tsx deleted file mode 100644 index f5624842ad..0000000000 --- a/src/pages/Account/AddressSelector.tsx +++ /dev/null @@ -1,223 +0,0 @@ -import { useCallback, useEffect, useRef, useState } from 'react' - -import { useWeb3React } from '@web3-react/core' - -import { Check, ChevronDown } from 'react-feather' -import styled, { css } from 'styled-components/macro' - -import { useOnClickOutside } from 'legacy/hooks/useOnClickOutside' -import { updateAddress } from 'legacy/state/affiliate/actions' -import { useAffiliateAddress } from 'legacy/state/affiliate/hooks' -import { useAppDispatch } from 'legacy/state/hooks' -import { isAddress, shortenAddress } from 'legacy/utils' - -import { useWalletInfo } from 'modules/wallet' - -import { ensNames } from 'pages/Account/ens' - -type AddressSelectorProps = { - address: string -} - -export default function AddressSelector(props: AddressSelectorProps) { - const { address } = props - const dispatch = useAppDispatch() - const selectedAddress = useAffiliateAddress() - const { provider } = useWeb3React() - const { chainId } = useWalletInfo() - const [open, setOpen] = useState(false) - const [items, setItems] = useState([address]) - const toggle = useCallback(() => setOpen((open) => !open), []) - const node = useRef(null) - useOnClickOutside(node, open ? toggle : undefined) - - const tryShortenAddress = useCallback((item?: string) => { - if (!item) { - return item - } - - try { - return shortenAddress(item) - } catch (error: any) { - return item - } - }, []) - - const handleSelectItem = useCallback( - (item: string) => { - dispatch(updateAddress(item)) - toggle() - }, - [dispatch, toggle] - ) - - useEffect(() => { - if (!chainId) { - return - } - - ensNames(chainId, address).then((response) => { - if ('error' in response) { - console.info(response.error) - setItems([address]) - return - } - setItems([...response, address]) - }) - }, [address, chainId]) - - useEffect(() => { - // if the user switches accounts, reset the selected address - const switchedAccounts = isAddress(selectedAddress) && selectedAddress !== address - if (switchedAccounts || !selectedAddress) { - dispatch(updateAddress(address)) - return - } - - // the selected address is a ens name, verify that resolves to the correct address - const verify = async () => { - const resolvedAddress = await provider?.resolveName(selectedAddress) - if (resolvedAddress !== address) { - dispatch(updateAddress(address)) - } - } - - verify() - }, [selectedAddress, address, dispatch, provider]) - - return ( - <> - {items.length === 1 ? ( - {tryShortenAddress(address)} - ) : ( - - - {tryShortenAddress(selectedAddress)} - - - {open && ( - - {items.map((item) => ( - handleSelectItem(item)}> - {' '} - {tryShortenAddress(item)} - - ))} - - )} - - )} - - ) -} - -const Wrapper = styled.div` - position: relative; - display: inline; - margin-right: 0.4rem; - ${({ theme }) => theme.mediaWidth.upToMedium` - justify-self: end; - `}; - - ${({ theme }) => theme.mediaWidth.upToSmall` - margin: 0 0.5rem 0 0; - width: initial; - text-overflow: ellipsis; - flex-shrink: 1; - `}; -` - -const MenuFlyout = styled.span` - background-color: ${({ theme }) => theme.bg4}; - border: 1px solid ${({ theme }) => theme.bg0}; - - box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), - 0px 24px 32px rgba(0, 0, 0, 0.01); - border-radius: 12px; - padding: 0.3rem; - display: flex; - flex-direction: column; - font-size: 1rem; - position: absolute; - left: 0; - top: 1.75rem; - z-index: 100; - min-width: 350px; - ${({ theme }) => theme.mediaWidth.upToMedium`; - min-width: 145px - `}; - - > { - padding: 12px; - } -` -const MenuItem = css` - align-items: center; - background-color: transparent; - border-radius: 12px; - color: ${({ theme }) => theme.text2}; - cursor: pointer; - display: flex; - flex: 1; - flex-direction: row; - font-size: 16px; - font-weight: 400; - justify-content: start; - :hover { - text-decoration: none; - } -` - -export const AddressInfo = styled.button` - align-items: center; - background-color: ${({ theme }) => theme.bg4}; - border-radius: 12px; - border: 1px solid ${({ theme }) => theme.bg0}; - color: ${({ theme }) => theme.text1}; - display: inline-flex; - flex-direction: row; - font-weight: 700; - font-size: 12px; - height: 100%; - margin: 0 0.4rem; - padding: 0.2rem 0.4rem; - - :hover, - :focus { - cursor: pointer; - outline: none; - border: 1px solid ${({ theme }) => theme.bg3}; - } -` -const ButtonMenuItem = styled.button<{ $selected?: boolean }>` - ${MenuItem} - cursor: ${({ $selected }) => ($selected ? 'initial' : 'pointer')}; - border: none; - box-shadow: none; - color: ${({ theme, $selected }) => ($selected ? theme.text2 : theme.text1)}; - background-color: ${({ theme, $selected }) => $selected && theme.primary1}; - outline: none; - font-weight: ${({ $selected }) => ($selected ? '700' : '500')}; - font-size: 12px; - text-transform: lowercase; - padding: 6px 10px 6px 5px; - - ${({ $selected }) => $selected && `margin: 3px 0;`} - - > ${AddressInfo} { - margin: 0 auto 0 8px; - } - - &:hover { - color: ${({ theme, $selected }) => !$selected && theme.text1}; - background: ${({ theme, $selected }) => !$selected && theme.bg4}; - } - - transition: background 0.13s ease-in-out; -` - -const GreenCheck = styled(Check)<{ $visible: boolean }>` - margin-right: 5px; - color: ${({ theme }) => theme.success}; - visibility: ${({ $visible }) => ($visible ? 'visible' : 'hidden')}; -` diff --git a/src/pages/Account/Affiliate.tsx b/src/pages/Account/Affiliate.tsx deleted file mode 100644 index 233e97fc6b..0000000000 --- a/src/pages/Account/Affiliate.tsx +++ /dev/null @@ -1,213 +0,0 @@ -import { SupportedChainId as ChainId } from '@cowprotocol/cow-sdk' - -import { RefreshCcw } from 'react-feather' - -import { Txt } from 'legacy/assets/styles/styled' -import Copy from 'legacy/components/Copy/CopyMod' -import NotificationBanner from 'legacy/components/NotificationBanner' -import { MouseoverTooltipContent } from 'legacy/components/Tooltip' -import useFetchProfile from 'legacy/hooks/useFetchProfile' -import useReferralLink from 'legacy/hooks/useReferralLink' -import useTimeAgo from 'legacy/hooks/useTimeAgo' -import { useAffiliateAddress } from 'legacy/state/affiliate/hooks' -import { shortenAddress } from 'legacy/utils' -import { getExplorerAddressLink } from 'legacy/utils/explorer' - -import { SectionTitle } from 'modules/application/pure/Page' -import { useWalletInfo, Web3Status } from 'modules/wallet' - -import { useHasOrders } from 'api/gnosisProtocol/hooks' -import { HelpCircle } from 'common/pure/HelpCircle' - -import AddressSelector from './AddressSelector' -import { - Wrapper, - GridWrap, - CardHead, - StyledContainer, - ExtLink, - ChildWrapper, - ItemTitle, - FlexWrap, - FlexCol, - Loader, - TimeFormatted, -} from './styled' -import { formatInt, formatDecimal } from './utils' - -const NotificationMessages = ({ error, chainId }: { error?: unknown; chainId: ChainId }) => ( - <> - {error && ( - - There was an error loading your profile data. Please try again later. - - )} - {chainId && chainId !== ChainId.MAINNET && ( - - Affiliate data is only available for Ethereum. Please change the network to see it. - - )} - -) - -export default function Affiliate() { - const referralLink = useReferralLink() - const { account, chainId = ChainId.MAINNET /* , error */ } = useWalletInfo() - const { profileData, isLoading } = useFetchProfile() - const lastUpdated = useTimeAgo(profileData?.lastUpdated) - const hasOrders = useHasOrders(account) - const selectedAddress = useAffiliateAddress() - - const isTradesTooltipVisible = account && chainId === ChainId.MAINNET && !!profileData?.totalTrades - - return ( - - - - Affiliate Program - {account && ( - - - - - Last updated: - {!lastUpdated ? ( - '-' - ) : ( - } wrap> - - {lastUpdated} - - - )} - - - - - {hasOrders && ( - - View all orders ↗ - - )} - - - )} - - - - - - - Your referral url - - - {referralLink ? ( - <> - - {referralLink.prefix} - {chainId === ChainId.GNOSIS_CHAIN ? ( - {shortenAddress(referralLink.address)} - ) : ( - - )} - - - - - - - ) : ( - '-' - )} - - - - - - Trades - - - - - - - - 🧑‍🌾 - - - {formatInt(profileData?.totalTrades)} - - - - Total trades - {isTradesTooltipVisible && ( - - - - )} - - - - - - 💰 - - - {formatDecimal(profileData?.tradeVolumeUsd)} - - - Total traded volume - - - - - - - Referrals - - - - - - - - 🤝 - - - {formatInt(profileData?.totalReferrals)} - - - Total referrals - - - - - 💸 - - - {formatDecimal(profileData?.referralVolumeUsd)} - - - Referrals volume - - - - - - {!account && } - - - ) -} From 5aba7cb5d12e777cab15c405ae21d5d6e9e4d506 Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Tue, 13 Jun 2023 17:48:31 +0600 Subject: [PATCH 15/67] feat(twap): add ComposableCowInfo to Order model (#2622) --- src/api/gnosisProtocol/hooks.ts | 48 ++++++++++++++----- src/common/types.ts | 18 +++++++ src/legacy/state/orders/actions.ts | 7 +++ .../state/orders/updaters/GpOrdersUpdater.ts | 16 +++++-- src/modules/twap/state/twapOrdersListAtom.ts | 16 +++++-- src/modules/twap/types.ts | 2 +- .../twap/utils/buildTwapOrdersItems.ts | 4 +- src/modules/twap/utils/emulateTwapAsOrder.ts | 4 +- src/utils/deepEqual.ts | 25 ++++++++++ 9 files changed, 114 insertions(+), 26 deletions(-) create mode 100644 src/common/types.ts create mode 100644 src/utils/deepEqual.ts diff --git a/src/api/gnosisProtocol/hooks.ts b/src/api/gnosisProtocol/hooks.ts index 08c2aa50f8..b786698965 100644 --- a/src/api/gnosisProtocol/hooks.ts +++ b/src/api/gnosisProtocol/hooks.ts @@ -7,12 +7,15 @@ import useSWR from 'swr' import { AMOUNT_OF_ORDERS_TO_FETCH } from 'legacy/constants' import { isBarnBackendEnv } from 'legacy/utils/environments' +import { isTruthy } from 'legacy/utils/misc' import { supportedChainId } from 'legacy/utils/supportedChainId' import { emulatedTwapOrdersAtom } from 'modules/twap/state/twapOrdersListAtom' import { TwapPartOrderItem, twapPartOrdersListAtom } from 'modules/twap/state/twapPartOrdersAtom' import { useWalletInfo } from 'modules/wallet' +import { OrderWithComposableCowInfo } from 'common/types' + import { getOrders } from './api' /** @@ -27,11 +30,10 @@ import { getOrders } from './api' * For non-PROD environment we do two requests: barn and prod * For PROD environment we do only one: prod. It depends on isBarnBackendEnv */ -export function useGpOrders(account?: string | null, refreshInterval?: number): EnrichedOrder[] { +export function useGpOrders(account?: string | null, refreshInterval?: number): OrderWithComposableCowInfo[] { const { chainId: _chainId } = useWalletInfo() const chainId = supportedChainId(_chainId) const emulatedTwapOrders = useAtomValue(emulatedTwapOrdersAtom) - const twapParticleOrders = useAtomValue(twapPartOrdersListAtom) const requestParams = useMemo(() => { return account ? { owner: account, limit: AMOUNT_OF_ORDERS_TO_FETCH } : null @@ -65,8 +67,24 @@ export function useGpOrders(account?: string | null, refreshInterval?: number): return isBarnBackendEnv ? loadedProdOrders : currentEnvOrders }, [currentEnvOrders, loadedProdOrders]) - // Take only orders are connected to TWAP orders - const twapRelatedOrders = useMemo(() => { + const twapChildOrders = useTwapChildOrders(prodOrders) + + const regularOrders: OrderWithComposableCowInfo[] = useMemo(() => { + if (!currentEnvOrders) return [] + + return currentEnvOrders.map((order) => ({ order })) + }, [currentEnvOrders]) + + return useMemo(() => { + return [...regularOrders, ...emulatedTwapOrders, ...twapChildOrders] + }, [regularOrders, emulatedTwapOrders, twapChildOrders]) +} + +// Take only orders are connected to TWAP orders +function useTwapChildOrders(prodOrders: EnrichedOrder[] | undefined): OrderWithComposableCowInfo[] { + const twapParticleOrders = useAtomValue(twapPartOrdersListAtom) + + return useMemo(() => { if (!prodOrders) return [] const particleOrdersMap = twapParticleOrders.reduce<{ [uid: string]: TwapPartOrderItem }>((acc, val) => { @@ -75,15 +93,21 @@ export function useGpOrders(account?: string | null, refreshInterval?: number): return acc }, {}) - return prodOrders.filter((order) => { - return particleOrdersMap[order.uid] - }) - }, [twapParticleOrders, prodOrders]) + return prodOrders + .map((order) => { + const particleOrder = particleOrdersMap[order.uid] - // Add only TWAP-related orders to the common list of orders - return useMemo(() => { - return [...(currentEnvOrders || []), ...emulatedTwapOrders, ...twapRelatedOrders] - }, [currentEnvOrders, emulatedTwapOrders, twapRelatedOrders]) + if (!particleOrder) return null + + return { + order, + composableCowInfo: { + parentId: particleOrder.twapOrderId, + }, + } as OrderWithComposableCowInfo + }) + .filter(isTruthy) + }, [twapParticleOrders, prodOrders]) } export function useHasOrders(account?: string | null): boolean | undefined { diff --git a/src/common/types.ts b/src/common/types.ts new file mode 100644 index 0000000000..435fe19ee3 --- /dev/null +++ b/src/common/types.ts @@ -0,0 +1,18 @@ +import { EnrichedOrder } from '@cowprotocol/cow-sdk' + +/** + * https://github.com/rndlabs/composable-cow/blob/main/src/ComposableCoW.sol + * Information about ComposableCoW conditional orders + * + * id - this parameter is specified when it's a conditional (parent) order + * parentId - this parameter is specified when it's a discrete (child) order + */ +export type ComposableCowInfo = { + id?: string + parentId?: string +} + +export type OrderWithComposableCowInfo = { + order: EnrichedOrder + composableCowInfo?: ComposableCowInfo +} diff --git a/src/legacy/state/orders/actions.ts b/src/legacy/state/orders/actions.ts index 8279eddafe..4c58edb276 100644 --- a/src/legacy/state/orders/actions.ts +++ b/src/legacy/state/orders/actions.ts @@ -8,6 +8,8 @@ import { createAction } from '@reduxjs/toolkit' import { SerializedToken } from 'legacy/state/user/types' +import { ComposableCowInfo } from 'common/types' + export enum OrderStatus { PENDING = 'pending', PRESIGNATURE_PENDING = 'presignaturePending', @@ -75,6 +77,11 @@ export interface BaseOrder extends Omit { * The order data is local to the device where the order was initiated */ isHidden?: boolean + + /** + * ComposableCoW-specific info + */ + composableCowInfo?: ComposableCowInfo } /** diff --git a/src/legacy/state/orders/updaters/GpOrdersUpdater.ts b/src/legacy/state/orders/updaters/GpOrdersUpdater.ts index 69a959dda9..3caaea6d02 100644 --- a/src/legacy/state/orders/updaters/GpOrdersUpdater.ts +++ b/src/legacy/state/orders/updaters/GpOrdersUpdater.ts @@ -1,6 +1,6 @@ import { useCallback, useEffect, useMemo, useRef } from 'react' -import { EnrichedOrder, EthflowData, OrderClass, SupportedChainId as ChainId } from '@cowprotocol/cow-sdk' +import { EthflowData, OrderClass, SupportedChainId as ChainId } from '@cowprotocol/cow-sdk' import { getAddress } from '@ethersproject/address' import { Token } from '@uniswap/sdk-core' @@ -16,6 +16,7 @@ import { supportedChainId } from 'legacy/utils/supportedChainId' import { useWalletInfo } from 'modules/wallet' import { useGpOrders } from 'api/gnosisProtocol/hooks' +import { OrderWithComposableCowInfo } from 'common/types' function _getTokenFromMapping( address: string, @@ -41,7 +42,7 @@ const statusMapping: Record = { } function _transformGpOrderToStoreOrder( - order: EnrichedOrder, + { order, composableCowInfo }: OrderWithComposableCowInfo, chainId: ChainId, allTokens: { [address: string]: Token | null } ): Order | undefined { @@ -99,6 +100,7 @@ function _transformGpOrderToStoreOrder( refundHash: ethflowData?.refundTxHash || undefined, buyTokenBalance: order.buyTokenBalance, sellTokenBalance: order.sellTokenBalance, + composableCowInfo, } // The function to compute the summary needs the Order instance to exist already @@ -126,14 +128,14 @@ function _getInputToken( } function _getMissingTokensAddresses( - orders: EnrichedOrder[], + orders: OrderWithComposableCowInfo[], tokens: Record, chainId: ChainId ): string[] { const tokensToFetch = new Set() // Find out which tokens are not yet loaded in the UI - orders.forEach(({ sellToken, buyToken }) => { + orders.forEach(({ order: { sellToken, buyToken } }) => { if (!_getTokenFromMapping(sellToken, chainId, tokens)) tokensToFetch.add(sellToken) if (!_getTokenFromMapping(buyToken, chainId, tokens)) tokensToFetch.add(buyToken) }) @@ -160,7 +162,11 @@ async function _fetchTokens( }, {}) } -function _filterOrders(orders: EnrichedOrder[], tokens: Record, chainId: ChainId): Order[] { +function _filterOrders( + orders: OrderWithComposableCowInfo[], + tokens: Record, + chainId: ChainId +): Order[] { return orders.reduce((acc, order) => { const storeOrder = _transformGpOrderToStoreOrder(order, chainId, tokens) if (storeOrder) { diff --git a/src/modules/twap/state/twapOrdersListAtom.ts b/src/modules/twap/state/twapOrdersListAtom.ts index 8206a3aded..a2519af0c5 100644 --- a/src/modules/twap/state/twapOrdersListAtom.ts +++ b/src/modules/twap/state/twapOrdersListAtom.ts @@ -1,17 +1,18 @@ import { atom } from 'jotai' import { atomWithStorage } from 'jotai/utils' -import deepEqual from 'fast-deep-equal' - import { tokensByAddressAtom } from 'modules/tokensList/state/tokensListAtom' import { walletInfoAtom } from 'modules/wallet/api/state' +import { OrderWithComposableCowInfo } from 'common/types' +import { deepEqual } from 'utils/deepEqual' + import { TwapOrderItem } from '../types' import { emulateTwapAsOrder } from '../utils/emulateTwapAsOrder' export type TwapOrdersList = { [key: string]: TwapOrderItem } -export const twapOrdersListAtom = atomWithStorage('twap-orders-list:v2', {}) +export const twapOrdersListAtom = atomWithStorage('twap-orders-list:v3', {}) export const updateTwapOrdersListAtom = atom(null, (get, set, nextState: TwapOrdersList) => { const currentState = get(twapOrdersListAtom) @@ -31,5 +32,12 @@ export const emulatedTwapOrdersAtom = atom((get) => { return orders .filter((order) => order.chainId === chainId && order.safeAddress.toLowerCase() === accountLowerCase) - .map((order) => emulateTwapAsOrder(tokens, order)) + .map((order) => { + return { + order: emulateTwapAsOrder(tokens, order), + composableCowInfo: { + uid: order.id, + }, + } + }) as OrderWithComposableCowInfo[] }) diff --git a/src/modules/twap/types.ts b/src/modules/twap/types.ts index 8349aa348f..b974ede035 100644 --- a/src/modules/twap/types.ts +++ b/src/modules/twap/types.ts @@ -42,7 +42,7 @@ export interface TwapOrderItem { status: TwapOrderStatus chainId: SupportedChainId safeAddress: string - hash: string + id: string submissionDate: string } diff --git a/src/modules/twap/utils/buildTwapOrdersItems.ts b/src/modules/twap/utils/buildTwapOrdersItems.ts index 6f95c30d40..7445c6bed3 100644 --- a/src/modules/twap/utils/buildTwapOrdersItems.ts +++ b/src/modules/twap/utils/buildTwapOrdersItems.ts @@ -27,7 +27,7 @@ function getTwapOrderItem( chainId: SupportedChainId, safeAddress: string, safeData: TwapOrdersSafeData, - hash: string, + id: string, authorized: boolean, discreteOrder: Order | undefined ): TwapOrderItem { @@ -41,7 +41,7 @@ function getTwapOrderItem( status, chainId, safeAddress, - hash, + id, submissionDate, } } diff --git a/src/modules/twap/utils/emulateTwapAsOrder.ts b/src/modules/twap/utils/emulateTwapAsOrder.ts index 6f3d7263ab..bc045b7856 100644 --- a/src/modules/twap/utils/emulateTwapAsOrder.ts +++ b/src/modules/twap/utils/emulateTwapAsOrder.ts @@ -14,7 +14,7 @@ const statusMap: Record = { } export function emulateTwapAsOrder(tokens: TokensByAddress, item: TwapOrderItem): EnrichedOrder { - const { safeAddress, hash, status } = item + const { safeAddress, id, status } = item const { sellToken, buyToken, partSellAmount, minPartLimit, n, t } = item.order const numOfParts = BigInt(n) const sellAmount = BigInt(partSellAmount) * numOfParts @@ -29,7 +29,7 @@ export function emulateTwapAsOrder(tokens: TokensByAddress, item: TwapOrderItem) sellToken, buyToken, validTo: Math.ceil(expirationTime.getTime() / 1000), - uid: hash, + uid: id, sellAmount: sellAmount.toString(), buyAmount: buyAmount.toString(), kind: OrderKind.SELL, diff --git a/src/utils/deepEqual.ts b/src/utils/deepEqual.ts new file mode 100644 index 0000000000..1200fc15a9 --- /dev/null +++ b/src/utils/deepEqual.ts @@ -0,0 +1,25 @@ +// https://dmitripavlutin.com/how-to-compare-objects-in-javascript/ +export function deepEqual(object1: any, object2: typeof object1): boolean { + const keys1 = Object.keys(object1) + const keys2 = Object.keys(object2) + + if (keys1.length !== keys2.length) { + return false + } + + for (const key of keys1) { + const val1 = object1[key] + const val2 = object2[key] + const areObjects = isObject(val1) && isObject(val2) + + if ((areObjects && !deepEqual(val1, val2)) || (!areObjects && val1 !== val2)) { + return false + } + } + + return true +} + +function isObject(object: unknown): object is Object { + return object != null && typeof object === 'object' +} From 55830a259a7d5289331c596fd05a63dde1f0b056 Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Tue, 13 Jun 2023 17:55:01 +0600 Subject: [PATCH 16/67] feat(twap): twap orders cancellation (#2623) --- src/abis/ComposableCoW.json | 13 ++++++ src/api/gnosisProtocol/hooks.ts | 4 +- .../useCancelOrder/onChainCancellation.ts | 30 +++++++++---- .../useGetOnChainCancellation.ts | 14 +++++-- .../useSendOnChainCancellation.test.ts | 7 ++-- .../useSendOnChainCancellation.ts | 26 ++++++------ .../OrdersTableContainer/OrderRow/index.tsx | 2 +- src/modules/twap/hooks/useCancelTwapOrder.ts | 42 +++++++++++++++++++ .../twap/services/cancelTwapOrderTxs.ts | 42 +++++++++++++++++++ src/modules/twap/state/twapOrdersListAtom.ts | 8 ++-- .../twap/updaters/TwapOrdersUpdater.tsx | 6 +-- src/modules/twap/utils/emulateTwapAsOrder.ts | 2 +- 12 files changed, 158 insertions(+), 38 deletions(-) create mode 100644 src/modules/twap/hooks/useCancelTwapOrder.ts create mode 100644 src/modules/twap/services/cancelTwapOrderTxs.ts diff --git a/src/abis/ComposableCoW.json b/src/abis/ComposableCoW.json index f86d84a355..3a82a38845 100644 --- a/src/abis/ComposableCoW.json +++ b/src/abis/ComposableCoW.json @@ -161,5 +161,18 @@ ], "stateMutability": "view", "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "singleOrderHash", + "type": "bytes32" + } + ], + "name": "remove", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" } ] diff --git a/src/api/gnosisProtocol/hooks.ts b/src/api/gnosisProtocol/hooks.ts index b786698965..7c4c78a3a5 100644 --- a/src/api/gnosisProtocol/hooks.ts +++ b/src/api/gnosisProtocol/hooks.ts @@ -93,7 +93,7 @@ function useTwapChildOrders(prodOrders: EnrichedOrder[] | undefined): OrderWithC return acc }, {}) - return prodOrders + const orderWithComposableCowInfo: OrderWithComposableCowInfo[] = prodOrders .map((order) => { const particleOrder = particleOrdersMap[order.uid] @@ -107,6 +107,8 @@ function useTwapChildOrders(prodOrders: EnrichedOrder[] | undefined): OrderWithC } as OrderWithComposableCowInfo }) .filter(isTruthy) + + return orderWithComposableCowInfo }, [twapParticleOrders, prodOrders]) } diff --git a/src/common/hooks/useCancelOrder/onChainCancellation.ts b/src/common/hooks/useCancelOrder/onChainCancellation.ts index ed6dbace64..ce0ab54fb0 100644 --- a/src/common/hooks/useCancelOrder/onChainCancellation.ts +++ b/src/common/hooks/useCancelOrder/onChainCancellation.ts @@ -1,21 +1,21 @@ import { BigNumber } from '@ethersproject/bignumber' -import { ContractTransaction } from '@ethersproject/contracts' import { Order } from 'legacy/state/orders/actions' import { calculateGasMargin } from 'legacy/utils/calculateGasMargin' -import { ETHFLOW_GAS_LIMIT_DEFAULT } from 'modules/swap/services/ethFlow/const' import { logTradeFlowError } from 'modules/trade/utils/logger' import { GPv2Settlement } from 'abis/types' import { CoWSwapEthFlow } from 'abis/types/ethflow' +// Fallback If we couldn't estimate gas for on-chain cancellation +const CANCELLATION_GAS_LIMIT_DEFAULT = BigNumber.from(150000) const LOG_LABEL = 'CANCEL ETH FLOW ORDER' export interface OnChainCancellation { estimatedGas: BigNumber - sendTransaction(): Promise + sendTransaction(): Promise } export async function getEthFlowCancellation( @@ -35,14 +35,20 @@ export async function getEthFlowCancellation( } const estimatedGas = await ethFlowContract.estimateGas.invalidateOrder(cancelOrderParams).catch((error: Error) => { - logTradeFlowError(LOG_LABEL, `Error estimating createOrder gas. Using default ${ETHFLOW_GAS_LIMIT_DEFAULT}`, error) - return ETHFLOW_GAS_LIMIT_DEFAULT + logTradeFlowError( + LOG_LABEL, + `Error estimating invalidateOrder gas. Using default ${CANCELLATION_GAS_LIMIT_DEFAULT}`, + error + ) + return CANCELLATION_GAS_LIMIT_DEFAULT }) return { estimatedGas, sendTransaction: () => { - return ethFlowContract.invalidateOrder(cancelOrderParams, { gasLimit: calculateGasMargin(estimatedGas) }) + return ethFlowContract + .invalidateOrder(cancelOrderParams, { gasLimit: calculateGasMargin(estimatedGas) }) + .then((res) => res.hash) }, } } @@ -51,14 +57,20 @@ export async function getOnChainCancellation(contract: GPv2Settlement, order: Or const cancelOrderParams = order.id const estimatedGas = await contract.estimateGas.invalidateOrder(cancelOrderParams).catch((error: Error) => { - logTradeFlowError(LOG_LABEL, `Error estimating createOrder gas. Using default ${ETHFLOW_GAS_LIMIT_DEFAULT}`, error) - return ETHFLOW_GAS_LIMIT_DEFAULT + logTradeFlowError( + LOG_LABEL, + `Error estimating invalidateOrder gas. Using default ${CANCELLATION_GAS_LIMIT_DEFAULT}`, + error + ) + return CANCELLATION_GAS_LIMIT_DEFAULT }) return { estimatedGas, sendTransaction: () => { - return contract.invalidateOrder(cancelOrderParams, { gasLimit: calculateGasMargin(estimatedGas) }) + return contract + .invalidateOrder(cancelOrderParams, { gasLimit: calculateGasMargin(estimatedGas) }) + .then((res) => res.hash) }, } } diff --git a/src/common/hooks/useCancelOrder/useGetOnChainCancellation.ts b/src/common/hooks/useCancelOrder/useGetOnChainCancellation.ts index 199d82970f..15dfba235d 100644 --- a/src/common/hooks/useCancelOrder/useGetOnChainCancellation.ts +++ b/src/common/hooks/useCancelOrder/useGetOnChainCancellation.ts @@ -4,6 +4,7 @@ import { useEthFlowContract, useGP2SettlementContract } from 'legacy/hooks/useCo import { Order } from 'legacy/state/orders/actions' import { getIsEthFlowOrder } from 'modules/swap/containers/EthFlowStepper' +import { useCancelTwapOrder } from 'modules/twap/hooks/useCancelTwapOrder' import { getEthFlowCancellation, @@ -14,17 +15,24 @@ import { export function useGetOnChainCancellation(): (order: Order) => Promise { const ethFlowContract = useEthFlowContract() const settlementContract = useGP2SettlementContract() + const cancelTwapOrder = useCancelTwapOrder() return useCallback( (order: Order) => { + const composableCowId = order.composableCowInfo?.id + + if (composableCowId) { + return cancelTwapOrder(composableCowId) + } + const isEthFlowOrder = getIsEthFlowOrder(order) if (isEthFlowOrder) { return getEthFlowCancellation(ethFlowContract!, order) - } else { - return getOnChainCancellation(settlementContract!, order) } + + return getOnChainCancellation(settlementContract!, order) }, - [ethFlowContract, settlementContract] + [ethFlowContract, settlementContract, cancelTwapOrder] ) } diff --git a/src/common/hooks/useCancelOrder/useSendOnChainCancellation.test.ts b/src/common/hooks/useCancelOrder/useSendOnChainCancellation.test.ts index f0d8376649..133c7a8282 100644 --- a/src/common/hooks/useCancelOrder/useSendOnChainCancellation.test.ts +++ b/src/common/hooks/useCancelOrder/useSendOnChainCancellation.test.ts @@ -12,6 +12,7 @@ import { useRequestOrderCancellation, useSetOrderCancellationHash } from 'legacy import { useWalletInfo } from 'modules/wallet' import { useSendOnChainCancellation } from './useSendOnChainCancellation' +import { WithMockedWeb3 } from '../../../test-utils' const chainId = 1 const settlementCancellationTxHash = '0xcfwj23g4fwe111' @@ -88,7 +89,7 @@ describe('useSendOnChainCancellation() + useGetOnChainCancellation()', () => { }) it('When is ETH-flow order, then should call eth-flow contract', async () => { - const { result } = renderHook(() => useSendOnChainCancellation()) + const { result } = renderHook(() => useSendOnChainCancellation(), { wrapper: WithMockedWeb3 }) await result.current({ ...orderMock, inputToken: NATIVE_CURRENCY_BUY_TOKEN[chainId] }) @@ -98,7 +99,7 @@ describe('useSendOnChainCancellation() + useGetOnChainCancellation()', () => { }) it('When is NOT ETH-flow order, then should call settlement contract', async () => { - const { result } = renderHook(() => useSendOnChainCancellation()) + const { result } = renderHook(() => useSendOnChainCancellation(), { wrapper: WithMockedWeb3 }) await result.current({ ...orderMock, inputToken: COW[chainId] }) @@ -109,7 +110,7 @@ describe('useSendOnChainCancellation() + useGetOnChainCancellation()', () => { describe('When a transaction is sent', () => { it('Then should change an order status, set a tx hash to order and add the transaction to store', async () => { - const { result } = renderHook(() => useSendOnChainCancellation()) + const { result } = renderHook(() => useSendOnChainCancellation(), { wrapper: WithMockedWeb3 }) await result.current({ ...orderMock, inputToken: COW[chainId] }) diff --git a/src/common/hooks/useCancelOrder/useSendOnChainCancellation.ts b/src/common/hooks/useCancelOrder/useSendOnChainCancellation.ts index c0946c5872..93516b7aa9 100644 --- a/src/common/hooks/useCancelOrder/useSendOnChainCancellation.ts +++ b/src/common/hooks/useCancelOrder/useSendOnChainCancellation.ts @@ -25,20 +25,18 @@ export function useSendOnChainCancellation() { const isEthFlowOrder = getIsEthFlowOrder(order) const { sendTransaction } = await getOnChainCancellation(order) - const receipt = await sendTransaction() - - if (receipt?.hash) { - cancelPendingOrder({ id: order.id, chainId }) - setOrderCancellationHash({ chainId, id: order.id, hash: receipt.hash }) - - if (isEthFlowOrder) { - addTransaction({ hash: receipt.hash, ethFlow: { orderId: order.id, subType: 'cancellation' } }) - } else { - addTransaction({ - hash: receipt.hash, - onChainCancellation: { orderId: order.id, sellTokenSymbol: order.inputToken.symbol || '' }, - }) - } + const hash = await sendTransaction() + + cancelPendingOrder({ id: order.id, chainId }) + setOrderCancellationHash({ chainId, id: order.id, hash }) + + if (isEthFlowOrder) { + addTransaction({ hash, ethFlow: { orderId: order.id, subType: 'cancellation' } }) + } else { + addTransaction({ + hash, + onChainCancellation: { orderId: order.id, sellTokenSymbol: order.inputToken.symbol || '' }, + }) } }, [addTransaction, getOnChainCancellation, cancelPendingOrder, chainId, setOrderCancellationHash] diff --git a/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx b/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx index c34e929ba5..325df5d4e9 100644 --- a/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx +++ b/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx @@ -176,7 +176,7 @@ export function OrderRow({ const isOrderCreating = CREATING_STATES.includes(order.status) return ( - + {/*Checkbox for multiple cancellation*/} {isRowSelectable && isOpenOrdersTab && ( diff --git a/src/modules/twap/hooks/useCancelTwapOrder.ts b/src/modules/twap/hooks/useCancelTwapOrder.ts new file mode 100644 index 0000000000..a3ab16348e --- /dev/null +++ b/src/modules/twap/hooks/useCancelTwapOrder.ts @@ -0,0 +1,42 @@ +import { useAtomValue } from 'jotai/utils' +import { useCallback } from 'react' + +import { BigNumber } from '@ethersproject/bignumber' + +import { useGP2SettlementContract } from 'legacy/hooks/useContract' + +import { useComposableCowContract } from 'modules/advancedOrders/hooks/useComposableCowContract' +import { useSafeAppsSdk } from 'modules/wallet/web3-react/hooks/useSafeAppsSdk' + +import { cancelTwapOrderTxs, estimateCancelTwapOrderTxs } from '../services/cancelTwapOrderTxs' +import { twapPartOrdersListAtom } from '../state/twapPartOrdersAtom' + +type TwapCancellation = { estimatedGas: BigNumber; sendTransaction(): Promise } + +export function useCancelTwapOrder(): (orderId: string) => Promise { + const twapPartOrders = useAtomValue(twapPartOrdersListAtom) + const safeAppsSdk = useSafeAppsSdk() + const settlementContract = useGP2SettlementContract() + const composableCowContract = useComposableCowContract() + + return useCallback( + async (orderId: string) => { + if (!composableCowContract || !settlementContract || !safeAppsSdk) { + throw new Error('Context is not full to cancel TWAP order') + } + + const partOrder = twapPartOrders.find((item) => item.twapOrderId === orderId) + const partOrderId = partOrder?.uid + + const context = { composableCowContract, settlementContract, orderId, partOrderId } + + return { + estimatedGas: await estimateCancelTwapOrderTxs(context), + sendTransaction: () => { + return safeAppsSdk.txs.send({ txs: cancelTwapOrderTxs(context) }).then((res) => res.safeTxHash) + }, + } + }, + [composableCowContract, settlementContract, safeAppsSdk, twapPartOrders] + ) +} diff --git a/src/modules/twap/services/cancelTwapOrderTxs.ts b/src/modules/twap/services/cancelTwapOrderTxs.ts new file mode 100644 index 0000000000..2eecd2b29d --- /dev/null +++ b/src/modules/twap/services/cancelTwapOrderTxs.ts @@ -0,0 +1,42 @@ +import { BigNumber } from '@ethersproject/bignumber' +import { MetaTransactionData } from '@safe-global/safe-core-sdk-types' + +import { ComposableCoW, GPv2Settlement } from 'abis/types' + +export interface CancelTwapOrderContext { + composableCowContract: ComposableCoW + settlementContract: GPv2Settlement + orderId: string + partOrderId?: string +} +export function cancelTwapOrderTxs(context: CancelTwapOrderContext): MetaTransactionData[] { + const { composableCowContract, settlementContract, orderId, partOrderId } = context + const cancelTwapOrderTx = { + to: composableCowContract.address, + data: composableCowContract.interface.encodeFunctionData('remove', [orderId]), + value: '0', + operation: 0, + } + + if (!partOrderId) return [cancelTwapOrderTx] + + const cancelTwapPartOrderTx = { + to: settlementContract.address, + data: settlementContract.interface.encodeFunctionData('invalidateOrder', [partOrderId]), + value: '0', + operation: 0, + } + + return [cancelTwapOrderTx, cancelTwapPartOrderTx] +} + +export async function estimateCancelTwapOrderTxs(context: CancelTwapOrderContext): Promise { + const { composableCowContract, settlementContract, orderId, partOrderId } = context + const canelComposableCowTxCost = await composableCowContract.estimateGas.remove(orderId) + + if (!partOrderId) return canelComposableCowTxCost + + const cancelPartOrderTx = await settlementContract.estimateGas.invalidateOrder(partOrderId) + + return canelComposableCowTxCost.add(cancelPartOrderTx) +} diff --git a/src/modules/twap/state/twapOrdersListAtom.ts b/src/modules/twap/state/twapOrdersListAtom.ts index a2519af0c5..c1168ef674 100644 --- a/src/modules/twap/state/twapOrdersListAtom.ts +++ b/src/modules/twap/state/twapOrdersListAtom.ts @@ -30,14 +30,16 @@ export const emulatedTwapOrdersAtom = atom((get) => { if (!accountLowerCase) return [] - return orders + const orderWithComposableCowInfo: OrderWithComposableCowInfo[] = orders .filter((order) => order.chainId === chainId && order.safeAddress.toLowerCase() === accountLowerCase) .map((order) => { return { order: emulateTwapAsOrder(tokens, order), composableCowInfo: { - uid: order.id, + id: order.id, }, } - }) as OrderWithComposableCowInfo[] + }) + + return orderWithComposableCowInfo }) diff --git a/src/modules/twap/updaters/TwapOrdersUpdater.tsx b/src/modules/twap/updaters/TwapOrdersUpdater.tsx index 8b3e4de30a..209b00fe48 100644 --- a/src/modules/twap/updaters/TwapOrdersUpdater.tsx +++ b/src/modules/twap/updaters/TwapOrdersUpdater.tsx @@ -27,7 +27,7 @@ export function TwapOrdersUpdater(props: { const twapDiscreteOrders = useTwapDiscreteOrders() const twapOrdersList = useAtomValue(twapOrdersListAtom) - const setTwapOrders = useUpdateAtom(updateTwapOrdersListAtom) + const updateTwapOrders = useUpdateAtom(updateTwapOrdersListAtom) const updateTwapPartOrders = useUpdateAtom(updateTwapPartOrdersAtom) const ordersSafeData = useFetchTwapOrdersFromSafe(props) @@ -67,8 +67,8 @@ export function TwapOrdersUpdater(props: { const items = buildTwapOrdersItems(chainId, safeAddress, allOrdersInfo, ordersAuthResult, twapDiscreteOrders) - setTwapOrders(items) - }, [chainId, safeAddress, allOrdersInfo, ordersAuthResult, twapDiscreteOrders, setTwapOrders]) + updateTwapOrders(items) + }, [chainId, safeAddress, allOrdersInfo, ordersAuthResult, twapDiscreteOrders, updateTwapOrders]) useEffect(() => { if (!partOrders) return diff --git a/src/modules/twap/utils/emulateTwapAsOrder.ts b/src/modules/twap/utils/emulateTwapAsOrder.ts index bc045b7856..23b62eb9d5 100644 --- a/src/modules/twap/utils/emulateTwapAsOrder.ts +++ b/src/modules/twap/utils/emulateTwapAsOrder.ts @@ -46,6 +46,6 @@ export function emulateTwapAsOrder(tokens: TokensByAddress, item: TwapOrderItem) executedSellAmountBeforeFees: '0', executedBuyAmount: '0', executedFeeAmount: '0', - invalidated: false, + invalidated: status === TwapOrderStatus.Cancelled, } } From f69371c29fd952f3aceade8d9ed0c9ea4f5eaf84 Mon Sep 17 00:00:00 2001 From: Leandro Date: Tue, 13 Jun 2023 07:07:29 -0700 Subject: [PATCH 17/67] feat(twap): twap review order - pt4 - duration fields (#2645) * refactor: rename and export ConfirmDetailsItem Props * refactor: use InfoIcon instead of re-creating the same component * refactor: made TwapConfirmDetails a pure component * feat: deadlinePartsDisplay outputs either 1h or 1 hour * chore: hide start time for now * feat: implement part and total duration * refactor: move presentation of durations to TwapConfirmDetails * feat: unhide Start time and set it to `Now` --- .../trade/pure/ConfirmDetailsItem/index.tsx | 13 +++----- .../TwapConfirmModal/TwapConfirmDetails.tsx | 30 ++++++++++++++----- .../containers/TwapConfirmModal/index.tsx | 8 ++++- .../twap/utils/deadlinePartsDisplay.ts | 10 +++---- 4 files changed, 39 insertions(+), 22 deletions(-) diff --git a/src/modules/trade/pure/ConfirmDetailsItem/index.tsx b/src/modules/trade/pure/ConfirmDetailsItem/index.tsx index 6e58e83278..470acb2d1d 100644 --- a/src/modules/trade/pure/ConfirmDetailsItem/index.tsx +++ b/src/modules/trade/pure/ConfirmDetailsItem/index.tsx @@ -2,15 +2,14 @@ import { ReactNode } from 'react' import { CornerDownRight } from 'react-feather' +import { InfoIcon } from 'legacy/components/InfoIcon' import { RowFixed } from 'legacy/components/Row' -import { MouseoverTooltipContent } from 'legacy/components/Tooltip' import { TextWrapper } from 'modules/swap/pure/Row/styled' -import { StyledInfoIcon } from 'modules/swap/pure/styled' import { Content, Row, Wrapper } from './styled' -type Props = { +export type ConfirmDetailsItemProps = { children: ReactNode label?: string tooltip?: ReactNode @@ -18,7 +17,7 @@ type Props = { fiatAmount?: string } -export function ConfirmDetailsItem(props: Props) { +export function ConfirmDetailsItem(props: ConfirmDetailsItemProps) { const { children, label, tooltip, withArrow = true } = props return ( @@ -30,11 +29,7 @@ export function ConfirmDetailsItem(props: Props) { {label && {label}} - {tooltip && ( - - - - )} + {tooltip && } {children} diff --git a/src/modules/twap/containers/TwapConfirmModal/TwapConfirmDetails.tsx b/src/modules/twap/containers/TwapConfirmModal/TwapConfirmDetails.tsx index d1b410e2ae..6198b69b13 100644 --- a/src/modules/twap/containers/TwapConfirmModal/TwapConfirmDetails.tsx +++ b/src/modules/twap/containers/TwapConfirmModal/TwapConfirmDetails.tsx @@ -1,30 +1,41 @@ +import React from 'react' + import { ConfirmDetailsItem } from 'modules/trade/pure/ConfirmDetailsItem' import { ReviewOrderModalAmountRow } from 'modules/trade/pure/ReviewOrderModalAmountRow' import { PartsState } from '../../state/partsStateAtom' +import { deadlinePartsDisplay } from '../../utils/deadlinePartsDisplay' export type TwapConfirmDetailsProps = { startTime: number | undefined partDuration: number | undefined + totalDuration: number | undefined partsState: PartsState } -export function TwapConfirmDetails(_props: TwapConfirmDetailsProps) { - const { startTime, partDuration, partsState } = _props +export const TwapConfirmDetails = React.memo(function TwapConfirmDetails(props: TwapConfirmDetailsProps) { + const { partDuration, totalDuration, partsState } = props const { numberOfPartsValue, inputPartAmount, inputFiatAmount, outputFiatAmount, outputPartAmount } = partsState const partsSuffix = ` part (1/${numberOfPartsValue})` const amountLabelSuffix = ' amount per' + partsSuffix + const partDurationDisplay = partDuration ? deadlinePartsDisplay(partDuration, true) : '' + const totalDurationDisplay = totalDuration ? deadlinePartsDisplay(totalDuration, true) : '' + return (
TWAP order split in {numberOfPartsValue} equal parts + + {/* Sell amount per part */} + + {/* Buy amount per part */} - {/*TODO: implement logic for time fields*/} + + {/* Start time */} - {startTime} + Now + + {/* Part duration */} - {partDuration} + {partDurationDisplay} + + {/* Total duration */} - {partDuration} + {totalDurationDisplay}
) -} +}) diff --git a/src/modules/twap/containers/TwapConfirmModal/index.tsx b/src/modules/twap/containers/TwapConfirmModal/index.tsx index fa8f954fac..937344c24a 100644 --- a/src/modules/twap/containers/TwapConfirmModal/index.tsx +++ b/src/modules/twap/containers/TwapConfirmModal/index.tsx @@ -62,6 +62,11 @@ export function TwapConfirmModal() { // This already takes into account the full order const minReceivedAmount = twapOrder?.buyAmount + const { timeInterval, numOfParts } = twapOrder || {} + + const partDuration = timeInterval + const totalDuration = timeInterval && numOfParts ? timeInterval * numOfParts : undefined + return ( diff --git a/src/modules/twap/utils/deadlinePartsDisplay.ts b/src/modules/twap/utils/deadlinePartsDisplay.ts index 2caae40a52..709cb65b70 100644 --- a/src/modules/twap/utils/deadlinePartsDisplay.ts +++ b/src/modules/twap/utils/deadlinePartsDisplay.ts @@ -11,7 +11,7 @@ export function customDeadlineToSeconds(customDeadline: TwapOrdersDeadline['cust return minutesToSeconds } -export function deadlinePartsDisplay(timeInterval: number): string { +export function deadlinePartsDisplay(timeInterval: number, longLabels = false): string { const timeMs = ms(`${timeInterval * 1000}ms`) const days = Math.floor(timeMs / oneD) @@ -20,10 +20,10 @@ export function deadlinePartsDisplay(timeInterval: number): string { const seconds = Math.floor((timeMs % oneM) / oneS) return [ - [days, 'd'], - [hours, 'h'], - [minutes, 'm'], - [seconds, 's'], + [days, longLabels ? ' days' : 'd'], + [hours, longLabels ? ' hours' : 'h'], + [minutes, longLabels ? ' minutes' : 'm'], + [seconds, longLabels ? ' seconds' : 's'], ] .filter(([value]) => !!value) .map(([value, suffix]) => `${value}${suffix}`) From feeba802f9d6875d1edd3271b41f2436616f3bdb Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Wed, 14 Jun 2023 13:52:05 +0600 Subject: [PATCH 18/67] fix(twap): improve twap orders state synchronisation (#2633) --- src/api/gnosisProtocol/api.ts | 23 ++++++--- src/common/hooks/useCancelOrder/index.ts | 2 +- .../useCancelOrder/onChainCancellation.ts | 33 ++++++++++--- .../useGetOnChainCancellation.ts | 9 ++-- .../useSendOnChainCancellation.test.ts | 1 + .../useSendOnChainCancellation.ts | 35 +++++++------ .../utils/isOrderOffChainCancellable.ts | 2 +- src/legacy/state/orders/reducer.ts | 6 ++- src/legacy/state/orders/updaters/utils.ts | 10 +++- .../ordersTable/pure/OrderStatusBox/index.tsx | 49 ++++++++++++------- .../OrdersTableContainer/OrderRow/index.tsx | 2 +- .../swap/containers/EthFlowStepper/index.tsx | 8 ++- src/modules/swap/services/types.ts | 1 + src/modules/twap/const.ts | 3 +- src/modules/twap/hooks/useCancelTwapOrder.ts | 31 +++++++++--- src/modules/twap/hooks/useCreateTwapOrder.ts | 35 ++++++++++++- .../twap/hooks/useFetchTwapPartOrders.ts | 2 +- .../twap/hooks/useTwapOrderCreationContext.ts | 8 +-- .../twap/hooks/useTwapOrdersAuthMulticall.ts | 4 +- .../PrimaryActionButton/getTwapFormState.tsx | 2 +- .../twap/pure/PrimaryActionButton/index.tsx | 1 - .../twap/services/createTwapOrderTxs.test.ts | 21 +++++--- .../twap/services/createTwapOrderTxs.ts | 48 ++++-------------- .../twap/services/fetchTwapOrdersFromSafe.ts | 8 ++- .../twap/services/settleTwapOrder.test.ts | 8 ++- src/modules/twap/services/settleTwapOrder.ts | 12 ++--- src/modules/twap/state/twapOrdersListAtom.ts | 12 ++++- src/modules/twap/state/twapPartOrdersAtom.ts | 2 +- src/modules/twap/types.ts | 2 +- .../twap/updaters/TwapOrdersUpdater.tsx | 16 +++--- .../twap/utils/buildTwapOrderParamsStruct.ts | 19 +++++++ .../twap/utils/buildTwapOrdersItems.ts | 7 +-- src/modules/twap/utils/getTwapOrderStatus.ts | 20 +++++++- src/modules/twap/utils/twapOrderToStruct.ts | 15 ++++++ .../twap/utils/updateTwapOrdersList.ts | 25 ++++++++++ .../getIsComposableCowChildOrder.ts | 5 ++ .../getIsComposableCowParentOrder.ts | 5 ++ 37 files changed, 335 insertions(+), 157 deletions(-) create mode 100644 src/modules/twap/utils/buildTwapOrderParamsStruct.ts create mode 100644 src/modules/twap/utils/twapOrderToStruct.ts create mode 100644 src/modules/twap/utils/updateTwapOrdersList.ts create mode 100644 src/utils/orderUtils/getIsComposableCowChildOrder.ts create mode 100644 src/utils/orderUtils/getIsComposableCowParentOrder.ts diff --git a/src/api/gnosisProtocol/api.ts b/src/api/gnosisProtocol/api.ts index cbc2e2f204..4c5d97fe23 100644 --- a/src/api/gnosisProtocol/api.ts +++ b/src/api/gnosisProtocol/api.ts @@ -1,7 +1,18 @@ -import { OrderBookApiError, PriceQuality, SupportedChainId as ChainId } from '@cowprotocol/cow-sdk' -import { OrderKind } from '@cowprotocol/cow-sdk' -import { OrderQuoteRequest, SigningScheme, OrderQuoteResponse, EnrichedOrder } from '@cowprotocol/cow-sdk' -import { NativePriceResponse, Trade, PartialApiContext, Address } from '@cowprotocol/cow-sdk' +import { + OrderBookApiError, + PriceQuality, + SupportedChainId as ChainId, + CowEnv, + OrderKind, + OrderQuoteRequest, + SigningScheme, + OrderQuoteResponse, + EnrichedOrder, + NativePriceResponse, + Trade, + PartialApiContext, + Address, +} from '@cowprotocol/cow-sdk' import { orderBookApi } from 'cowSdk' @@ -140,8 +151,8 @@ export async function getQuote(params: FeeQuoteParams): Promise { - return orderBookApi.getOrder(orderId, { chainId }) +export async function getOrder(chainId: ChainId, orderId: string, env?: CowEnv): Promise { + return orderBookApi.getOrder(orderId, { chainId, env }) } export async function getOrders( diff --git a/src/common/hooks/useCancelOrder/index.ts b/src/common/hooks/useCancelOrder/index.ts index 560b558a9a..90bd592da4 100644 --- a/src/common/hooks/useCancelOrder/index.ts +++ b/src/common/hooks/useCancelOrder/index.ts @@ -49,7 +49,7 @@ export function useCancelOrder(): (order: Order) => UseCancelOrderReturn { (order: Order) => { // Check the 'cancellability' - const isEthFlowOrder = getIsEthFlowOrder(order) + const isEthFlowOrder = getIsEthFlowOrder(order.inputToken.address) // 1. EthFlow orders will never be able to be cancelled offChain // 2. The wallet must support offChain singing diff --git a/src/common/hooks/useCancelOrder/onChainCancellation.ts b/src/common/hooks/useCancelOrder/onChainCancellation.ts index ce0ab54fb0..e888b78fc8 100644 --- a/src/common/hooks/useCancelOrder/onChainCancellation.ts +++ b/src/common/hooks/useCancelOrder/onChainCancellation.ts @@ -12,10 +12,17 @@ import { CoWSwapEthFlow } from 'abis/types/ethflow' const CANCELLATION_GAS_LIMIT_DEFAULT = BigNumber.from(150000) const LOG_LABEL = 'CANCEL ETH FLOW ORDER' +export type CancelledOrderInfo = { + txHash: string + orderId: string + sellTokenAddress: string + sellTokenSymbol?: string +} + export interface OnChainCancellation { estimatedGas: BigNumber - sendTransaction(): Promise + sendTransaction(processCancelledOrder: (cancelledOrderInfo: CancelledOrderInfo) => void): Promise } export async function getEthFlowCancellation( @@ -45,10 +52,17 @@ export async function getEthFlowCancellation( return { estimatedGas, - sendTransaction: () => { + sendTransaction: (processCancelledOrder) => { return ethFlowContract .invalidateOrder(cancelOrderParams, { gasLimit: calculateGasMargin(estimatedGas) }) - .then((res) => res.hash) + .then((res) => { + processCancelledOrder({ + txHash: res.hash, + orderId: order.id, + sellTokenAddress: order.inputToken.address, + sellTokenSymbol: order.inputToken.symbol, + }) + }) }, } } @@ -67,10 +81,15 @@ export async function getOnChainCancellation(contract: GPv2Settlement, order: Or return { estimatedGas, - sendTransaction: () => { - return contract - .invalidateOrder(cancelOrderParams, { gasLimit: calculateGasMargin(estimatedGas) }) - .then((res) => res.hash) + sendTransaction: (processCancelledOrder) => { + return contract.invalidateOrder(cancelOrderParams, { gasLimit: calculateGasMargin(estimatedGas) }).then((res) => { + processCancelledOrder({ + txHash: res.hash, + orderId: order.id, + sellTokenAddress: order.inputToken.address, + sellTokenSymbol: order.inputToken.symbol, + }) + }) }, } } diff --git a/src/common/hooks/useCancelOrder/useGetOnChainCancellation.ts b/src/common/hooks/useCancelOrder/useGetOnChainCancellation.ts index 15dfba235d..c56a755bab 100644 --- a/src/common/hooks/useCancelOrder/useGetOnChainCancellation.ts +++ b/src/common/hooks/useCancelOrder/useGetOnChainCancellation.ts @@ -11,6 +11,7 @@ import { getOnChainCancellation, OnChainCancellation, } from 'common/hooks/useCancelOrder/onChainCancellation' +import { getIsComposableCowParentOrder } from 'utils/orderUtils/getIsComposableCowParentOrder' export function useGetOnChainCancellation(): (order: Order) => Promise { const ethFlowContract = useEthFlowContract() @@ -19,13 +20,13 @@ export function useGetOnChainCancellation(): (order: Order) => Promise { - const composableCowId = order.composableCowInfo?.id + const isComposableCowOrder = getIsComposableCowParentOrder(order) - if (composableCowId) { - return cancelTwapOrder(composableCowId) + if (isComposableCowOrder) { + return cancelTwapOrder(order) } - const isEthFlowOrder = getIsEthFlowOrder(order) + const isEthFlowOrder = getIsEthFlowOrder(order.inputToken.address) if (isEthFlowOrder) { return getEthFlowCancellation(ethFlowContract!, order) diff --git a/src/common/hooks/useCancelOrder/useSendOnChainCancellation.test.ts b/src/common/hooks/useCancelOrder/useSendOnChainCancellation.test.ts index 133c7a8282..7d301fbed6 100644 --- a/src/common/hooks/useCancelOrder/useSendOnChainCancellation.test.ts +++ b/src/common/hooks/useCancelOrder/useSendOnChainCancellation.test.ts @@ -12,6 +12,7 @@ import { useRequestOrderCancellation, useSetOrderCancellationHash } from 'legacy import { useWalletInfo } from 'modules/wallet' import { useSendOnChainCancellation } from './useSendOnChainCancellation' + import { WithMockedWeb3 } from '../../../test-utils' const chainId = 1 diff --git a/src/common/hooks/useCancelOrder/useSendOnChainCancellation.ts b/src/common/hooks/useCancelOrder/useSendOnChainCancellation.ts index 93516b7aa9..04c9c5c368 100644 --- a/src/common/hooks/useCancelOrder/useSendOnChainCancellation.ts +++ b/src/common/hooks/useCancelOrder/useSendOnChainCancellation.ts @@ -7,6 +7,7 @@ import { useRequestOrderCancellation, useSetOrderCancellationHash } from 'legacy import { getIsEthFlowOrder } from 'modules/swap/containers/EthFlowStepper' import { useWalletInfo } from 'modules/wallet' +import { CancelledOrderInfo } from './onChainCancellation' import { useGetOnChainCancellation } from './useGetOnChainCancellation' export function useSendOnChainCancellation() { @@ -16,29 +17,33 @@ export function useSendOnChainCancellation() { const addTransaction = useTransactionAdder() const getOnChainCancellation = useGetOnChainCancellation() - return useCallback( - async (order: Order) => { - if (!chainId) { - return - } + const processCancelledOrder = useCallback( + ({ txHash, orderId, sellTokenAddress, sellTokenSymbol }: CancelledOrderInfo) => { + if (!chainId) return - const isEthFlowOrder = getIsEthFlowOrder(order) - const { sendTransaction } = await getOnChainCancellation(order) - - const hash = await sendTransaction() + const isEthFlowOrder = getIsEthFlowOrder(sellTokenAddress) - cancelPendingOrder({ id: order.id, chainId }) - setOrderCancellationHash({ chainId, id: order.id, hash }) + cancelPendingOrder({ id: orderId, chainId }) + setOrderCancellationHash({ chainId, id: orderId, hash: txHash }) if (isEthFlowOrder) { - addTransaction({ hash, ethFlow: { orderId: order.id, subType: 'cancellation' } }) + addTransaction({ hash: txHash, ethFlow: { orderId, subType: 'cancellation' } }) } else { addTransaction({ - hash, - onChainCancellation: { orderId: order.id, sellTokenSymbol: order.inputToken.symbol || '' }, + hash: txHash, + onChainCancellation: { orderId, sellTokenSymbol: sellTokenSymbol || '' }, }) } }, - [addTransaction, getOnChainCancellation, cancelPendingOrder, chainId, setOrderCancellationHash] + [chainId, cancelPendingOrder, setOrderCancellationHash, addTransaction] + ) + + return useCallback( + async (order: Order) => { + const { sendTransaction } = await getOnChainCancellation(order) + + await sendTransaction(processCancelledOrder) + }, + [processCancelledOrder, getOnChainCancellation] ) } diff --git a/src/common/utils/isOrderOffChainCancellable.ts b/src/common/utils/isOrderOffChainCancellable.ts index edd7870f24..7d3d80f303 100644 --- a/src/common/utils/isOrderOffChainCancellable.ts +++ b/src/common/utils/isOrderOffChainCancellable.ts @@ -3,5 +3,5 @@ import { getIsEthFlowOrder } from 'modules/swap/containers/EthFlowStepper' import { CancellableOrder, isOrderCancellable } from 'common/utils/isOrderCancellable' export function isOrderOffChainCancellable(order: CancellableOrder): boolean { - return !getIsEthFlowOrder(order) && isOrderCancellable(order) + return !getIsEthFlowOrder(order.inputToken.address) && isOrderCancellable(order) } diff --git a/src/legacy/state/orders/reducer.ts b/src/legacy/state/orders/reducer.ts index d0ecf9fca4..d8f0384ab2 100644 --- a/src/legacy/state/orders/reducer.ts +++ b/src/legacy/state/orders/reducer.ts @@ -251,13 +251,17 @@ export default createReducer(initialState, (builder) => popOrder(state, chainId, OrderStatus.FAILED, id) const validTo = getValidTo(newOrder.apiAdditionalInfo, newOrder) + const isComposableCowOrder = !!orderObj?.order?.composableCowInfo // merge existing and new order objects const order = orderObj ? { ...orderObj.order, validTo, apiAdditionalInfo: newOrder.apiAdditionalInfo, - isCancelling: newOrder.isCancelling, + // Don't reset isCancelling status for ComposableCow orders + // Because currently we don't have a backend for it + // And this status is stored only on Frontend + isCancelling: isComposableCowOrder ? !!orderObj.order.isCancelling : newOrder.isCancelling, class: newOrder.class, openSince: newOrder.openSince || orderObj.order.openSince, status, diff --git a/src/legacy/state/orders/updaters/utils.ts b/src/legacy/state/orders/updaters/utils.ts index 31c9c8862c..1f10db5d4a 100644 --- a/src/legacy/state/orders/updaters/utils.ts +++ b/src/legacy/state/orders/updaters/utils.ts @@ -8,6 +8,8 @@ import { stringToCurrency } from 'legacy/state/swap/extension' import { getOrder, OrderID } from 'api/gnosisProtocol' import { formatTokenAmount } from 'utils/amountFormat' import { formatSymbol } from 'utils/format' +import { getIsComposableCowChildOrder } from 'utils/orderUtils/getIsComposableCowChildOrder' +import { getIsComposableCowParentOrder } from 'utils/orderUtils/getIsComposableCowParentOrder' export type OrderLogPopupMixData = OrderFulfillmentData | OrderID @@ -65,10 +67,16 @@ export async function fetchOrderPopupData(orderFromStore: Order, chainId: ChainI if (orderFromStore.status === OrderStatus.CREATING) { return null } + // Skip ComposableCow orders + if (getIsComposableCowParentOrder(orderFromStore)) { + return null + } // Get order from API let orderFromApi: EnrichedOrder | null = null try { - orderFromApi = await getOrder(chainId, orderFromStore.id) + const isComposableCowChildOrder = getIsComposableCowChildOrder(orderFromStore) + // For ComposableCow child orders always request PROD order-book + orderFromApi = await getOrder(chainId, orderFromStore.id, isComposableCowChildOrder ? 'prod' : undefined) } catch (e: any) { console.debug( `[PendingOrdersUpdater] Failed to fetch order popup data on chain ${chainId} for order ${orderFromStore.id}` diff --git a/src/modules/ordersTable/pure/OrderStatusBox/index.tsx b/src/modules/ordersTable/pure/OrderStatusBox/index.tsx index ce0df2e3af..eb7f136326 100644 --- a/src/modules/ordersTable/pure/OrderStatusBox/index.tsx +++ b/src/modules/ordersTable/pure/OrderStatusBox/index.tsx @@ -16,10 +16,10 @@ const Wrapper = styled.div<{ }>` --height: 28px; --statusColor: ${({ theme, status, cancelling, partiallyFilled }) => - cancelling - ? theme.text1 - : status === OrderStatus.CANCELLED + status === OrderStatus.CANCELLED ? theme.danger + : cancelling + ? theme.text1 : status === OrderStatus.FULFILLED || partiallyFilled ? theme.success : status === OrderStatus.PENDING // OPEN order @@ -60,6 +60,32 @@ const Wrapper = styled.div<{ } ` +function getOrderStatusTitle(order: ParsedOrder): string { + // Cancelled status takes precedence + if (order.status === OrderStatus.CANCELLED) { + return orderStatusTitleMap[order.status] + } + + // Cancelling is not a real order status + if (order.isCancelling) { + return 'Cancelling...' + } + + // We consider the order fully filled for display purposes even if not 100% filled + // For this reason we use the flag to override the order status + if (order.executionData.fullyFilled) { + return orderStatusTitleMap[OrderStatus.FULFILLED] + } + + // Partially filled is also not a real status + if (order.executionData.partiallyFilled) { + return 'Partially Filled' + } + + // Finally, map order status to their display version + return orderStatusTitleMap[order.status] +} + export type OrderStatusBoxProps = { order: ParsedOrder widthAuto?: boolean @@ -79,22 +105,7 @@ export function OrderStatusBox({ order, widthAuto, withWarning, onClick }: Order onClick={onClick} > {/* Status overrides for special cases */} - { - // Cancelling is not a real order status - order.isCancelling - ? 'Cancelling...' - : // Cancelled status takes precedence - order.status === OrderStatus.CANCELLED - ? orderStatusTitleMap[order.status] - : // We consider the order fully filled for display purposes even if not 100% filled - // For this reason we use the flag to override the order status - order.executionData.fullyFilled - ? orderStatusTitleMap[OrderStatus.FULFILLED] - : // Partially filled is also not a real status - order.executionData.partiallyFilled - ? 'Partially Filled' - : orderStatusTitleMap[order.status] // Finally, map order status to their display version - } + {getOrderStatusTitle(order)}
) } diff --git a/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx b/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx index 325df5d4e9..eb6793ca4a 100644 --- a/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx +++ b/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/index.tsx @@ -183,7 +183,7 @@ export function OrderRow({ orderActions.toggleOrderForCancellation(order)} /> diff --git a/src/modules/swap/containers/EthFlowStepper/index.tsx b/src/modules/swap/containers/EthFlowStepper/index.tsx index 47ff95c0f7..e685f0d5a9 100644 --- a/src/modules/swap/containers/EthFlowStepper/index.tsx +++ b/src/modules/swap/containers/EthFlowStepper/index.tsx @@ -1,5 +1,3 @@ -import { Token } from '@uniswap/sdk-core' - import { NATIVE_CURRENCY_BUY_ADDRESS } from 'legacy/constants' import { useAllTransactions } from 'legacy/state/enhancedTransactions/hooks' import { EnhancedTransactionDetails } from 'legacy/state/enhancedTransactions/reducer' @@ -34,7 +32,7 @@ export function EthFlowStepper(props: EthFlowStepperProps) { const state = mapOrderToEthFlowStepperState(order, creationTx, cancellationTx) - const isEthFlowOrder = getIsEthFlowOrder(order) + const isEthFlowOrder = getIsEthFlowOrder(order?.inputToken.address || '') if (!order || !state || !isEthFlowOrder) { return null @@ -119,6 +117,6 @@ function didRefundFail(order: Order): boolean | undefined { } // TODO: move this somewhere else? -export function getIsEthFlowOrder(order: { inputToken: Token } | undefined): boolean { - return order?.inputToken.address === NATIVE_CURRENCY_BUY_ADDRESS +export function getIsEthFlowOrder(inputTokenAddress: string): boolean { + return inputTokenAddress === NATIVE_CURRENCY_BUY_ADDRESS } diff --git a/src/modules/swap/services/types.ts b/src/modules/swap/services/types.ts index fff1849f59..e10afd2f56 100644 --- a/src/modules/swap/services/types.ts +++ b/src/modules/swap/services/types.ts @@ -15,6 +15,7 @@ import { SwapFlowAnalyticsContext } from 'modules/trade/utils/analytics' import { GPv2Settlement } from 'abis/types' import { CoWSwapEthFlow } from 'abis/types/ethflow' + import { FlowType } from '../hooks/useFlowContext' export interface BaseFlowContext { diff --git a/src/modules/twap/const.ts b/src/modules/twap/const.ts index 2285da8fb5..714b4f57f8 100644 --- a/src/modules/twap/const.ts +++ b/src/modules/twap/const.ts @@ -32,5 +32,4 @@ export const TWAP_HANDLER_ADDRESS: Record = { 5: '0xa12d770028d7072b80baeb6a1df962374fd13d9a', } -// TODO: Add filled status -export const TWAP_NOT_PENDING_STATUSES = [TwapOrderStatus.Cancelled, TwapOrderStatus.Expired] +export const TWAP_PENDING_STATUSES = [TwapOrderStatus.WaitSigning, TwapOrderStatus.Pending, TwapOrderStatus.Scheduled] diff --git a/src/modules/twap/hooks/useCancelTwapOrder.ts b/src/modules/twap/hooks/useCancelTwapOrder.ts index a3ab16348e..eff3fda6b4 100644 --- a/src/modules/twap/hooks/useCancelTwapOrder.ts +++ b/src/modules/twap/hooks/useCancelTwapOrder.ts @@ -1,26 +1,31 @@ import { useAtomValue } from 'jotai/utils' import { useCallback } from 'react' -import { BigNumber } from '@ethersproject/bignumber' - import { useGP2SettlementContract } from 'legacy/hooks/useContract' +import { Order } from 'legacy/state/orders/actions' import { useComposableCowContract } from 'modules/advancedOrders/hooks/useComposableCowContract' import { useSafeAppsSdk } from 'modules/wallet/web3-react/hooks/useSafeAppsSdk' +import type { OnChainCancellation } from 'common/hooks/useCancelOrder/onChainCancellation' + import { cancelTwapOrderTxs, estimateCancelTwapOrderTxs } from '../services/cancelTwapOrderTxs' import { twapPartOrdersListAtom } from '../state/twapPartOrdersAtom' -type TwapCancellation = { estimatedGas: BigNumber; sendTransaction(): Promise } - -export function useCancelTwapOrder(): (orderId: string) => Promise { +export function useCancelTwapOrder(): (order: Order) => Promise { const twapPartOrders = useAtomValue(twapPartOrdersListAtom) const safeAppsSdk = useSafeAppsSdk() const settlementContract = useGP2SettlementContract() const composableCowContract = useComposableCowContract() return useCallback( - async (orderId: string) => { + async (order: Order) => { + const orderId = order.composableCowInfo?.id + + if (!orderId) { + throw new Error('Wrong orderId for TWAP order cancellation') + } + if (!composableCowContract || !settlementContract || !safeAppsSdk) { throw new Error('Context is not full to cancel TWAP order') } @@ -32,8 +37,18 @@ export function useCancelTwapOrder(): (orderId: string) => Promise { - return safeAppsSdk.txs.send({ txs: cancelTwapOrderTxs(context) }).then((res) => res.safeTxHash) + sendTransaction: (processCancelledOrder) => { + return safeAppsSdk.txs.send({ txs: cancelTwapOrderTxs(context) }).then((res) => { + const txHash = res.safeTxHash + const sellTokenAddress = order.inputToken.address + const sellTokenSymbol = order.inputToken.symbol + + processCancelledOrder({ txHash, orderId, sellTokenAddress, sellTokenSymbol }) + + if (partOrderId) { + processCancelledOrder({ txHash, orderId: partOrderId, sellTokenAddress, sellTokenSymbol }) + } + }) }, } }, diff --git a/src/modules/twap/hooks/useCreateTwapOrder.ts b/src/modules/twap/hooks/useCreateTwapOrder.ts index fe58c5c259..441f38ed53 100644 --- a/src/modules/twap/hooks/useCreateTwapOrder.ts +++ b/src/modules/twap/hooks/useCreateTwapOrder.ts @@ -1,4 +1,5 @@ import { useAtomValue } from 'jotai' +import { useUpdateAtom } from 'jotai/utils' import { useCallback } from 'react' import { CurrencyAmount, Token } from '@uniswap/sdk-core' @@ -8,14 +9,22 @@ import { Nullish } from 'types' import { useAdvancedOrdersDerivedState } from 'modules/advancedOrders' import { useTradeConfirmActions } from 'modules/trade' +import { useWalletInfo } from 'modules/wallet' import { useTwapOrderCreationContext } from './useTwapOrderCreationContext' import { settleTwapOrder } from '../services/settleTwapOrder' import { twapOrderAtom } from '../state/twapOrderAtom' +import { addTwapOrderToListAtom } from '../state/twapOrdersListAtom' +import { TwapOrderStatus } from '../types' +import { buildTwapOrderParamsStruct } from '../utils/buildTwapOrderParamsStruct' +import { getConditionalOrderId } from '../utils/getConditionalOrderId' +import { twapOrderToStruct } from '../utils/twapOrderToStruct' export function useCreateTwapOrder() { + const { chainId, account } = useWalletInfo() const twapOrder = useAtomValue(twapOrderAtom) + const addTwapOrderToList = useUpdateAtom(addTwapOrderToListAtom) const { inputCurrencyAmount, outputCurrencyAmount } = useAdvancedOrdersDerivedState() @@ -24,6 +33,7 @@ export function useCreateTwapOrder() { const tradeConfirmActions = useTradeConfirmActions() return useCallback(async () => { + if (!chainId || !account) return if (!inputCurrencyAmount || !outputCurrencyAmount || !twapOrderCreationContext || !twapOrder) return const pendingTrade = { @@ -32,15 +42,36 @@ export function useCreateTwapOrder() { } const startTime = Math.round((Date.now() + ms`1m`) / 1000) // Now + 1 min + const twapOrderWithStartTime = { ...twapOrder, startTime } + const paramsStruct = buildTwapOrderParamsStruct(chainId, twapOrderWithStartTime) + const orderId = getConditionalOrderId(paramsStruct) tradeConfirmActions.onSign(pendingTrade) try { - const { safeTxHash } = await settleTwapOrder({ ...twapOrder, startTime }, twapOrderCreationContext) + const { safeTxHash } = await settleTwapOrder(twapOrderWithStartTime, paramsStruct, twapOrderCreationContext) + + addTwapOrderToList({ + order: twapOrderToStruct(twapOrder), + status: TwapOrderStatus.WaitSigning, + chainId, + safeAddress: account, + submissionDate: new Date().toISOString(), + id: orderId, + }) tradeConfirmActions.onSuccess(safeTxHash) } catch (error) { tradeConfirmActions.onError(error.message || error) } - }, [inputCurrencyAmount, outputCurrencyAmount, twapOrder, tradeConfirmActions, twapOrderCreationContext]) + }, [ + chainId, + account, + inputCurrencyAmount, + outputCurrencyAmount, + twapOrder, + tradeConfirmActions, + twapOrderCreationContext, + addTwapOrderToList, + ]) } diff --git a/src/modules/twap/hooks/useFetchTwapPartOrders.ts b/src/modules/twap/hooks/useFetchTwapPartOrders.ts index 4c60c7066c..7e3e963b76 100644 --- a/src/modules/twap/hooks/useFetchTwapPartOrders.ts +++ b/src/modules/twap/hooks/useFetchTwapPartOrders.ts @@ -74,7 +74,7 @@ async function getTwapPartOrderItem( sellAmount: partOrder.sellAmount.toString(), buyAmount: partOrder.buyAmount.toString(), feeAmount: partOrder.feeAmount.toString(), - kind: 'sell', // TODO: discuss it, smart-contract returns bytes here + kind: 'sell', // Twap order is always sell partiallyFillable: partOrder.partiallyFillable, } as Order diff --git a/src/modules/twap/hooks/useTwapOrderCreationContext.ts b/src/modules/twap/hooks/useTwapOrderCreationContext.ts index 486b15cb3a..e6414d0235 100644 --- a/src/modules/twap/hooks/useTwapOrderCreationContext.ts +++ b/src/modules/twap/hooks/useTwapOrderCreationContext.ts @@ -1,4 +1,3 @@ -import { SupportedChainId } from '@cowprotocol/cow-sdk' import SafeAppsSDK from '@safe-global/safe-apps-sdk' import { CurrencyAmount, Token } from '@uniswap/sdk-core' @@ -8,7 +7,6 @@ import { Erc20 } from 'legacy/abis/types' import { useTokenContract } from 'legacy/hooks/useContract' import { useComposableCowContract } from 'modules/advancedOrders/hooks/useComposableCowContract' -import { useWalletInfo } from 'modules/wallet' import { useSafeAppsSdk } from 'modules/wallet/web3-react/hooks/useSafeAppsSdk' import { ComposableCoW } from 'abis/types' @@ -16,7 +14,6 @@ import { useNeedsApproval } from 'common/hooks/useNeedsApproval' import { useTradeSpenderAddress } from 'common/hooks/useTradeSpenderAddress' export interface TwapOrderCreationContext { - chainId: SupportedChainId safeAppsSdk: SafeAppsSDK composableCowContract: ComposableCoW needsApproval: boolean @@ -27,14 +24,13 @@ export interface TwapOrderCreationContext { export function useTwapOrderCreationContext( inputAmount: Nullish> ): TwapOrderCreationContext | null { - const { chainId } = useWalletInfo() const safeAppsSdk = useSafeAppsSdk() const composableCowContract = useComposableCowContract() const needsApproval = useNeedsApproval(inputAmount) const erc20Contract = useTokenContract(inputAmount?.currency.address) const spender = useTradeSpenderAddress() - if (!composableCowContract || !erc20Contract || !chainId || !safeAppsSdk || !spender) return null + if (!composableCowContract || !erc20Contract || !safeAppsSdk || !spender) return null - return { chainId, safeAppsSdk, composableCowContract, erc20Contract, needsApproval, spender } + return { safeAppsSdk, composableCowContract, erc20Contract, needsApproval, spender } } diff --git a/src/modules/twap/hooks/useTwapOrdersAuthMulticall.ts b/src/modules/twap/hooks/useTwapOrdersAuthMulticall.ts index a4a14ba887..722d786c81 100644 --- a/src/modules/twap/hooks/useTwapOrdersAuthMulticall.ts +++ b/src/modules/twap/hooks/useTwapOrdersAuthMulticall.ts @@ -21,12 +21,12 @@ export function useTwapOrdersAuthMulticall( const results = useSingleContractMultipleData(composableCowContract, 'singleOrders', input, DEFAULT_LISTENER_OPTIONS) return useMemo(() => { - const loadedResults = results.filter((result) => !result.loading) + const loadedResults = results.filter((result) => !result.loading && result.valid) if (loadedResults.length !== ordersInfo.length) return null return ordersInfo.reduce((acc, val, index) => { - acc[val.id] = !!loadedResults[index].result?.[0] + acc[val.id] = loadedResults[index].result?.[0] return acc }, {} as TwapOrdersAuthResult) }, [ordersInfo, results]) diff --git a/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx b/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx index 0e598b2546..1b7a263e1d 100644 --- a/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx +++ b/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx @@ -10,7 +10,7 @@ export interface TwapFormStateParams { export enum TwapFormState { LOADING_SAFE_INFO, NOT_SAFE, - ORDER_NOT_SPECIFIED, // TODO: reveal details + ORDER_NOT_SPECIFIED, NEED_FALLBACK_HANDLER, CAN_CREATE_ORDER, } diff --git a/src/modules/twap/pure/PrimaryActionButton/index.tsx b/src/modules/twap/pure/PrimaryActionButton/index.tsx index d82a740e12..a6f99a75ff 100644 --- a/src/modules/twap/pure/PrimaryActionButton/index.tsx +++ b/src/modules/twap/pure/PrimaryActionButton/index.tsx @@ -10,7 +10,6 @@ export interface PrimaryActionButtonContext { setFallbackHandler(): void } -// TODO: extend with common trade widget states // TODO: set correct buttons text const buttonsMap: Record JSX.Element> = { [TwapFormState.LOADING_SAFE_INFO]: () => ( diff --git a/src/modules/twap/services/createTwapOrderTxs.test.ts b/src/modules/twap/services/createTwapOrderTxs.test.ts index 1b758d086a..93ec86f47e 100644 --- a/src/modules/twap/services/createTwapOrderTxs.test.ts +++ b/src/modules/twap/services/createTwapOrderTxs.test.ts @@ -1,12 +1,17 @@ import { SupportedChainId } from '@cowprotocol/cow-sdk' import { CurrencyAmount } from '@uniswap/sdk-core' +import { COW } from 'legacy/constants/tokens' +import { WETH_GOERLI } from 'legacy/utils/goerli/constants' + import { createTwapOrderTxs } from './createTwapOrderTxs' -import { COW } from '../../../legacy/constants/tokens' -import { WETH_GOERLI } from '../../../legacy/utils/goerli/constants' +import { COMPOSABLE_COW_ADDRESS } from '../../advancedOrders' import { TwapOrderCreationContext } from '../hooks/useTwapOrderCreationContext' import { TWAPOrder } from '../types' +import { buildTwapOrderParamsStruct } from '../utils/buildTwapOrderParamsStruct' + +const chainId = SupportedChainId.GOERLI const order: TWAPOrder = { sellAmount: CurrencyAmount.fromRawAmount(COW[SupportedChainId.GOERLI], 100_000_000_000), @@ -33,9 +38,11 @@ describe('Create TWAP order', () => { approveFn = jest.fn().mockReturnValue(APPROVE_TX_DATA) context = { - chainId: SupportedChainId.GOERLI, safeAppsSdk: null as any, - composableCowContract: { interface: { encodeFunctionData: createCowFn } } as any, + composableCowContract: { + interface: { encodeFunctionData: createCowFn }, + address: COMPOSABLE_COW_ADDRESS[chainId], + } as any, needsApproval: false, spender: '0xB4FBF271143F4FBf7B91A5ded31805e42b222222', erc20Contract: { interface: { encodeFunctionData: approveFn } } as any, @@ -43,7 +50,8 @@ describe('Create TWAP order', () => { }) it('When sell token is approved, then should generate only creation transaction', () => { - const result = createTwapOrderTxs(order, { ...context, needsApproval: false }) + const paramsStruct = buildTwapOrderParamsStruct(chainId, order) + const result = createTwapOrderTxs(order, paramsStruct, { ...context, needsApproval: false }) expect(createCowFn).toHaveBeenCalledTimes(1) expect(createCowFn.mock.calls[0]).toMatchSnapshot() @@ -53,7 +61,8 @@ describe('Create TWAP order', () => { }) it('When sell token is NOT approved, then should generate approval and creation transactions', () => { - const result = createTwapOrderTxs(order, { ...context, needsApproval: true }) + const paramsStruct = buildTwapOrderParamsStruct(chainId, order) + const result = createTwapOrderTxs(order, paramsStruct, { ...context, needsApproval: true }) expect(createCowFn).toHaveBeenCalledTimes(1) expect(createCowFn.mock.calls[0]).toMatchSnapshot() diff --git a/src/modules/twap/services/createTwapOrderTxs.ts b/src/modules/twap/services/createTwapOrderTxs.ts index ed6d12c8bf..d734f6de52 100644 --- a/src/modules/twap/services/createTwapOrderTxs.ts +++ b/src/modules/twap/services/createTwapOrderTxs.ts @@ -1,51 +1,21 @@ -import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { defaultAbiCoder } from '@ethersproject/abi' -import { hexZeroPad } from '@ethersproject/bytes' import { MetaTransactionData } from '@safe-global/safe-core-sdk-types' -import { COMPOSABLE_COW_ADDRESS } from 'modules/advancedOrders' - -import { IConditionalOrder } from 'abis/types/ComposableCoW' - -import { TWAP_HANDLER_ADDRESS, TWAP_ORDER_STRUCT } from '../const' import { TwapOrderCreationContext } from '../hooks/useTwapOrderCreationContext' -import { TWAPOrder, TWAPOrderStruct } from '../types' - -function getTwapOrderParamsStruct( - chainId: SupportedChainId, - order: TWAPOrder -): IConditionalOrder.ConditionalOrderParamsStruct { - const twapOrderData: TWAPOrderStruct = { - sellToken: order.sellAmount.currency.address, - buyToken: order.buyAmount.currency.address, - receiver: order.receiver, - partSellAmount: order.sellAmount.divide(order.numOfParts).quotient.toString(), - minPartLimit: order.buyAmount.divide(order.numOfParts).quotient.toString(), - t0: order.startTime, - n: order.numOfParts, - t: order.timeInterval, - span: order.span, - } +import { ConditionalOrderParams, TWAPOrder } from '../types' - return { - handler: TWAP_HANDLER_ADDRESS[chainId], - salt: hexZeroPad(Buffer.from(Date.now().toString(16), 'hex'), 32), - staticInput: defaultAbiCoder.encode([TWAP_ORDER_STRUCT], [twapOrderData]), - } -} - -export function createTwapOrderTxs(order: TWAPOrder, context: TwapOrderCreationContext): MetaTransactionData[] { - const { chainId, composableCowContract, needsApproval, erc20Contract, spender } = context +export function createTwapOrderTxs( + order: TWAPOrder, + paramsStruct: ConditionalOrderParams, + context: TwapOrderCreationContext +): MetaTransactionData[] { + const { composableCowContract, needsApproval, erc20Contract, spender } = context const sellTokenAddress = order.sellAmount.currency.address const sellAmountAtoms = order.sellAmount.quotient.toString() - // TODO: support other conditional orders (stop loss, GAT, etc.) - const creationParams = getTwapOrderParamsStruct(chainId, order) - const createOrderTx = { - to: COMPOSABLE_COW_ADDRESS[chainId], - data: composableCowContract.interface.encodeFunctionData('create', [creationParams, true]), + to: composableCowContract.address, + data: composableCowContract.interface.encodeFunctionData('create', [paramsStruct, true]), value: '0', operation: 0, } diff --git a/src/modules/twap/services/fetchTwapOrdersFromSafe.ts b/src/modules/twap/services/fetchTwapOrdersFromSafe.ts index 48356bb848..40dc8017db 100644 --- a/src/modules/twap/services/fetchTwapOrdersFromSafe.ts +++ b/src/modules/twap/services/fetchTwapOrdersFromSafe.ts @@ -10,6 +10,7 @@ import { ConditionalOrderParams } from '../types' export interface TwapOrdersSafeData { params: ConditionalOrderParams submissionDate: string + executionDate: string isExecuted: boolean } @@ -38,10 +39,13 @@ export async function fetchTwapOrdersFromSafe( if (!params) return null + const { isExecuted, submissionDate, executionDate } = result + return { params, - isExecuted: result.isExecuted, - submissionDate: result.submissionDate, + isExecuted, + submissionDate, + executionDate, } }) .filter(isTruthy) diff --git a/src/modules/twap/services/settleTwapOrder.test.ts b/src/modules/twap/services/settleTwapOrder.test.ts index 6c72905082..8045d79570 100644 --- a/src/modules/twap/services/settleTwapOrder.test.ts +++ b/src/modules/twap/services/settleTwapOrder.test.ts @@ -19,8 +19,10 @@ import { useTradeSpenderAddress } from 'common/hooks/useTradeSpenderAddress' import { settleTwapOrder } from './settleTwapOrder' +import { COMPOSABLE_COW_ADDRESS } from '../../advancedOrders' import { useTwapOrderCreationContext } from '../hooks/useTwapOrderCreationContext' import { TWAPOrder } from '../types' +import { buildTwapOrderParamsStruct } from '../utils/buildTwapOrderParamsStruct' jest.mock('modules/wallet/web3-react/hooks/useSafeAppsSdk') jest.mock('modules/advancedOrders/hooks/useComposableCowContract') @@ -39,6 +41,9 @@ const order: TWAPOrder = { timeInterval: 350, span: 0, } + +const paramsStruct = buildTwapOrderParamsStruct(chainId, order) + const useSafeAppsSdkMock = useSafeAppsSdk as jest.MockedFunction const useComposableCowContractMock = useComposableCowContract as jest.MockedFunction const useNeedsApprovalMock = useNeedsApproval as jest.MockedFunction @@ -52,6 +57,7 @@ describe('settleTwapOrder - integration test', () => { useSafeAppsSdkMock.mockReturnValue({ txs: { send: () => Promise.resolve({ safeTxHash: '0x00b' }) } } as any) useComposableCowContractMock.mockReturnValue({ interface: { encodeFunctionData: () => '0xCREATE_COW_TX_DATA' }, + address: COMPOSABLE_COW_ADDRESS[chainId], } as any) useNeedsApprovalMock.mockReturnValue(true) useTokenContractMock.mockReturnValue({ interface: { encodeFunctionData: () => '0xAPPROVE_TX_DATA' } } as any) @@ -70,7 +76,7 @@ describe('settleTwapOrder - integration test', () => { if (!context) return null - return settleTwapOrder(order, context) + return settleTwapOrder(order, paramsStruct, context) }) expect(await result.current).toEqual({ safeTxHash: '0x00b' }) diff --git a/src/modules/twap/services/settleTwapOrder.ts b/src/modules/twap/services/settleTwapOrder.ts index e58df7fcd4..e11efa71b6 100644 --- a/src/modules/twap/services/settleTwapOrder.ts +++ b/src/modules/twap/services/settleTwapOrder.ts @@ -3,20 +3,16 @@ import { SendTransactionsResponse } from '@safe-global/safe-apps-sdk' import { createTwapOrderTxs } from './createTwapOrderTxs' import { TwapOrderCreationContext } from '../hooks/useTwapOrderCreationContext' -import { TWAPOrder } from '../types' +import { ConditionalOrderParams, TWAPOrder } from '../types' export async function settleTwapOrder( order: TWAPOrder, + paramsStruct: ConditionalOrderParams, context: TwapOrderCreationContext ): Promise { const { safeAppsSdk } = context - const txs = createTwapOrderTxs(order, context) + const txs = createTwapOrderTxs(order, paramsStruct, context) - const response = await safeAppsSdk.txs.send({ txs }) - - // TODO: process the sent transaction - console.log('TWAP order: ', response) - - return response + return await safeAppsSdk.txs.send({ txs }) } diff --git a/src/modules/twap/state/twapOrdersListAtom.ts b/src/modules/twap/state/twapOrdersListAtom.ts index c1168ef674..43533bb153 100644 --- a/src/modules/twap/state/twapOrdersListAtom.ts +++ b/src/modules/twap/state/twapOrdersListAtom.ts @@ -9,6 +9,7 @@ import { deepEqual } from 'utils/deepEqual' import { TwapOrderItem } from '../types' import { emulateTwapAsOrder } from '../utils/emulateTwapAsOrder' +import { updateTwapOrdersList } from '../utils/updateTwapOrdersList' export type TwapOrdersList = { [key: string]: TwapOrderItem } @@ -16,12 +17,19 @@ export const twapOrdersListAtom = atomWithStorage('twap-orders-l export const updateTwapOrdersListAtom = atom(null, (get, set, nextState: TwapOrdersList) => { const currentState = get(twapOrdersListAtom) + const newState = updateTwapOrdersList(currentState, nextState) - if (!deepEqual(currentState, nextState)) { - set(twapOrdersListAtom, nextState) + if (!deepEqual(currentState, newState)) { + set(twapOrdersListAtom, newState) } }) +export const addTwapOrderToListAtom = atom(null, (get, set, order: TwapOrderItem) => { + const currentState = get(twapOrdersListAtom) + + set(twapOrdersListAtom, { ...currentState, [order.id]: order }) +}) + export const emulatedTwapOrdersAtom = atom((get) => { const { account, chainId } = get(walletInfoAtom) const tokens = get(tokensByAddressAtom) diff --git a/src/modules/twap/state/twapPartOrdersAtom.ts b/src/modules/twap/state/twapPartOrdersAtom.ts index d26582ed92..bba7770079 100644 --- a/src/modules/twap/state/twapPartOrdersAtom.ts +++ b/src/modules/twap/state/twapPartOrdersAtom.ts @@ -27,7 +27,7 @@ export const updateTwapPartOrdersAtom = atom(null, (get, set, nextState: TwapPar } }) -export const twapPartOrdersListAtom = atom((get) => { +export const twapPartOrdersListAtom = atom((get) => { const { account, chainId } = get(walletInfoAtom) if (!account || !chainId) return [] diff --git a/src/modules/twap/types.ts b/src/modules/twap/types.ts index b974ede035..61d1cf02f9 100644 --- a/src/modules/twap/types.ts +++ b/src/modules/twap/types.ts @@ -59,4 +59,4 @@ export interface TwapOrderInfo { isExpired: boolean } -export type TwapOrdersAuthResult = { [key: string]: boolean } +export type TwapOrdersAuthResult = { [key: string]: boolean | undefined } diff --git a/src/modules/twap/updaters/TwapOrdersUpdater.tsx b/src/modules/twap/updaters/TwapOrdersUpdater.tsx index 209b00fe48..e9200e724a 100644 --- a/src/modules/twap/updaters/TwapOrdersUpdater.tsx +++ b/src/modules/twap/updaters/TwapOrdersUpdater.tsx @@ -5,7 +5,7 @@ import { SupportedChainId } from '@cowprotocol/cow-sdk' import { ComposableCoW } from 'abis/types' -import { TWAP_NOT_PENDING_STATUSES } from '../const' +import { TWAP_PENDING_STATUSES } from '../const' import { useFetchTwapOrdersFromSafe } from '../hooks/useFetchTwapOrdersFromSafe' import { useFetchTwapPartOrders } from '../hooks/useFetchTwapPartOrders' import { useTwapDiscreteOrders } from '../hooks/useTwapDiscreteOrders' @@ -46,21 +46,21 @@ export function TwapOrdersUpdater(props: { }, [ordersSafeData]) // Here we can split all orders in two groups: 1. Not signed + expired, 2. Open + cancelled - const openOrCancelledOrders = useMemo(() => { + const pendingOrCancelledOrders = useMemo(() => { return allOrdersInfo.filter((info) => { - const existedOrder = twapOrdersList[info.id] + const existingOrder = twapOrdersList[info.id] - if (existedOrder) { - return !TWAP_NOT_PENDING_STATUSES.includes(existedOrder.status) + if (existingOrder) { + return TWAP_PENDING_STATUSES.includes(existingOrder.status) } return !info.isExpired && info.safeData.isExecuted }) }, [allOrdersInfo, twapOrdersList]) - const partOrders = useFetchTwapPartOrders(safeAddress, chainId, composableCowContract, openOrCancelledOrders) - // Here we know which orders are cancelled: if it's auth !== true, then it's cancelled - const ordersAuthResult = useTwapOrdersAuthMulticall(safeAddress, composableCowContract, openOrCancelledOrders) + const partOrders = useFetchTwapPartOrders(safeAddress, chainId, composableCowContract, pendingOrCancelledOrders) + // Here we know which orders are cancelled: if it's auth === false, then it's cancelled + const ordersAuthResult = useTwapOrdersAuthMulticall(safeAddress, composableCowContract, pendingOrCancelledOrders) useEffect(() => { if (!ordersAuthResult || !twapDiscreteOrders) return diff --git a/src/modules/twap/utils/buildTwapOrderParamsStruct.ts b/src/modules/twap/utils/buildTwapOrderParamsStruct.ts new file mode 100644 index 0000000000..a04c55cb96 --- /dev/null +++ b/src/modules/twap/utils/buildTwapOrderParamsStruct.ts @@ -0,0 +1,19 @@ +import { SupportedChainId } from '@cowprotocol/cow-sdk' +import { defaultAbiCoder } from '@ethersproject/abi' +import { hexZeroPad } from '@ethersproject/bytes' + +import { twapOrderToStruct } from './twapOrderToStruct' + +import { TWAP_HANDLER_ADDRESS, TWAP_ORDER_STRUCT } from '../const' +import { ConditionalOrderParams, TWAPOrder } from '../types' + +// TODO: support other conditional orders (stop loss, GAT, etc.) +export function buildTwapOrderParamsStruct(chainId: SupportedChainId, order: TWAPOrder): ConditionalOrderParams { + const twapOrderData = twapOrderToStruct(order) + + return { + handler: TWAP_HANDLER_ADDRESS[chainId], + salt: hexZeroPad(Buffer.from(Date.now().toString(16), 'hex'), 32), + staticInput: defaultAbiCoder.encode([TWAP_ORDER_STRUCT], [twapOrderData]), + } +} diff --git a/src/modules/twap/utils/buildTwapOrdersItems.ts b/src/modules/twap/utils/buildTwapOrdersItems.ts index 7445c6bed3..3b2084f771 100644 --- a/src/modules/twap/utils/buildTwapOrdersItems.ts +++ b/src/modules/twap/utils/buildTwapOrdersItems.ts @@ -28,13 +28,14 @@ function getTwapOrderItem( safeAddress: string, safeData: TwapOrdersSafeData, id: string, - authorized: boolean, + authorized: boolean | undefined, discreteOrder: Order | undefined ): TwapOrderItem { - const { params, submissionDate, isExecuted } = safeData + const { params, isExecuted, submissionDate } = safeData + const executionDate = new Date(safeData.executionDate) const order = parseTwapOrderStruct(params.staticInput) - const status = getTwapOrderStatus(order, isExecuted, authorized, discreteOrder) + const status = getTwapOrderStatus(order, isExecuted, executionDate, authorized, discreteOrder) return { order, diff --git a/src/modules/twap/utils/getTwapOrderStatus.ts b/src/modules/twap/utils/getTwapOrderStatus.ts index b12d3ef727..4590514e0e 100644 --- a/src/modules/twap/utils/getTwapOrderStatus.ts +++ b/src/modules/twap/utils/getTwapOrderStatus.ts @@ -1,18 +1,23 @@ +import ms from 'ms.macro' + import { Order, PENDING_STATES } from 'legacy/state/orders/actions' import { TwapOrderStatus, TWAPOrderStruct } from '../types' +const AUTH_THRESHOLD = ms`1m` + export function getTwapOrderStatus( order: TWAPOrderStruct, isExecuted: boolean, - auth: boolean, + executionDate: Date, + auth: boolean | undefined, discreteOrder: Order | undefined ): TwapOrderStatus { if (isTwapOrderExpired(order)) return TwapOrderStatus.Expired if (!isExecuted) return TwapOrderStatus.WaitSigning - if (!auth) return TwapOrderStatus.Cancelled + if (shouldCheckAuth(executionDate) && auth === false) return TwapOrderStatus.Cancelled if (discreteOrder && PENDING_STATES.includes(discreteOrder.status)) return TwapOrderStatus.Pending @@ -26,3 +31,14 @@ export function isTwapOrderExpired(order: TWAPOrderStruct): boolean { return nowTimestamp > endTime } + +/** + * ComposableCow.singleOrders returns false by default + * To avoid false-positive values, we should not check authorized flag within first minute after execution time + */ +function shouldCheckAuth(executionDate: Date): boolean { + const executionTimestamp = executionDate.getTime() + const nowTimestamp = Date.now() + + return nowTimestamp > executionTimestamp + AUTH_THRESHOLD +} diff --git a/src/modules/twap/utils/twapOrderToStruct.ts b/src/modules/twap/utils/twapOrderToStruct.ts new file mode 100644 index 0000000000..10f0b2482b --- /dev/null +++ b/src/modules/twap/utils/twapOrderToStruct.ts @@ -0,0 +1,15 @@ +import { TWAPOrder, TWAPOrderStruct } from '../types' + +export function twapOrderToStruct(order: TWAPOrder): TWAPOrderStruct { + return { + sellToken: order.sellAmount.currency.address, + buyToken: order.buyAmount.currency.address, + receiver: order.receiver, + partSellAmount: order.sellAmount.divide(order.numOfParts).quotient.toString(), + minPartLimit: order.buyAmount.divide(order.numOfParts).quotient.toString(), + t0: order.startTime, + n: order.numOfParts, + t: order.timeInterval, + span: order.span, + } +} diff --git a/src/modules/twap/utils/updateTwapOrdersList.ts b/src/modules/twap/utils/updateTwapOrdersList.ts new file mode 100644 index 0000000000..6277161a92 --- /dev/null +++ b/src/modules/twap/utils/updateTwapOrdersList.ts @@ -0,0 +1,25 @@ +import { TWAP_PENDING_STATUSES } from '../const' +import { TwapOrdersList } from '../state/twapOrdersListAtom' + +/** + * + * This function merge current state of TWAP orders list with a new one + * It only updates an order if it in a pending state + */ +export function updateTwapOrdersList(currentState: TwapOrdersList, nextState: TwapOrdersList): TwapOrdersList { + // Filter nextState and left only orders with pending statuses + const newState = Object.keys(nextState).reduce((acc, orderId) => { + const currentOrder = currentState[orderId] + + // Insert an order if it's not exist in the state + // Update an order only if it's in pending state + // Otherwise, don't update it + if (!currentOrder || TWAP_PENDING_STATUSES.includes(currentOrder.status)) { + acc[orderId] = nextState[orderId] + } + + return acc + }, {}) + + return { ...currentState, ...newState } +} diff --git a/src/utils/orderUtils/getIsComposableCowChildOrder.ts b/src/utils/orderUtils/getIsComposableCowChildOrder.ts new file mode 100644 index 0000000000..2b1e1387da --- /dev/null +++ b/src/utils/orderUtils/getIsComposableCowChildOrder.ts @@ -0,0 +1,5 @@ +import { ComposableCowInfo } from 'common/types' + +export function getIsComposableCowChildOrder(order: { composableCowInfo?: ComposableCowInfo }): boolean { + return !!order.composableCowInfo?.parentId +} diff --git a/src/utils/orderUtils/getIsComposableCowParentOrder.ts b/src/utils/orderUtils/getIsComposableCowParentOrder.ts new file mode 100644 index 0000000000..f45f07ee6b --- /dev/null +++ b/src/utils/orderUtils/getIsComposableCowParentOrder.ts @@ -0,0 +1,5 @@ +import { ComposableCowInfo } from 'common/types' + +export function getIsComposableCowParentOrder(order: { composableCowInfo?: ComposableCowInfo }): boolean { + return !!order.composableCowInfo?.id +} From 3aa51c4437ccc8cbabc5f0554d17311a3320a74e Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Wed, 14 Jun 2023 16:17:09 +0600 Subject: [PATCH 19/67] ci: deploy on barn/staging automatically after creating a release (#2655) --- .github/workflows/ci.yml | 13 ------------- .github/workflows/release-please.yml | 11 +++++++++++ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4170bc5847..db410501bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -159,19 +159,6 @@ jobs: with: env_name: dev - vercel-pre-prod: - # Deploys to Vercel staging and barn environments - name: Vercel pre-prod - needs: [test, lint] - if: startsWith(github.ref, 'refs/tags/v') - uses: ./.github/workflows/vercel.yml - secrets: inherit - strategy: - matrix: - env_name: [barn, staging] # deploys both in parallel - with: - env_name: ${{ matrix.env_name }} - vercel-prod: # Deploys to Vercel prod environment name: Vercel prod diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index e3e63e5e33..db021674c4 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -14,7 +14,18 @@ jobs: runs-on: ubuntu-latest steps: - uses: google-github-actions/release-please-action@v3 + name: release with: release-type: node package-name: release-please-action default-branch: main + # Deploys to Vercel staging and barn environments + - uses: ./.github/workflows/vercel.yml + name: deploy + if: ${{ steps.release.outputs.release_created }} + secrets: inherit + strategy: + matrix: + env_name: [ barn, staging ] # deploys both in parallel + with: + env_name: ${{ matrix.env_name }} From f92ffff21441298c8e981e039a7708d0bb3bf510 Mon Sep 17 00:00:00 2001 From: Leandro Date: Wed, 14 Jun 2023 06:56:28 -0700 Subject: [PATCH 20/67] feat(twap): launch darkly feature flag (#2654) * refactor: add subfolder hooks/featureFlags * feat: add hook for advancedOrdersEnabled feature flag * feat: add utils for building main menu items * feat: add useMenuItems hook * feat: use mainMenu from hook on header * feat: add FeatureGuard to TradeWigetLinks with advancedOrdersEnabled flag * refactor: remove ADVANCED_ORDERS_FEATURE_FLAG * refactor: add useTradeRouteContext * feat: redirect from advanced to swap when flag is off * chore: add clone-deep to package.json (was already a peer dep) * fix: make deep copy of MAIN_MENU to avoid mutating original * refactor: more specific useMemo deps to avoid unnecessary re-renders * fix: unit tests --- package.json | 4 ++- src/common/containers/FeatureGuard/index.tsx | 2 +- .../{ => featureFlags}/useFeatureFlags.ts | 0 .../useIsEthFlowBundlingEnabled.ts | 2 +- .../useIsTxBundlingEnabled.ts | 2 +- .../hooks/useIsAdvancedOrdersEnabled.ts | 7 ++++ src/constants/featureFlags.ts | 2 -- src/legacy/components/Header/index.tsx | 23 +++++++------ .../containers/TradeWidgetLinks/index.tsx | 22 +++--------- .../LimitOrdersConfirmModal/index.tsx | 4 +-- .../containers/LimitOrdersWidget/index.tsx | 6 ++-- .../containers/SettingsWidget/index.tsx | 2 +- .../hooks/useHandleOrderPlacement.test.ts | 6 ++-- .../hooks/useIsSafeApprovalBundle.ts | 2 +- .../limitOrders/hooks/useTradeFlowContext.ts | 4 +-- src/modules/mainMenu/constants/mainMenu.ts | 15 ++------ src/modules/mainMenu/hooks.ts | 12 +++++++ src/modules/mainMenu/index.ts | 2 ++ src/modules/mainMenu/utils.ts | 34 +++++++++++++++++++ src/modules/swap/hooks/useIsSafeEthFlow.ts | 2 +- .../swap/hooks/useSwapButtonContext.ts | 4 +-- .../trade/hooks/useTradeRouteContext.ts | 19 +++++++++++ .../hooks/useTradeFormValidationContext.ts | 2 +- src/pages/AdvancedOrders/index.tsx | 15 ++++++++ src/utils/featureFlags.ts | 4 +-- yarn.lock | 5 +++ 26 files changed, 136 insertions(+), 66 deletions(-) rename src/common/hooks/{ => featureFlags}/useFeatureFlags.ts (100%) rename src/common/hooks/{ => featureFlags}/useIsEthFlowBundlingEnabled.ts (78%) rename src/common/hooks/{ => featureFlags}/useIsTxBundlingEnabled.ts (77%) create mode 100644 src/common/hooks/useIsAdvancedOrdersEnabled.ts create mode 100644 src/modules/mainMenu/hooks.ts create mode 100644 src/modules/mainMenu/utils.ts create mode 100644 src/modules/trade/hooks/useTradeRouteContext.ts diff --git a/package.json b/package.json index f115069b3a..48e3220821 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,6 @@ "@sentry/react": "^7.3.0", "@sentry/tracing": "^7.3.0", "@sentry/webpack-plugin": "^1.17.1", - "@types/ms": "^0.7.31", "@uniswap/governance": "^1.0.2", "@uniswap/liquidity-staker": "^1.0.2", "@uniswap/merkle-distributor": "1.0.1", @@ -128,6 +127,7 @@ "bnc-sdk": "^4.6.0", "buffer": "^6.0.3", "cids": "^1.0.0", + "clone-deep": "^4.0.1", "copy-to-clipboard": "^3.2.0", "cross-env": "^7.0.3", "d3": "^7.8.1", @@ -208,8 +208,10 @@ "@testing-library/react-hooks": "^8.0.1", "@testing-library/user-event": "^13.5.0", "@typechain/ethers-v5": "^10.2.0", + "@types/clone-deep": "^4.0.1", "@types/d3": "^7.4.0", "@types/jest": "^27.5.2", + "@types/ms": "^0.7.31", "@types/ms.macro": "^2.0.0", "@types/node": "^16.18.11", "@types/react": "^18.0.26", diff --git a/src/common/containers/FeatureGuard/index.tsx b/src/common/containers/FeatureGuard/index.tsx index 136477dba1..b731802dd7 100644 --- a/src/common/containers/FeatureGuard/index.tsx +++ b/src/common/containers/FeatureGuard/index.tsx @@ -1,6 +1,6 @@ import { ReactNode } from 'react' -import { useFeatureFlags } from 'common/hooks/useFeatureFlags' +import { useFeatureFlags } from 'common/hooks/featureFlags/useFeatureFlags' interface FeatureGuardProps { featureFlag: string diff --git a/src/common/hooks/useFeatureFlags.ts b/src/common/hooks/featureFlags/useFeatureFlags.ts similarity index 100% rename from src/common/hooks/useFeatureFlags.ts rename to src/common/hooks/featureFlags/useFeatureFlags.ts diff --git a/src/common/hooks/useIsEthFlowBundlingEnabled.ts b/src/common/hooks/featureFlags/useIsEthFlowBundlingEnabled.ts similarity index 78% rename from src/common/hooks/useIsEthFlowBundlingEnabled.ts rename to src/common/hooks/featureFlags/useIsEthFlowBundlingEnabled.ts index 82ca3e4dc8..83e356e127 100644 --- a/src/common/hooks/useIsEthFlowBundlingEnabled.ts +++ b/src/common/hooks/featureFlags/useIsEthFlowBundlingEnabled.ts @@ -1,6 +1,6 @@ import { useIsBundlingSupported } from 'modules/wallet' -import { useFeatureFlags } from 'common/hooks/useFeatureFlags' +import { useFeatureFlags } from 'common/hooks/featureFlags/useFeatureFlags' export function useIsEthFlowBundlingEnabled(): boolean { const isBundlingSupported = useIsBundlingSupported() diff --git a/src/common/hooks/useIsTxBundlingEnabled.ts b/src/common/hooks/featureFlags/useIsTxBundlingEnabled.ts similarity index 77% rename from src/common/hooks/useIsTxBundlingEnabled.ts rename to src/common/hooks/featureFlags/useIsTxBundlingEnabled.ts index c258ed745e..d4fc5a328d 100644 --- a/src/common/hooks/useIsTxBundlingEnabled.ts +++ b/src/common/hooks/featureFlags/useIsTxBundlingEnabled.ts @@ -1,6 +1,6 @@ import { useIsBundlingSupported } from 'modules/wallet' -import { useFeatureFlags } from 'common/hooks/useFeatureFlags' +import { useFeatureFlags } from 'common/hooks/featureFlags/useFeatureFlags' export function useIsTxBundlingEnabled(): boolean { const isBundlingSupported = useIsBundlingSupported() diff --git a/src/common/hooks/useIsAdvancedOrdersEnabled.ts b/src/common/hooks/useIsAdvancedOrdersEnabled.ts new file mode 100644 index 0000000000..2fb41c4c8b --- /dev/null +++ b/src/common/hooks/useIsAdvancedOrdersEnabled.ts @@ -0,0 +1,7 @@ +import { useFeatureFlags } from './featureFlags/useFeatureFlags' + +export function useIsAdvancedOrdersEnabled(): boolean { + const { advancedOrdersEnabled } = useFeatureFlags() + + return advancedOrdersEnabled +} diff --git a/src/constants/featureFlags.ts b/src/constants/featureFlags.ts index b4c9f01ee3..bb23b1417d 100644 --- a/src/constants/featureFlags.ts +++ b/src/constants/featureFlags.ts @@ -4,5 +4,3 @@ export const ordersTableFeatures = { DISPLAY_EST_EXECUTION_PRICE: false, DISPLAY_EXECUTION_TIME: false, } - -export const ADVANCED_ORDERS_FEATURE_FLAG = 'advanced-orders' diff --git a/src/legacy/components/Header/index.tsx b/src/legacy/components/Header/index.tsx index 1eb14be138..30981bc97e 100644 --- a/src/legacy/components/Header/index.tsx +++ b/src/legacy/components/Header/index.tsx @@ -1,7 +1,6 @@ -import React, { useState, useEffect, useCallback, useMemo } from 'react' +import React, { useCallback, useEffect, useMemo, useState } from 'react' -import { SupportedChainId as ChainId } from '@cowprotocol/cow-sdk' -import { SupportedChainId } from '@cowprotocol/cow-sdk' +import { SupportedChainId as ChainId, SupportedChainId } from '@cowprotocol/cow-sdk' import SVG from 'react-inlinesvg' import { useNavigate } from 'react-router-dom' @@ -9,14 +8,14 @@ import { useNavigate } from 'react-router-dom' import { toggleDarkModeAnalytics } from 'legacy/components/analytics' import CowBalanceButton from 'legacy/components/CowBalanceButton' import NetworkSelector from 'legacy/components/Header/NetworkSelector' -import { useMediaQuery, upToSmall, upToMedium, upToLarge, LargeAndUp } from 'legacy/hooks/useMediaQuery' +import { LargeAndUp, upToLarge, upToMedium, upToSmall, useMediaQuery } from 'legacy/hooks/useMediaQuery' import { useDarkModeManager } from 'legacy/state/user/hooks' import { cowSwapLogo } from 'legacy/theme/cowSwapAssets' import { supportedChainId } from 'legacy/utils/supportedChainId' import { addBodyClass, removeBodyClass } from 'legacy/utils/toggleBodyClass' import { OrdersPanel } from 'modules/account/containers/OrdersPanel' -import { MAIN_MENU, MainMenuContext } from 'modules/mainMenu' +import { MainMenuContext, useMenuItems } from 'modules/mainMenu' import { MenuTree } from 'modules/mainMenu/pure/MenuTree' import { useSwapRawState } from 'modules/swap/hooks/useSwapRawState' import { useNativeCurrencyBalances } from 'modules/tokens/hooks/useCurrencyBalance' @@ -29,16 +28,16 @@ import { Routes } from 'constants/routes' import MobileMenuIcon from './MobileMenuIcon' import { - Wrapper, - Title, - LogoImage, - HeaderModWrapper, - UniIcon, AccountElement, BalanceText, HeaderControls, HeaderElement, + HeaderModWrapper, HeaderRow, + LogoImage, + Title, + UniIcon, + Wrapper, } from './styled' // Assets @@ -70,6 +69,8 @@ export default function Header() { !isOrdersPanelOpen && removeBodyClass('noScroll') } + const menuItems = useMenuItems() + const navigate = useNavigate() const handleBalanceButtonClick = () => navigate('/account') const isUpToLarge = useMediaQuery(upToLarge) @@ -128,7 +129,7 @@ export default function Header() { - + diff --git a/src/modules/application/containers/TradeWidgetLinks/index.tsx b/src/modules/application/containers/TradeWidgetLinks/index.tsx index a51d26fbf2..8e895d31d6 100644 --- a/src/modules/application/containers/TradeWidgetLinks/index.tsx +++ b/src/modules/application/containers/TradeWidgetLinks/index.tsx @@ -1,27 +1,15 @@ -import { useMemo } from 'react' - import { Trans } from '@lingui/macro' -import { useTradeState } from 'modules/trade/hooks/useTradeState' +import { useTradeRouteContext } from 'modules/trade/hooks/useTradeRouteContext' import { parameterizeTradeRoute } from 'modules/trade/utils/parameterizeTradeRoute' -import { ADVANCED_ORDERS_FEATURE_FLAG } from 'constants/featureFlags' +import { FeatureGuard } from 'common/containers/FeatureGuard' import { Routes } from 'constants/routes' -import { FeatureFlag } from 'utils/featureFlags' import * as styledEl from './styled' export function TradeWidgetLinks() { - const { state } = useTradeState() - - const tradeContext = useMemo( - () => ({ - inputCurrencyId: state?.inputCurrencyId || undefined, - outputCurrencyId: state?.outputCurrencyId || undefined, - chainId: state?.chainId?.toString(), - }), - [state] - ) + const tradeContext = useTradeRouteContext() return ( @@ -43,7 +31,7 @@ export function TradeWidgetLinks() { - {FeatureFlag.get(ADVANCED_ORDERS_FEATURE_FLAG) && ( + (isActive ? 'active' : undefined)} @@ -55,7 +43,7 @@ export function TradeWidgetLinks() { - )} + ) } diff --git a/src/modules/limitOrders/containers/LimitOrdersConfirmModal/index.tsx b/src/modules/limitOrders/containers/LimitOrdersConfirmModal/index.tsx index aeb0462196..bb40ab4381 100644 --- a/src/modules/limitOrders/containers/LimitOrdersConfirmModal/index.tsx +++ b/src/modules/limitOrders/containers/LimitOrdersConfirmModal/index.tsx @@ -13,9 +13,9 @@ import { executionPriceAtom } from 'modules/limitOrders/state/executionPriceAtom import { limitOrdersSettingsAtom } from 'modules/limitOrders/state/limitOrdersSettingsAtom' import { limitRateAtom } from 'modules/limitOrders/state/limitRateAtom' import { partiallyFillableOverrideAtom } from 'modules/limitOrders/state/partiallyFillableOverride' -import { TradeConfirmModal, useTradeConfirmActions, TradeConfirmation } from 'modules/trade' +import { TradeConfirmation, TradeConfirmModal, useTradeConfirmActions } from 'modules/trade' -import { useFeatureFlags } from 'common/hooks/useFeatureFlags' +import { useFeatureFlags } from 'common/hooks/featureFlags/useFeatureFlags' import { useRateInfoParams } from 'common/hooks/useRateInfoParams' import { CurrencyPreviewInfo } from 'common/pure/CurrencyAmountPreview' import { TokenSymbol } from 'common/pure/TokenSymbol' diff --git a/src/modules/limitOrders/containers/LimitOrdersWidget/index.tsx b/src/modules/limitOrders/containers/LimitOrdersWidget/index.tsx index fb01752552..f6817a6f92 100644 --- a/src/modules/limitOrders/containers/LimitOrdersWidget/index.tsx +++ b/src/modules/limitOrders/containers/LimitOrdersWidget/index.tsx @@ -15,11 +15,11 @@ import { useLimitOrdersPriceImpactParams } from 'modules/limitOrders/hooks/useLi import { useSetupLimitOrderAmountsFromUrl } from 'modules/limitOrders/hooks/useSetupLimitOrderAmountsFromUrl' import { InfoBanner } from 'modules/limitOrders/pure/InfoBanner' import { partiallyFillableOverrideAtom } from 'modules/limitOrders/state/partiallyFillableOverride' -import { useSetupTradeState, TradeWidget } from 'modules/trade' +import { TradeWidget, useSetupTradeState } from 'modules/trade' import { useIsWrapOrUnwrap } from 'modules/trade/hooks/useIsWrapOrUnwrap' -import { useTradeQuote, useSetTradeQuoteParams } from 'modules/tradeQuote' +import { useSetTradeQuoteParams, useTradeQuote } from 'modules/tradeQuote' -import { useFeatureFlags } from 'common/hooks/useFeatureFlags' +import { useFeatureFlags } from 'common/hooks/featureFlags/useFeatureFlags' import { useRateInfoParams } from 'common/hooks/useRateInfoParams' import { CurrencyInfo } from 'common/pure/CurrencyInputPanel/types' diff --git a/src/modules/limitOrders/containers/SettingsWidget/index.tsx b/src/modules/limitOrders/containers/SettingsWidget/index.tsx index 86d7dd895a..7d8e3dcde3 100644 --- a/src/modules/limitOrders/containers/SettingsWidget/index.tsx +++ b/src/modules/limitOrders/containers/SettingsWidget/index.tsx @@ -4,7 +4,7 @@ import React, { useCallback, useState } from 'react' import { Menu, MenuItem } from '@reach/menu-button' -import { useFeatureFlags } from 'common/hooks/useFeatureFlags' +import { useFeatureFlags } from 'common/hooks/featureFlags/useFeatureFlags' import { ExpertModeModal } from 'common/pure/ExpertModeModal' import * as styledEl from './styled' diff --git a/src/modules/limitOrders/hooks/useHandleOrderPlacement.test.ts b/src/modules/limitOrders/hooks/useHandleOrderPlacement.test.ts index 2d0b1cdd5e..3ee0352b0a 100644 --- a/src/modules/limitOrders/hooks/useHandleOrderPlacement.test.ts +++ b/src/modules/limitOrders/hooks/useHandleOrderPlacement.test.ts @@ -9,13 +9,13 @@ import { safeBundleFlow } from 'modules/limitOrders/services/safeBundleFlow' import { tradeFlow } from 'modules/limitOrders/services/tradeFlow' import { TradeFlowContext } from 'modules/limitOrders/services/types' -import { useIsTxBundlingEnabled } from 'common/hooks/useIsTxBundlingEnabled' +import { useIsTxBundlingEnabled } from 'common/hooks/featureFlags/useIsTxBundlingEnabled' import { useNeedsApproval } from 'common/hooks/useNeedsApproval' import { withModalProvider } from 'utils/withModalProvider' import { useHandleOrderPlacement } from './useHandleOrderPlacement' -import { TradeConfirmActions } from '../../trade/hooks/useTradeConfirmActions' +import { TradeConfirmActions } from '../../trade' import { TradeAmounts } from '../../trade/types/TradeAmounts' import { limitOrdersRawStateAtom, updateLimitOrdersRawStateAtom } from '../state/limitOrdersRawStateAtom' import { defaultLimitOrdersSettings } from '../state/limitOrdersSettingsAtom' @@ -25,7 +25,7 @@ jest.mock('modules/limitOrders/services/safeBundleFlow') jest.mock('modules/limitOrders/hooks/useSafeBundleFlowContext') jest.mock('common/hooks/useNeedsApproval') -jest.mock('common/hooks/useIsTxBundlingEnabled') +jest.mock('common/hooks/featureFlags/useIsTxBundlingEnabled') const mockTradeFlow = tradeFlow as jest.MockedFunction const mockSafeBundleFlow = safeBundleFlow as jest.MockedFunction diff --git a/src/modules/limitOrders/hooks/useIsSafeApprovalBundle.ts b/src/modules/limitOrders/hooks/useIsSafeApprovalBundle.ts index 0b2bcc1dc0..aad313adb9 100644 --- a/src/modules/limitOrders/hooks/useIsSafeApprovalBundle.ts +++ b/src/modules/limitOrders/hooks/useIsSafeApprovalBundle.ts @@ -2,7 +2,7 @@ import { Currency, CurrencyAmount } from '@uniswap/sdk-core' import { Nullish } from 'types' -import { useIsTxBundlingEnabled } from 'common/hooks/useIsTxBundlingEnabled' +import { useIsTxBundlingEnabled } from 'common/hooks/featureFlags/useIsTxBundlingEnabled' import { useNeedsApproval } from 'common/hooks/useNeedsApproval' export function useIsSafeApprovalBundle(amount: Nullish>): boolean { diff --git a/src/modules/limitOrders/hooks/useTradeFlowContext.ts b/src/modules/limitOrders/hooks/useTradeFlowContext.ts index 62f14d61b2..523c0dc393 100644 --- a/src/modules/limitOrders/hooks/useTradeFlowContext.ts +++ b/src/modules/limitOrders/hooks/useTradeFlowContext.ts @@ -10,14 +10,14 @@ import { useGP2SettlementContract } from 'legacy/hooks/useContract' import useENSAddress from 'legacy/hooks/useENSAddress' import { AppDispatch } from 'legacy/state' -import { useUploadAppData, useAppData } from 'modules/appData' +import { useAppData, useUploadAppData } from 'modules/appData' import { useRateImpact } from 'modules/limitOrders/hooks/useRateImpact' import { TradeFlowContext } from 'modules/limitOrders/services/types' import { limitOrdersSettingsAtom } from 'modules/limitOrders/state/limitOrdersSettingsAtom' import { useTradeQuote } from 'modules/tradeQuote' import { useGnosisSafeInfo, useWalletDetails, useWalletInfo } from 'modules/wallet' -import { useFeatureFlags } from 'common/hooks/useFeatureFlags' +import { useFeatureFlags } from 'common/hooks/featureFlags/useFeatureFlags' import { useLimitOrdersDerivedState } from './useLimitOrdersDerivedState' diff --git a/src/modules/mainMenu/constants/mainMenu.ts b/src/modules/mainMenu/constants/mainMenu.ts index 06ad41396a..96756960b6 100644 --- a/src/modules/mainMenu/constants/mainMenu.ts +++ b/src/modules/mainMenu/constants/mainMenu.ts @@ -11,12 +11,9 @@ import IMAGE_TERMS_AND_CONDITIONS from 'legacy/assets/cow-swap/terms-and-conditi import IMAGE_TWITTER from 'legacy/assets/cow-swap/twitter.svg' import { CONTRACTS_CODE_LINK, DISCORD_LINK, DOCS_LINK, DUNE_DASHBOARD_LINK, TWITTER_LINK } from 'legacy/constants' -import { ADVANCED_ORDERS_FEATURE_FLAG } from 'constants/featureFlags' import { Routes } from 'constants/routes' -import { FeatureFlag } from 'utils/featureFlags' -import { isNotNullish } from 'utils/isNotNullish' -import { BasicMenuLink, InternalLink, MainMenuItemId, MenuItemKind, MenuLink, MenuTreeItem } from '../types' +import { BasicMenuLink, InternalLink, MainMenuItemId, MenuItemKind, MenuTreeItem } from '../types' export const isBasicMenuLink = (item: any): item is BasicMenuLink => { return !!(item.title && item.url) @@ -50,15 +47,7 @@ export const MAIN_MENU: MenuTreeItem[] = [ title: 'Limit orders', url: Routes.LIMIT_ORDER, }, - FeatureFlag.get(ADVANCED_ORDERS_FEATURE_FLAG) - ? { - id: MainMenuItemId.ADVANCED_ORDERS, - kind: MenuItemKind.DYNAMIC_LINK, - title: 'Advanced orders', - url: Routes.ADVANCED_ORDERS, - } - : null, - ].filter(isNotNullish) as MenuLink[], + ], }, ], }, diff --git a/src/modules/mainMenu/hooks.ts b/src/modules/mainMenu/hooks.ts new file mode 100644 index 0000000000..3644d491dc --- /dev/null +++ b/src/modules/mainMenu/hooks.ts @@ -0,0 +1,12 @@ +import { useMemo } from 'react' + +import { useIsAdvancedOrdersEnabled } from 'common/hooks/useIsAdvancedOrdersEnabled' + +import { MenuTreeItem } from './types' +import { buildMainMenuTreeItems } from './utils' + +export function useMenuItems(): MenuTreeItem[] { + const isAdvancedOrdersEnabled = useIsAdvancedOrdersEnabled() + + return useMemo(() => buildMainMenuTreeItems({ isAdvancedOrdersEnabled }), [isAdvancedOrdersEnabled]) +} diff --git a/src/modules/mainMenu/index.ts b/src/modules/mainMenu/index.ts index 28508e16a2..52c64f81db 100644 --- a/src/modules/mainMenu/index.ts +++ b/src/modules/mainMenu/index.ts @@ -1,2 +1,4 @@ export * from './constants/mainMenu' export * from './types' +export * from './utils' +export * from './hooks' diff --git a/src/modules/mainMenu/utils.ts b/src/modules/mainMenu/utils.ts new file mode 100644 index 0000000000..7835aa5120 --- /dev/null +++ b/src/modules/mainMenu/utils.ts @@ -0,0 +1,34 @@ +import cloneDeep from 'clone-deep' + +import { MAIN_MENU } from './constants/mainMenu' +import { DropDownItem, MainMenuItemId, MenuItemKind, MenuTreeItem } from './types' + +import { Routes } from '../../constants/routes' + +const ADVANCED_ORDERS_MENU_TITLE = 'Advanced orders' + +export type BuildMainMenuTreeItemsParams = { + isAdvancedOrdersEnabled: boolean +} + +export function buildMainMenuTreeItems({ isAdvancedOrdersEnabled }: BuildMainMenuTreeItemsParams): MenuTreeItem[] { + if (!isAdvancedOrdersEnabled) { + return MAIN_MENU + } + + // Make a deep copy to avoid mutating original + const mainMenuCopy = cloneDeep(MAIN_MENU) + + // Assume trade menu is at the first position + const [tradeMenu] = mainMenuCopy + + // Add to the bottom of the list + ;(tradeMenu as DropDownItem).items[0].links.push({ + id: MainMenuItemId.ADVANCED_ORDERS, + kind: MenuItemKind.DYNAMIC_LINK, + title: ADVANCED_ORDERS_MENU_TITLE, + url: Routes.ADVANCED_ORDERS, + }) + + return mainMenuCopy +} diff --git a/src/modules/swap/hooks/useIsSafeEthFlow.ts b/src/modules/swap/hooks/useIsSafeEthFlow.ts index b963c32a7b..a108950381 100644 --- a/src/modules/swap/hooks/useIsSafeEthFlow.ts +++ b/src/modules/swap/hooks/useIsSafeEthFlow.ts @@ -1,4 +1,4 @@ -import { useIsEthFlowBundlingEnabled } from 'common/hooks/useIsEthFlowBundlingEnabled' +import { useIsEthFlowBundlingEnabled } from 'common/hooks/featureFlags/useIsEthFlowBundlingEnabled' import { useIsSwapEth } from './useIsSwapEth' diff --git a/src/modules/swap/hooks/useSwapButtonContext.ts b/src/modules/swap/hooks/useSwapButtonContext.ts index 7f4ccbf2ac..8a13005ddd 100644 --- a/src/modules/swap/hooks/useSwapButtonContext.ts +++ b/src/modules/swap/hooks/useSwapButtonContext.ts @@ -26,9 +26,9 @@ import { useWrappedToken } from 'modules/trade/hooks/useWrappedToken' import { useGnosisSafeInfo, useWalletDetails, useWalletInfo } from 'modules/wallet' import { useTradeApproveState } from 'common/containers/TradeApprove/useTradeApproveState' -import { useIsEthFlowBundlingEnabled } from 'common/hooks/useIsEthFlowBundlingEnabled' +import { useIsEthFlowBundlingEnabled } from 'common/hooks/featureFlags/useIsEthFlowBundlingEnabled' +import { useIsTxBundlingEnabled } from 'common/hooks/featureFlags/useIsTxBundlingEnabled' import { useIsSmartContractWallet } from 'common/hooks/useIsSmartContractWallet' -import { useIsTxBundlingEnabled } from 'common/hooks/useIsTxBundlingEnabled' import { formatSymbol } from 'utils/format' import { useSafeBundleEthFlowContext } from './useSafeBundleEthFlowContext' diff --git a/src/modules/trade/hooks/useTradeRouteContext.ts b/src/modules/trade/hooks/useTradeRouteContext.ts new file mode 100644 index 0000000000..8ca0975037 --- /dev/null +++ b/src/modules/trade/hooks/useTradeRouteContext.ts @@ -0,0 +1,19 @@ +import { useMemo } from 'react' + +import { useTradeState } from './useTradeState' + +import { TradeUrlParams } from '../types/TradeRawState' + +export function useTradeRouteContext(): TradeUrlParams { + const { state } = useTradeState() + const { inputCurrencyId, outputCurrencyId, chainId } = state || {} + + return useMemo( + () => ({ + inputCurrencyId: inputCurrencyId || undefined, + outputCurrencyId: outputCurrencyId || undefined, + chainId: chainId?.toString(), + }), + [inputCurrencyId, outputCurrencyId, chainId] + ) +} diff --git a/src/modules/tradeFormValidation/hooks/useTradeFormValidationContext.ts b/src/modules/tradeFormValidation/hooks/useTradeFormValidationContext.ts index f4ad4ad11f..0074bcfdf4 100644 --- a/src/modules/tradeFormValidation/hooks/useTradeFormValidationContext.ts +++ b/src/modules/tradeFormValidation/hooks/useTradeFormValidationContext.ts @@ -10,7 +10,7 @@ import { useTradeQuote } from 'modules/tradeQuote' import { useGnosisSafeInfo, useWalletDetails, useWalletInfo } from 'modules/wallet' import { useTradeApproveState } from 'common/containers/TradeApprove' -import { useIsTxBundlingEnabled } from 'common/hooks/useIsTxBundlingEnabled' +import { useIsTxBundlingEnabled } from 'common/hooks/featureFlags/useIsTxBundlingEnabled' import { TradeFormValidationCommonContext } from '../types' diff --git a/src/pages/AdvancedOrders/index.tsx b/src/pages/AdvancedOrders/index.tsx index 0a777311fc..a44807628b 100644 --- a/src/pages/AdvancedOrders/index.tsx +++ b/src/pages/AdvancedOrders/index.tsx @@ -1,10 +1,25 @@ +import { Navigate } from 'react-router-dom' + import { AdvancedOrdersWidget } from 'modules/advancedOrders' import { OrdersTableWidget } from 'modules/ordersTable' +import { useTradeRouteContext } from 'modules/trade/hooks/useTradeRouteContext' import * as styledEl from 'modules/trade/pure/TradePageLayout' +import { parameterizeTradeRoute } from 'modules/trade/utils/parameterizeTradeRoute' import { TradeFormValidationUpdater } from 'modules/tradeFormValidation' import { TwapFormWidget } from 'modules/twap' +import { useIsAdvancedOrdersEnabled } from 'common/hooks/useIsAdvancedOrdersEnabled' +import { Routes as RoutesEnum } from 'constants/routes' + export default function AdvancedOrdersPage() { + const isAdvancedOrdersEnabled = useIsAdvancedOrdersEnabled() + const tradeContext = useTradeRouteContext() + + if (!isAdvancedOrdersEnabled) { + // To prevent direct access when the flag is off + return + } + return ( <> {/*TODO: add isExpertMode value*/} diff --git a/src/utils/featureFlags.ts b/src/utils/featureFlags.ts index 7527a4705f..11d570c7a9 100644 --- a/src/utils/featureFlags.ts +++ b/src/utils/featureFlags.ts @@ -1,10 +1,8 @@ import { isProdLike } from 'legacy/utils/environments' -import { ADVANCED_ORDERS_FEATURE_FLAG } from 'constants/featureFlags' - // We can define here some flags to be enabled while we develop // TODO: update this before the deployment -const ENABLED_FOR_DEVELOP = [ADVANCED_ORDERS_FEATURE_FLAG] +const ENABLED_FOR_DEVELOP: string[] = [] export class FeatureFlag { static get(name: string) { diff --git a/yarn.lock b/yarn.lock index 7e2d691884..14d5ea1e9e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3852,6 +3852,11 @@ "@types/node" "*" "@types/responselike" "^1.0.0" +"@types/clone-deep@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/clone-deep/-/clone-deep-4.0.1.tgz#7c488443ab9f571cd343d774551b78e9264ea990" + integrity sha512-bdkCSkyVHsgl3Goe1y16T9k6JuQx7SiDREkq728QjKmTZkGJZuS8R3gGcnGzVuGBP0mssKrzM/GlMOQxtip9cg== + "@types/connect-history-api-fallback@^1.3.5": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae" From bd7e840425da8872662d9f1b650cc5b608e3d9ec Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Thu, 15 Jun 2023 13:45:40 +0600 Subject: [PATCH 21/67] feat(twap): display twap-specific information on order receipt modal (#2652) --- src/common/pure/SafeWalletLink/index.tsx | 24 ++++++ src/common/types.ts | 10 +++ .../EnhancedTransactionLink/index.tsx | 5 +- .../Transaction/ActivityDetails.tsx | 4 +- .../containers/Transaction/StatusDetails.tsx | 26 +----- .../containers/OrdersReceiptModal/index.tsx | 7 ++ .../pure/ReceiptModal/FieldLabel.tsx | 4 +- .../pure/ReceiptModal/fields/SafeTxFields.tsx | 54 ++++++++++++ .../ordersTable/pure/ReceiptModal/index.tsx | 86 ++++++++++++++----- .../ordersTable/pure/ReceiptModal/styled.ts | 5 ++ .../twap/hooks/useFetchTwapOrdersFromSafe.ts | 3 +- .../twap/hooks/useFetchTwapPartOrders.ts | 2 +- .../twap/hooks/useTwapOrderByChildId.ts | 17 ++++ src/modules/twap/hooks/useTwapOrderById.ts | 13 +++ src/modules/twap/index.ts | 2 + .../twap/services/fetchTwapOrdersFromSafe.ts | 32 +++---- src/modules/twap/state/twapOrdersListAtom.ts | 2 +- src/modules/twap/types.ts | 10 ++- .../twap/updaters/TwapOrdersUpdater.tsx | 6 +- .../twap/utils/buildTwapOrdersItems.ts | 11 +-- src/modules/wallet/api/assets/safe-logo.svg | 10 +++ 21 files changed, 252 insertions(+), 81 deletions(-) create mode 100644 src/common/pure/SafeWalletLink/index.tsx create mode 100644 src/modules/ordersTable/pure/ReceiptModal/fields/SafeTxFields.tsx create mode 100644 src/modules/twap/hooks/useTwapOrderByChildId.ts create mode 100644 src/modules/twap/hooks/useTwapOrderById.ts create mode 100644 src/modules/wallet/api/assets/safe-logo.svg diff --git a/src/common/pure/SafeWalletLink/index.tsx b/src/common/pure/SafeWalletLink/index.tsx new file mode 100644 index 0000000000..65f7c80e1b --- /dev/null +++ b/src/common/pure/SafeWalletLink/index.tsx @@ -0,0 +1,24 @@ +import { ExternalLink } from 'legacy/theme' + +import { getSafeWebUrl } from 'api/gnosisSafe' + +export function SafeWalletLink(props: { + chainId: number + safeTransaction?: { safe: string; safeTxHash: string } +}): JSX.Element | null { + const { chainId, safeTransaction } = props + + if (!safeTransaction) { + return null + } + + const { safe, safeTxHash } = safeTransaction + const safeUrl = getSafeWebUrl(chainId, safe, safeTxHash) + + // Only show the link to the safe, if we have the "safeUrl" + if (safeUrl === null) { + return null + } + + return View Safe ↗ +} diff --git a/src/common/types.ts b/src/common/types.ts index 435fe19ee3..79b41ccd55 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -16,3 +16,13 @@ export type OrderWithComposableCowInfo = { order: EnrichedOrder composableCowInfo?: ComposableCowInfo } + +export type SafeTransactionParams = { + submissionDate: string + executionDate: string + isExecuted: boolean + nonce: number + confirmationsRequired: number + confirmations: number + safeTxHash: string +} diff --git a/src/legacy/components/EnhancedTransactionLink/index.tsx b/src/legacy/components/EnhancedTransactionLink/index.tsx index b868c8a01a..0efd6413f4 100644 --- a/src/legacy/components/EnhancedTransactionLink/index.tsx +++ b/src/legacy/components/EnhancedTransactionLink/index.tsx @@ -1,9 +1,10 @@ import { ExplorerLink } from 'legacy/components/ExplorerLink' import { EnhancedTransactionDetails, HashType } from 'legacy/state/enhancedTransactions/reducer' -import { GnosisSafeLink } from 'modules/account/containers/Transaction/StatusDetails' import { useGnosisSafeInfo, useWalletInfo } from 'modules/wallet' +import { SafeWalletLink } from 'common/pure/SafeWalletLink' + interface Props { tx: EnhancedTransactionDetails } @@ -24,7 +25,7 @@ export function EnhancedTransactionLink(props: Props) { return null } - return + return } else { return } diff --git a/src/modules/account/containers/Transaction/ActivityDetails.tsx b/src/modules/account/containers/Transaction/ActivityDetails.tsx index 076952d417..e524a59db3 100644 --- a/src/modules/account/containers/Transaction/ActivityDetails.tsx +++ b/src/modules/account/containers/Transaction/ActivityDetails.tsx @@ -16,11 +16,11 @@ import { useCancelOrder } from 'common/hooks/useCancelOrder' import { useGetSurplusData } from 'common/hooks/useGetSurplusFiatValue' import { CurrencyLogo } from 'common/pure/CurrencyLogo' import { RateInfoParams, RateInfo } from 'common/pure/RateInfo' +import { SafeWalletLink } from 'common/pure/SafeWalletLink' import { TokenAmount } from 'common/pure/TokenAmount' import { ActivityDerivedState } from './index' -import { GnosisSafeLink } from './StatusDetails' import { StatusDetails } from './StatusDetails' import { Summary, @@ -135,7 +135,7 @@ function GnosisSafeTxDetails(props: { {signaturesMessage} {/* View in: Gnosis Safe */} - + ) } diff --git a/src/modules/account/containers/Transaction/StatusDetails.tsx b/src/modules/account/containers/Transaction/StatusDetails.tsx index 9bce001801..4cf611e58e 100644 --- a/src/modules/account/containers/Transaction/StatusDetails.tsx +++ b/src/modules/account/containers/Transaction/StatusDetails.tsx @@ -1,5 +1,3 @@ -import { SafeMultisigTransactionResponse } from '@safe-global/safe-core-sdk-types' - import { ExternalLink as LinkIconFeather } from 'react-feather' import SVG from 'react-inlinesvg' @@ -9,38 +7,16 @@ import OrderExpiredImage from 'legacy/assets/cow-swap/order-expired.svg' import OrderOpenImage from 'legacy/assets/cow-swap/order-open.svg' import PresignaturePendingImage from 'legacy/assets/cow-swap/order-presignature-pending.svg' import { getActivityState } from 'legacy/hooks/useActivityDerivedState' -import { ExternalLink } from 'legacy/theme' import { ExplorerDataType, getExplorerLink } from 'legacy/utils/getExplorerLink' import { getSafeWebUrl } from 'api/gnosisSafe' import { CancelButton } from 'common/pure/CancelButton' import { isOrderCancellable } from 'common/utils/isOrderCancellable' -import { StatusLabel, StatusLabelWrapper, StatusLabelBelow, CancelTxLink } from './styled' +import { CancelTxLink, StatusLabel, StatusLabelBelow, StatusLabelWrapper } from './styled' import { ActivityDerivedState, determinePillColour } from './index' -export function GnosisSafeLink(props: { - chainId: number - safeTransaction?: SafeMultisigTransactionResponse -}): JSX.Element | null { - const { chainId, safeTransaction } = props - - if (!safeTransaction) { - return null - } - - const { safe, safeTxHash } = safeTransaction - const safeUrl = getSafeWebUrl(chainId, safe, safeTxHash) - - // Only show the link to the safe, if we have the "safeUrl" - if (safeUrl === null) { - return null - } - - return View Safe ↗ -} - function _getStateLabel(activityDerivedState: ActivityDerivedState) { const activityState = getActivityState(activityDerivedState) diff --git a/src/modules/ordersTable/containers/OrdersReceiptModal/index.tsx b/src/modules/ordersTable/containers/OrdersReceiptModal/index.tsx index e82d3125b9..8f3cf5ccd7 100644 --- a/src/modules/ordersTable/containers/OrdersReceiptModal/index.tsx +++ b/src/modules/ordersTable/containers/OrdersReceiptModal/index.tsx @@ -6,6 +6,7 @@ import { supportedChainId } from 'legacy/utils/supportedChainId' import { PendingOrdersPrices } from 'modules/orders/state/pendingOrdersPricesAtom' import { ReceiptModal } from 'modules/ordersTable/pure/ReceiptModal' +import { useTwapOrderById, useTwapOrderByChildId } from 'modules/twap' import { useWalletInfo } from 'modules/wallet' import { calculatePrice } from 'utils/orderUtils/calculatePrice' @@ -22,6 +23,10 @@ export function OrdersReceiptModal(props: OrdersReceiptModalProps) { const { chainId: _chainId } = useWalletInfo() const closeReceiptModal = useCloseReceiptModal() const chainId = supportedChainId(_chainId) + const twapOrderById = useTwapOrderById(order?.id) + const twapOrderByChildId = useTwapOrderByChildId(order?.id) + const twapOrder = twapOrderById || twapOrderByChildId + const isTwapPartOrder = !!twapOrderByChildId if (!chainId || !order) { return null @@ -63,6 +68,8 @@ export function OrdersReceiptModal(props: OrdersReceiptModalProps) { estimatedExecutionPrice={estimatedExecutionPrice} chainId={chainId} order={order} + twapOrder={twapOrder} + isTwapPartOrder={isTwapPartOrder} isOpen={!!order} onDismiss={closeReceiptModal} /> diff --git a/src/modules/ordersTable/pure/ReceiptModal/FieldLabel.tsx b/src/modules/ordersTable/pure/ReceiptModal/FieldLabel.tsx index f04233ba00..216ec9cef5 100644 --- a/src/modules/ordersTable/pure/ReceiptModal/FieldLabel.tsx +++ b/src/modules/ordersTable/pure/ReceiptModal/FieldLabel.tsx @@ -5,11 +5,13 @@ import * as styledEl from './styled' interface Props { label: string tooltip?: string | JSX.Element + prefix?: string | JSX.Element } -export function FieldLabel({ label, tooltip }: Props) { +export function FieldLabel({ label, tooltip, prefix }: Props) { return ( + {prefix} {label} {tooltip && } diff --git a/src/modules/ordersTable/pure/ReceiptModal/fields/SafeTxFields.tsx b/src/modules/ordersTable/pure/ReceiptModal/fields/SafeTxFields.tsx new file mode 100644 index 0000000000..421b8fca42 --- /dev/null +++ b/src/modules/ordersTable/pure/ReceiptModal/fields/SafeTxFields.tsx @@ -0,0 +1,54 @@ +import { SupportedChainId } from '@cowprotocol/cow-sdk' + +import styled from 'styled-components/macro' + +import safeLogo from 'modules/wallet/api/assets/safe-logo.svg' + +import { SafeWalletLink } from 'common/pure/SafeWalletLink' + +import { FieldLabel } from '../FieldLabel' +import { Field } from '../styled' + +const SafeIcon = styled.img` + margin-right: 5px; +` + +export interface SafeTxFieldsProps { + chainId: SupportedChainId + safeAddress: string + safeTxHash: string + nonce: number + confirmations: number + confirmationsRequired: number +} + +export function SafeTxFields(props: SafeTxFieldsProps) { + const { chainId, safeAddress, safeTxHash, nonce, confirmationsRequired, confirmations } = props + const safeTransaction = { safe: safeAddress, safeTxHash } + + const safeLogoImg = + + return ( + <> + + +
+ {safeTxHash.slice(0, 8)} {' - '} + +
+
+ + + + {nonce} + + + + + + {confirmations} / {confirmationsRequired} + + + + ) +} diff --git a/src/modules/ordersTable/pure/ReceiptModal/index.tsx b/src/modules/ordersTable/pure/ReceiptModal/index.tsx index 928ba9def9..20d1c2939a 100644 --- a/src/modules/ordersTable/pure/ReceiptModal/index.tsx +++ b/src/modules/ordersTable/pure/ReceiptModal/index.tsx @@ -4,6 +4,9 @@ import { CurrencyAmount, Fraction, Token } from '@uniswap/sdk-core' import { OrderStatus } from 'legacy/state/orders/actions' import { CloseIcon } from 'legacy/theme' +import { TwapOrderItem } from 'modules/twap/types' + +import { InlineBanner } from 'common/pure/InlineBanner' import { GpModal } from 'common/pure/Modal' import { getSellAmountWithFee } from 'utils/orderUtils/getSellAmountWithFee' import { ParsedOrder } from 'utils/orderUtils/parseOrder' @@ -12,6 +15,7 @@ import { CurrencyField } from './CurrencyField' import { DateField } from './DateField' import { FeeField } from './FeeField' import { FieldLabel } from './FieldLabel' +import { SafeTxFields } from './fields/SafeTxFields' import { FilledField } from './FilledField' import { IdField } from './IdField' import { OrderTypeField } from './OrderTypeField' @@ -23,6 +27,8 @@ import { SurplusField } from './SurplusField' interface ReceiptProps { isOpen: boolean order: ParsedOrder + twapOrder: TwapOrderItem | null + isTwapPartOrder: boolean chainId: SupportedChainId onDismiss: () => void sellAmount: CurrencyAmount @@ -61,6 +67,8 @@ export function ReceiptModal({ isOpen, onDismiss, order, + twapOrder, + isTwapPartOrder, chainId, buyAmount, limitPrice, @@ -73,6 +81,7 @@ export function ReceiptModal({ const inputLabel = order.kind === OrderKind.SELL ? 'You sell' : 'You sell at most' const outputLabel = order.kind === OrderKind.SELL ? 'You receive at least' : 'You receive exactly' + const safeTxParams = twapOrder?.safeTxParams return ( @@ -82,6 +91,19 @@ export function ReceiptModal({ onDismiss()} /> + {twapOrder && ( + + + + )} + @@ -114,20 +136,26 @@ export function ReceiptModal({ )} - - - - - - - - - - - - - - + {/*TODO: Currently, we don't have this information for parent TWAP orders*/} + {/*The condition should be removed once we have the data*/} + {(!twapOrder || isTwapPartOrder) && ( + <> + + + + + + + + + + + + + + + + )} @@ -144,14 +172,28 @@ export function ReceiptModal({ - - {order.executionData.activityId && ( - <> - - - - )} - + {/*TODO: add a link to explorer when it will support TWAP orders*/} + {!twapOrder && ( + + {order.executionData.activityId && ( + <> + + + + )} + + )} + {/*TODO: make it more generic. The ReceiptModal should know about TWAP and should display Safe info for any ComposableCow order*/} + {twapOrder && safeTxParams && ( + + )} diff --git a/src/modules/ordersTable/pure/ReceiptModal/styled.ts b/src/modules/ordersTable/pure/ReceiptModal/styled.ts index a968c45f2d..dce6acf859 100644 --- a/src/modules/ordersTable/pure/ReceiptModal/styled.ts +++ b/src/modules/ordersTable/pure/ReceiptModal/styled.ts @@ -59,6 +59,10 @@ export const FieldsWrapper = styled.div` `} ` +export const InfoBannerWrapper = styled.div` + margin: 12px; +` + export const Field = styled.div` display: flex; flex-flow: row wrap; @@ -122,6 +126,7 @@ export const LabelText = styled.span` export const Label = styled.div` display: flex; gap: 4px; + align-items: center; // TODO: Override required to remove inline styles from StyledInfoIcon parent. // Need to refactor and remove the inline styles. diff --git a/src/modules/twap/hooks/useFetchTwapOrdersFromSafe.ts b/src/modules/twap/hooks/useFetchTwapOrdersFromSafe.ts index b2f18b34aa..583870133d 100644 --- a/src/modules/twap/hooks/useFetchTwapOrdersFromSafe.ts +++ b/src/modules/twap/hooks/useFetchTwapOrdersFromSafe.ts @@ -5,7 +5,8 @@ import ms from 'ms.macro' import { ComposableCoW } from 'abis/types' import { useSafeApiKit } from 'api/gnosisSafe/hooks/useSafeApiKit' -import { fetchTwapOrdersFromSafe, TwapOrdersSafeData } from '../services/fetchTwapOrdersFromSafe' +import { fetchTwapOrdersFromSafe } from '../services/fetchTwapOrdersFromSafe' +import { TwapOrdersSafeData } from '../types' const PENDING_TWAP_UPDATE_INTERVAL = ms`10s` diff --git a/src/modules/twap/hooks/useFetchTwapPartOrders.ts b/src/modules/twap/hooks/useFetchTwapPartOrders.ts index 7e3e963b76..715d1dd383 100644 --- a/src/modules/twap/hooks/useFetchTwapPartOrders.ts +++ b/src/modules/twap/hooks/useFetchTwapPartOrders.ts @@ -20,7 +20,7 @@ export function useFetchTwapPartOrders( ordersInfo: TwapOrderInfo[] ): TwapPartOrders | null { const ordersToVerifyParams = useMemo(() => { - return ordersInfo.map((info) => info.safeData.params) + return ordersInfo.map((info) => info.safeData.conditionalOrderParams) }, [ordersInfo]) const ordersTradeableData = useTwapOrdersTradeableMulticall(safeAddress, composableCowContract, ordersToVerifyParams) diff --git a/src/modules/twap/hooks/useTwapOrderByChildId.ts b/src/modules/twap/hooks/useTwapOrderByChildId.ts new file mode 100644 index 0000000000..7a26d34d91 --- /dev/null +++ b/src/modules/twap/hooks/useTwapOrderByChildId.ts @@ -0,0 +1,17 @@ +import { useAtomValue } from 'jotai/utils' +import { useMemo } from 'react' + +import { useTwapOrderById } from './useTwapOrderById' + +import { twapPartOrdersListAtom } from '../state/twapPartOrdersAtom' +import { TwapOrderItem } from '../types' + +export function useTwapOrderByChildId(orderId: string | undefined): TwapOrderItem | null { + const twapPartOrdersList = useAtomValue(twapPartOrdersListAtom) + + const parentId = useMemo(() => { + return twapPartOrdersList.find(({ uid }) => uid === orderId)?.twapOrderId + }, [orderId, twapPartOrdersList]) + + return useTwapOrderById(parentId) +} diff --git a/src/modules/twap/hooks/useTwapOrderById.ts b/src/modules/twap/hooks/useTwapOrderById.ts new file mode 100644 index 0000000000..8699832c98 --- /dev/null +++ b/src/modules/twap/hooks/useTwapOrderById.ts @@ -0,0 +1,13 @@ +import { useAtomValue } from 'jotai/utils' +import { useMemo } from 'react' + +import { twapOrdersListAtom } from '../state/twapOrdersListAtom' +import { TwapOrderItem } from '../types' + +export function useTwapOrderById(orderId: string | undefined): TwapOrderItem | null { + const twapOrdersList = useAtomValue(twapOrdersListAtom) + + return useMemo(() => { + return (orderId && twapOrdersList[orderId]) || null + }, [orderId, twapOrdersList]) +} diff --git a/src/modules/twap/index.ts b/src/modules/twap/index.ts index 04222f36d9..b9406afb0f 100644 --- a/src/modules/twap/index.ts +++ b/src/modules/twap/index.ts @@ -1 +1,3 @@ export * from './containers/TwapFormWidget' +export * from './hooks/useTwapOrderById' +export * from './hooks/useTwapOrderByChildId' diff --git a/src/modules/twap/services/fetchTwapOrdersFromSafe.ts b/src/modules/twap/services/fetchTwapOrdersFromSafe.ts index 40dc8017db..59b43a419e 100644 --- a/src/modules/twap/services/fetchTwapOrdersFromSafe.ts +++ b/src/modules/twap/services/fetchTwapOrdersFromSafe.ts @@ -5,14 +5,7 @@ import { isTruthy } from 'legacy/utils/misc' import { ComposableCoW } from 'abis/types' -import { ConditionalOrderParams } from '../types' - -export interface TwapOrdersSafeData { - params: ConditionalOrderParams - submissionDate: string - executionDate: string - isExecuted: boolean -} +import { ConditionalOrderParams, TwapOrdersSafeData } from '../types' // ComposableCoW.create method const CREATE_COMPOSABLE_ORDER_SELECTOR = '6bfae1ca' @@ -26,7 +19,7 @@ export async function fetchTwapOrdersFromSafe( const results = allTxs?.results || [] return results - .map((result) => { + .map((result) => { if (!result.data || !isSafeMultisigTransactionListResponse(result)) return null const selectorIndex = result.data.indexOf(CREATE_COMPOSABLE_ORDER_SELECTOR) @@ -35,17 +28,24 @@ export async function fetchTwapOrdersFromSafe( const callData = '0x' + result.data.substring(selectorIndex) - const params = parseConditionalOrderParams(safeAddress, composableCowContract, callData) + const conditionalOrderParams = parseConditionalOrderParams(safeAddress, composableCowContract, callData) - if (!params) return null + if (!conditionalOrderParams) return null - const { isExecuted, submissionDate, executionDate } = result + const { isExecuted, submissionDate, executionDate, nonce, confirmationsRequired, confirmations, safeTxHash } = + result return { - params, - isExecuted, - submissionDate, - executionDate, + conditionalOrderParams, + safeTxParams: { + isExecuted, + submissionDate, + executionDate, + confirmationsRequired, + confirmations: confirmations?.length || 0, + safeTxHash, + nonce, + }, } }) .filter(isTruthy) diff --git a/src/modules/twap/state/twapOrdersListAtom.ts b/src/modules/twap/state/twapOrdersListAtom.ts index 43533bb153..70cb99cf13 100644 --- a/src/modules/twap/state/twapOrdersListAtom.ts +++ b/src/modules/twap/state/twapOrdersListAtom.ts @@ -13,7 +13,7 @@ import { updateTwapOrdersList } from '../utils/updateTwapOrdersList' export type TwapOrdersList = { [key: string]: TwapOrderItem } -export const twapOrdersListAtom = atomWithStorage('twap-orders-list:v3', {}) +export const twapOrdersListAtom = atomWithStorage('twap-orders-list:v4', {}) export const updateTwapOrdersListAtom = atom(null, (get, set, nextState: TwapOrdersList) => { const currentState = get(twapOrdersListAtom) diff --git a/src/modules/twap/types.ts b/src/modules/twap/types.ts index 61d1cf02f9..ad84b4c64c 100644 --- a/src/modules/twap/types.ts +++ b/src/modules/twap/types.ts @@ -1,7 +1,7 @@ import { SupportedChainId } from '@cowprotocol/cow-sdk' import { CurrencyAmount, Token } from '@uniswap/sdk-core' -import { TwapOrdersSafeData } from './services/fetchTwapOrdersFromSafe' +import { SafeTransactionParams } from '../../common/types' // Read more: https://github.com/rndlabs/composable-cow#data-structure export interface TWAPOrder { @@ -37,13 +37,19 @@ export enum TwapOrderStatus { Expired = 'Expired', } +export interface TwapOrdersSafeData { + conditionalOrderParams: ConditionalOrderParams + safeTxParams: SafeTransactionParams +} + export interface TwapOrderItem { order: TWAPOrderStruct status: TwapOrderStatus chainId: SupportedChainId + submissionDate: string safeAddress: string id: string - submissionDate: string + safeTxParams?: SafeTransactionParams } export interface ConditionalOrderParams { diff --git a/src/modules/twap/updaters/TwapOrdersUpdater.tsx b/src/modules/twap/updaters/TwapOrdersUpdater.tsx index e9200e724a..3f811804f1 100644 --- a/src/modules/twap/updaters/TwapOrdersUpdater.tsx +++ b/src/modules/twap/updaters/TwapOrdersUpdater.tsx @@ -33,8 +33,8 @@ export function TwapOrdersUpdater(props: { const allOrdersInfo: TwapOrderInfo[] = useMemo(() => { return ordersSafeData.map((data) => { - const id = getConditionalOrderId(data.params) - const order = parseTwapOrderStruct(data.params.staticInput) + const id = getConditionalOrderId(data.conditionalOrderParams) + const order = parseTwapOrderStruct(data.conditionalOrderParams.staticInput) return { id, @@ -54,7 +54,7 @@ export function TwapOrdersUpdater(props: { return TWAP_PENDING_STATUSES.includes(existingOrder.status) } - return !info.isExpired && info.safeData.isExecuted + return !info.isExpired && info.safeData.safeTxParams.isExecuted }) }, [allOrdersInfo, twapOrdersList]) diff --git a/src/modules/twap/utils/buildTwapOrdersItems.ts b/src/modules/twap/utils/buildTwapOrdersItems.ts index 3b2084f771..6788be02c7 100644 --- a/src/modules/twap/utils/buildTwapOrdersItems.ts +++ b/src/modules/twap/utils/buildTwapOrdersItems.ts @@ -6,9 +6,8 @@ import { getTwapOrderStatus } from './getTwapOrderStatus' import { parseTwapOrderStruct } from './parseTwapOrderStruct' import { TwapToDiscreteOrders } from '../hooks/useTwapDiscreteOrders' -import { TwapOrdersSafeData } from '../services/fetchTwapOrdersFromSafe' import { TwapOrdersList } from '../state/twapOrdersListAtom' -import { TwapOrderItem, TwapOrderInfo, TwapOrdersAuthResult } from '../types' +import { TwapOrderItem, TwapOrderInfo, TwapOrdersAuthResult, TwapOrdersSafeData } from '../types' export function buildTwapOrdersItems( chainId: SupportedChainId, @@ -31,10 +30,11 @@ function getTwapOrderItem( authorized: boolean | undefined, discreteOrder: Order | undefined ): TwapOrderItem { - const { params, isExecuted, submissionDate } = safeData + const { conditionalOrderParams, safeTxParams } = safeData + const { isExecuted, submissionDate } = safeTxParams - const executionDate = new Date(safeData.executionDate) - const order = parseTwapOrderStruct(params.staticInput) + const executionDate = new Date(safeTxParams.executionDate) + const order = parseTwapOrderStruct(conditionalOrderParams.staticInput) const status = getTwapOrderStatus(order, isExecuted, executionDate, authorized, discreteOrder) return { @@ -44,5 +44,6 @@ function getTwapOrderItem( safeAddress, id, submissionDate, + safeTxParams, } } diff --git a/src/modules/wallet/api/assets/safe-logo.svg b/src/modules/wallet/api/assets/safe-logo.svg new file mode 100644 index 0000000000..9044e15fc0 --- /dev/null +++ b/src/modules/wallet/api/assets/safe-logo.svg @@ -0,0 +1,10 @@ + + + + + + + + + + From 2851c6599f3bd04d4db129ecb8b6addd2a5e0ad5 Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Thu, 15 Jun 2023 16:10:08 +0600 Subject: [PATCH 22/67] feat(twap): setup fallback handler and wallet validation UI (#2669) --- src/common/pure/InlineBanner/banners.tsx | 81 ++++++------- src/common/pure/InlineBanner/index.cosmos.tsx | 45 ++++---- src/common/pure/InlineBanner/index.tsx | 9 +- .../ordersTable/pure/ReceiptModal/index.tsx | 13 +-- .../services/validateTradeForm.ts | 32 +++--- src/modules/tradeFormValidation/types.ts | 10 +- .../twap/containers/ActionButtons/index.tsx | 30 +++-- .../containers/TwapConfirmModal/index.tsx | 8 +- .../containers/TwapFormWarnings/index.tsx | 46 ++++++++ .../warnings/FallbackHandlerWarning.tsx | 74 ++++++++++++ .../warnings/UnsupportedWalletWarning.tsx | 29 +++++ .../twap/containers/TwapFormWidget/index.tsx | 18 ++- src/modules/twap/hooks/useCreateTwapOrder.ts | 108 +++++++++++------- .../hooks/useExtensibleFallbackContext.ts | 11 +- .../twap/hooks/useSetupFallbackHandler.ts | 15 --- src/modules/twap/hooks/useTwapFormState.ts | 7 +- .../twap/hooks/useTwapOrderCreationContext.ts | 8 +- .../PrimaryActionButton/getTwapFormState.tsx | 23 ++-- .../twap/pure/PrimaryActionButton/index.tsx | 21 ++-- .../twap/services/createTwapOrderTxs.test.ts | 1 - .../extensibleFallbackSetupTxs.test.tsx | 2 +- .../services/extensibleFallbackSetupTxs.ts | 3 +- .../services/getSignatureVerifierContract.ts | 3 +- .../twap/services/settleTwapOrder.test.ts | 84 -------------- src/modules/twap/services/settleTwapOrder.ts | 18 --- .../setupExtensibleFallbackHandler.test.ts | 80 ------------- .../setupExtensibleFallbackHandler.ts | 19 --- .../services/verifyExtensibleFallback.test.ts | 2 +- .../twap/services/verifyExtensibleFallback.ts | 3 +- .../twap/state/twapOrdersSettingsAtom.ts | 2 + 30 files changed, 383 insertions(+), 422 deletions(-) create mode 100644 src/modules/twap/containers/TwapFormWarnings/index.tsx create mode 100644 src/modules/twap/containers/TwapFormWarnings/warnings/FallbackHandlerWarning.tsx create mode 100644 src/modules/twap/containers/TwapFormWarnings/warnings/UnsupportedWalletWarning.tsx delete mode 100644 src/modules/twap/hooks/useSetupFallbackHandler.ts delete mode 100644 src/modules/twap/services/settleTwapOrder.test.ts delete mode 100644 src/modules/twap/services/settleTwapOrder.ts delete mode 100644 src/modules/twap/services/setupExtensibleFallbackHandler.test.ts delete mode 100644 src/modules/twap/services/setupExtensibleFallbackHandler.ts diff --git a/src/common/pure/InlineBanner/banners.tsx b/src/common/pure/InlineBanner/banners.tsx index 6d3baca7eb..0097e78d95 100644 --- a/src/common/pure/InlineBanner/banners.tsx +++ b/src/common/pure/InlineBanner/banners.tsx @@ -10,15 +10,12 @@ import { InlineBanner } from './index' export function BundleTxApprovalBanner() { return ( - - Token approval: For your convenience, token approval and order placement will be bundled into - a single transaction, streamlining your experience! - - } - /> + + <> + Token approval: For your convenience, token approval and order placement will be bundled into a + single transaction, streamlining your experience! + + ) } @@ -29,16 +26,13 @@ export type BundleTxWrapBannerProps = { export function BundleTxWrapBanner({ nativeCurrencySymbol, wrappedCurrencySymbol }: BundleTxWrapBannerProps) { return ( - - Token wrapping: For your convenience, CoW Swap will bundle all the necessary actions for this - trade into a single transaction. This includes the {nativeCurrencySymbol} wrapping and, if needed,{' '} - {wrappedCurrencySymbol} approval. Even if the trade fails, your wrapping and approval will be done! - - } - /> + + <> + Token wrapping: For your convenience, CoW Swap will bundle all the necessary actions for this + trade into a single transaction. This includes the {nativeCurrencySymbol} wrapping and, if needed,{' '} + {wrappedCurrencySymbol} approval. Even if the trade fails, your wrapping and approval will be done! + + ) } @@ -53,18 +47,15 @@ export function BundleTxSafeWcBanner({ nativeCurrencySymbol, supportsWrapping }: const supportsWrappingText = supportsWrapping ? `${nativeCurrencySymbol} wrapping, ` : '' return ( - - Use the Safe web app for streamlined trading: {supportsWrappingText}token approval and orders bundled in one - go! Only available in the{' '} - - CoW Swap Safe App↗ - - - } - /> + + <> + Use the Safe web app for streamlined trading: {supportsWrappingText}token approval and orders bundled in one go! + Only available in the{' '} + + CoW Swap Safe App↗ + + + ) } @@ -75,20 +66,18 @@ export type SmallVolumeWarningBannerProps = { export function SmallVolumeWarningBanner({ feePercentage, feeAmount }: SmallVolumeWarningBannerProps) { return ( - - Small orders are unlikely to be executed. For this order, network fees would be{' '} - - {feePercentage?.toFixed(2)}% ( - ) - {' '} - of your sell amount! Therefore, your order is unlikely to execute. - {/*
*/} - {/* TODO: add link to somewhere */} - {/*Learn more ↗*/} - - } - /> + + <> + Small orders are unlikely to be executed. For this order, network fees would be{' '} + + {feePercentage?.toFixed(2)}% ( + ) + {' '} + of your sell amount! Therefore, your order is unlikely to execute. + {/*
*/} + {/* TODO: add link to somewhere */} + {/*Learn more ↗*/} + +
) } diff --git a/src/common/pure/InlineBanner/index.cosmos.tsx b/src/common/pure/InlineBanner/index.cosmos.tsx index d35f103b91..dda1795d9c 100644 --- a/src/common/pure/InlineBanner/index.cosmos.tsx +++ b/src/common/pure/InlineBanner/index.cosmos.tsx @@ -5,47 +5,50 @@ import { InlineBanner } from '.' const Fixtures = { 'default (alert)': ( - This is an alert banner.} /> + + This is an alert banner. + ), alert: ( - This is an alert banner (explicitly passed type).} /> + + This is an alert banner (explicitly passed type). + ), information: ( - - Token approval: For your convenience, token approval and order placement will be bundled - into a single transaction, streamlining your experience! - - } - /> + + <> + Token approval: For your convenience, token approval and order placement will be bundled into + a single transaction, streamlining your experience! + + ), success: ( - Operation completed successfully!} /> + + Operation completed successfully! + ), danger: ( - Something went wrong! Please try again.} /> + + Something went wrong! Please try again. + ), smallVolumeWarning: ( - - Small orders are unlikely to be executed. For this order, network fees would be 1.00% (2500 COW) of - your sell amount! Therefore, your order is unlikely to execute. - - } - /> + + <> + Small orders are unlikely to be executed. For this order, network fees would be 1.00% (2500 COW) of + your sell amount! Therefore, your order is unlikely to execute. + + ), } diff --git a/src/common/pure/InlineBanner/index.tsx b/src/common/pure/InlineBanner/index.tsx index 90147b9781..67a3fb7518 100644 --- a/src/common/pure/InlineBanner/index.tsx +++ b/src/common/pure/InlineBanner/index.tsx @@ -65,20 +65,21 @@ const Wrapper = styled.span<{ color: string }>` ` export type InlineBannerProps = { - content: ReactNode + children?: ReactNode className?: string + hideIcon?: boolean type?: BannerType } -export function InlineBanner({ content, className, type = 'alert' }: InlineBannerProps) { +export function InlineBanner({ children, className, hideIcon, type = 'alert' }: InlineBannerProps) { const theme = useTheme() const config = BANNER_CONFIG[type] const color = theme[config.colorKey] return ( - - {content} + {!hideIcon && } + {children} ) } diff --git a/src/modules/ordersTable/pure/ReceiptModal/index.tsx b/src/modules/ordersTable/pure/ReceiptModal/index.tsx index 20d1c2939a..0edf3564d1 100644 --- a/src/modules/ordersTable/pure/ReceiptModal/index.tsx +++ b/src/modules/ordersTable/pure/ReceiptModal/index.tsx @@ -93,14 +93,11 @@ export function ReceiptModal({ {twapOrder && ( - + + {isTwapPartOrder + ? `Part of a ${twapOrder.order.n}-part TWAP order split` + : `TWAP order split into ${twapOrder.order.n} parts`} + )} diff --git a/src/modules/tradeFormValidation/services/validateTradeForm.ts b/src/modules/tradeFormValidation/services/validateTradeForm.ts index ab47164060..0ec6ff9224 100644 --- a/src/modules/tradeFormValidation/services/validateTradeForm.ts +++ b/src/modules/tradeFormValidation/services/validateTradeForm.ts @@ -36,22 +36,6 @@ export function validateTradeForm(context: TradeFormValidationContext): TradeFor return TradeFormValidation.WrapUnwrapFlow } - if (!inputCurrency || !outputCurrency) { - return TradeFormValidation.CurrencyNotSet - } - - if (inputAmountIsNotSet) { - return TradeFormValidation.InputAmountNotSet - } - - if (recipient && !recipientEnsAddress && !isAddress(recipient)) { - return TradeFormValidation.RecipientInvalid - } - - if (isSwapUnsupported) { - return TradeFormValidation.CurrencyNotSupported - } - if (tradeQuote.error) { return TradeFormValidation.QuoteErrors } @@ -68,6 +52,22 @@ export function validateTradeForm(context: TradeFormValidationContext): TradeFor return TradeFormValidation.SafeReadonlyUser } + if (!inputCurrency || !outputCurrency) { + return TradeFormValidation.CurrencyNotSet + } + + if (inputAmountIsNotSet) { + return TradeFormValidation.InputAmountNotSet + } + + if (recipient && !recipientEnsAddress && !isAddress(recipient)) { + return TradeFormValidation.RecipientInvalid + } + + if (isSwapUnsupported) { + return TradeFormValidation.CurrencyNotSupported + } + if (!tradeQuote.response) { return TradeFormValidation.QuoteLoading } diff --git a/src/modules/tradeFormValidation/types.ts b/src/modules/tradeFormValidation/types.ts index 0ef075cf36..919ed40c4f 100644 --- a/src/modules/tradeFormValidation/types.ts +++ b/src/modules/tradeFormValidation/types.ts @@ -8,11 +8,6 @@ export enum TradeFormValidation { WrapUnwrapAmountNotSet, WrapUnwrapFlow, - // Quote request params - CurrencyNotSet, - InputAmountNotSet, - RecipientInvalid, - // Quote errors QuoteErrors, CurrencyNotSupported, @@ -22,6 +17,11 @@ export enum TradeFormValidation { WalletNotSupported, SafeReadonlyUser, + // Quote request params + CurrencyNotSet, + InputAmountNotSet, + RecipientInvalid, + // Quote loading indicator QuoteLoading, diff --git a/src/modules/twap/containers/ActionButtons/index.tsx b/src/modules/twap/containers/ActionButtons/index.tsx index 0de5f4376e..1f91b66f36 100644 --- a/src/modules/twap/containers/ActionButtons/index.tsx +++ b/src/modules/twap/containers/ActionButtons/index.tsx @@ -1,31 +1,37 @@ import React from 'react' import { useTradeConfirmActions } from 'modules/trade' -import { TradeFormButtons, useGetTradeFormValidation } from 'modules/tradeFormValidation' +import { TradeFormButtons, TradeFormValidation } from 'modules/tradeFormValidation' import { useTradeFormButtonContext } from 'modules/tradeFormValidation' -import { useSetupFallbackHandler } from '../../hooks/useSetupFallbackHandler' -import { useTwapFormState } from '../../hooks/useTwapFormState' import { PrimaryActionButton } from '../../pure/PrimaryActionButton' +import { TwapFormState } from '../../pure/PrimaryActionButton/getTwapFormState' -export function ActionButtons() { - const setFallbackHandler = useSetupFallbackHandler() +interface ActionButtonsProps { + localFormValidation: TwapFormState | null + primaryFormValidation: TradeFormValidation | null + walletIsNotConnected: boolean +} + +export function ActionButtons({ + localFormValidation, + primaryFormValidation, + walletIsNotConnected, +}: ActionButtonsProps) { const tradeConfirmActions = useTradeConfirmActions() - const localFormValidation = useTwapFormState() - const primaryFormValidation = useGetTradeFormValidation() + + const confirmTrade = tradeConfirmActions.onOpen const primaryActionContext = { - setFallbackHandler, - openConfirmModal: tradeConfirmActions.onOpen, + confirmTrade, } - const confirmTrade = tradeConfirmActions.onOpen - const tradeFormButtonContext = useTradeFormButtonContext('TWAP order', { doTrade: confirmTrade, confirmTrade }) if (!tradeFormButtonContext) return null - if (localFormValidation) { + // Show local form validation errors only when wallet is connected + if (localFormValidation && !walletIsNotConnected) { return } diff --git a/src/modules/twap/containers/TwapConfirmModal/index.tsx b/src/modules/twap/containers/TwapConfirmModal/index.tsx index 937344c24a..de181da5b4 100644 --- a/src/modules/twap/containers/TwapConfirmModal/index.tsx +++ b/src/modules/twap/containers/TwapConfirmModal/index.tsx @@ -14,7 +14,11 @@ import { partsStateAtom } from '../../state/partsStateAtom' import { twapOrderAtom } from '../../state/twapOrderAtom' import { twapOrderSlippage } from '../../state/twapOrdersSettingsAtom' -export function TwapConfirmModal() { +interface TwapConfirmModalProps { + fallbackHandlerIsNotSet: boolean +} + +export function TwapConfirmModal({ fallbackHandlerIsNotSet }: TwapConfirmModalProps) { const { inputCurrencyAmount, inputCurrencyFiatAmount, @@ -73,7 +77,7 @@ export function TwapConfirmModal() { title="Review TWAP order" inputCurrencyInfo={inputCurrencyInfo} outputCurrencyInfo={outputCurrencyInfo} - onConfirm={createTwapOrder} + onConfirm={() => createTwapOrder(fallbackHandlerIsNotSet)} onDismiss={tradeConfirmActions.onDismiss} isConfirmDisabled={isConfirmDisabled} priceImpact={priceImpact} diff --git a/src/modules/twap/containers/TwapFormWarnings/index.tsx b/src/modules/twap/containers/TwapFormWarnings/index.tsx new file mode 100644 index 0000000000..c8ca085add --- /dev/null +++ b/src/modules/twap/containers/TwapFormWarnings/index.tsx @@ -0,0 +1,46 @@ +import { useAtomValue } from 'jotai' +import { useUpdateAtom } from 'jotai/utils' +import { useCallback } from 'react' + +import { FallbackHandlerWarning } from './warnings/FallbackHandlerWarning' +import { UnsupportedWalletWarning } from './warnings/UnsupportedWalletWarning' + +import { useIsSafeViaWc } from '../../../wallet' +import { NEED_FALLBACK_HANDLER_STATES, TwapFormState } from '../../pure/PrimaryActionButton/getTwapFormState' +import { twapOrdersSettingsAtom, updateTwapOrdersSettingsAtom } from '../../state/twapOrdersSettingsAtom' + +interface TwapFormWarningsProps { + localFormValidation: TwapFormState | null + walletIsNotConnected: boolean +} + +export function TwapFormWarnings({ localFormValidation, walletIsNotConnected }: TwapFormWarningsProps) { + const { isFallbackHandlerSetupAccepted } = useAtomValue(twapOrdersSettingsAtom) + const updateTwapOrdersSettings = useUpdateAtom(updateTwapOrdersSettingsAtom) + const isSafeViaWc = useIsSafeViaWc() + + const toggleFallbackHandlerSetupFlag = useCallback( + (isFallbackHandlerSetupAccepted: boolean) => { + updateTwapOrdersSettings({ isFallbackHandlerSetupAccepted }) + }, + [updateTwapOrdersSettings] + ) + + // Don't display any warnings while a wallet is not connected + if (walletIsNotConnected) return null + + if (localFormValidation === TwapFormState.NOT_SAFE) { + return + } + + if (localFormValidation && NEED_FALLBACK_HANDLER_STATES.includes(localFormValidation)) { + return ( + + ) + } + + return null +} diff --git a/src/modules/twap/containers/TwapFormWarnings/warnings/FallbackHandlerWarning.tsx b/src/modules/twap/containers/TwapFormWarnings/warnings/FallbackHandlerWarning.tsx new file mode 100644 index 0000000000..943e9a09aa --- /dev/null +++ b/src/modules/twap/containers/TwapFormWarnings/warnings/FallbackHandlerWarning.tsx @@ -0,0 +1,74 @@ +import styled from 'styled-components/macro' + +import { InlineBanner } from 'common/pure/InlineBanner' + +const CheckboxWrapper = styled.div` + font-weight: bold; + text-align: left; + margin-left: 25px; +` + +const WarningCheckboxWrapper = styled(CheckboxWrapper)` + width: 90%; + margin: -50px auto 12px; + position: relative; + top: 70px; +` + +const WarningCheckbox = styled.label` + display: flex; + gap: 5px; + cursor: pointer; +` + +const InlineBannerWithCheckbox = styled(InlineBanner)` + padding-bottom: 80px; +` + +interface FallbackHandlerWarningProps { + isFallbackHandlerSetupAccepted: boolean + toggleFallbackHandlerSetupFlag(isChecked: boolean): void +} + +export function FallbackHandlerWarning({ + isFallbackHandlerSetupAccepted, + toggleFallbackHandlerSetupFlag, +}: FallbackHandlerWarningProps) { + const fallbackHandlerCheckbox = ( + + toggleFallbackHandlerSetupFlag(event.currentTarget.checked)} + /> + + Modify Safe's fallback handler +
when placing order +
+
+ ) + + if (isFallbackHandlerSetupAccepted) { + return ( +
+ + {fallbackHandlerCheckbox} + +
+ ) + } else { + return ( +
+ + Unsupported Safe detected +
+ Connected Safe lacks required fallback handler. Switch to a compatible Safe or modify fallback handler for + TWAP orders when placing your order.{' '} + {/*Learn more*/} + {/*TODO: set a proper link*/} + {fallbackHandlerCheckbox} +
+
+ ) + } +} diff --git a/src/modules/twap/containers/TwapFormWarnings/warnings/UnsupportedWalletWarning.tsx b/src/modules/twap/containers/TwapFormWarnings/warnings/UnsupportedWalletWarning.tsx new file mode 100644 index 0000000000..520013db54 --- /dev/null +++ b/src/modules/twap/containers/TwapFormWarnings/warnings/UnsupportedWalletWarning.tsx @@ -0,0 +1,29 @@ +import { ExternalLink } from 'legacy/theme' + +import { InlineBanner } from 'common/pure/InlineBanner' + +export function UnsupportedWalletWarning({ isSafeViaWc }: { isSafeViaWc: boolean }) { + if (isSafeViaWc) { + return ( + + <> + Use the Safe web app for advanced trading. Only available in the{' '} + + CoW Swap Safe App↗ + + + + ) + } + + return ( + + Unsupported wallet detected: TWAP orders currently require a Safe with a special fallback + handler.
+ Have one? Switch to it! {/*Need setup? Click here.*/}{' '} +
+ Future updates may extend wallet support! + {/*TODO: set a proper link*/} +
+ ) +} diff --git a/src/modules/twap/containers/TwapFormWidget/index.tsx b/src/modules/twap/containers/TwapFormWidget/index.tsx index 28d170f25c..49bad11d24 100644 --- a/src/modules/twap/containers/TwapFormWidget/index.tsx +++ b/src/modules/twap/containers/TwapFormWidget/index.tsx @@ -8,6 +8,7 @@ import { useIsWrapOrUnwrap } from 'modules/trade/hooks/useIsWrapOrUnwrap' import { useTradeState } from 'modules/trade/hooks/useTradeState' import { TradeNumberInput } from 'modules/trade/pure/TradeNumberInput' import { TradeTextBox } from 'modules/trade/pure/TradeTextBox' +import { TradeFormValidation, useGetTradeFormValidation } from 'modules/tradeFormValidation' import { QuoteObserverUpdater } from 'modules/twap/updaters/QuoteObserverUpdater' import { useIsSafeApp, useWalletInfo } from 'modules/wallet' @@ -16,8 +17,10 @@ import { useRateInfoParams } from 'common/hooks/useRateInfoParams' import * as styledEl from './styled' import { DEFAULT_TWAP_SLIPPAGE, defaultNumOfParts, orderDeadlines } from '../../const' +import { useTwapFormState } from '../../hooks/useTwapFormState' import { AmountParts } from '../../pure/AmountParts' import { DeadlineSelector } from '../../pure/DeadlineSelector' +import { NEED_FALLBACK_HANDLER_STATES } from '../../pure/PrimaryActionButton/getTwapFormState' import { partsStateAtom } from '../../state/partsStateAtom' import { twapTimeIntervalAtom } from '../../state/twapOrderAtom' import { twapOrdersSettingsAtom, updateTwapOrdersSettingsAtom } from '../../state/twapOrdersSettingsAtom' @@ -25,6 +28,7 @@ import { TwapOrdersUpdater } from '../../updaters/TwapOrdersUpdater' import { deadlinePartsDisplay } from '../../utils/deadlinePartsDisplay' import { ActionButtons } from '../ActionButtons' import { TwapConfirmModal } from '../TwapConfirmModal' +import { TwapFormWarnings } from '../TwapFormWarnings' export function TwapFormWidget() { const { chainId, account } = useWalletInfo() @@ -39,6 +43,9 @@ export function TwapFormWidget() { const partsState = useAtomValue(partsStateAtom) const timeInterval = useAtomValue(twapTimeIntervalAtom) const updateSettingsState = useUpdateAtom(updateTwapOrdersSettingsAtom) + + const localFormValidation = useTwapFormState() + const primaryFormValidation = useGetTradeFormValidation() const isWrapOrUnwrap = useIsWrapOrUnwrap() const composableCowContract = useComposableCowContract() @@ -51,6 +58,8 @@ export function TwapFormWidget() { isCustomDeadline, } + const walletIsNotConnected = primaryFormValidation === TradeFormValidation.WalletNotConnected + const fallbackHandlerIsNotSet = !!localFormValidation && NEED_FALLBACK_HANDLER_STATES.includes(localFormValidation) const shouldLoadTwapOrders = !!(isSafeApp && chainId && account && composableCowContract) // Reset output amount when num of parts or input amount are changed @@ -64,7 +73,7 @@ export function TwapFormWidget() { {shouldLoadTwapOrders && ( )} - + {!isWrapOrUnwrap && ( @@ -109,7 +118,12 @@ export function TwapFormWidget() { - + + ) } diff --git a/src/modules/twap/hooks/useCreateTwapOrder.ts b/src/modules/twap/hooks/useCreateTwapOrder.ts index 441f38ed53..261dbcd989 100644 --- a/src/modules/twap/hooks/useCreateTwapOrder.ts +++ b/src/modules/twap/hooks/useCreateTwapOrder.ts @@ -11,9 +11,12 @@ import { useAdvancedOrdersDerivedState } from 'modules/advancedOrders' import { useTradeConfirmActions } from 'modules/trade' import { useWalletInfo } from 'modules/wallet' +import { useExtensibleFallbackContext } from './useExtensibleFallbackContext' import { useTwapOrderCreationContext } from './useTwapOrderCreationContext' -import { settleTwapOrder } from '../services/settleTwapOrder' +import { useSafeAppsSdk } from '../../wallet/web3-react/hooks/useSafeAppsSdk' +import { createTwapOrderTxs } from '../services/createTwapOrderTxs' +import { extensibleFallbackSetupTxs } from '../services/extensibleFallbackSetupTxs' import { twapOrderAtom } from '../state/twapOrderAtom' import { addTwapOrderToListAtom } from '../state/twapOrdersListAtom' import { TwapOrderStatus } from '../types' @@ -28,50 +31,69 @@ export function useCreateTwapOrder() { const { inputCurrencyAmount, outputCurrencyAmount } = useAdvancedOrdersDerivedState() + const safeAppsSdk = useSafeAppsSdk() const twapOrderCreationContext = useTwapOrderCreationContext(inputCurrencyAmount as Nullish>) + const extensibleFallbackContext = useExtensibleFallbackContext() const tradeConfirmActions = useTradeConfirmActions() - return useCallback(async () => { - if (!chainId || !account) return - if (!inputCurrencyAmount || !outputCurrencyAmount || !twapOrderCreationContext || !twapOrder) return - - const pendingTrade = { - inputAmount: inputCurrencyAmount, - outputAmount: outputCurrencyAmount, - } - - const startTime = Math.round((Date.now() + ms`1m`) / 1000) // Now + 1 min - const twapOrderWithStartTime = { ...twapOrder, startTime } - const paramsStruct = buildTwapOrderParamsStruct(chainId, twapOrderWithStartTime) - const orderId = getConditionalOrderId(paramsStruct) - - tradeConfirmActions.onSign(pendingTrade) - - try { - const { safeTxHash } = await settleTwapOrder(twapOrderWithStartTime, paramsStruct, twapOrderCreationContext) - - addTwapOrderToList({ - order: twapOrderToStruct(twapOrder), - status: TwapOrderStatus.WaitSigning, - chainId, - safeAddress: account, - submissionDate: new Date().toISOString(), - id: orderId, - }) - - tradeConfirmActions.onSuccess(safeTxHash) - } catch (error) { - tradeConfirmActions.onError(error.message || error) - } - }, [ - chainId, - account, - inputCurrencyAmount, - outputCurrencyAmount, - twapOrder, - tradeConfirmActions, - twapOrderCreationContext, - addTwapOrderToList, - ]) + return useCallback( + async (fallbackHandlerIsNotSet: boolean) => { + if (!chainId || !account) return + if ( + !inputCurrencyAmount || + !outputCurrencyAmount || + !twapOrderCreationContext || + !extensibleFallbackContext || + !safeAppsSdk || + !twapOrder + ) + return + + const pendingTrade = { + inputAmount: inputCurrencyAmount, + outputAmount: outputCurrencyAmount, + } + + const startTime = Math.round((Date.now() + ms`1m`) / 1000) // Now + 1 min + const twapOrderWithStartTime = { ...twapOrder, startTime } + const paramsStruct = buildTwapOrderParamsStruct(chainId, twapOrderWithStartTime) + const orderId = getConditionalOrderId(paramsStruct) + + tradeConfirmActions.onSign(pendingTrade) + + try { + const fallbackSetupTxs = fallbackHandlerIsNotSet + ? await extensibleFallbackSetupTxs(extensibleFallbackContext) + : [] + const createOrderTxs = createTwapOrderTxs(twapOrderWithStartTime, paramsStruct, twapOrderCreationContext) + const { safeTxHash } = await safeAppsSdk.txs.send({ txs: [...fallbackSetupTxs, ...createOrderTxs] }) + + addTwapOrderToList({ + order: twapOrderToStruct(twapOrder), + status: TwapOrderStatus.WaitSigning, + chainId, + safeAddress: account, + submissionDate: new Date().toISOString(), + id: orderId, + }) + + tradeConfirmActions.onSuccess(safeTxHash) + } catch (error) { + tradeConfirmActions.onError(error.message || error) + } + }, + [ + chainId, + account, + inputCurrencyAmount, + outputCurrencyAmount, + twapOrder, + tradeConfirmActions, + twapOrderCreationContext, + extensibleFallbackContext, + addTwapOrderToList, + safeAppsSdk, + ] + ) } diff --git a/src/modules/twap/hooks/useExtensibleFallbackContext.ts b/src/modules/twap/hooks/useExtensibleFallbackContext.ts index 9123a894d8..6e7d0998f6 100644 --- a/src/modules/twap/hooks/useExtensibleFallbackContext.ts +++ b/src/modules/twap/hooks/useExtensibleFallbackContext.ts @@ -1,30 +1,27 @@ import { SupportedChainId } from '@cowprotocol/cow-sdk' import { Web3Provider } from '@ethersproject/providers' -import SafeAppsSDK from '@safe-global/safe-apps-sdk' import { useWeb3React } from '@web3-react/core' import { useGP2SettlementContract } from 'legacy/hooks/useContract' import { isSupportedChain } from 'legacy/utils/supportedChainId' import { useWalletInfo } from 'modules/wallet' -import { useSafeAppsSdk } from 'modules/wallet/web3-react/hooks/useSafeAppsSdk' import { GPv2Settlement } from 'abis/types' export interface ExtensibleFallbackContext { + safeAddress: string chainId: SupportedChainId - safeAppsSdk: SafeAppsSDK settlementContract: GPv2Settlement provider: Web3Provider } export function useExtensibleFallbackContext(): ExtensibleFallbackContext | null { - const { chainId } = useWalletInfo() - const safeAppsSdk = useSafeAppsSdk() + const { chainId, account } = useWalletInfo() const settlementContract = useGP2SettlementContract() const { provider } = useWeb3React() - if (!safeAppsSdk || !settlementContract || !provider || !isSupportedChain(chainId)) return null + if (!account || !settlementContract || !provider || !isSupportedChain(chainId)) return null - return { safeAppsSdk, settlementContract, provider, chainId } + return { settlementContract, provider, chainId, safeAddress: account } } diff --git a/src/modules/twap/hooks/useSetupFallbackHandler.ts b/src/modules/twap/hooks/useSetupFallbackHandler.ts deleted file mode 100644 index 6883146868..0000000000 --- a/src/modules/twap/hooks/useSetupFallbackHandler.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { useCallback } from 'react' - -import { useExtensibleFallbackContext } from './useExtensibleFallbackContext' - -import { setupExtensibleFallbackHandler } from '../services/setupExtensibleFallbackHandler' - -export function useSetupFallbackHandler() { - const extensibleFallbackContext = useExtensibleFallbackContext() - - return useCallback(() => { - if (!extensibleFallbackContext) return - - setupExtensibleFallbackHandler(extensibleFallbackContext) - }, [extensibleFallbackContext]) -} diff --git a/src/modules/twap/hooks/useTwapFormState.ts b/src/modules/twap/hooks/useTwapFormState.ts index 32c7b2e0d3..a03df6b3ae 100644 --- a/src/modules/twap/hooks/useTwapFormState.ts +++ b/src/modules/twap/hooks/useTwapFormState.ts @@ -9,11 +9,12 @@ import { useIsSafeApp } from '../../wallet' import { getTwapFormState, TwapFormState } from '../pure/PrimaryActionButton/getTwapFormState' import { verifyExtensibleFallback } from '../services/verifyExtensibleFallback' import { twapOrderAtom } from '../state/twapOrderAtom' +import { twapOrdersSettingsAtom } from '../state/twapOrdersSettingsAtom' export function useTwapFormState(): TwapFormState | null { const isSafeApp = useIsSafeApp() const extensibleFallbackContext = useExtensibleFallbackContext() - + const { isFallbackHandlerSetupAccepted } = useAtomValue(twapOrdersSettingsAtom) const twapOrder = useAtomValue(twapOrderAtom) const verification = useAsyncMemo( @@ -23,6 +24,6 @@ export function useTwapFormState(): TwapFormState | null { ) return useMemo(() => { - return getTwapFormState({ isSafeApp, verification, twapOrder }) - }, [isSafeApp, verification, twapOrder]) + return getTwapFormState({ isSafeApp, isFallbackHandlerSetupAccepted, verification, twapOrder }) + }, [isSafeApp, isFallbackHandlerSetupAccepted, verification, twapOrder]) } diff --git a/src/modules/twap/hooks/useTwapOrderCreationContext.ts b/src/modules/twap/hooks/useTwapOrderCreationContext.ts index e6414d0235..02a300adf0 100644 --- a/src/modules/twap/hooks/useTwapOrderCreationContext.ts +++ b/src/modules/twap/hooks/useTwapOrderCreationContext.ts @@ -1,4 +1,3 @@ -import SafeAppsSDK from '@safe-global/safe-apps-sdk' import { CurrencyAmount, Token } from '@uniswap/sdk-core' import { Nullish } from 'types' @@ -7,14 +6,12 @@ import { Erc20 } from 'legacy/abis/types' import { useTokenContract } from 'legacy/hooks/useContract' import { useComposableCowContract } from 'modules/advancedOrders/hooks/useComposableCowContract' -import { useSafeAppsSdk } from 'modules/wallet/web3-react/hooks/useSafeAppsSdk' import { ComposableCoW } from 'abis/types' import { useNeedsApproval } from 'common/hooks/useNeedsApproval' import { useTradeSpenderAddress } from 'common/hooks/useTradeSpenderAddress' export interface TwapOrderCreationContext { - safeAppsSdk: SafeAppsSDK composableCowContract: ComposableCoW needsApproval: boolean spender: string @@ -24,13 +21,12 @@ export interface TwapOrderCreationContext { export function useTwapOrderCreationContext( inputAmount: Nullish> ): TwapOrderCreationContext | null { - const safeAppsSdk = useSafeAppsSdk() const composableCowContract = useComposableCowContract() const needsApproval = useNeedsApproval(inputAmount) const erc20Contract = useTokenContract(inputAmount?.currency.address) const spender = useTradeSpenderAddress() - if (!composableCowContract || !erc20Contract || !safeAppsSdk || !spender) return null + if (!composableCowContract || !erc20Contract || !spender) return null - return { safeAppsSdk, composableCowContract, erc20Contract, needsApproval, spender } + return { composableCowContract, erc20Contract, needsApproval, spender } } diff --git a/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx b/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx index 1b7a263e1d..c7954723c2 100644 --- a/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx +++ b/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx @@ -3,28 +3,35 @@ import { TWAPOrder } from '../../types' export interface TwapFormStateParams { isSafeApp: boolean + isFallbackHandlerSetupAccepted: boolean verification: ExtensibleFallbackVerification | null twapOrder: TWAPOrder | null } export enum TwapFormState { - LOADING_SAFE_INFO, - NOT_SAFE, - ORDER_NOT_SPECIFIED, - NEED_FALLBACK_HANDLER, - CAN_CREATE_ORDER, + LOADING_SAFE_INFO = 'LOADING_SAFE_INFO', + NOT_SAFE = 'NOT_SAFE', + NEED_FALLBACK_HANDLER = 'NEED_FALLBACK_HANDLER', + ACCEPTED_FALLBACK_HANDLER_SETUP = 'ACCEPTED_FALLBACK_HANDLER_SETUP', } +export const NEED_FALLBACK_HANDLER_STATES = [ + TwapFormState.NEED_FALLBACK_HANDLER, + TwapFormState.ACCEPTED_FALLBACK_HANDLER_SETUP, +] + export function getTwapFormState(props: TwapFormStateParams): TwapFormState | null { - const { isSafeApp, verification, twapOrder } = props + const { twapOrder, isSafeApp, isFallbackHandlerSetupAccepted, verification } = props if (!isSafeApp) return TwapFormState.NOT_SAFE if (verification === null) return TwapFormState.LOADING_SAFE_INFO - if (!twapOrder) return TwapFormState.ORDER_NOT_SPECIFIED + if (twapOrder && verification !== ExtensibleFallbackVerification.HAS_DOMAIN_VERIFIER) { + if (isFallbackHandlerSetupAccepted) { + return TwapFormState.ACCEPTED_FALLBACK_HANDLER_SETUP + } - if (verification !== ExtensibleFallbackVerification.HAS_DOMAIN_VERIFIER) { return TwapFormState.NEED_FALLBACK_HANDLER } diff --git a/src/modules/twap/pure/PrimaryActionButton/index.tsx b/src/modules/twap/pure/PrimaryActionButton/index.tsx index a6f99a75ff..7e08c6dd9a 100644 --- a/src/modules/twap/pure/PrimaryActionButton/index.tsx +++ b/src/modules/twap/pure/PrimaryActionButton/index.tsx @@ -6,11 +6,9 @@ import { ButtonSize } from 'legacy/theme/enum' import { TwapFormState } from './getTwapFormState' export interface PrimaryActionButtonContext { - openConfirmModal(): void - setFallbackHandler(): void + confirmTrade(): void } -// TODO: set correct buttons text const buttonsMap: Record JSX.Element> = { [TwapFormState.LOADING_SAFE_INFO]: () => ( @@ -19,22 +17,17 @@ const buttonsMap: Record ), [TwapFormState.NOT_SAFE]: () => ( - Please, connect to Safe + Unsupported wallet ), - [TwapFormState.ORDER_NOT_SPECIFIED]: () => ( + [TwapFormState.NEED_FALLBACK_HANDLER]: () => ( - Please, specify an order + Unsupported Safe ), - [TwapFormState.NEED_FALLBACK_HANDLER]: ({ setFallbackHandler }: PrimaryActionButtonContext) => ( - - Set fallback handler - - ), - [TwapFormState.CAN_CREATE_ORDER]: ({ openConfirmModal }: PrimaryActionButtonContext) => ( - - Create TWAP order + [TwapFormState.ACCEPTED_FALLBACK_HANDLER_SETUP]: ({ confirmTrade }: PrimaryActionButtonContext) => ( + + Review TWAP order ), } diff --git a/src/modules/twap/services/createTwapOrderTxs.test.ts b/src/modules/twap/services/createTwapOrderTxs.test.ts index 93ec86f47e..e80fb829eb 100644 --- a/src/modules/twap/services/createTwapOrderTxs.test.ts +++ b/src/modules/twap/services/createTwapOrderTxs.test.ts @@ -38,7 +38,6 @@ describe('Create TWAP order', () => { approveFn = jest.fn().mockReturnValue(APPROVE_TX_DATA) context = { - safeAppsSdk: null as any, composableCowContract: { interface: { encodeFunctionData: createCowFn }, address: COMPOSABLE_COW_ADDRESS[chainId], diff --git a/src/modules/twap/services/extensibleFallbackSetupTxs.test.tsx b/src/modules/twap/services/extensibleFallbackSetupTxs.test.tsx index c255da2697..62d3a75d8d 100644 --- a/src/modules/twap/services/extensibleFallbackSetupTxs.test.tsx +++ b/src/modules/twap/services/extensibleFallbackSetupTxs.test.tsx @@ -9,7 +9,7 @@ describe('extensibleFallbackSetupTxs - service to generate transactions for Exte it('Should create a bundle of two transactions: setFallbackHandler and setDomainVerifier', async () => { const context: ExtensibleFallbackContext = { chainId: SupportedChainId.GOERLI, - safeAppsSdk: { safe: { getInfo: () => ({ safeAddress: '0xA12D770028d7072b80BAEb6A1df962cccfd1dddd' }) } } as any, + safeAddress: '0xA12D770028d7072b80BAEb6A1df962cccfd1dddd', settlementContract: { callStatic: { domainSeparator: () => '0xa5b986c2f5845d520bcb903639360b147735589732066cea24a3a59678025c94' }, } as any, diff --git a/src/modules/twap/services/extensibleFallbackSetupTxs.ts b/src/modules/twap/services/extensibleFallbackSetupTxs.ts index f7b5c29383..e48bb2ce7b 100644 --- a/src/modules/twap/services/extensibleFallbackSetupTxs.ts +++ b/src/modules/twap/services/extensibleFallbackSetupTxs.ts @@ -7,9 +7,8 @@ import { getSignatureVerifierContract } from './getSignatureVerifierContract' import { ExtensibleFallbackContext } from '../hooks/useExtensibleFallbackContext' export async function extensibleFallbackSetupTxs(context: ExtensibleFallbackContext): Promise { - const { chainId, safeAppsSdk, settlementContract } = context + const { chainId, safeAddress, settlementContract } = context - const { safeAddress } = await safeAppsSdk.safe.getInfo() const domainSeparator = await settlementContract.callStatic.domainSeparator() const signatureVerifierContract = await getSignatureVerifierContract(context) diff --git a/src/modules/twap/services/getSignatureVerifierContract.ts b/src/modules/twap/services/getSignatureVerifierContract.ts index 423bfc0d6f..18c82dc1a5 100644 --- a/src/modules/twap/services/getSignatureVerifierContract.ts +++ b/src/modules/twap/services/getSignatureVerifierContract.ts @@ -8,8 +8,7 @@ import { ExtensibleFallbackContext } from '../hooks/useExtensibleFallbackContext export async function getSignatureVerifierContract( context: ExtensibleFallbackContext ): Promise { - const { safeAppsSdk, provider } = context - const { safeAddress } = await safeAppsSdk.safe.getInfo() + const { safeAddress, provider } = context return new Contract(safeAddress, SignatureVerifierMuxerABI, provider) as SignatureVerifierMuxer } diff --git a/src/modules/twap/services/settleTwapOrder.test.ts b/src/modules/twap/services/settleTwapOrder.test.ts deleted file mode 100644 index 8045d79570..0000000000 --- a/src/modules/twap/services/settleTwapOrder.test.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { useUpdateAtom } from 'jotai/utils' -import { useEffect } from 'react' - -import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { CurrencyAmount } from '@uniswap/sdk-core' - -import { renderHook } from '@testing-library/react-hooks' - -import { COW } from 'legacy/constants/tokens' -import { useTokenContract } from 'legacy/hooks/useContract' -import { WETH_GOERLI } from 'legacy/utils/goerli/constants' - -import { useComposableCowContract } from 'modules/advancedOrders/hooks/useComposableCowContract' -import { walletInfoAtom } from 'modules/wallet/api/state' -import { useSafeAppsSdk } from 'modules/wallet/web3-react/hooks/useSafeAppsSdk' - -import { useNeedsApproval } from 'common/hooks/useNeedsApproval' -import { useTradeSpenderAddress } from 'common/hooks/useTradeSpenderAddress' - -import { settleTwapOrder } from './settleTwapOrder' - -import { COMPOSABLE_COW_ADDRESS } from '../../advancedOrders' -import { useTwapOrderCreationContext } from '../hooks/useTwapOrderCreationContext' -import { TWAPOrder } from '../types' -import { buildTwapOrderParamsStruct } from '../utils/buildTwapOrderParamsStruct' - -jest.mock('modules/wallet/web3-react/hooks/useSafeAppsSdk') -jest.mock('modules/advancedOrders/hooks/useComposableCowContract') -jest.mock('common/hooks/useNeedsApproval') -jest.mock('legacy/hooks/useContract') -jest.mock('common/hooks/useTradeSpenderAddress') - -const chainId = SupportedChainId.GOERLI - -const order: TWAPOrder = { - sellAmount: CurrencyAmount.fromRawAmount(COW[chainId], 100_000_000), - buyAmount: CurrencyAmount.fromRawAmount(WETH_GOERLI, 5_000), - receiver: '0xca063a2ab07491ee991dcecb456d1265f842b568', - numOfParts: 5, - startTime: 1497076708, - timeInterval: 350, - span: 0, -} - -const paramsStruct = buildTwapOrderParamsStruct(chainId, order) - -const useSafeAppsSdkMock = useSafeAppsSdk as jest.MockedFunction -const useComposableCowContractMock = useComposableCowContract as jest.MockedFunction -const useNeedsApprovalMock = useNeedsApproval as jest.MockedFunction -const useTokenContractMock = useTokenContract as jest.MockedFunction -const useTradeSpenderAddressMock = useTradeSpenderAddress as jest.MockedFunction - -describe('settleTwapOrder - integration test', () => { - beforeEach(() => { - jest.spyOn(Date, 'now').mockImplementation(() => 1497076708000) - - useSafeAppsSdkMock.mockReturnValue({ txs: { send: () => Promise.resolve({ safeTxHash: '0x00b' }) } } as any) - useComposableCowContractMock.mockReturnValue({ - interface: { encodeFunctionData: () => '0xCREATE_COW_TX_DATA' }, - address: COMPOSABLE_COW_ADDRESS[chainId], - } as any) - useNeedsApprovalMock.mockReturnValue(true) - useTokenContractMock.mockReturnValue({ interface: { encodeFunctionData: () => '0xAPPROVE_TX_DATA' } } as any) - useTradeSpenderAddressMock.mockReturnValue('0xes8p7e9n3dbedr4e7caf8451050d1948be717679') - }) - - it('Should send a bundle of transactions to Safe with a TWAP order creation', async () => { - const { result } = renderHook(() => { - const updateWalletInfo = useUpdateAtom(walletInfoAtom) - - useEffect(() => { - updateWalletInfo({ chainId }) - }, [updateWalletInfo]) - - const context = useTwapOrderCreationContext(order.sellAmount) - - if (!context) return null - - return settleTwapOrder(order, paramsStruct, context) - }) - - expect(await result.current).toEqual({ safeTxHash: '0x00b' }) - }) -}) diff --git a/src/modules/twap/services/settleTwapOrder.ts b/src/modules/twap/services/settleTwapOrder.ts deleted file mode 100644 index e11efa71b6..0000000000 --- a/src/modules/twap/services/settleTwapOrder.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { SendTransactionsResponse } from '@safe-global/safe-apps-sdk' - -import { createTwapOrderTxs } from './createTwapOrderTxs' - -import { TwapOrderCreationContext } from '../hooks/useTwapOrderCreationContext' -import { ConditionalOrderParams, TWAPOrder } from '../types' - -export async function settleTwapOrder( - order: TWAPOrder, - paramsStruct: ConditionalOrderParams, - context: TwapOrderCreationContext -): Promise { - const { safeAppsSdk } = context - - const txs = createTwapOrderTxs(order, paramsStruct, context) - - return await safeAppsSdk.txs.send({ txs }) -} diff --git a/src/modules/twap/services/setupExtensibleFallbackHandler.test.ts b/src/modules/twap/services/setupExtensibleFallbackHandler.test.ts deleted file mode 100644 index 5b4ae4ad0b..0000000000 --- a/src/modules/twap/services/setupExtensibleFallbackHandler.test.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { useUpdateAtom } from 'jotai/utils' -import { useEffect } from 'react' - -import { Web3Provider } from '@ethersproject/providers' -import { useWeb3React } from '@web3-react/core' - -import { renderHook } from '@testing-library/react-hooks' -import { WithMockedWeb3 } from 'test-utils' - -import { useGP2SettlementContract } from 'legacy/hooks/useContract' - -import { walletInfoAtom } from 'modules/wallet/api/state' -import { useSafeAppsSdk } from 'modules/wallet/web3-react/hooks/useSafeAppsSdk' - -import { setupExtensibleFallbackHandler } from './setupExtensibleFallbackHandler' - -import { useExtensibleFallbackContext } from '../hooks/useExtensibleFallbackContext' - -const chainId = 5 - -jest.mock('modules/wallet/web3-react/hooks/useSafeAppsSdk') -jest.mock('legacy/hooks/useContract') -jest.mock('@web3-react/core', () => { - return { - ...jest.requireActual('@web3-react/core'), - useWeb3React: jest.fn(), - } -}) - -const defaultJsonRpcHandler = (method: string, params?: any[]) => { - if (method === 'eth_chainId') return Promise.resolve(5) - - // domainVerifiers() - if (method === 'eth_call' && params?.[0]?.data?.startsWith('0x51cad5ee')) { - //Composable cow address - return Promise.resolve('0x000000000000000000000000a31b99bd44528c7bae9e1f675d810ae13b0e29aa') - } - - return Promise.resolve(null) -} - -const useWeb3ReactMock = useWeb3React as jest.MockedFunction -const useSafeAppsSdkMock = useSafeAppsSdk as jest.MockedFunction -const useGP2SettlementContractMock = useGP2SettlementContract as jest.MockedFunction - -describe('setupExtensibleFallbackHandler - integration test', () => { - beforeEach(() => { - useWeb3ReactMock.mockReturnValue({ provider: new Web3Provider(defaultJsonRpcHandler) } as any) - useSafeAppsSdkMock.mockReturnValue({ - safe: { - getInfo: () => ({ safeAddress: '0xA12D770028d7072b80BAEb6A1df962cccfd1dddd' }), - }, - txs: { send: () => Promise.resolve({ safeTxHash: '0x00c' }) }, - } as any) - useGP2SettlementContractMock.mockReturnValue({ - callStatic: { domainSeparator: () => '0xa5b986c2f5845d520bcb903639360b147735589732066cea24a3a59678025c94' }, - } as any) - }) - - it('Should create a bundle of transaction to setup fallback handler and domain verifier', async () => { - const { result } = renderHook( - () => { - const updateWalletInfo = useUpdateAtom(walletInfoAtom) - - useEffect(() => { - updateWalletInfo({ chainId }) - }, [updateWalletInfo]) - - const context = useExtensibleFallbackContext() - - if (!context) return null - - return setupExtensibleFallbackHandler(context) - }, - { wrapper: WithMockedWeb3 } - ) - - expect(await result.current).toEqual({ safeTxHash: '0x00c' }) - }) -}) diff --git a/src/modules/twap/services/setupExtensibleFallbackHandler.ts b/src/modules/twap/services/setupExtensibleFallbackHandler.ts deleted file mode 100644 index 05c93f61ca..0000000000 --- a/src/modules/twap/services/setupExtensibleFallbackHandler.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { SendTransactionsResponse } from '@safe-global/safe-apps-sdk' - -import { extensibleFallbackSetupTxs } from './extensibleFallbackSetupTxs' - -import { ExtensibleFallbackContext } from '../hooks/useExtensibleFallbackContext' - -export async function setupExtensibleFallbackHandler( - context: ExtensibleFallbackContext -): Promise { - const { safeAppsSdk } = context - - const txs = await extensibleFallbackSetupTxs(context) - const response = await safeAppsSdk.txs.send({ txs }) - - // TODO: process the sent transaction - console.log('SET HANDLER: ', response) - - return response -} diff --git a/src/modules/twap/services/verifyExtensibleFallback.test.ts b/src/modules/twap/services/verifyExtensibleFallback.test.ts index 1cd5300a10..7e778389d0 100644 --- a/src/modules/twap/services/verifyExtensibleFallback.test.ts +++ b/src/modules/twap/services/verifyExtensibleFallback.test.ts @@ -12,7 +12,7 @@ const defaultJsonRpcHandler = (method: string) => { } const context: ExtensibleFallbackContext = { chainId: SupportedChainId.GOERLI, - safeAppsSdk: { safe: { getInfo: () => ({ safeAddress: '0x360Ba61Bc799edfa01e306f1eCCb2F6e0C3C8c8e' }) } } as any, + safeAddress: '0x360Ba61Bc799edfa01e306f1eCCb2F6e0C3C8c8e', settlementContract: { callStatic: { domainSeparator: () => '0xa5b986c2f5845d520bcb903639360b147735589732066cea24a3a59678025c94' }, } as any, diff --git a/src/modules/twap/services/verifyExtensibleFallback.ts b/src/modules/twap/services/verifyExtensibleFallback.ts index caa2be5466..134a5d264b 100644 --- a/src/modules/twap/services/verifyExtensibleFallback.ts +++ b/src/modules/twap/services/verifyExtensibleFallback.ts @@ -16,9 +16,8 @@ export enum ExtensibleFallbackVerification { export async function verifyExtensibleFallback( context: ExtensibleFallbackContext ): Promise { - const { chainId, safeAppsSdk, settlementContract } = context + const { chainId, safeAddress, settlementContract } = context const composableCowContractAddress = COMPOSABLE_COW_ADDRESS[chainId] - const { safeAddress } = await safeAppsSdk.safe.getInfo() const domainSeparator = await settlementContract.callStatic.domainSeparator() const signatureVerifierContract = await getSignatureVerifierContract(context) diff --git a/src/modules/twap/state/twapOrdersSettingsAtom.ts b/src/modules/twap/state/twapOrdersSettingsAtom.ts index 1faeae10ef..0a0af68e16 100644 --- a/src/modules/twap/state/twapOrdersSettingsAtom.ts +++ b/src/modules/twap/state/twapOrdersSettingsAtom.ts @@ -19,6 +19,7 @@ export interface TwapOrdersDeadline { export interface TwapOrdersSettingsState extends TwapOrdersDeadline { readonly numberOfPartsValue: number readonly slippageValue: number | null + readonly isFallbackHandlerSetupAccepted: boolean } export const defaultCustomDeadline: TwapOrdersDeadline['customDeadline'] = { @@ -34,6 +35,7 @@ export const defaultTwapOrdersSettings: TwapOrdersSettingsState = { numberOfPartsValue: defaultNumOfParts, // null = auto slippageValue: null, + isFallbackHandlerSetupAccepted: false, } export const twapOrdersSettingsAtom = atomWithStorage( From 9b60df40f12c98860185e8b331b170ea3b45e24d Mon Sep 17 00:00:00 2001 From: Leandro Date: Thu, 15 Jun 2023 07:44:21 -0700 Subject: [PATCH 23/67] feat(twap): validation and warnings - sell amount too small (#2661) * refactor: remove duplicated entry for USDC * feat: update button error msg when sell amount is too small * refactor: use fiat amount from state rather than query it again * refactor: move MINIMUM_PART_SELL_AMOUNT_FIAT to const * refactor: move isSellAmountTooSmall to utils * feat: add SmallPartVolumeWarningBanner * feat: show warning when part amount < 5k * feat: update banner text with text from design files * refactor: use fiat amount from partsStateAtom * fix: reduce gchain min part amount to 5 and goerli to 100 * Fix code style issues with Prettier --------- Co-authored-by: Lint Action --- src/legacy/hooks/useStablecoinPrice/index.ts | 3 +-- src/modules/twap/const.ts | 10 ++++++- .../containers/TwapFormWarnings/index.tsx | 10 ++++--- .../warnings/SmallPartVolumeWarning.tsx | 26 +++++++++++++++++++ .../TwapFormWarnings/warnings/index.ts | 3 +++ src/modules/twap/hooks/useTwapFormState.ts | 18 ++++++++++--- .../PrimaryActionButton/getTwapFormState.tsx | 15 ++++++++++- .../twap/pure/PrimaryActionButton/index.tsx | 5 ++++ .../twap/utils/isSellAmountTooSmall.ts | 19 ++++++++++++++ 9 files changed, 99 insertions(+), 10 deletions(-) create mode 100644 src/modules/twap/containers/TwapFormWarnings/warnings/SmallPartVolumeWarning.tsx create mode 100644 src/modules/twap/containers/TwapFormWarnings/warnings/index.ts create mode 100644 src/modules/twap/utils/isSellAmountTooSmall.ts diff --git a/src/legacy/hooks/useStablecoinPrice/index.ts b/src/legacy/hooks/useStablecoinPrice/index.ts index 5d9832c857..6908a4991a 100644 --- a/src/legacy/hooks/useStablecoinPrice/index.ts +++ b/src/legacy/hooks/useStablecoinPrice/index.ts @@ -7,7 +7,7 @@ import { unstable_batchedUpdates as batchedUpdate } from 'react-dom' import { Nullish } from 'types' import { DEFAULT_NETWORK_FOR_LISTS } from 'legacy/constants/lists' -import { USDC, USDC_MAINNET } from 'legacy/constants/tokens' +import { USDC } from 'legacy/constants/tokens' import { useGetGpPriceStrategy } from 'legacy/hooks/useGetGpPriceStrategy' import { stringToCurrency } from 'legacy/state/swap/extension' import { useGetGpUsdcPrice } from 'legacy/utils/price' @@ -23,7 +23,6 @@ import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount' export const getUsdQuoteValidTo = () => Math.ceil(Date.now() / 1000) + 600 const STABLECOIN_AMOUNT_OUT: { [chain in SupportedChainId]: CurrencyAmount } = { - [SupportedChainId.MAINNET]: CurrencyAmount.fromRawAmount(USDC_MAINNET, 100_000e6), [SupportedChainId.MAINNET]: CurrencyAmount.fromRawAmount(USDC[SupportedChainId.MAINNET], 100e6), [SupportedChainId.GOERLI]: CurrencyAmount.fromRawAmount(USDC[SupportedChainId.GOERLI], 100e6), [SupportedChainId.GNOSIS_CHAIN]: CurrencyAmount.fromRawAmount(USDC[SupportedChainId.GNOSIS_CHAIN], 10_000e6), diff --git a/src/modules/twap/const.ts b/src/modules/twap/const.ts index 714b4f57f8..5a908c5cad 100644 --- a/src/modules/twap/const.ts +++ b/src/modules/twap/const.ts @@ -1,8 +1,10 @@ import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { Percent } from '@uniswap/sdk-core' +import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core' import ms from 'ms.macro' +import { USDC } from 'legacy/constants/tokens' + import { TwapOrderStatus } from './types' export const DEFAULT_TWAP_SLIPPAGE = new Percent(10, 100) // 10% @@ -33,3 +35,9 @@ export const TWAP_HANDLER_ADDRESS: Record = { } export const TWAP_PENDING_STATUSES = [TwapOrderStatus.WaitSigning, TwapOrderStatus.Pending, TwapOrderStatus.Scheduled] + +export const MINIMUM_PART_SELL_AMOUNT_FIAT: Record> = { + [SupportedChainId.MAINNET]: CurrencyAmount.fromRawAmount(USDC[SupportedChainId.MAINNET], 5_000e6), // 5k + [SupportedChainId.GOERLI]: CurrencyAmount.fromRawAmount(USDC[SupportedChainId.GOERLI], 100e6), // 100 + [SupportedChainId.GNOSIS_CHAIN]: CurrencyAmount.fromRawAmount(USDC[SupportedChainId.GNOSIS_CHAIN], 5e6), // 5 +} diff --git a/src/modules/twap/containers/TwapFormWarnings/index.tsx b/src/modules/twap/containers/TwapFormWarnings/index.tsx index c8ca085add..e0aa8047b4 100644 --- a/src/modules/twap/containers/TwapFormWarnings/index.tsx +++ b/src/modules/twap/containers/TwapFormWarnings/index.tsx @@ -2,10 +2,9 @@ import { useAtomValue } from 'jotai' import { useUpdateAtom } from 'jotai/utils' import { useCallback } from 'react' -import { FallbackHandlerWarning } from './warnings/FallbackHandlerWarning' -import { UnsupportedWalletWarning } from './warnings/UnsupportedWalletWarning' +import { FallbackHandlerWarning, SmallPartVolumeWarning, UnsupportedWalletWarning } from './warnings' -import { useIsSafeViaWc } from '../../../wallet' +import { useIsSafeViaWc, useWalletInfo } from '../../../wallet' import { NEED_FALLBACK_HANDLER_STATES, TwapFormState } from '../../pure/PrimaryActionButton/getTwapFormState' import { twapOrdersSettingsAtom, updateTwapOrdersSettingsAtom } from '../../state/twapOrdersSettingsAtom' @@ -18,6 +17,7 @@ export function TwapFormWarnings({ localFormValidation, walletIsNotConnected }: const { isFallbackHandlerSetupAccepted } = useAtomValue(twapOrdersSettingsAtom) const updateTwapOrdersSettings = useUpdateAtom(updateTwapOrdersSettingsAtom) const isSafeViaWc = useIsSafeViaWc() + const { chainId } = useWalletInfo() const toggleFallbackHandlerSetupFlag = useCallback( (isFallbackHandlerSetupAccepted: boolean) => { @@ -42,5 +42,9 @@ export function TwapFormWarnings({ localFormValidation, walletIsNotConnected }: ) } + if (chainId && localFormValidation === TwapFormState.SELL_AMOUNT_TOO_SMALL) { + return + } + return null } diff --git a/src/modules/twap/containers/TwapFormWarnings/warnings/SmallPartVolumeWarning.tsx b/src/modules/twap/containers/TwapFormWarnings/warnings/SmallPartVolumeWarning.tsx new file mode 100644 index 0000000000..8c9b900233 --- /dev/null +++ b/src/modules/twap/containers/TwapFormWarnings/warnings/SmallPartVolumeWarning.tsx @@ -0,0 +1,26 @@ +import { SupportedChainId } from '@cowprotocol/cow-sdk' + +import { InlineBanner } from 'common/pure/InlineBanner' +import { TokenAmount } from 'common/pure/TokenAmount' + +import { MINIMUM_PART_SELL_AMOUNT_FIAT } from '../../../const' + +export type SmallPartVolumeWarningBannerProps = { + chainId: SupportedChainId +} + +export function SmallPartVolumeWarning({ chainId }: SmallPartVolumeWarningBannerProps) { + const amount = MINIMUM_PART_SELL_AMOUNT_FIAT[chainId] + + return ( + + <> + Minimum sell size: The sell amount per part of your TWAP order should be at least{' '} + + $ + + . Decrease the number of parts or increase the total sell amount. + + + ) +} diff --git a/src/modules/twap/containers/TwapFormWarnings/warnings/index.ts b/src/modules/twap/containers/TwapFormWarnings/warnings/index.ts new file mode 100644 index 0000000000..d45eae3692 --- /dev/null +++ b/src/modules/twap/containers/TwapFormWarnings/warnings/index.ts @@ -0,0 +1,3 @@ +export { FallbackHandlerWarning } from './FallbackHandlerWarning' +export { SmallPartVolumeWarning } from './SmallPartVolumeWarning' +export { UnsupportedWalletWarning } from './UnsupportedWalletWarning' diff --git a/src/modules/twap/hooks/useTwapFormState.ts b/src/modules/twap/hooks/useTwapFormState.ts index a03df6b3ae..a22ddc5ba8 100644 --- a/src/modules/twap/hooks/useTwapFormState.ts +++ b/src/modules/twap/hooks/useTwapFormState.ts @@ -3,11 +3,13 @@ import { useMemo } from 'react' import { useAsyncMemo } from 'use-async-memo' +import { useIsSafeApp, useWalletInfo } from 'modules/wallet' + import { useExtensibleFallbackContext } from './useExtensibleFallbackContext' -import { useIsSafeApp } from '../../wallet' import { getTwapFormState, TwapFormState } from '../pure/PrimaryActionButton/getTwapFormState' import { verifyExtensibleFallback } from '../services/verifyExtensibleFallback' +import { partsStateAtom } from '../state/partsStateAtom' import { twapOrderAtom } from '../state/twapOrderAtom' import { twapOrdersSettingsAtom } from '../state/twapOrdersSettingsAtom' @@ -15,7 +17,10 @@ export function useTwapFormState(): TwapFormState | null { const isSafeApp = useIsSafeApp() const extensibleFallbackContext = useExtensibleFallbackContext() const { isFallbackHandlerSetupAccepted } = useAtomValue(twapOrdersSettingsAtom) + const { chainId } = useWalletInfo() + const twapOrder = useAtomValue(twapOrderAtom) + const { inputFiatAmount: sellAmountPartFiat } = useAtomValue(partsStateAtom) const verification = useAsyncMemo( () => (extensibleFallbackContext ? verifyExtensibleFallback(extensibleFallbackContext) : null), @@ -24,6 +29,13 @@ export function useTwapFormState(): TwapFormState | null { ) return useMemo(() => { - return getTwapFormState({ isSafeApp, isFallbackHandlerSetupAccepted, verification, twapOrder }) - }, [isSafeApp, isFallbackHandlerSetupAccepted, verification, twapOrder]) + return getTwapFormState({ + isSafeApp, + isFallbackHandlerSetupAccepted, + verification, + twapOrder, + sellAmountPartFiat, + chainId, + }) + }, [isSafeApp, isFallbackHandlerSetupAccepted, verification, twapOrder, sellAmountPartFiat, chainId]) } diff --git a/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx b/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx index c7954723c2..e25ddbd72f 100644 --- a/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx +++ b/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx @@ -1,11 +1,19 @@ +import { SupportedChainId } from '@cowprotocol/cow-sdk' +import { Currency, CurrencyAmount } from '@uniswap/sdk-core' + +import { Nullish } from 'types' + import { ExtensibleFallbackVerification } from '../../services/verifyExtensibleFallback' import { TWAPOrder } from '../../types' +import { isSellAmountTooSmall } from '../../utils/isSellAmountTooSmall' export interface TwapFormStateParams { isSafeApp: boolean isFallbackHandlerSetupAccepted: boolean verification: ExtensibleFallbackVerification | null twapOrder: TWAPOrder | null + sellAmountPartFiat: Nullish> + chainId: SupportedChainId | undefined } export enum TwapFormState { @@ -13,6 +21,7 @@ export enum TwapFormState { NOT_SAFE = 'NOT_SAFE', NEED_FALLBACK_HANDLER = 'NEED_FALLBACK_HANDLER', ACCEPTED_FALLBACK_HANDLER_SETUP = 'ACCEPTED_FALLBACK_HANDLER_SETUP', + SELL_AMOUNT_TOO_SMALL = 'SELL_AMOUNT_TOO_SMALL', } export const NEED_FALLBACK_HANDLER_STATES = [ @@ -21,7 +30,7 @@ export const NEED_FALLBACK_HANDLER_STATES = [ ] export function getTwapFormState(props: TwapFormStateParams): TwapFormState | null { - const { twapOrder, isSafeApp, isFallbackHandlerSetupAccepted, verification } = props + const { twapOrder, isSafeApp, isFallbackHandlerSetupAccepted, verification, sellAmountPartFiat, chainId } = props if (!isSafeApp) return TwapFormState.NOT_SAFE @@ -35,5 +44,9 @@ export function getTwapFormState(props: TwapFormStateParams): TwapFormState | nu return TwapFormState.NEED_FALLBACK_HANDLER } + if (isSellAmountTooSmall(sellAmountPartFiat, chainId)) { + return TwapFormState.SELL_AMOUNT_TOO_SMALL + } + return null } diff --git a/src/modules/twap/pure/PrimaryActionButton/index.tsx b/src/modules/twap/pure/PrimaryActionButton/index.tsx index 7e08c6dd9a..00b6e9ae8e 100644 --- a/src/modules/twap/pure/PrimaryActionButton/index.tsx +++ b/src/modules/twap/pure/PrimaryActionButton/index.tsx @@ -30,6 +30,11 @@ const buttonsMap: Record Review TWAP order ), + [TwapFormState.SELL_AMOUNT_TOO_SMALL]: () => ( + + Sell amount too small + + ), } export interface PrimaryActionButtonProps { diff --git a/src/modules/twap/utils/isSellAmountTooSmall.ts b/src/modules/twap/utils/isSellAmountTooSmall.ts new file mode 100644 index 0000000000..d4a3f6324d --- /dev/null +++ b/src/modules/twap/utils/isSellAmountTooSmall.ts @@ -0,0 +1,19 @@ +import { SupportedChainId } from '@cowprotocol/cow-sdk' +import { Currency, CurrencyAmount } from '@uniswap/sdk-core' + +import { Nullish } from 'types' + +import { MINIMUM_PART_SELL_AMOUNT_FIAT } from '../const' + +export function isSellAmountTooSmall( + sellAmount: Nullish>, + chainId: SupportedChainId | undefined +): boolean { + if (!chainId) { + return false + } + + const minimum = MINIMUM_PART_SELL_AMOUNT_FIAT[chainId] + + return !!minimum && !!sellAmount && sellAmount.lessThan(minimum) +} From 67b1f43ffbea957ccc242fa9432c8efddccb7ab8 Mon Sep 17 00:00:00 2001 From: Leandro Date: Thu, 15 Jun 2023 08:57:56 -0700 Subject: [PATCH 24/67] feat(twap): validation and warnings - minimum part time (#2680) * feat: add part time interval logic * feat: add SmallPartTimeWarning banner --- src/modules/twap/const.ts | 2 ++ .../twap/containers/TwapFormWarnings/index.tsx | 11 ++++++++++- .../warnings/SmallPartTimeWarning.tsx | 17 +++++++++++++++++ .../TwapFormWarnings/warnings/index.ts | 1 + src/modules/twap/hooks/useTwapFormState.ts | 6 ++++-- .../PrimaryActionButton/getTwapFormState.tsx | 11 ++++++++++- .../twap/pure/PrimaryActionButton/index.tsx | 5 +++++ .../twap/utils/isPartTimeIntervalTooShort.ts | 5 +++++ 8 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 src/modules/twap/containers/TwapFormWarnings/warnings/SmallPartTimeWarning.tsx create mode 100644 src/modules/twap/utils/isPartTimeIntervalTooShort.ts diff --git a/src/modules/twap/const.ts b/src/modules/twap/const.ts index 5a908c5cad..a46ac986cd 100644 --- a/src/modules/twap/const.ts +++ b/src/modules/twap/const.ts @@ -41,3 +41,5 @@ export const MINIMUM_PART_SELL_AMOUNT_FIAT: Record } + if (localFormValidation === TwapFormState.PART_TIME_INTERVAL_TOO_SHORT) { + return + } + return null } diff --git a/src/modules/twap/containers/TwapFormWarnings/warnings/SmallPartTimeWarning.tsx b/src/modules/twap/containers/TwapFormWarnings/warnings/SmallPartTimeWarning.tsx new file mode 100644 index 0000000000..3196bee934 --- /dev/null +++ b/src/modules/twap/containers/TwapFormWarnings/warnings/SmallPartTimeWarning.tsx @@ -0,0 +1,17 @@ +import { InlineBanner } from 'common/pure/InlineBanner' + +import { MINIMUM_PART_TIME } from '../../../const' +import { deadlinePartsDisplay } from '../../../utils/deadlinePartsDisplay' + +export function SmallPartTimeWarning() { + const time = deadlinePartsDisplay(MINIMUM_PART_TIME, true) + + return ( + + <> + Minimum part time: A minimum of {time} between parts is required. Decrease the + number of parts or increase the total duration. + + + ) +} diff --git a/src/modules/twap/containers/TwapFormWarnings/warnings/index.ts b/src/modules/twap/containers/TwapFormWarnings/warnings/index.ts index d45eae3692..ad16230bf0 100644 --- a/src/modules/twap/containers/TwapFormWarnings/warnings/index.ts +++ b/src/modules/twap/containers/TwapFormWarnings/warnings/index.ts @@ -1,3 +1,4 @@ export { FallbackHandlerWarning } from './FallbackHandlerWarning' +export { SmallPartTimeWarning } from './SmallPartTimeWarning' export { SmallPartVolumeWarning } from './SmallPartVolumeWarning' export { UnsupportedWalletWarning } from './UnsupportedWalletWarning' diff --git a/src/modules/twap/hooks/useTwapFormState.ts b/src/modules/twap/hooks/useTwapFormState.ts index a22ddc5ba8..24a1633d30 100644 --- a/src/modules/twap/hooks/useTwapFormState.ts +++ b/src/modules/twap/hooks/useTwapFormState.ts @@ -10,7 +10,7 @@ import { useExtensibleFallbackContext } from './useExtensibleFallbackContext' import { getTwapFormState, TwapFormState } from '../pure/PrimaryActionButton/getTwapFormState' import { verifyExtensibleFallback } from '../services/verifyExtensibleFallback' import { partsStateAtom } from '../state/partsStateAtom' -import { twapOrderAtom } from '../state/twapOrderAtom' +import { twapOrderAtom, twapTimeIntervalAtom } from '../state/twapOrderAtom' import { twapOrdersSettingsAtom } from '../state/twapOrdersSettingsAtom' export function useTwapFormState(): TwapFormState | null { @@ -21,6 +21,7 @@ export function useTwapFormState(): TwapFormState | null { const twapOrder = useAtomValue(twapOrderAtom) const { inputFiatAmount: sellAmountPartFiat } = useAtomValue(partsStateAtom) + const partTime = useAtomValue(twapTimeIntervalAtom) const verification = useAsyncMemo( () => (extensibleFallbackContext ? verifyExtensibleFallback(extensibleFallbackContext) : null), @@ -36,6 +37,7 @@ export function useTwapFormState(): TwapFormState | null { twapOrder, sellAmountPartFiat, chainId, + partTime, }) - }, [isSafeApp, isFallbackHandlerSetupAccepted, verification, twapOrder, sellAmountPartFiat, chainId]) + }, [isSafeApp, isFallbackHandlerSetupAccepted, verification, twapOrder, sellAmountPartFiat, chainId, partTime]) } diff --git a/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx b/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx index e25ddbd72f..9b08c8ee3e 100644 --- a/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx +++ b/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx @@ -5,6 +5,7 @@ import { Nullish } from 'types' import { ExtensibleFallbackVerification } from '../../services/verifyExtensibleFallback' import { TWAPOrder } from '../../types' +import { isPartTimeIntervalTooShort } from '../../utils/isPartTimeIntervalTooShort' import { isSellAmountTooSmall } from '../../utils/isSellAmountTooSmall' export interface TwapFormStateParams { @@ -14,6 +15,7 @@ export interface TwapFormStateParams { twapOrder: TWAPOrder | null sellAmountPartFiat: Nullish> chainId: SupportedChainId | undefined + partTime: number | undefined } export enum TwapFormState { @@ -22,6 +24,7 @@ export enum TwapFormState { NEED_FALLBACK_HANDLER = 'NEED_FALLBACK_HANDLER', ACCEPTED_FALLBACK_HANDLER_SETUP = 'ACCEPTED_FALLBACK_HANDLER_SETUP', SELL_AMOUNT_TOO_SMALL = 'SELL_AMOUNT_TOO_SMALL', + PART_TIME_INTERVAL_TOO_SHORT = 'PART_TIME_INTERVAL_TOO_SHORT', } export const NEED_FALLBACK_HANDLER_STATES = [ @@ -30,7 +33,8 @@ export const NEED_FALLBACK_HANDLER_STATES = [ ] export function getTwapFormState(props: TwapFormStateParams): TwapFormState | null { - const { twapOrder, isSafeApp, isFallbackHandlerSetupAccepted, verification, sellAmountPartFiat, chainId } = props + const { twapOrder, isSafeApp, isFallbackHandlerSetupAccepted, verification, sellAmountPartFiat, chainId, partTime } = + props if (!isSafeApp) return TwapFormState.NOT_SAFE @@ -48,5 +52,10 @@ export function getTwapFormState(props: TwapFormStateParams): TwapFormState | nu return TwapFormState.SELL_AMOUNT_TOO_SMALL } + // Not using `twapOrder.timeInterval` because it's not filled until the order is ready + if (isPartTimeIntervalTooShort(partTime)) { + return TwapFormState.PART_TIME_INTERVAL_TOO_SHORT + } + return null } diff --git a/src/modules/twap/pure/PrimaryActionButton/index.tsx b/src/modules/twap/pure/PrimaryActionButton/index.tsx index 00b6e9ae8e..7d61ce99cb 100644 --- a/src/modules/twap/pure/PrimaryActionButton/index.tsx +++ b/src/modules/twap/pure/PrimaryActionButton/index.tsx @@ -35,6 +35,11 @@ const buttonsMap: Record Sell amount too small ), + [TwapFormState.PART_TIME_INTERVAL_TOO_SHORT]: () => ( + + Interval time too short + + ), } export interface PrimaryActionButtonProps { diff --git a/src/modules/twap/utils/isPartTimeIntervalTooShort.ts b/src/modules/twap/utils/isPartTimeIntervalTooShort.ts new file mode 100644 index 0000000000..87669bd515 --- /dev/null +++ b/src/modules/twap/utils/isPartTimeIntervalTooShort.ts @@ -0,0 +1,5 @@ +import { MINIMUM_PART_TIME } from '../const' + +export function isPartTimeIntervalTooShort(partTime: number | undefined): boolean { + return partTime !== undefined && partTime < MINIMUM_PART_TIME +} From 7415cb65f54cd6586857e6c4973fd48681aa0e9e Mon Sep 17 00:00:00 2001 From: Nenad Vracar <34926005+nenadV91@users.noreply.github.com> Date: Fri, 16 Jun 2023 10:24:10 +0200 Subject: [PATCH 25/67] fix: unsupported chain app crash (#2676) --- src/lib/hooks/useNativeCurrency.ts | 5 ++++- src/modules/trade/hooks/useIsNativeInOrOut.ts | 5 +++++ src/modules/trade/hooks/useIsWrappedInOrOut.ts | 5 +++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/lib/hooks/useNativeCurrency.ts b/src/lib/hooks/useNativeCurrency.ts index d879317d3c..9779be7776 100644 --- a/src/lib/hooks/useNativeCurrency.ts +++ b/src/lib/hooks/useNativeCurrency.ts @@ -4,13 +4,16 @@ import { SupportedChainId } from '@cowprotocol/cow-sdk' import { NativeCurrency } from '@uniswap/sdk-core' import { nativeOnChain } from 'legacy/constants/tokens' +import { supportedChainId } from 'legacy/utils/supportedChainId' import { useWalletInfo } from 'modules/wallet' export const MAINNET_NATIVE_CURRENCY = nativeOnChain(SupportedChainId.MAINNET) export default function useNativeCurrency(): NativeCurrency { - const { chainId } = useWalletInfo() + const { chainId: _chainId } = useWalletInfo() + const chainId = supportedChainId(_chainId) + return useMemo( () => chainId diff --git a/src/modules/trade/hooks/useIsNativeInOrOut.ts b/src/modules/trade/hooks/useIsNativeInOrOut.ts index d9dff25ae3..c54bcebc69 100644 --- a/src/modules/trade/hooks/useIsNativeInOrOut.ts +++ b/src/modules/trade/hooks/useIsNativeInOrOut.ts @@ -3,6 +3,7 @@ import { useMemo } from 'react' import { SupportedChainId } from '@cowprotocol/cow-sdk' import { NATIVE_CURRENCY_BUY_TOKEN } from 'legacy/constants' +import { supportedChainId } from 'legacy/utils/supportedChainId' import { useWalletInfo } from 'modules/wallet' @@ -13,6 +14,10 @@ import { useTradeState } from './useTradeState' function getIsNativeToken(chainId: SupportedChainId, tokenId: string): boolean { const nativeToken = NATIVE_CURRENCY_BUY_TOKEN[chainId] + if (!supportedChainId(chainId)) { + return false + } + return doesTokenMatchSymbolOrAddress(nativeToken, tokenId) } diff --git a/src/modules/trade/hooks/useIsWrappedInOrOut.ts b/src/modules/trade/hooks/useIsWrappedInOrOut.ts index ce62913a0c..6e23ba8cfa 100644 --- a/src/modules/trade/hooks/useIsWrappedInOrOut.ts +++ b/src/modules/trade/hooks/useIsWrappedInOrOut.ts @@ -3,6 +3,7 @@ import { useMemo } from 'react' import { SupportedChainId } from '@cowprotocol/cow-sdk' import { WRAPPED_NATIVE_CURRENCY } from 'legacy/constants/tokens' +import { supportedChainId } from 'legacy/utils/supportedChainId' import { useWalletInfo } from 'modules/wallet' @@ -13,6 +14,10 @@ import { useTradeState } from './useTradeState' function getIsWrappedNativeToken(chainId: SupportedChainId, tokenId: string): boolean { const nativeToken = WRAPPED_NATIVE_CURRENCY[chainId] + if (!supportedChainId(chainId)) { + return false + } + return doesTokenMatchSymbolOrAddress(nativeToken, tokenId) } From e32c84ffe6e246f52633abf1c22ab811c50f1f15 Mon Sep 17 00:00:00 2001 From: Nenad Vracar <34926005+nenadV91@users.noreply.github.com> Date: Fri, 16 Jun 2023 13:14:37 +0200 Subject: [PATCH 26/67] Feature/twap analytics (#2668) * feat: add twap flow analytics * feat: change SWAP with TRADE and add some events * fix: small word update * feat: rename swap to trade * feat: add TWAP order type * fix: separate event for placing advanced order * fix: error event for TWAP --- .../components/analytics/events/swapEvents.ts | 8 ++--- .../analytics/events/transactionEvents.ts | 31 ++++++++++++------- src/legacy/components/analytics/types.ts | 6 +++- src/legacy/state/swap/hooks.tsx | 3 +- .../hooks/useAdvancedOrdersActions.ts | 2 ++ .../hooks/useLimitOrdersWidgetActions.ts | 3 ++ .../limitOrders/services/tradeFlow/index.ts | 2 +- src/modules/swap/services/ethFlow/index.ts | 2 +- src/modules/swap/services/swapFlow/index.ts | 2 +- .../trade/hooks/useSwitchTokensPlaces.ts | 3 ++ src/modules/trade/utils/analytics.ts | 25 ++++++++------- src/modules/twap/hooks/useCreateTwapOrder.ts | 13 ++++++++ 12 files changed, 68 insertions(+), 32 deletions(-) diff --git a/src/legacy/components/analytics/events/swapEvents.ts b/src/legacy/components/analytics/events/swapEvents.ts index 54f3a49427..ce403b32e1 100644 --- a/src/legacy/components/analytics/events/swapEvents.ts +++ b/src/legacy/components/analytics/events/swapEvents.ts @@ -14,14 +14,14 @@ export function currencySelectAnalytics(field: Field, label: string | undefined) export function setMaxSellTokensAnalytics() { sendEvent({ - category: Category.SWAP, + category: Category.TRADE, action: 'Set Maximun Sell Tokens', }) } function _changeSwapAmountAnalytics(field: Field, value: number) { sendEvent({ - category: Category.SWAP, + category: Category.TRADE, action: `Change ${field} field amount`, value, }) @@ -33,14 +33,14 @@ export const changeSwapAmountAnalytics = debounce(([field, value]: [Field, numbe export function switchTokensAnalytics() { sendEvent({ - category: Category.SWAP, + category: Category.TRADE, action: 'Switch INPUT/OUTPUT tokens', }) } export function initialPriceLoadAnalytics() { sendEvent({ - category: Category.SWAP, + category: Category.TRADE, action: 'Initial Price estimation', }) } diff --git a/src/legacy/components/analytics/events/transactionEvents.ts b/src/legacy/components/analytics/events/transactionEvents.ts index 6be62a94b2..2e0415f397 100644 --- a/src/legacy/components/analytics/events/transactionEvents.ts +++ b/src/legacy/components/analytics/events/transactionEvents.ts @@ -8,15 +8,16 @@ import { sendMicrosoftEvent } from '../pixel/microsoft' import { sendPavedEvent } from '../pixel/paved' import { sendRedditEvent } from '../pixel/reddit' import { sendTwitterEvent } from '../pixel/twitter' -import { Category } from '../types' +import { AnalyticsOrderType, Category } from '../types' -const LABEL_FROM_CLASS: Record = { +const LABEL_FROM_CLASS: Record = { [OrderClass.LIMIT]: 'Limit Order', [OrderClass.MARKET]: 'Market Order', [OrderClass.LIQUIDITY]: 'Liquidity Order', + TWAP: 'TWAP Order', } -function getClassLabel(orderClass: OrderClass, label?: string) { +function getClassLabel(orderClass: AnalyticsOrderType, label?: string) { const classLabel = LABEL_FROM_CLASS[orderClass] return label ? `${label}::${classLabel}` : classLabel } @@ -51,33 +52,39 @@ export function claimAnalytics(action: ClaimAction, value?: number) { type ApprovalAction = 'Send' | 'Sign' | 'Reject' | 'Error' export function approvalAnalytics(action: ApprovalAction, label?: string, value?: number) { sendEvent({ - category: Category.SWAP, + category: Category.TRADE, action: `${action} Token Approval`, label, value, }) } -export type SwapAction = 'Send' | 'Error' | 'Reject' | 'Bundle Approve and Swap' | 'Bundled Eth Flow' -export function swapAnalytics(action: SwapAction, orderClass: OrderClass, label?: string, value?: number) { +export type TradeAction = + | 'Send' + | 'Error' + | 'Reject' + | 'Bundle Approve and Swap' + | 'Bundled Eth Flow' + | 'Place Advanced Order' +export function tradeAnalytics(action: TradeAction, orderClass: AnalyticsOrderType, label?: string, value?: number) { sendEvent({ - category: Category.SWAP, + category: Category.TRADE, action, label: getClassLabel(orderClass, label), value, }) } -export function signSwapAnalytics(orderClass: OrderClass, label?: string) { +export function signTradeAnalytics(orderClass: AnalyticsOrderType, label?: string) { sendEvent({ - category: Category.SWAP, + category: Category.TRADE, action: 'Sign', label: getClassLabel(orderClass, label), }) } export type OrderType = 'Posted' | 'Executed' | 'Canceled' | 'Expired' -export function orderAnalytics(action: OrderType, orderClass: OrderClass, label?: string) { +export function orderAnalytics(action: OrderType, orderClass: AnalyticsOrderType, label?: string) { if (action === 'Posted') { sendFacebookEvent(PIXEL_EVENTS.POST_TRADE) sendLinkedinEvent(PIXEL_EVENTS.POST_TRADE) @@ -88,7 +95,7 @@ export function orderAnalytics(action: OrderType, orderClass: OrderClass, label? } sendEvent({ - category: Category.SWAP, + category: Category.TRADE, action, label: getClassLabel(orderClass, label), }) @@ -96,7 +103,7 @@ export function orderAnalytics(action: OrderType, orderClass: OrderClass, label? export function priceOutOfRangeAnalytics(isUnfillable: boolean, label: string) { sendEvent({ - category: Category.SWAP, + category: Category.TRADE, action: `Order price is ${isUnfillable ? 'out of' : 'back to'} market`, label, }) diff --git a/src/legacy/components/analytics/types.ts b/src/legacy/components/analytics/types.ts index 3cf365d447..9c6735b855 100644 --- a/src/legacy/components/analytics/types.ts +++ b/src/legacy/components/analytics/types.ts @@ -1,5 +1,7 @@ +import { OrderClass } from '@cowprotocol/cow-sdk' + export enum Category { - SWAP = 'Swap', + TRADE = 'Trade', LIST = 'Lists', CURRENCY_SELECT = 'Currency Select', EXPERT_MODE = 'Expert mode', @@ -29,3 +31,5 @@ export enum Dimensions { customBrowserType = 'customBrowserType', userAddress = 'userAddress', } + +export type AnalyticsOrderType = OrderClass | 'TWAP' diff --git a/src/legacy/state/swap/hooks.tsx b/src/legacy/state/swap/hooks.tsx index 09691cd1f2..db27472ee4 100644 --- a/src/legacy/state/swap/hooks.tsx +++ b/src/legacy/state/swap/hooks.tsx @@ -6,7 +6,7 @@ import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core' import { t } from '@lingui/macro' import { ParsedQs } from 'qs' -import { changeSwapAmountAnalytics } from 'legacy/components/analytics' +import { changeSwapAmountAnalytics, switchTokensAnalytics } from 'legacy/components/analytics' import { FEE_SIZE_THRESHOLD, INITIAL_ALLOWED_SLIPPAGE_PERCENT } from 'legacy/constants' import { useCurrency } from 'legacy/hooks/Tokens' import useENS from 'legacy/hooks/useENS' @@ -109,6 +109,7 @@ export function useSwapActionHandlers(): SwapActions { navigate(chainId, { inputCurrencyId: outputCurrencyId, outputCurrencyId: inputCurrencyId }) dispatch(switchCurrencies()) + switchTokensAnalytics() }, [swapState, navigate, chainId, dispatch]) const onUserInput = useCallback( diff --git a/src/modules/advancedOrders/hooks/useAdvancedOrdersActions.ts b/src/modules/advancedOrders/hooks/useAdvancedOrdersActions.ts index 837a27734a..b519a5eeaa 100644 --- a/src/modules/advancedOrders/hooks/useAdvancedOrdersActions.ts +++ b/src/modules/advancedOrders/hooks/useAdvancedOrdersActions.ts @@ -3,6 +3,7 @@ import { useCallback } from 'react' import { Currency } from '@uniswap/sdk-core' +import { changeSwapAmountAnalytics } from 'legacy/components/analytics' import { Field } from 'legacy/state/swap/actions' import { useNavigateOnCurrencySelection } from 'modules/trade/hooks/useNavigateOnCurrencySelection' @@ -37,6 +38,7 @@ export function useAdvancedOrdersActions() { const onUserInput = useCallback( (field: Field, typedValue: string) => { + changeSwapAmountAnalytics(field, Number(typedValue)) updateCurrencyAmount({ amount: { isTyped: true, value: typedValue }, currency: inputCurrency, diff --git a/src/modules/limitOrders/containers/LimitOrdersWidget/hooks/useLimitOrdersWidgetActions.ts b/src/modules/limitOrders/containers/LimitOrdersWidget/hooks/useLimitOrdersWidgetActions.ts index ee48824e23..a936614263 100644 --- a/src/modules/limitOrders/containers/LimitOrdersWidget/hooks/useLimitOrdersWidgetActions.ts +++ b/src/modules/limitOrders/containers/LimitOrdersWidget/hooks/useLimitOrdersWidgetActions.ts @@ -3,6 +3,7 @@ import { useCallback } from 'react' import { OrderKind } from '@cowprotocol/cow-sdk' +import { changeSwapAmountAnalytics } from 'legacy/components/analytics' import { Field } from 'legacy/state/swap/actions' import { updateLimitOrdersRawStateAtom } from 'modules/limitOrders' @@ -34,6 +35,8 @@ export function useLimitOrdersWidgetActions(): TradeWidgetActions { const value = tryParseCurrencyAmount(typedValue, currency) || null + changeSwapAmountAnalytics(field, Number(typedValue)) + updateCurrencyAmount({ activeRate, amount: value, diff --git a/src/modules/limitOrders/services/tradeFlow/index.ts b/src/modules/limitOrders/services/tradeFlow/index.ts index 32f93752eb..720d5a671e 100644 --- a/src/modules/limitOrders/services/tradeFlow/index.ts +++ b/src/modules/limitOrders/services/tradeFlow/index.ts @@ -41,7 +41,7 @@ export async function tradeFlow( } logTradeFlow('LIMIT ORDER FLOW', 'STEP 2: send transaction') - tradeFlowAnalytics.swap(swapFlowAnalyticsContext) + tradeFlowAnalytics.trade(swapFlowAnalyticsContext) beforeTrade?.() const validTo = calculateLimitOrdersDeadline(settingsState) diff --git a/src/modules/swap/services/ethFlow/index.ts b/src/modules/swap/services/ethFlow/index.ts index 59fff32fc1..ba405d9814 100644 --- a/src/modules/swap/services/ethFlow/index.ts +++ b/src/modules/swap/services/ethFlow/index.ts @@ -36,7 +36,7 @@ export async function ethFlow( logTradeFlow('ETH FLOW', 'STEP 2: send transaction') // TODO: check if we need own eth flow analytics or more generic - tradeFlowAnalytics.swap(swapFlowAnalyticsContext) + tradeFlowAnalytics.trade(swapFlowAnalyticsContext) swapConfirmManager.sendTransaction(context.trade) logTradeFlow('ETH FLOW', 'STEP 3: Get Unique Order Id (prevent collisions)') diff --git a/src/modules/swap/services/swapFlow/index.ts b/src/modules/swap/services/swapFlow/index.ts index 4dc2a3c016..510f440d55 100644 --- a/src/modules/swap/services/swapFlow/index.ts +++ b/src/modules/swap/services/swapFlow/index.ts @@ -24,7 +24,7 @@ export async function swapFlow( } logTradeFlow('SWAP FLOW', 'STEP 2: send transaction') - tradeFlowAnalytics.swap(input.swapFlowAnalyticsContext) + tradeFlowAnalytics.trade(input.swapFlowAnalyticsContext) input.swapConfirmManager.sendTransaction(input.context.trade) try { diff --git a/src/modules/trade/hooks/useSwitchTokensPlaces.ts b/src/modules/trade/hooks/useSwitchTokensPlaces.ts index f99e14322b..0e8fc09352 100644 --- a/src/modules/trade/hooks/useSwitchTokensPlaces.ts +++ b/src/modules/trade/hooks/useSwitchTokensPlaces.ts @@ -1,5 +1,7 @@ import { useCallback } from 'react' +import { switchTokensAnalytics } from 'legacy/components/analytics' + import { useWalletInfo } from 'modules/wallet' import { FractionUtils } from 'utils/fractionUtils' @@ -27,6 +29,7 @@ export function useSwitchTokensPlaces(stateOverride: Partial Date: Thu, 22 Jun 2023 13:56:55 +0100 Subject: [PATCH 27/67] feat: integrate with nx (#2695) * feat: integrate with nx * chore: improve config --- nx.json | 45 +++++++ package.json | 156 ++++++++++++++++----- yarn.lock | 373 ++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 523 insertions(+), 51 deletions(-) create mode 100644 nx.json diff --git a/nx.json b/nx.json new file mode 100644 index 0000000000..148b0d5bdb --- /dev/null +++ b/nx.json @@ -0,0 +1,45 @@ +{ + "namedInputs": { + "default": ["{projectRoot}/**/*"], + "production": ["!{projectRoot}/**/*.test.tsx"] + }, + "targetDefaults": { + "build": { + "inputs": ["production", "^production"], + "dependsOn": ["^build"], + "outputs": ["{projectRoot}/build"] + }, + "test": { + "inputs": ["default", "^production", "{workspaceRoot}/package.json"] + } + }, + "tasksRunnerOptions": { + "default": { + "runner": "nx-cloud", + "options": { + "cacheableOperations": [ + "build", + "build:analyze", + "ipfs:build", + "contracts:add:export", + "contracts:compile:abi", + "contracts:compile:abi-ethflow", + "contracts:compile:abi-uniswap", + "contracts:compile:v3", + "contracts:compile", + "optimize-bundle", + "i18n:extract", + "i18n:compile", + "i18n:pseudo", + "cosmos:export", + "sitemap", + "writeVersion" + ], + "accessToken": "MTEyNmY3NzgtMGUzMy00NGI1LTk4NzEtZTIzNzBiMjRjZGY3fHJlYWQtd3JpdGU=" + } + } + }, + "affected": { + "defaultBase": "develop" + } +} diff --git a/package.json b/package.json index 48e3220821..d2dcf55054 100644 --- a/package.json +++ b/package.json @@ -6,44 +6,50 @@ "author": "", "license": "ISC", "scripts": { - "start": "craco start", - "build": "yarn i18n:compile && craco build && yarn writeVersion", - "test": "craco test --env=jsdom", - "start:ssl": "HTTPS=true yarn start", - "start:service-worker": "yarn build && yarn serve", - "lint:fix": "yarn run eslint --fix ./", - "mock": "REACT_APP_MOCK=true yarn start", - "build:analyze": "cross-env REACT_APP_ANALYZE_BUNDLE=true yarn build", - "ipfs:build": "cross-env PUBLIC_URL=\".\" yarn build", - "ipfs:publish": "ipfs-deploy build -p pinata -O", - "test:debug": "craco --inspect-brk test --runInBand --no-cache", + "start": "nx exec -- craco start", + "build": "nx exec -- yarn _build ", + "test": "nx exec -- craco test --env=jsdom", + "start:ssl": "nx exec -- HTTPS=true yarn start", + "start:service-worker": "nx exec -- yarn _start:service-worker ", + "lint:fix": "nx exec -- yarn run eslint --fix ./", + "mock": "nx exec -- REACT_APP_MOCK=true yarn start", + "build:analyze": "nx exec -- cross-env REACT_APP_ANALYZE_BUNDLE=true yarn build", + "ipfs:build": "nx exec -- cross-env PUBLIC_URL=\".\" yarn build", + "ipfs:publish": "nx exec -- ipfs-deploy build -p pinata -O", + "test:debug": "nx exec -- craco --inspect-brk test --runInBand --no-cache", "eject": "react-scripts eject", - "integration-test": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run'", - "serve": "serve build -l 3000", - "serve:ci": "serve website -l 3000", - "cypress": "cypress open", - "contracts:add:export": "node ./scripts/contracts_add_export.js", - "contracts:compile:abi": "typechain --target ethers-v5 --out-dir src/abis/types './src/abis/**/*.json'", - "contracts:compile:abi-ethflow": "typechain --target ethers-v5 --out-dir src/abis/types/ethflow \"./node_modules/@cowprotocol/ethflowcontract/artifacts/CoWSwapEthFlow.sol/*.json\"", - "contracts:compile:abi-uniswap": "typechain --target ethers-v5 --out-dir src/legacy/abis/types \"./src/legacy/abis/**/*.json\"", - "contracts:compile:v3": "typechain --target ethers-v5 --out-dir src/legacy/types/v3 \"./node_modules/@uniswap/**/artifacts/contracts/**/*[!dbg].json\"", - "contracts:compile": "yarn contracts:compile:abi && yarn contracts:compile:abi-ethflow && yarn contracts:add:export && yarn contracts:compile:abi-uniswap && yarn contracts:compile:v3", + "integration-test": "nx exec -- start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run'", + "serve": "nx exec -- serve build -l 3000", + "serve:ci": "nx exec -- serve website -l 3000", + "cypress": "nx exec -- cypress open", + "contracts:add:export": "nx exec -- node ./scripts/contracts_add_export.js", + "contracts:compile:abi": "nx exec -- typechain --target ethers-v5 --out-dir src/abis/types './src/abis/**/*.json'", + "contracts:compile:abi-ethflow": "nx exec -- typechain --target ethers-v5 --out-dir src/abis/types/ethflow \"./node_modules/@cowprotocol/ethflowcontract/artifacts/CoWSwapEthFlow.sol/*.json\"", + "contracts:compile:abi-uniswap": "nx exec -- typechain --target ethers-v5 --out-dir src/legacy/abis/types \"./src/legacy/abis/**/*.json\"", + "contracts:compile:v3": "nx exec -- typechain --target ethers-v5 --out-dir src/legacy/types/v3 \"./node_modules/@uniswap/**/artifacts/contracts/**/*[!dbg].json\"", + "contracts:compile": "nx exec -- yarn _contracts:compile ", "prei18n:extract": "touch src/locales/en-US.po", - "optimize-bundle": "node ./scripts/fix-ethereumjs-bundle.js", - "patch-package": "patch-package", - "i18n:extract": "cross-env NODE_ENV=development lingui extract --locale en-US", - "i18n:compile": "yarn i18n:extract && lingui compile", - "i18n:pseudo": "lingui extract --locale pseudo && lingui compile", + "optimize-bundle": "nx exec -- node ./scripts/fix-ethereumjs-bundle.js", + "patch-package": "nx exec -- patch-package", + "i18n:extract": "nx exec -- cross-env NODE_ENV=development lingui extract --locale en-US", + "i18n:compile": "nx exec -- yarn _i18n:compile ", + "i18n:pseudo": "nx exec -- yarn _i18n:pseudo ", "postinstall": "yarn contracts:compile && yarn i18n:compile && yarn patch-package", "prepare": "husky install", "prepublishOnly": "yarn widgets:build", - "test:e2e": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run --record'", - "cosmos:clear": "rm -rf ./public/cosmos", - "cosmos:run": "yarn cosmos:clear && cosmos", - "cosmos:export": "cross-env NODE_ENV=development cosmos-export", - "widgets:build": "rollup --config --failAfterWarnings --configPlugin typescript2", - "sitemap": "node src/sitemap", - "writeVersion": "node src/writeVersion" + "test:e2e": "nx exec -- start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run --record'", + "cosmos:clear": "nx exec -- rm -rf ./public/cosmos", + "cosmos:run": "nx exec -- yarn _cosmos:run ", + "cosmos:export": "nx exec -- cross-env NODE_ENV=development cosmos-export", + "widgets:build": "nx exec -- rollup --config --failAfterWarnings --configPlugin typescript2", + "sitemap": "nx exec -- node src/sitemap", + "writeVersion": "nx exec -- node src/writeVersion", + "_build": "yarn i18n:compile && craco build && yarn writeVersion", + "_start:service-worker": "yarn build && yarn serve", + "_contracts:compile": "yarn contracts:compile:abi && yarn contracts:compile:abi-ethflow && yarn contracts:add:export && yarn contracts:compile:abi-uniswap && yarn contracts:compile:v3", + "_i18n:compile": "yarn i18n:extract && lingui compile", + "_i18n:pseudo": "lingui extract --locale pseudo && lingui compile", + "_cosmos:run": "yarn cosmos:clear && cosmos" }, "browser": { "crypto": false @@ -244,6 +250,88 @@ "stream-http": "^3.2.0", "ts-mockito": "^2.6.1", "typechain": "^8.1.1", - "webpack-bundle-analyzer": "^4.7.0" + "webpack-bundle-analyzer": "^4.7.0", + "nx": "16.3.2", + "nx-cloud": "latest" + }, + "nx": { + "targets": { + "build": { + "outputs": [ + "{projectRoot}/build", + "{projectRoot}/src/locales" + ] + }, + "build:analyze": { + "outputs": [ + "{projectRoot}/build" + ] + }, + "ipfs:build": { + "outputs": [ + "{projectRoot}/build" + ] + }, + "optimize-bundle": { + "outputs": [ + "{projectRoot}/../node_modules/@ethereumjs/common/dist.browser/genesisStates/mainnet.json" + ] + }, + "contracts:add:export": { + "outputs": [ + "{projectRoot}/src/abis/types/index.ts" + ] + }, + "contracts:compile:abi": { + "outputs": [ + "{projectRoot}/src/abis/types" + ] + }, + "contracts:compile:abi-ethflow": { + "outputs": [ + "{projectRoot}/src/abis/types/ethflow" + ] + }, + "contracts:compile:abi-uniswap": { + "outputs": [ + "{projectRoot}/src/legacy/abis/types" + ] + }, + "contracts:compile:v3": { + "outputs": [ + "{projectRoot}/src/legacy/types/v3" + ] + }, + "i18n:extract": { + "outputs": [ + "{projectRoot}/src/locales" + ] + }, + "i18n:compile": { + "outputs": [ + "{projectRoot}/src/locales" + ] + }, + "i18n:pseudo": { + "outputs": [ + "{projectRoot}/src/locales" + ] + }, + "cosmos:export": { + "outputs": [ + "{projectRoot}/public/cosmos" + ] + }, + "sitemap": { + "outputs": [ + "{projectRoot}/public/sitemap.xml" + ] + }, + "writeVersion": { + "outputs": [ + "{projectRoot}/public/version.json" + ] + } + } } } diff --git a/yarn.lock b/yarn.lock index 14d5ea1e9e..15e35441f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2725,6 +2725,70 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@nrwl/nx-cloud@16.0.5": + version "16.0.5" + resolved "https://registry.yarnpkg.com/@nrwl/nx-cloud/-/nx-cloud-16.0.5.tgz#c963480a71c4afa964fbbe9e4d6bbf222764e9cd" + integrity sha512-1p82ym8WE9ziejwgPslstn19iV/VkHfHfKr/5YOnfCHQS+NxUf92ogcYhHXtqWLblVZ9Zs4W4pkSXK4e04wCmQ== + dependencies: + nx-cloud "16.0.5" + +"@nrwl/tao@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-16.3.2.tgz#eefc1974342afbbe48e4e5351d6707ad2f9fb179" + integrity sha512-2Kg7dtv6JcQagCZPSq+okceI81NqmXGGgbKWqS7sOfdmp1otxS9uiUFNXw+Pdtnw38mdRviMtSOXScntu4sUKg== + dependencies: + nx "16.3.2" + +"@nx/nx-darwin-arm64@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nx/nx-darwin-arm64/-/nx-darwin-arm64-16.3.2.tgz#83b6e78b27d2d7da8f7626560f52070c8735d28a" + integrity sha512-YfYVNfsJBzBcBnJUU4AcA6A4QMkgnVlETfp4KGL36Otq542mRY1ISGHdox63ocI5AKh5gay5AaGcR4wR9PU9Vg== + +"@nx/nx-darwin-x64@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nx/nx-darwin-x64/-/nx-darwin-x64-16.3.2.tgz#0ae2a64356542c5fb73ca8038ce10ec4512e7fcb" + integrity sha512-bJtpozz0zSRVRrcQ76GrlT3TWEGTymLYWrVG51bH5KZ46t6/a4EQBI3uL3vubMmOZ0jR4ywybOcPBBhxmBJ68w== + +"@nx/nx-freebsd-x64@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nx/nx-freebsd-x64/-/nx-freebsd-x64-16.3.2.tgz#202adf4d6070f47ed46450f006ecd50851147c74" + integrity sha512-ZvufI0bWqT67nLbBo6ejrIGxypdoedRQTP/tudWbs/4isvxLe1uVku1BfKCTQUsJG367SqNOU1H5kzI/MRr3ow== + +"@nx/nx-linux-arm-gnueabihf@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-16.3.2.tgz#62314a82566e3647866b9dd4167a2d0e1397f001" + integrity sha512-IQL4kxdiZLvifar7+SIum3glRuVsxtE0dL8RvteSDXrxDQnaTUrjILC+VGhalRmk7ngBbGKNrhWOeeL7390CzQ== + +"@nx/nx-linux-arm64-gnu@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-16.3.2.tgz#02826400aa55b8f44bac83332dd29647d0e95001" + integrity sha512-f6AWgPVu3mfUEoOBa0rY2/7QY0Or9eR0KtLFpcPh7RUpxPw2EXzIbjD/0RGipdpspSrgiMKbZpsUjo6mXBFsQA== + +"@nx/nx-linux-arm64-musl@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-16.3.2.tgz#a0a81520e0904aa026a7ab0a8a3bf3facec9f14c" + integrity sha512-AvrWcYz7021E3b5P9/0i26p60XMZfw86Epks51L6AhlflarlOH4AcEChc7APMtb1ELAIbDWx2S6oIDRbQ7rtVA== + +"@nx/nx-linux-x64-gnu@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-16.3.2.tgz#e79b5c142ec8d9bfb458ea5803bc4b62abbcf296" + integrity sha512-K2pWGAcbCNm6b7UZI9cc8z4Rb540QcuepBXD7akjPjWerzXriT6VCn4i9mVKsCg2mwSfknTJJVJ1PZwJSmTl/Q== + +"@nx/nx-linux-x64-musl@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-16.3.2.tgz#900aee8f171638b9fb44378e2ac0548cb4aa99a7" + integrity sha512-sY1QDuQlqyYiRPJZanrtV07tU0DOXiCrWb0pDsGiO0qHuUSmW5Vw17GWEY4z3rt0/5U8fJ+/9WQrneviOmsOKg== + +"@nx/nx-win32-arm64-msvc@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-16.3.2.tgz#88db772b3535648e147b1a0206b1a1fe875fa9a5" + integrity sha512-wBfohT2hjrLKn9WFHvG0MFVk7uYhgYNiptnTLdTouziHgFyZ08vyl7XYBq55BwHPMQ5iswVoEfjn/5ZBfCPscg== + +"@nx/nx-win32-x64-msvc@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-16.3.2.tgz#2195faaf1fc465c7a89bfdd62323fdd2a5d91f15" + integrity sha512-QC0sWrfQm0/WdvvM//7UAgm+otbak6bznZ0zawTeqmLBh1hLjNeweyzSVKQEtZtlzDMKpzCVuuwkJq+VKBLvmw== + "@openzeppelin/contracts@3.4.1-solc-0.7-2": version "3.4.1-solc-0.7-2" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.1-solc-0.7-2.tgz#371c67ebffe50f551c3146a9eec5fe6ffe862e92" @@ -2735,6 +2799,14 @@ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.2-solc-0.7.tgz#38f4dbab672631034076ccdf2f3201fab1726635" integrity sha512-W6QmqgkADuFcTLzHL8vVoNBtkwjvQRpYIAom7KiUNoLKghyx3FgH0GBjt8NRvigV1ZmMOBllvE1By1C+bi8WpA== +"@parcel/watcher@2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.4.tgz#f300fef4cc38008ff4b8c29d92588eced3ce014b" + integrity sha512-cTDi+FUDBIUOBKEtj+nhiJ71AZVlkAsQFuGQTun5tV9mwQBQgZvhCzG+URPQc8myeN32yRVZEfVAPCs1RW+Jvg== + dependencies: + node-addon-api "^3.2.1" + node-gyp-build "^4.3.0" + "@pedrouid/environment@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@pedrouid/environment/-/environment-1.0.1.tgz#858f0f8a057340e0b250398b75ead77d6f4342ec" @@ -5627,6 +5699,21 @@ resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== +"@yarnpkg/parsers@^3.0.0-rc.18": + version "3.0.0-rc.45" + resolved "https://registry.yarnpkg.com/@yarnpkg/parsers/-/parsers-3.0.0-rc.45.tgz#fcc7d0ab7828afdb20d1e13160b1d117c07536f4" + integrity sha512-Aj0aHBV/crFQTpKQvL6k1xNiOhnlfVLu06LunelQAvl1MTeWrSi8LD9UJJDCFJiG4kx8NysUE6Tx0KZyPQUzIw== + dependencies: + js-yaml "^3.10.0" + tslib "^2.4.0" + +"@zkochan/js-yaml@0.0.6": + version "0.0.6" + resolved "https://registry.yarnpkg.com/@zkochan/js-yaml/-/js-yaml-0.0.6.tgz#975f0b306e705e28b8068a07737fa46d3fc04826" + integrity sha512-nzvgl3VfhcELQ8LyVrYOru+UtAy1nrygk2+AGbTm8a5YcO6o8lSjAT+pfg3vJWxIoZKOUhrK6UU7xW/+00kQrg== + dependencies: + argparse "^2.0.1" + JSONStream@^1.0.4, JSONStream@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -6184,6 +6271,15 @@ axios@0.21.2: dependencies: follow-redirects "^1.14.0" +axios@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.1.3.tgz#8274250dada2edf53814ed7db644b9c2866c1e35" + integrity sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + axios@^0.19.2: version "0.19.2" resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" @@ -6198,6 +6294,15 @@ axios@^0.21.0, axios@^0.21.1: dependencies: follow-redirects "^1.14.0" +axios@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" + integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + axobject-query@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.1.1.tgz#3b6e5c6d4e43ca7ba51c5babf99d22a9c68485e1" @@ -6571,7 +6676,7 @@ bindings@^1.3.0: dependencies: file-uri-to-path "1.0.0" -bl@^4.0.0, bl@^4.1.0: +bl@^4.0.0, bl@^4.0.3, bl@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== @@ -7291,6 +7396,11 @@ chownr@^1.1.4: resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + chrome-trace-event@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" @@ -7373,13 +7483,18 @@ cli-boxes@^2.2.0: resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== -cli-cursor@^3.1.0: +cli-cursor@3.1.0, cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: restore-cursor "^3.1.0" +cli-spinners@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" + integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== + cli-spinners@^2.2.0, cli-spinners@^2.5.0: version "2.7.0" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" @@ -9135,7 +9250,7 @@ dotenv-expand@^5.1.0: resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== -dotenv@^10.0.0: +dotenv@^10.0.0, dotenv@~10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== @@ -9176,7 +9291,7 @@ duplexer3@^0.1.4: resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.5.tgz#0b5e4d7bad5de8901ea4440624c8e1d20099217e" integrity sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA== -duplexer@^0.1.2: +duplexer@^0.1.1, duplexer@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== @@ -9345,7 +9460,7 @@ enhanced-resolve@^5.10.0: graceful-fs "^4.2.4" tapable "^2.2.0" -enquirer@^2.3.6: +enquirer@^2.3.6, enquirer@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== @@ -10345,6 +10460,17 @@ fast-fifo@^1.0.0: resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.1.0.tgz#17d1a3646880b9891dfa0c54e69c5fef33cad779" integrity sha512-Kl29QoNbNvn4nhDsLYjyIAaIqaJB6rBx5p3sL9VjaefJ+eMFBWVZiaoguaoZfzEKr5RhAti0UgM8703akGPJ6g== +fast-glob@3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" + integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-glob@^3.2.12, fast-glob@^3.2.9: version "3.2.12" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" @@ -10409,7 +10535,7 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" -figures@^3.0.0, figures@^3.2.0: +figures@3.2.0, figures@^3.0.0, figures@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== @@ -10573,7 +10699,7 @@ follow-redirects@1.5.10: dependencies: debug "=3.1.0" -follow-redirects@^1.0.0, follow-redirects@^1.14.0: +follow-redirects@^1.0.0, follow-redirects@^1.14.0, follow-redirects@^1.15.0: version "1.15.2" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== @@ -10668,6 +10794,11 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + fs-extra@10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" @@ -10686,7 +10817,7 @@ fs-extra@^10.0.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^11.0.0: +fs-extra@^11.0.0, fs-extra@^11.1.0: version "11.1.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== @@ -10739,6 +10870,13 @@ fs-minipass@^1.2.7: dependencies: minipass "^2.6.0" +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + fs-monkey@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3" @@ -10940,6 +11078,18 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== +glob@7.1.4: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@7.1.7: version "7.1.7" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" @@ -11608,7 +11758,7 @@ ieee754@^1.1.13, ieee754@^1.2.1: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^5.2.0: +ignore@^5.0.4, ignore@^5.2.0: version "5.2.4" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== @@ -13434,7 +13584,7 @@ js-yaml@4.1.0, js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -js-yaml@^3.13.1: +js-yaml@^3.10.0, js-yaml@^3.13.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -13587,6 +13737,11 @@ json5@^2.1.2, json5@^2.2.0, json5@^2.2.2: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== +jsonc-parser@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" + integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -13796,6 +13951,11 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +lines-and-columns@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.3.tgz#b2f0badedb556b747020ab8ea7f0373e22efac1b" + integrity sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w== + listr2@^3.8.3: version "3.14.0" resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.14.0.tgz#23101cc62e1375fd5836b248276d1d2b51fdbe9e" @@ -14436,6 +14596,13 @@ minimalistic-crypto-utils@^1.0.1: dependencies: brace-expansion "^1.1.7" +minimatch@3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.5.tgz#4da8f1290ee0f0f8e83d60ca69f8f134068604a3" + integrity sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw== + dependencies: + brace-expansion "^1.1.7" + minimatch@5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" @@ -14480,6 +14647,13 @@ minipass@^2.6.0, minipass@^2.9.0: safe-buffer "^5.1.2" yallist "^3.0.0" +minipass@^3.0.0: + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + minizlib@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" @@ -14487,6 +14661,14 @@ minizlib@^1.3.3: dependencies: minipass "^2.9.0" +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + mkdirp-promise@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" @@ -14506,7 +14688,7 @@ mkdirp@*: dependencies: minimist "^1.2.6" -mkdirp@^1.0.4: +mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -14873,6 +15055,11 @@ node-addon-api@^2.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== +node-addon-api@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" + integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== + node-cache@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d" @@ -14928,6 +15115,11 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== +node-machine-id@^1.1.12: + version "1.1.12" + resolved "https://registry.yarnpkg.com/node-machine-id/-/node-machine-id-1.1.12.tgz#37904eee1e59b320bb9c5d6c0a59f3b469cb6267" + integrity sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ== + node-releases@^2.0.6: version "2.0.8" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae" @@ -15042,6 +15234,73 @@ nwsapi@^2.2.0: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== +nx-cloud@16.0.5, nx-cloud@latest: + version "16.0.5" + resolved "https://registry.yarnpkg.com/nx-cloud/-/nx-cloud-16.0.5.tgz#fa0b0185d254405ec47fcbcdbbd8b12ff1add096" + integrity sha512-13P7r0aKikjBtmdZrNorwXzVPeVIV4MLEwqGY+DEG6doLBtI5KqEQk/d5B5l2dCF2BEi/LXEmLYCmf9gwbOJ+Q== + dependencies: + "@nrwl/nx-cloud" "16.0.5" + axios "1.1.3" + chalk "^4.1.0" + dotenv "~10.0.0" + fs-extra "^11.1.0" + node-machine-id "^1.1.12" + open "~8.4.0" + strip-json-comments "^3.1.1" + tar "6.1.11" + yargs-parser ">=21.1.1" + +nx@16.3.2: + version "16.3.2" + resolved "https://registry.yarnpkg.com/nx/-/nx-16.3.2.tgz#92a2d7ef06d15b3b111b7cf9d35de08de0a22d90" + integrity sha512-fOzCVL7qoCJAcYTJwvJ9j+PSaL791ro4AICWuLxaphZsp2jcLoav4Ev7ONPks2Wlkt8FS9bee3nqQ3w1ya36Og== + dependencies: + "@nrwl/tao" "16.3.2" + "@parcel/watcher" "2.0.4" + "@yarnpkg/lockfile" "^1.1.0" + "@yarnpkg/parsers" "^3.0.0-rc.18" + "@zkochan/js-yaml" "0.0.6" + axios "^1.0.0" + chalk "^4.1.0" + cli-cursor "3.1.0" + cli-spinners "2.6.1" + cliui "^7.0.2" + dotenv "~10.0.0" + enquirer "~2.3.6" + fast-glob "3.2.7" + figures "3.2.0" + flat "^5.0.2" + fs-extra "^11.1.0" + glob "7.1.4" + ignore "^5.0.4" + js-yaml "4.1.0" + jsonc-parser "3.2.0" + lines-and-columns "~2.0.3" + minimatch "3.0.5" + npm-run-path "^4.0.1" + open "^8.4.0" + semver "7.3.4" + string-width "^4.2.3" + strong-log-transformer "^2.1.0" + tar-stream "~2.2.0" + tmp "~0.2.1" + tsconfig-paths "^4.1.2" + tslib "^2.3.0" + v8-compile-cache "2.3.0" + yargs "^17.6.2" + yargs-parser "21.1.1" + optionalDependencies: + "@nx/nx-darwin-arm64" "16.3.2" + "@nx/nx-darwin-x64" "16.3.2" + "@nx/nx-freebsd-x64" "16.3.2" + "@nx/nx-linux-arm-gnueabihf" "16.3.2" + "@nx/nx-linux-arm64-gnu" "16.3.2" + "@nx/nx-linux-arm64-musl" "16.3.2" + "@nx/nx-linux-x64-gnu" "16.3.2" + "@nx/nx-linux-x64-musl" "16.3.2" + "@nx/nx-win32-arm64-msvc" "16.3.2" + "@nx/nx-win32-x64-msvc" "16.3.2" + oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" @@ -15205,6 +15464,15 @@ open@^8.0.9, open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" +open@~8.4.0: + version "8.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + opener@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" @@ -17890,6 +18158,13 @@ semver-diff@^3.1.1: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== +semver@7.3.4: + version "7.3.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" + integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== + dependencies: + lru-cache "^6.0.0" + semver@7.3.8, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: version "7.3.8" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" @@ -18656,6 +18931,15 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== +strong-log-transformer@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" + integrity sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA== + dependencies: + duplexer "^0.1.1" + minimist "^1.2.0" + through "^2.3.4" + sturdy-websocket@^0.1.12: version "0.1.12" resolved "https://registry.yarnpkg.com/sturdy-websocket/-/sturdy-websocket-0.1.12.tgz#84bb779f948b585a695f76961dc7d1c4a5e87629" @@ -18887,6 +19171,29 @@ tar-pack@^3.4.1: tar "^2.2.1" uid-number "^0.0.6" +tar-stream@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +tar@6.1.11: + version "6.1.11" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" + integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + tar@^2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.2.tgz#0ca8848562c7299b8b446ff6a4d60cdbb23edc40" @@ -19034,7 +19341,7 @@ through2@~0.6.3: readable-stream ">=1.0.33-1 <1.1.0-0" xtend ">=4.0.0 <4.1.0-0" -"through@>=2.2.7 <3", through@^2.3.6, through@^2.3.8: +"through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== @@ -19270,6 +19577,15 @@ tsconfig-paths@^3.14.1: minimist "^1.2.6" strip-bom "^3.0.0" +tsconfig-paths@^4.1.2: + version "4.2.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" + integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== + dependencies: + json5 "^2.2.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + tslib@1.14.1, tslib@^1.0.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -19280,6 +19596,11 @@ tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== +tslib@^2.3.0, tslib@^2.4.0: + version "2.5.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" + integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -19780,6 +20101,11 @@ v8-compile-cache-lib@^3.0.1: resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== +v8-compile-cache@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== + v8-to-istanbul@^8.1.0: version "8.1.1" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" @@ -20930,6 +21256,11 @@ yargs-parser@20.2.4: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== +yargs-parser@21.1.1, yargs-parser@>=21.1.1, yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + yargs-parser@^10.0.0: version "10.1.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" @@ -20958,11 +21289,6 @@ yargs-parser@^20.2.2, yargs-parser@^20.2.3: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs-parser@^21.1.1: - version "21.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" - integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== - yargs-unparser@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" @@ -21032,6 +21358,19 @@ yargs@^17.0.0: y18n "^5.0.5" yargs-parser "^21.1.1" +yargs@^17.6.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + yauzl@^2.10.0: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" From 49b1273878ee6eb98add817c6da40c80ff01be95 Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Thu, 22 Jun 2023 14:01:52 +0100 Subject: [PATCH 28/67] Add library and UI library (#2699) * chore: add lib ui * docs: add documentation for ui libraty in readme * chore: add lib * chore: add react lib * chore: add dependencies * feat: allow to use ui components from other projects * fix: fix circular dependency * chore: use NX to run all tests * fix: make test to work * chore: lint fix * chore: add build command * Fix code style issues with Prettier * fix: fix e2e * chore: clean package --------- Co-authored-by: Lint Action --- .../workflows/integration-tests.yaml | 2 +- .github/uniswap-original/workflows/tests.yaml | 2 +- .verdaccio/config.yml | 29 + .verdaccio/htpasswd | 1 + README.md | 9 + craco.config.js | 2 + jest.config.ts | 7 + jest.preset.js | 3 + nx.json | 19 +- package.json | 54 +- project.json | 47 + src/.eslintrc.json | 13 + src/{ => common}/constants/common.ts | 0 src/{ => common}/constants/featureFlags.ts | 0 src/{ => common}/constants/intl.ts | 0 src/{ => common}/constants/launchDarkly.ts | 0 src/{ => common}/constants/quote.ts | 0 src/{ => common}/constants/routes.ts | 1 + .../RequestCancellationModal.tsx | 2 +- src/common/pure/FiatAmount/index.tsx | 2 +- src/common/pure/TokenAmount/index.tsx | 2 +- .../TransactionSubmittedContent/index.tsx | 2 +- src/cow-react/index.tsx | 2 +- src/jest.config.ts | 13 + .../components/AmplitudeAnalytics/index.ts | 4 +- .../ErrorBoundary/ErrorBoundaryMod.tsx | 2 +- src/legacy/components/Header/index.tsx | 2 +- .../FollowPendingTxPopupUI.tsx | 2 +- src/legacy/components/Popups/PopupItem.tsx | 96 +- src/legacy/components/Popups/PopupItemMod.tsx | 121 - src/legacy/components/Popups/PopupsMod.tsx | 77 - .../components/Popups/TransactionPopup.tsx | 49 +- .../components/Popups/TransactionPopupMod.tsx | 47 - src/legacy/components/Popups/index.tsx | 75 +- src/legacy/components/Popups/styled.ts | 69 + .../components/Tokens/TokensTableRow.tsx | 2 +- .../usePriceImpact/useFallbackPriceImpact.ts | 2 +- .../orders/middleware/appziMiddleware.test.ts | 2 +- .../updaters/UnfillableOrdersUpdater.ts | 2 +- src/libs/ui-utils/.eslintrc.json | 20 + src/libs/ui-utils/README.md | 11 + src/libs/ui-utils/jest.config.ts | 10 + src/libs/ui-utils/package.json | 5 + src/libs/ui-utils/project.json | 41 + src/libs/ui-utils/src/index.ts | 1 + src/libs/ui-utils/src/lib/return5.spec.ts | 7 + src/libs/ui-utils/src/lib/return5.ts | 3 + src/libs/ui-utils/tsconfig.json | 22 + src/libs/ui-utils/tsconfig.lib.json | 10 + src/libs/ui-utils/tsconfig.spec.json | 9 + src/libs/ui-utils/vite.config.ts | 48 + src/libs/ui/.eslintrc.json | 20 + src/libs/ui/README.md | 7 + src/libs/ui/jest.config.ts | 11 + src/libs/ui/package.json | 12 + src/libs/ui/project.json | 46 + src/libs/ui/src/index.ts | 1 + src/libs/ui/src/lib/ui.spec.tsx | 10 + src/libs/ui/src/lib/ui.tsx | 16 + src/libs/ui/tsconfig.json | 21 + src/libs/ui/tsconfig.lib.json | 23 + src/libs/ui/tsconfig.spec.json | 20 + src/libs/ui/vite.config.ts | 49 + .../application/containers/App/RoutesApp.tsx | 5 +- .../containers/TradeWidgetLinks/index.tsx | 2 +- .../application/containers/WithLDProvider.tsx | 2 +- .../containers/LimitOrdersWarnings/index.tsx | 2 +- .../containers/RateInput/index.tsx | 2 +- .../pure/LimitOrdersDetails/index.tsx | 4 +- src/modules/mainMenu/constants/mainMenu.ts | 2 +- src/modules/mainMenu/pure/MenuTree/index.tsx | 2 +- src/modules/mainMenu/types.ts | 2 +- src/modules/mainMenu/utils.ts | 2 +- .../OrderRow/EstimatedExecutionPrice.tsx | 2 +- .../pure/OrdersTableContainer/OrdersTable.tsx | 2 +- src/modules/trade/hooks/useTradeTypeInfo.ts | 2 +- .../trade/utils/parameterizeTradeRoute.ts | 2 +- .../wallet/api/pure/WalletModal/index.tsx | 2 +- src/pages/About/index.tsx | 2 +- src/pages/Account/index.tsx | 2 +- src/pages/AdvancedOrders/index.tsx | 2 +- src/pages/Claim/ClaimingStatus.tsx | 2 +- src/pages/Faq/AffiliateFaq.tsx | 2 +- src/pages/Faq/TokenFaq.tsx | 2 +- src/pages/Faq/index.tsx | 2 +- src/pages/KitchenSink/index.tsx | 18 + src/pages/Swap/index.tsx | 2 +- src/utils/amountFormat/index.ts | 2 +- tools/scripts/publish.mjs | 58 + tsconfig.base.json | 7 +- tsconfig.json | 9 +- tsconfig.spec.json | 9 + yarn.lock | 4454 ++++++++++++++++- 93 files changed, 5318 insertions(+), 477 deletions(-) create mode 100644 .verdaccio/config.yml create mode 100644 .verdaccio/htpasswd create mode 100644 jest.config.ts create mode 100644 jest.preset.js create mode 100644 project.json create mode 100644 src/.eslintrc.json rename src/{ => common}/constants/common.ts (100%) rename src/{ => common}/constants/featureFlags.ts (100%) rename src/{ => common}/constants/intl.ts (100%) rename src/{ => common}/constants/launchDarkly.ts (100%) rename src/{ => common}/constants/quote.ts (100%) rename src/{ => common}/constants/routes.ts (96%) create mode 100644 src/jest.config.ts delete mode 100644 src/legacy/components/Popups/PopupItemMod.tsx delete mode 100644 src/legacy/components/Popups/PopupsMod.tsx delete mode 100644 src/legacy/components/Popups/TransactionPopupMod.tsx create mode 100644 src/legacy/components/Popups/styled.ts create mode 100644 src/libs/ui-utils/.eslintrc.json create mode 100644 src/libs/ui-utils/README.md create mode 100644 src/libs/ui-utils/jest.config.ts create mode 100644 src/libs/ui-utils/package.json create mode 100644 src/libs/ui-utils/project.json create mode 100644 src/libs/ui-utils/src/index.ts create mode 100644 src/libs/ui-utils/src/lib/return5.spec.ts create mode 100644 src/libs/ui-utils/src/lib/return5.ts create mode 100644 src/libs/ui-utils/tsconfig.json create mode 100644 src/libs/ui-utils/tsconfig.lib.json create mode 100644 src/libs/ui-utils/tsconfig.spec.json create mode 100644 src/libs/ui-utils/vite.config.ts create mode 100644 src/libs/ui/.eslintrc.json create mode 100644 src/libs/ui/README.md create mode 100644 src/libs/ui/jest.config.ts create mode 100644 src/libs/ui/package.json create mode 100644 src/libs/ui/project.json create mode 100644 src/libs/ui/src/index.ts create mode 100644 src/libs/ui/src/lib/ui.spec.tsx create mode 100644 src/libs/ui/src/lib/ui.tsx create mode 100644 src/libs/ui/tsconfig.json create mode 100644 src/libs/ui/tsconfig.lib.json create mode 100644 src/libs/ui/tsconfig.spec.json create mode 100644 src/libs/ui/vite.config.ts create mode 100644 src/pages/KitchenSink/index.tsx create mode 100644 tools/scripts/publish.mjs create mode 100644 tsconfig.spec.json diff --git a/.github/uniswap-original/workflows/integration-tests.yaml b/.github/uniswap-original/workflows/integration-tests.yaml index 18cd058e8a..625af36bc3 100644 --- a/.github/uniswap-original/workflows/integration-tests.yaml +++ b/.github/uniswap-original/workflows/integration-tests.yaml @@ -45,7 +45,7 @@ jobs: REACT_APP_NETWORK_URL: 'https://mainnet.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847' REACT_APP_SERVICE_WORKER: false - - run: yarn integration-test + - run: yarn e2e env: CYPRESS_INTEGRATION_TEST_PRIVATE_KEY: ${{ secrets.CYPRESS_INTEGRATION_TEST_PRIVATE_KEY }} CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} diff --git a/.github/uniswap-original/workflows/tests.yaml b/.github/uniswap-original/workflows/tests.yaml index 07897f61b9..2170ddaa57 100644 --- a/.github/uniswap-original/workflows/tests.yaml +++ b/.github/uniswap-original/workflows/tests.yaml @@ -36,7 +36,7 @@ jobs: env: CI: false REACT_APP_NETWORK_URL: 'https://mainnet.infura.io/v3/586e7e6b7c7e437aa41f5da496a749f5' - - run: yarn integration-test + - run: yarn e2e unit-tests: name: Unit tests diff --git a/.verdaccio/config.yml b/.verdaccio/config.yml new file mode 100644 index 0000000000..1805e5ac80 --- /dev/null +++ b/.verdaccio/config.yml @@ -0,0 +1,29 @@ +# path to a directory with all packages +storage: ../tmp/local-registry/storage + +auth: + htpasswd: + file: ./htpasswd + +# a list of other known repositories we can talk to +uplinks: + npmjs: + url: https://registry.npmjs.org/ + maxage: 60m + +packages: + '**': + # give all users (including non-authenticated users) full access + # because it is a local registry + access: $all + publish: $all + unpublish: $all + + # if package is not available locally, proxy requests to npm registry + proxy: npmjs + +# log settings +logs: + type: stdout + format: pretty + level: http diff --git a/.verdaccio/htpasswd b/.verdaccio/htpasswd new file mode 100644 index 0000000000..8391cd4b0a --- /dev/null +++ b/.verdaccio/htpasswd @@ -0,0 +1 @@ +test:$6FrCaT/v0dwE:autocreated 2020-03-25T19:10:50.254Z diff --git a/README.md b/README.md index 64282bac93..f26b7b4b4e 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,15 @@ This will start a server on the `http://localhost:5000/` yarn run cosmos ``` +## Build/test UI Library + +CoW Swap has a library of reusable components. + +```bash +yarn ui:build +yarn ui:test +``` + ## Configuring the environment The app has some default configuration, but it's highly encouraged to define your own. diff --git a/craco.config.js b/craco.config.js index db43969195..1f6848c4c4 100644 --- a/craco.config.js +++ b/craco.config.js @@ -60,6 +60,8 @@ module.exports = { plugins, alias: { 'bn.js': path.resolve(__dirname, 'node_modules/bn.js/lib/bn.js'), + '@cowprotocol/ui': path.resolve(__dirname, 'src/libs/ui/src/index.ts'), + '@cowprotocol/ui-utils': path.resolve(__dirname, 'src/libs/ui-utils/src/index.ts'), }, // https://webpack.js.org/configuration configure: (webpackConfig) => ({ diff --git a/jest.config.ts b/jest.config.ts new file mode 100644 index 0000000000..9b66958ca6 --- /dev/null +++ b/jest.config.ts @@ -0,0 +1,7 @@ +import { getJestProjects } from '@nx/jest' + +const config = { + projects: getJestProjects(), +} + +export default config diff --git a/jest.preset.js b/jest.preset.js new file mode 100644 index 0000000000..8cd53f8e9b --- /dev/null +++ b/jest.preset.js @@ -0,0 +1,3 @@ +const nxPreset = require('@nx/jest/preset').default + +module.exports = { ...nxPreset } diff --git a/nx.json b/nx.json index 148b0d5bdb..c7a5ef90a6 100644 --- a/nx.json +++ b/nx.json @@ -1,7 +1,12 @@ { "namedInputs": { "default": ["{projectRoot}/**/*"], - "production": ["!{projectRoot}/**/*.test.tsx"] + "production": [ + "!{projectRoot}/**/*.test.tsx", + "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)", + "!{projectRoot}/tsconfig.spec.json", + "!{projectRoot}/jest.config.[jt]s" + ] }, "targetDefaults": { "build": { @@ -10,7 +15,7 @@ "outputs": ["{projectRoot}/build"] }, "test": { - "inputs": ["default", "^production", "{workspaceRoot}/package.json"] + "inputs": ["default", "^production", "{workspaceRoot}/package.json", "{workspaceRoot}/jest.preset.js"] } }, "tasksRunnerOptions": { @@ -41,5 +46,15 @@ }, "affected": { "defaultBase": "develop" + }, + "generators": { + "@nx/react": { + "application": { + "babel": true + }, + "library": { + "unitTestRunner": "jest" + } + } } } diff --git a/package.json b/package.json index d2dcf55054..bc6658fa58 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,10 @@ "license": "ISC", "scripts": { "start": "nx exec -- craco start", - "build": "nx exec -- yarn _build ", - "test": "nx exec -- craco test --env=jsdom", + "build": "nx run-many -t build", + "build:cowswap": "nx build cowswap", + "test": "nx run-many -t test", + "e2e": "nx run-many -t e2e", "start:ssl": "nx exec -- HTTPS=true yarn start", "start:service-worker": "nx exec -- yarn _start:service-worker ", "lint:fix": "nx exec -- yarn run eslint --fix ./", @@ -18,7 +20,6 @@ "ipfs:publish": "nx exec -- ipfs-deploy build -p pinata -O", "test:debug": "nx exec -- craco --inspect-brk test --runInBand --no-cache", "eject": "react-scripts eject", - "integration-test": "nx exec -- start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run'", "serve": "nx exec -- serve build -l 3000", "serve:ci": "nx exec -- serve website -l 3000", "cypress": "nx exec -- cypress open", @@ -37,14 +38,12 @@ "postinstall": "yarn contracts:compile && yarn i18n:compile && yarn patch-package", "prepare": "husky install", "prepublishOnly": "yarn widgets:build", - "test:e2e": "nx exec -- start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run --record'", "cosmos:clear": "nx exec -- rm -rf ./public/cosmos", "cosmos:run": "nx exec -- yarn _cosmos:run ", "cosmos:export": "nx exec -- cross-env NODE_ENV=development cosmos-export", "widgets:build": "nx exec -- rollup --config --failAfterWarnings --configPlugin typescript2", "sitemap": "nx exec -- node src/sitemap", "writeVersion": "nx exec -- node src/writeVersion", - "_build": "yarn i18n:compile && craco build && yarn writeVersion", "_start:service-worker": "yarn build && yarn serve", "_contracts:compile": "yarn contracts:compile:abi && yarn contracts:compile:abi-ethflow && yarn contracts:add:export && yarn contracts:compile:abi-uniswap && yarn contracts:compile:v3", "_i18n:compile": "yarn i18n:extract && lingui compile", @@ -81,6 +80,7 @@ "@cowprotocol/cow-sdk": "^2.0.4", "@cowprotocol/ethflowcontract": "cowprotocol/ethflowcontract.git#v1.0.0-artifacts", "@davatar/react": "1.8.1", + "@ethersproject/bignumber": "^5.7.0", "@fontsource/ibm-plex-mono": "^4.5.1", "@fontsource/inter": "^4.5.1", "@ledgerhq/connect-kit-loader": "^1.0.2", @@ -186,10 +186,12 @@ "setimmediate": "^1.0.5", "simple-sitemap-renderer": "^1.1.0", "styled-components": "^5.3.0", + "styled-jsx": "5.1.2", "swr": "^1.0.1", "text-encoding-polyfill": "^0.6.7", "timeago.js": "^4.0.2", "tiny-invariant": "^1.2.0", + "tslib": "^2.3.0", "typescript": "^4.9.4", "ua-parser-js": "^1.0.32", "use-async-memo": "^1.2.4", @@ -208,20 +210,25 @@ "@commitlint/config-conventional": "^17.6.1", "@craco/craco": "^7.0.0", "@ethersproject/experimental": "^5.7.0", + "@nx/eslint-plugin": "^16.3.2", + "@nx/jest": "16.3.2", + "@nx/js": "16.3.2", + "@nx/react": "^16.3.2", + "@nx/vite": "16.3.2", "@simbathesailor/babel-plugin-use-what-changed": "^2.1.0", "@testing-library/jest-dom": "^5.16.5", - "@testing-library/react": "^13.4.0", + "@testing-library/react": "14.0.0", "@testing-library/react-hooks": "^8.0.1", "@testing-library/user-event": "^13.5.0", "@typechain/ethers-v5": "^10.2.0", "@types/clone-deep": "^4.0.1", "@types/d3": "^7.4.0", - "@types/jest": "^27.5.2", + "@types/jest": "^29.4.0", "@types/ms": "^0.7.31", "@types/ms.macro": "^2.0.0", - "@types/node": "^16.18.11", - "@types/react": "^18.0.26", - "@types/react-dom": "^18.0.10", + "@types/node": "18.14.2", + "@types/react": "18.0.28", + "@types/react-dom": "18.0.11", "@types/react-helmet": "^6.1.6", "@types/react-router-hash-link": "^2.4.5", "@types/react-virtualized-auto-sizer": "^1.0.1", @@ -230,29 +237,50 @@ "@types/styled-components": "^5.1.26", "@types/ua-parser-js": "^0.7.36", "@types/wcag-contrast": "^3.0.0", + "@vitejs/plugin-react": "^3.0.0", + "@vitest/ui": "^0.31.0", "assert": "^2.0.0", + "babel-jest": "^29.4.1", "craco-workbox": "^0.2.0", "create-react-app": "^5.0.1", "crypto-browserify": "^3.12.0", "cypress": "^12.4.1", "eslint-config-react-app": "^7.0.1", "eslint-plugin-cypress": "^2.12.1", + "eslint-plugin-import": "2.27.5", + "eslint-plugin-jsx-a11y": "6.7.1", + "eslint-plugin-react": "7.32.2", + "eslint-plugin-react-hooks": "4.6.0", "eslint-plugin-unused-imports": "^2.0.0", "https-browserify": "^1.0.0", "husky": "^8.0.3", + "jest": "^29.5.0", + "jest-environment-jsdom": "^29.5.0", "jest-fetch-mock": "^3.0.3", "jest-styled-components": "^7.1.1", + "jsdom": "~20.0.3", "launchdarkly-js-client-sdk": "^3.1.2", + "nx": "16.3.2", + "nx-cloud": "latest", "patch-package": "^6.5.1", + "prettier": "^2.6.2", "react-cosmos": "^5.7.2", + "serve": "^14.2.0", + "start-server-and-test": "^2.0.0", "stream": "^0.0.2", "stream-browserify": "^3.0.0", "stream-http": "^3.2.0", + "ts-jest": "^29.1.0", "ts-mockito": "^2.6.1", + "ts-node": "10.9.1", "typechain": "^8.1.1", - "webpack-bundle-analyzer": "^4.7.0", - "nx": "16.3.2", - "nx-cloud": "latest" + "verdaccio": "^5.0.4", + "vite": "^4.3.4", + "vite-plugin-dts": "~1.7.1", + "vite-plugin-eslint": "^1.8.1", + "vite-tsconfig-paths": "^4.0.2", + "vitest": "^0.31.0", + "webpack-bundle-analyzer": "^4.7.0" }, "nx": { "targets": { diff --git a/project.json b/project.json new file mode 100644 index 0000000000..ced528c01b --- /dev/null +++ b/project.json @@ -0,0 +1,47 @@ +{ + "name": "cowswap", + "$schema": "node_modules/nx/schemas/project-schema.json", + "targets": { + "test": { + "executor": "nx:run-commands", + "options": { + "command": "craco test src --env=jsdom --testPathIgnorePatterns src/libs --no-watch --watchAll=false" + } + }, + "build": { + "executor": "nx:run-commands", + "options": { + "command": "yarn i18n:compile && craco build && yarn writeVersion" + } + }, + "e2e": { + "executor": "nx:run-commands", + "options": { + "command": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run'" + }, + "dependsOn": ["^build"] + }, + "local-registry": { + "executor": "@nx/js:verdaccio", + "options": { + "port": 4873, + "config": ".verdaccio/config.yml", + "storage": "tmp/local-registry/storage" + } + }, + "test:nx": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectName}"], + "options": { + "jestConfig": "jest.config.ts", + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "codeCoverage": true + } + } + } + } +} diff --git a/src/.eslintrc.json b/src/.eslintrc.json new file mode 100644 index 0000000000..8f34c1b963 --- /dev/null +++ b/src/.eslintrc.json @@ -0,0 +1,13 @@ +{ + "extends": ["react-app", "react-app/jest", "plugin:cypress/recommended", "../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["cypress/**", "cypress-custom/**"], + "rules": { + "jest/valid-expect": 0, + "testing-library/await-async-utils": 0 + } + } + ] +} diff --git a/src/constants/common.ts b/src/common/constants/common.ts similarity index 100% rename from src/constants/common.ts rename to src/common/constants/common.ts diff --git a/src/constants/featureFlags.ts b/src/common/constants/featureFlags.ts similarity index 100% rename from src/constants/featureFlags.ts rename to src/common/constants/featureFlags.ts diff --git a/src/constants/intl.ts b/src/common/constants/intl.ts similarity index 100% rename from src/constants/intl.ts rename to src/common/constants/intl.ts diff --git a/src/constants/launchDarkly.ts b/src/common/constants/launchDarkly.ts similarity index 100% rename from src/constants/launchDarkly.ts rename to src/common/constants/launchDarkly.ts diff --git a/src/constants/quote.ts b/src/common/constants/quote.ts similarity index 100% rename from src/constants/quote.ts rename to src/common/constants/quote.ts diff --git a/src/constants/routes.ts b/src/common/constants/routes.ts similarity index 96% rename from src/constants/routes.ts rename to src/common/constants/routes.ts index 8d379d5155..058f7d386d 100644 --- a/src/constants/routes.ts +++ b/src/common/constants/routes.ts @@ -26,4 +26,5 @@ export enum Routes { DOCS = '/docs', STATS = '/stats', TWITTER = '/twitter', + KITCHEN_SINK = '/kitchen-sink', } diff --git a/src/common/pure/CancellationModal/RequestCancellationModal.tsx b/src/common/pure/CancellationModal/RequestCancellationModal.tsx index dfe65cb41c..832637b012 100644 --- a/src/common/pure/CancellationModal/RequestCancellationModal.tsx +++ b/src/common/pure/CancellationModal/RequestCancellationModal.tsx @@ -12,9 +12,9 @@ import NotificationBanner from 'legacy/components/NotificationBanner' import { LegacyConfirmationModalContent } from 'legacy/components/TransactionConfirmationModal/LegacyConfirmationModalContent' import { LinkStyledButton } from 'legacy/theme' +import { Routes } from 'common/constants/routes' import { CancellationType } from 'common/hooks/useCancelOrder/state' import { TokenAmount } from 'common/pure/TokenAmount' -import { Routes } from 'constants/routes' export type RequestCancellationModalProps = { summary?: string diff --git a/src/common/pure/FiatAmount/index.tsx b/src/common/pure/FiatAmount/index.tsx index 89edd8525e..2b9d656353 100644 --- a/src/common/pure/FiatAmount/index.tsx +++ b/src/common/pure/FiatAmount/index.tsx @@ -3,7 +3,7 @@ import { FractionLike, Nullish } from 'types' import { LONG_PRECISION } from 'legacy/constants' -import { AMOUNTS_FORMATTING_FEATURE_FLAG } from 'constants/featureFlags' +import { AMOUNTS_FORMATTING_FEATURE_FLAG } from 'common/constants/featureFlags' import { formatFiatAmount } from 'utils/amountFormat' import { FeatureFlag } from 'utils/featureFlags' import { FractionUtils } from 'utils/fractionUtils' diff --git a/src/common/pure/TokenAmount/index.tsx b/src/common/pure/TokenAmount/index.tsx index 58271bdd6e..5fbcdb2bf0 100644 --- a/src/common/pure/TokenAmount/index.tsx +++ b/src/common/pure/TokenAmount/index.tsx @@ -4,8 +4,8 @@ import { FractionLike, Nullish } from 'types' import { LONG_PRECISION } from 'legacy/constants' +import { AMOUNTS_FORMATTING_FEATURE_FLAG } from 'common/constants/featureFlags' import { TokenSymbol, TokenSymbolProps } from 'common/pure/TokenSymbol' -import { AMOUNTS_FORMATTING_FEATURE_FLAG } from 'constants/featureFlags' import { formatTokenAmount } from 'utils/amountFormat' import { FeatureFlag } from 'utils/featureFlags' import { FractionUtils } from 'utils/fractionUtils' diff --git a/src/common/pure/TransactionSubmittedContent/index.tsx b/src/common/pure/TransactionSubmittedContent/index.tsx index faae800b49..d7c6db7b70 100644 --- a/src/common/pure/TransactionSubmittedContent/index.tsx +++ b/src/common/pure/TransactionSubmittedContent/index.tsx @@ -18,7 +18,7 @@ import { ActivityDerivedState } from 'modules/account/containers/Transaction' import { EthFlowStepper } from 'modules/swap/containers/EthFlowStepper' import AddToMetamask from 'modules/wallet/web3-react/containers/AddToMetamask' -import { Routes } from 'constants/routes' +import { Routes } from 'common/constants/routes' import * as styledEl from './styled' diff --git a/src/cow-react/index.tsx b/src/cow-react/index.tsx index f12aeb39fe..5a71fd1767 100644 --- a/src/cow-react/index.tsx +++ b/src/cow-react/index.tsx @@ -15,7 +15,7 @@ import * as serviceWorkerRegistration from 'serviceWorkerRegistration' import AppziButton from 'legacy/components/AppziButton' import Blocklist from 'legacy/components/Blocklist' -import Popups from 'legacy/components/Popups' +import { Popups } from 'legacy/components/Popups' import Web3Provider from 'legacy/components/Web3Provider' import store from 'legacy/state' import ThemeProvider, { FixedGlobalStyle, ThemedGlobalStyle } from 'legacy/theme' diff --git a/src/jest.config.ts b/src/jest.config.ts new file mode 100644 index 0000000000..191e42a220 --- /dev/null +++ b/src/jest.config.ts @@ -0,0 +1,13 @@ +// this is not used for now. we use "craco test", but eventually we will + +/* eslint-disable */ +export default { + displayName: 'cowswap', + preset: '../jest.preset.js', + transform: { + '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest', + '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/react/babel'] }], + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../coverage/cowswap', +} diff --git a/src/legacy/components/AmplitudeAnalytics/index.ts b/src/legacy/components/AmplitudeAnalytics/index.ts index d6533cc94d..f056e58a21 100644 --- a/src/legacy/components/AmplitudeAnalytics/index.ts +++ b/src/legacy/components/AmplitudeAnalytics/index.ts @@ -7,9 +7,9 @@ import { Identify, identify, init, track } from '@amplitude/analytics-browser' * member of the organization on Amplitude to view details. */ export function initializeAnalytics(isDevEnvironment = process.env.NODE_ENV === 'development') { - if (isDevEnvironment) return - const API_KEY = process.env.REACT_APP_AMPLITUDE_KEY + if (isDevEnvironment || !API_KEY) return + if (typeof API_KEY === 'undefined') { throw new Error(`REACT_APP_AMPLITUDE_KEY must be a defined environment variable`) } diff --git a/src/legacy/components/ErrorBoundary/ErrorBoundaryMod.tsx b/src/legacy/components/ErrorBoundary/ErrorBoundaryMod.tsx index ff4d46a6fa..5e68f2110a 100644 --- a/src/legacy/components/ErrorBoundary/ErrorBoundaryMod.tsx +++ b/src/legacy/components/ErrorBoundary/ErrorBoundaryMod.tsx @@ -15,7 +15,7 @@ import { MEDIA_WIDTHS } from 'legacy/theme' import { Page } from 'modules/application/pure/Page' -import { Routes } from 'constants/routes' +import { Routes } from 'common/constants/routes' /* const FallbackWrapper = styled.div` display: flex; diff --git a/src/legacy/components/Header/index.tsx b/src/legacy/components/Header/index.tsx index 30981bc97e..8e08f4c30f 100644 --- a/src/legacy/components/Header/index.tsx +++ b/src/legacy/components/Header/index.tsx @@ -23,8 +23,8 @@ import { useTradeState } from 'modules/trade/hooks/useTradeState' import { getDefaultTradeRawState } from 'modules/trade/types/TradeRawState' import { useWalletInfo, Web3Status } from 'modules/wallet' +import { Routes } from 'common/constants/routes' import { TokenAmount } from 'common/pure/TokenAmount' -import { Routes } from 'constants/routes' import MobileMenuIcon from './MobileMenuIcon' import { diff --git a/src/legacy/components/Popups/FollowPendingTxPopup/FollowPendingTxPopupUI.tsx b/src/legacy/components/Popups/FollowPendingTxPopup/FollowPendingTxPopupUI.tsx index 1f66f6a644..8f5d1c36f6 100644 --- a/src/legacy/components/Popups/FollowPendingTxPopup/FollowPendingTxPopupUI.tsx +++ b/src/legacy/components/Popups/FollowPendingTxPopup/FollowPendingTxPopupUI.tsx @@ -4,7 +4,7 @@ import { Text } from 'rebass' import styled from 'styled-components/macro' import { AutoColumn } from 'legacy/components/Column' -import { StyledClose as IconClose } from 'legacy/components/Popups/PopupItemMod' +import { StyledClose as IconClose } from 'legacy/components/Popups/styled' import Tooltip, { TooltipProps } from 'legacy/components/Tooltip' interface PopupContentProps { diff --git a/src/legacy/components/Popups/PopupItem.tsx b/src/legacy/components/Popups/PopupItem.tsx index 0d07865ccf..bda9448bf1 100644 --- a/src/legacy/components/Popups/PopupItem.tsx +++ b/src/legacy/components/Popups/PopupItem.tsx @@ -1,34 +1,15 @@ -import styled from 'styled-components/macro' +import { useCallback, useContext, useEffect } from 'react' -import { PopupContent } from 'legacy/state/application/reducer' - -import { default as PopupItemUni, Popup, Fader } from './PopupItemMod' +import { useSpring } from '@react-spring/web' +import { ThemeContext } from 'styled-components/macro' -const Wrapper = styled.div` - ${Popup} { - ${({ theme }) => theme.mediaWidth.upToSmall` - margin: 0 0 16px; - min-width: 100%; - `} - } - - ${Fader} { - background-color: ${({ theme }) => theme.disabled}; - height: 4px; - } - - a { - text-decoration: underline; - color: ${({ theme }) => theme.textLink}; - } -` +import FailedNetworkSwitchPopup from 'legacy/components/Popups/FailedNetworkSwitchPopup' +import { WarningPopup } from 'legacy/components/Popups/WarningPopup' +import { useRemovePopup } from 'legacy/state/application/hooks' +import { PopupContent } from 'legacy/state/application/reducer' -export const PopupItemWrapper = styled(PopupItemUni)` - ${(props) => props.className} { - border: 2px solid ${({ theme }) => theme.black}; - box-shadow: 2px 2px 0 ${({ theme }) => theme.black}; - } -` +import { AnimatedFader, PopupItemWrapper, PopupWrapper, StyledClose } from './styled' +import { TransactionPopup } from './TransactionPopup' export function PopupItem({ removeAfterMs, @@ -39,11 +20,60 @@ export function PopupItem({ content: PopupContent popKey: string }) { + const removePopup = useRemovePopup() + const removeThisPopup = useCallback(() => removePopup(popKey), [popKey, removePopup]) + useEffect(() => { + if (removeAfterMs === null) return undefined + + const timeout = setTimeout(() => { + removeThisPopup() + }, removeAfterMs) + + return () => { + clearTimeout(timeout) + } + }, [removeAfterMs, removeThisPopup]) + + const theme = useContext(ThemeContext) + + // mod + const isTxn = 'txn' in content + const isUnsupportedNetwork = 'unsupportedNetwork' in content + const isMetaTxn = 'metatxn' in content + const isWarningTxn = 'warning' in content + + let popupContent + if (isTxn) { + const { + txn: { hash, success, summary }, + } = content + popupContent = + } else if (isMetaTxn) { + const { + metatxn: { id, success, summary }, + } = content + popupContent = + } else if (isUnsupportedNetwork) { + popupContent = + } else if ('failedSwitchNetwork' in content) { + popupContent = + } else if (isWarningTxn) { + popupContent = + } + + const faderStyle = useSpring({ + from: { width: '100%' }, + to: { width: '0%' }, + config: { duration: removeAfterMs ?? undefined }, + }) + return ( - - - + + + + {popupContent} + {removeAfterMs !== null ? : null} + + ) } - -export default PopupItem diff --git a/src/legacy/components/Popups/PopupItemMod.tsx b/src/legacy/components/Popups/PopupItemMod.tsx deleted file mode 100644 index 6438dcfe29..0000000000 --- a/src/legacy/components/Popups/PopupItemMod.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import { useCallback, useContext, useEffect } from 'react' - -import { animated, useSpring } from '@react-spring/web' -import { X } from 'react-feather' -import styled, { FlattenInterpolation, ThemeContext, ThemeProps, DefaultTheme } from 'styled-components/macro' - -import FailedNetworkSwitchPopup from 'legacy/components/Popups/FailedNetworkSwitchPopup' -import { WarningPopup } from 'legacy/components/Popups/WarningPopup' -import { useRemovePopup } from 'legacy/state/application/hooks' -import { PopupContent } from 'legacy/state/application/reducer' - -import TransactionPopup from './TransactionPopupMod' - -// MOD imports - -export const StyledClose = styled(X)` - position: absolute; - right: 10px; - top: 10px; - - :hover { - cursor: pointer; - } -` -export const Popup = styled.div<{ css?: FlattenInterpolation> }>` - display: inline-block; - width: 100%; - //padding: 1em; - background-color: ${({ theme }) => theme.bg1}; - position: relative; - border-radius: 10px; - padding: 20px; - padding-right: 35px; - overflow: hidden; - - ${({ theme }) => theme.mediaWidth.upToSmall` - min-width: 290px; - &:not(:last-of-type) { - margin-right: 20px; - } - `} - - ${({ css }) => css && css} -` -export const Fader = styled.div` - position: absolute; - bottom: 0px; - left: 0px; - width: 100%; - height: 2px; - background-color: ${({ theme }) => theme.bg3}; -` - -const AnimatedFader = animated(Fader) - -export default function PopupItem({ - removeAfterMs, - content, - popKey, - className, // mod -}: { - removeAfterMs: number | null - content: PopupContent - popKey: string - className?: string -}) { - const removePopup = useRemovePopup() - const removeThisPopup = useCallback(() => removePopup(popKey), [popKey, removePopup]) - useEffect(() => { - if (removeAfterMs === null) return undefined - - const timeout = setTimeout(() => { - removeThisPopup() - }, removeAfterMs) - - return () => { - clearTimeout(timeout) - } - }, [removeAfterMs, removeThisPopup]) - - const theme = useContext(ThemeContext) - - // mod - const isTxn = 'txn' in content - const isUnsupportedNetwork = 'unsupportedNetwork' in content - const isMetaTxn = 'metatxn' in content - const isWarningTxn = 'warning' in content - - let popupContent - if (isTxn) { - const { - txn: { hash, success, summary }, - } = content - popupContent = - } else if (isMetaTxn) { - const { - metatxn: { id, success, summary }, - } = content - popupContent = - } else if (isUnsupportedNetwork) { - popupContent = - } else if ('failedSwitchNetwork' in content) { - popupContent = - } else if (isWarningTxn) { - popupContent = - } - - const faderStyle = useSpring({ - from: { width: '100%' }, - to: { width: '0%' }, - config: { duration: removeAfterMs ?? undefined }, - }) - - return ( - - - {popupContent} - {removeAfterMs !== null ? : null} - - ) -} diff --git a/src/legacy/components/Popups/PopupsMod.tsx b/src/legacy/components/Popups/PopupsMod.tsx deleted file mode 100644 index 3f15276e35..0000000000 --- a/src/legacy/components/Popups/PopupsMod.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { SupportedChainId } from '@cowprotocol/cow-sdk' - -import styled from 'styled-components/macro' - -import { AutoColumn } from 'legacy/components/Column' -import PopupItem from 'legacy/components/Popups/PopupItem' -import { useActivePopups } from 'legacy/state/application/hooks' -import { useURLWarningVisible } from 'legacy/state/user/hooks' -import { MEDIA_WIDTHS } from 'legacy/theme' - -import { useWalletInfo } from 'modules/wallet' - -import { MobilePopupWrapper } from './index' - -export const MobilePopupInner = styled.div` - height: 99%; - overflow-x: auto; - overflow-y: hidden; - display: flex; - flex-direction: row; - -webkit-overflow-scrolling: touch; - ::-webkit-scrollbar { - display: none; - } -` - -const StopOverflowQuery = `@media screen and (min-width: ${MEDIA_WIDTHS.upToMedium + 1}px) and (max-width: ${ - MEDIA_WIDTHS.upToMedium + 500 -}px)` - -const FixedPopupColumn = styled(AutoColumn)<{ extraPadding: boolean; xlPadding: boolean }>` - position: fixed; - top: ${({ extraPadding }) => (extraPadding ? '132px' : '88px')}; - right: 1rem; - max-width: 355px !important; - width: 100%; - z-index: 3; - - ${({ theme }) => theme.mediaWidth.upToSmall` - display: none; - `}; - - ${StopOverflowQuery} { - top: ${({ extraPadding, xlPadding }) => (xlPadding ? '132px' : extraPadding ? '64px' : '56px')}; - } -` - -export default function Popups() { - // get all popups - const activePopups = useActivePopups() - - const urlWarningActive = useURLWarningVisible() - - // need extra padding if network is not L1 Ethereum - const { chainId } = useWalletInfo() - const isNotOnMainnet = Boolean(chainId && chainId !== SupportedChainId.MAINNET) - - return ( - <> - - {activePopups.map((item) => ( - - ))} - - 0}> - - {activePopups // reverse so new items up front - .slice(0) - .reverse() - .map((item) => ( - - ))} - - - - ) -} diff --git a/src/legacy/components/Popups/TransactionPopup.tsx b/src/legacy/components/Popups/TransactionPopup.tsx index 65010a7c26..7ef87eeeab 100644 --- a/src/legacy/components/Popups/TransactionPopup.tsx +++ b/src/legacy/components/Popups/TransactionPopup.tsx @@ -1,2 +1,47 @@ -export * from './TransactionPopupMod' -export { default } from './TransactionPopupMod' +import { useContext } from 'react' + +import { AlertCircle, CheckCircle } from 'react-feather' +import styled, { ThemeContext } from 'styled-components/macro' + +import { AutoColumn } from 'legacy/components/Column' +import { ExplorerLink } from 'legacy/components/ExplorerLink' +import { AutoRow } from 'legacy/components/Row' +import { ThemedText } from 'legacy/theme' + +import { useWalletInfo } from 'modules/wallet' + +const RowNoFlex = styled(AutoRow)` + flex-wrap: nowrap; +` + +export function TransactionPopup({ + hash, + success, + summary, +}: { + hash: string + success?: boolean + summary?: string | JSX.Element +}) { + const { chainId } = useWalletInfo() + + const theme = useContext(ThemeContext) + + return ( + +
+ {success ? : } +
+ + {!summary || typeof summary === 'string' ? ( + + {summary ?? 'Hash: ' + hash.slice(0, 8) + '...' + hash.slice(58, 65)} + + ) : ( + summary + )} + {chainId && } + +
+ ) +} diff --git a/src/legacy/components/Popups/TransactionPopupMod.tsx b/src/legacy/components/Popups/TransactionPopupMod.tsx deleted file mode 100644 index 8398e4ee95..0000000000 --- a/src/legacy/components/Popups/TransactionPopupMod.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { useContext } from 'react' - -import { AlertCircle, CheckCircle } from 'react-feather' -import styled, { ThemeContext } from 'styled-components/macro' - -import { AutoColumn } from 'legacy/components/Column' -import { ExplorerLink } from 'legacy/components/ExplorerLink' -import { AutoRow } from 'legacy/components/Row' -import { ThemedText } from 'legacy/theme' - -import { useWalletInfo } from 'modules/wallet' - -const RowNoFlex = styled(AutoRow)` - flex-wrap: nowrap; -` - -export default function TransactionPopup({ - hash, - success, - summary, -}: { - hash: string - success?: boolean - summary?: string | JSX.Element -}) { - const { chainId } = useWalletInfo() - - const theme = useContext(ThemeContext) - - return ( - -
- {success ? : } -
- - {!summary || typeof summary === 'string' ? ( - - {summary ?? 'Hash: ' + hash.slice(0, 8) + '...' + hash.slice(58, 65)} - - ) : ( - summary - )} - {chainId && } - -
- ) -} diff --git a/src/legacy/components/Popups/index.tsx b/src/legacy/components/Popups/index.tsx index 1128a316cf..5332d36975 100644 --- a/src/legacy/components/Popups/index.tsx +++ b/src/legacy/components/Popups/index.tsx @@ -1,7 +1,27 @@ +import { SupportedChainId } from '@cowprotocol/cow-sdk' + import { transparentize } from 'polished' import styled from 'styled-components/macro' -import { MobilePopupInner } from './PopupsMod' +import { AutoColumn } from 'legacy/components/Column' +import { PopupItem } from 'legacy/components/Popups/PopupItem' +import { useActivePopups } from 'legacy/state/application/hooks' +import { useURLWarningVisible } from 'legacy/state/user/hooks' +import { MEDIA_WIDTHS } from 'legacy/theme' + +import { useWalletInfo } from 'modules/wallet' + +export const MobilePopupInner = styled.div` + height: 99%; + overflow-x: auto; + overflow-y: hidden; + display: flex; + flex-direction: row; + -webkit-overflow-scrolling: touch; + ::-webkit-scrollbar { + display: none; + } +` export const MobilePopupWrapper = styled.div<{ show: boolean }>` position: relative; @@ -31,5 +51,54 @@ export const MobilePopupWrapper = styled.div<{ show: boolean }>` } ` -export * from './PopupsMod' -export { default } from './PopupsMod' +const StopOverflowQuery = `@media screen and (min-width: ${MEDIA_WIDTHS.upToMedium + 1}px) and (max-width: ${ + MEDIA_WIDTHS.upToMedium + 500 +}px)` + +const FixedPopupColumn = styled(AutoColumn)<{ extraPadding: boolean; xlPadding: boolean }>` + position: fixed; + top: ${({ extraPadding }) => (extraPadding ? '132px' : '88px')}; + right: 1rem; + max-width: 355px !important; + width: 100%; + z-index: 3; + + ${({ theme }) => theme.mediaWidth.upToSmall` + display: none; + `}; + + ${StopOverflowQuery} { + top: ${({ extraPadding, xlPadding }) => (xlPadding ? '132px' : extraPadding ? '64px' : '56px')}; + } +` + +export function Popups() { + // get all popups + const activePopups = useActivePopups() + + const urlWarningActive = useURLWarningVisible() + + // need extra padding if network is not L1 Ethereum + const { chainId } = useWalletInfo() + const isNotOnMainnet = Boolean(chainId && chainId !== SupportedChainId.MAINNET) + + return ( + <> + + {activePopups.map((item) => ( + + ))} + + 0}> + + {activePopups // reverse so new items up front + .slice(0) + .reverse() + .map((item) => ( + + ))} + + + + ) +} diff --git a/src/legacy/components/Popups/styled.ts b/src/legacy/components/Popups/styled.ts new file mode 100644 index 0000000000..47baad791a --- /dev/null +++ b/src/legacy/components/Popups/styled.ts @@ -0,0 +1,69 @@ +import { animated } from '@react-spring/web' +import { X } from 'react-feather' +import styled, { DefaultTheme, FlattenInterpolation, ThemeProps } from 'styled-components/macro' + +const Fader = styled.div` + position: absolute; + bottom: 0px; + left: 0px; + width: 100%; + height: 2px; + background-color: ${({ theme }) => theme.bg3}; +` + +export const PopupWrapper = styled.div<{ css?: FlattenInterpolation> }>` + display: inline-block; + width: 100%; + //padding: 1em; + background-color: ${({ theme }) => theme.bg1}; + position: relative; + border-radius: 10px; + padding: 20px; + padding-right: 35px; + overflow: hidden; + + ${({ theme }) => theme.mediaWidth.upToSmall` + min-width: 290px; + &:not(:last-of-type) { + margin-right: 20px; + } + `} + + ${({ css }) => css && css} +` + +export const PopupItemWrapper = styled.div` + ${(props) => props.className} { + border: 2px solid ${({ theme }) => theme.black}; + box-shadow: 2px 2px 0 ${({ theme }) => theme.black}; + } + + ${PopupWrapper} { + ${({ theme }) => theme.mediaWidth.upToSmall` + margin: 0 0 16px; + min-width: 100%; + `} + } + + ${Fader} { + background-color: ${({ theme }) => theme.disabled}; + height: 4px; + } + + a { + text-decoration: underline; + color: ${({ theme }) => theme.textLink}; + } +` + +export const StyledClose = styled(X)` + position: absolute; + right: 10px; + top: 10px; + + :hover { + cursor: pointer; + } +` + +export const AnimatedFader = animated(Fader) diff --git a/src/legacy/components/Tokens/TokensTableRow.tsx b/src/legacy/components/Tokens/TokensTableRow.tsx index d8519827d3..5c07a2671d 100644 --- a/src/legacy/components/Tokens/TokensTableRow.tsx +++ b/src/legacy/components/Tokens/TokensTableRow.tsx @@ -20,10 +20,10 @@ import { getBlockExplorerUrl } from 'legacy/utils' import { parameterizeTradeRoute } from 'modules/trade/utils/parameterizeTradeRoute' import { useWalletInfo } from 'modules/wallet' +import { Routes } from 'common/constants/routes' import { useAreThereTokensWithSameSymbol } from 'common/hooks/useAreThereTokensWithSameSymbol' import { TokenAmount } from 'common/pure/TokenAmount' import { TokenSymbol } from 'common/pure/TokenSymbol' -import { Routes } from 'constants/routes' import { CardsSpinner, ExtLink } from 'pages/Account/styled' import BalanceCell from './BalanceCell' diff --git a/src/legacy/hooks/usePriceImpact/useFallbackPriceImpact.ts b/src/legacy/hooks/usePriceImpact/useFallbackPriceImpact.ts index fae648f631..56a5264f91 100644 --- a/src/legacy/hooks/usePriceImpact/useFallbackPriceImpact.ts +++ b/src/legacy/hooks/usePriceImpact/useFallbackPriceImpact.ts @@ -8,7 +8,7 @@ import { QuoteInformationObject } from 'legacy/state/price/reducer' import { calculateFallbackPriceImpact } from 'legacy/utils/price' import { LegacyFeeQuoteParams } from 'api/gnosisProtocol/legacy/types' -import { PRICE_QUOTE_VALID_TO_TIME } from 'constants/quote' +import { PRICE_QUOTE_VALID_TO_TIME } from 'common/constants/quote' import { FallbackPriceImpactParams, PriceImpactTrade } from './types' import useExactInSwap, { useCalculateQuote } from './useQuoteAndSwap' diff --git a/src/legacy/state/orders/middleware/appziMiddleware.test.ts b/src/legacy/state/orders/middleware/appziMiddleware.test.ts index d7cee50434..aca3231a30 100644 --- a/src/legacy/state/orders/middleware/appziMiddleware.test.ts +++ b/src/legacy/state/orders/middleware/appziMiddleware.test.ts @@ -1,5 +1,4 @@ import { OrderClass } from '@cowprotocol/cow-sdk' -import { getOrderByIdFromState } from '../helpers' import { AnyAction, Dispatch, MiddlewareAPI } from 'redux' import { instance, mock, resetCalls, when } from 'ts-mockito' @@ -9,6 +8,7 @@ import { isOrderInPendingTooLong, openNpsAppziSometimes } from 'legacy/utils/app import { appziMiddleware } from './appziMiddleware' import { AppState } from '../../index' +import { getOrderByIdFromState } from '../helpers' jest.mock('legacy/utils/appzi') jest.mock('../helpers', () => { diff --git a/src/legacy/state/orders/updaters/UnfillableOrdersUpdater.ts b/src/legacy/state/orders/updaters/UnfillableOrdersUpdater.ts index aa708bb168..642adfd859 100644 --- a/src/legacy/state/orders/updaters/UnfillableOrdersUpdater.ts +++ b/src/legacy/state/orders/updaters/UnfillableOrdersUpdater.ts @@ -30,7 +30,7 @@ import { supportedChainId } from 'legacy/utils/supportedChainId' import { updatePendingOrderPricesAtom } from 'modules/orders/state/pendingOrdersPricesAtom' import { useWalletInfo } from 'modules/wallet' -import { PRICE_QUOTE_VALID_TO_TIME } from 'constants/quote' +import { PRICE_QUOTE_VALID_TO_TIME } from 'common/constants/quote' /** * Thin wrapper around `getBestPrice` that builds the params and returns null on failure diff --git a/src/libs/ui-utils/.eslintrc.json b/src/libs/ui-utils/.eslintrc.json new file mode 100644 index 0000000000..9a654cf03a --- /dev/null +++ b/src/libs/ui-utils/.eslintrc.json @@ -0,0 +1,20 @@ +{ + // TODO: Once we get rid of craco, cra + // "extends": ["plugin:@nx/react"], + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/src/libs/ui-utils/README.md b/src/libs/ui-utils/README.md new file mode 100644 index 0000000000..b820e6bfdf --- /dev/null +++ b/src/libs/ui-utils/README.md @@ -0,0 +1,11 @@ +# ui-utils + +This library was generated with [Nx](https://nx.dev). + +## Bui-utilslding + +Run `nx bui-utilsld ui-utils` to bui-utilsld the library. + +## Running unit tests + +Run `nx test ui-utils` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/src/libs/ui-utils/jest.config.ts b/src/libs/ui-utils/jest.config.ts new file mode 100644 index 0000000000..d94436dab8 --- /dev/null +++ b/src/libs/ui-utils/jest.config.ts @@ -0,0 +1,10 @@ +/* eslint-disable */ +export default { + displayName: 'ui-utils', + preset: '../../../jest.preset.js', + transform: { + '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../../coverage/libs/ui-utils', +} diff --git a/src/libs/ui-utils/package.json b/src/libs/ui-utils/package.json new file mode 100644 index 0000000000..c098259ef5 --- /dev/null +++ b/src/libs/ui-utils/package.json @@ -0,0 +1,5 @@ +{ + "name": "@cowprotocol/ui-utils", + "version": "0.0.1", + "type": "commonjs" +} diff --git a/src/libs/ui-utils/project.json b/src/libs/ui-utils/project.json new file mode 100644 index 0000000000..72c3785288 --- /dev/null +++ b/src/libs/ui-utils/project.json @@ -0,0 +1,41 @@ +{ + "name": "ui-utils", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "src/libs/ui-utils/src", + "projectType": "library", + "targets": { + "build": { + "executor": "@nx/vite:build", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/ui-utils" + } + }, + "publish": { + "command": "node tools/scripts/publish.mjs ui {args.ver} {args.tag}", + "dependsOn": ["build"] + }, + "lint": { + "executor": "@nx/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["src/libs/ui-utils/**/*.ts"] + } + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "src/libs/ui-utils/jest.config.ts", + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "codeCoverage": true + } + } + } + }, + "tags": [] +} diff --git a/src/libs/ui-utils/src/index.ts b/src/libs/ui-utils/src/index.ts new file mode 100644 index 0000000000..800f1d6d63 --- /dev/null +++ b/src/libs/ui-utils/src/index.ts @@ -0,0 +1 @@ +export * from './lib/return5' diff --git a/src/libs/ui-utils/src/lib/return5.spec.ts b/src/libs/ui-utils/src/lib/return5.spec.ts new file mode 100644 index 0000000000..9c757e558c --- /dev/null +++ b/src/libs/ui-utils/src/lib/return5.spec.ts @@ -0,0 +1,7 @@ +import { return5 } from './return5' + +describe('return5', () => { + it('should work', () => { + expect(return5()).toEqual(5) + }) +}) diff --git a/src/libs/ui-utils/src/lib/return5.ts b/src/libs/ui-utils/src/lib/return5.ts new file mode 100644 index 0000000000..69fec1c1e9 --- /dev/null +++ b/src/libs/ui-utils/src/lib/return5.ts @@ -0,0 +1,3 @@ +export function return5(): number { + return 5 +} diff --git a/src/libs/ui-utils/tsconfig.json b/src/libs/ui-utils/tsconfig.json new file mode 100644 index 0000000000..8122543a9a --- /dev/null +++ b/src/libs/ui-utils/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/src/libs/ui-utils/tsconfig.lib.json b/src/libs/ui-utils/tsconfig.lib.json new file mode 100644 index 0000000000..4befa7f099 --- /dev/null +++ b/src/libs/ui-utils/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/src/libs/ui-utils/tsconfig.spec.json b/src/libs/ui-utils/tsconfig.spec.json new file mode 100644 index 0000000000..b2ee74a6b1 --- /dev/null +++ b/src/libs/ui-utils/tsconfig.spec.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] +} diff --git a/src/libs/ui-utils/vite.config.ts b/src/libs/ui-utils/vite.config.ts new file mode 100644 index 0000000000..0e148279f6 --- /dev/null +++ b/src/libs/ui-utils/vite.config.ts @@ -0,0 +1,48 @@ +/// +import { joinPathFragments } from '@nx/devkit' +import { defineConfig } from 'vite' +import dts from 'vite-plugin-dts' +import viteTsConfigPaths from 'vite-tsconfig-paths' + +export default defineConfig({ + cacheDir: '../../../node_modules/.vite/ui', + + plugins: [ + dts({ + entryRoot: 'src', + tsConfigFilePath: joinPathFragments(__dirname, 'tsconfig.lib.json'), + skipDiagnostics: true, + }), + + viteTsConfigPaths({ + root: '../../../', + }), + ], + + // Uncomment this if you are using workers. + // worker: { + // plugins: [ + // viteTsConfigPaths({ + // root: '../../../', + // }), + // ], + // }, + + // Configuration for building your library. + // See: https://vitejs.dev/guide/build.html#library-mode + build: { + lib: { + // Could also be a dictionary or array of multiple entry points. + entry: 'src/index.ts', + name: 'ui', + fileName: 'index', + // Change this to the formats you want to support. + // Don't forgot to update your package.json as well. + formats: ['es', 'cjs'], + }, + rollupOptions: { + // External packages that should not be bundled into your library. + external: [], + }, + }, +}) diff --git a/src/libs/ui/.eslintrc.json b/src/libs/ui/.eslintrc.json new file mode 100644 index 0000000000..9a654cf03a --- /dev/null +++ b/src/libs/ui/.eslintrc.json @@ -0,0 +1,20 @@ +{ + // TODO: Once we get rid of craco, cra + // "extends": ["plugin:@nx/react"], + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/src/libs/ui/README.md b/src/libs/ui/README.md new file mode 100644 index 0000000000..d01cabf1f9 --- /dev/null +++ b/src/libs/ui/README.md @@ -0,0 +1,7 @@ +# ui + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test ui` to execute the unit tests via [Vitest](https://vitest.dev/). diff --git a/src/libs/ui/jest.config.ts b/src/libs/ui/jest.config.ts new file mode 100644 index 0000000000..18a8fadfc5 --- /dev/null +++ b/src/libs/ui/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'ui', + preset: '../../../jest.preset.js', + transform: { + '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest', + '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/react/babel'] }], + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../../coverage/libs/ui', +} diff --git a/src/libs/ui/package.json b/src/libs/ui/package.json new file mode 100644 index 0000000000..3201535ecf --- /dev/null +++ b/src/libs/ui/package.json @@ -0,0 +1,12 @@ +{ + "name": "@cowprotocol/ui", + "version": "0.0.1", + "main": "./index.js", + "types": "./index.d.ts", + "exports": { + ".": { + "import": "./index.mjs", + "require": "./index.js" + } + } +} diff --git a/src/libs/ui/project.json b/src/libs/ui/project.json new file mode 100644 index 0000000000..dae99e4a2a --- /dev/null +++ b/src/libs/ui/project.json @@ -0,0 +1,46 @@ +{ + "name": "ui", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "src/libs/ui/src", + "projectType": "library", + "tags": [], + "targets": { + "lint": { + "executor": "@nx/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["src/libs/ui/**/*.{ts,tsx,js,jsx}"] + } + }, + "build": { + "executor": "@nx/vite:build", + "outputs": ["{options.outputPath}"], + "defaultConfiguration": "production", + "options": { + "outputPath": "dist/libs/ui" + }, + "configurations": { + "development": { + "mode": "development" + }, + "production": { + "mode": "production" + } + } + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "src/libs/ui/jest.config.ts", + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "codeCoverage": true + } + } + } + } +} diff --git a/src/libs/ui/src/index.ts b/src/libs/ui/src/index.ts new file mode 100644 index 0000000000..c8ce325a11 --- /dev/null +++ b/src/libs/ui/src/index.ts @@ -0,0 +1 @@ +export * from './lib/ui' diff --git a/src/libs/ui/src/lib/ui.spec.tsx b/src/libs/ui/src/lib/ui.spec.tsx new file mode 100644 index 0000000000..a5690cf131 --- /dev/null +++ b/src/libs/ui/src/lib/ui.spec.tsx @@ -0,0 +1,10 @@ +import { render } from '@testing-library/react' + +import { PinkTitle } from './ui' + +describe('Ui', () => { + it('should render successfully', () => { + const { baseElement } = render() + expect(baseElement).toBeTruthy() + }) +}) diff --git a/src/libs/ui/src/lib/ui.tsx b/src/libs/ui/src/lib/ui.tsx new file mode 100644 index 0000000000..d38956cdf2 --- /dev/null +++ b/src/libs/ui/src/lib/ui.tsx @@ -0,0 +1,16 @@ +import { PropsWithChildren } from 'react' + +import { return5 } from '@cowprotocol/ui-utils' + +/* eslint-disable-next-line */ +export interface UiProps extends PropsWithChildren {} + +export function PinkTitle(props: UiProps) { + return ( +
+

+ {props.children} (return5 = {return5()}) +

+
+ ) +} diff --git a/src/libs/ui/tsconfig.json b/src/libs/ui/tsconfig.json new file mode 100644 index 0000000000..acc38d499c --- /dev/null +++ b/src/libs/ui/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": false, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "types": ["vite/client"] + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "extends": "../../../tsconfig.base.json" +} diff --git a/src/libs/ui/tsconfig.lib.json b/src/libs/ui/tsconfig.lib.json new file mode 100644 index 0000000000..34489e9d3f --- /dev/null +++ b/src/libs/ui/tsconfig.lib.json @@ -0,0 +1,23 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": ["node", "vite/client"] + }, + "files": [ + "../../../node_modules/@nx/react/typings/styled-jsx.d.ts", + "../../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../../node_modules/@nx/react/typings/image.d.ts" + ], + "exclude": [ + "**/*.spec.ts", + "**/*.test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.spec.js", + "**/*.test.js", + "**/*.spec.jsx", + "**/*.test.jsx" + ], + "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] +} diff --git a/src/libs/ui/tsconfig.spec.json b/src/libs/ui/tsconfig.spec.json new file mode 100644 index 0000000000..25b7af8f6d --- /dev/null +++ b/src/libs/ui/tsconfig.spec.json @@ -0,0 +1,20 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts" + ] +} diff --git a/src/libs/ui/vite.config.ts b/src/libs/ui/vite.config.ts new file mode 100644 index 0000000000..8f939a4cb4 --- /dev/null +++ b/src/libs/ui/vite.config.ts @@ -0,0 +1,49 @@ +/// +import { joinPathFragments } from '@nx/devkit' +import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite' +import dts from 'vite-plugin-dts' +import viteTsConfigPaths from 'vite-tsconfig-paths' + +export default defineConfig({ + cacheDir: '../../../node_modules/.vite/ui', + + plugins: [ + dts({ + entryRoot: 'src', + tsConfigFilePath: joinPathFragments(__dirname, 'tsconfig.lib.json'), + skipDiagnostics: true, + }), + react(), + viteTsConfigPaths({ + root: '../../../', + }), + ], + + // Uncomment this if you are using workers. + // worker: { + // plugins: [ + // viteTsConfigPaths({ + // root: '../../../', + // }), + // ], + // }, + + // Configuration for building your library. + // See: https://vitejs.dev/guide/build.html#library-mode + build: { + lib: { + // Could also be a dictionary or array of multiple entry points. + entry: 'src/index.ts', + name: 'ui', + fileName: 'index', + // Change this to the formats you want to support. + // Don't forgot to update your package.json as well. + formats: ['es', 'cjs'], + }, + rollupOptions: { + // External packages that should not be bundled into your library. + external: ['react', 'react-dom', 'react/jsx-runtime'], + }, + }, +}) diff --git a/src/modules/application/containers/App/RoutesApp.tsx b/src/modules/application/containers/App/RoutesApp.tsx index 91ef2c4ecd..3182aa6f1a 100644 --- a/src/modules/application/containers/App/RoutesApp.tsx +++ b/src/modules/application/containers/App/RoutesApp.tsx @@ -8,7 +8,7 @@ import Loader from 'legacy/components/Loader' import { DISCORD_LINK, DOCS_LINK, DUNE_DASHBOARD_LINK, TWITTER_LINK } from 'legacy/constants' import { RedirectPathToSwapOnly } from 'legacy/pages/Swap/redirects' -import { Routes as RoutesEnum } from 'constants/routes' +import { Routes as RoutesEnum } from 'common/constants/routes' import Account, { AccountOverview } from 'pages/Account' import AnySwapAffectedUsers from 'pages/error/AnySwapAffectedUsers' import { SwapPage } from 'pages/Swap' @@ -36,6 +36,8 @@ const EthFlowFaq = lazy(() => import(/* webpackChunkName: "eth_flow_faq" */ 'pag const AccountTokensOverview = lazy(() => import(/* webpackChunkName: "tokens_overview" */ 'pages/Account/Tokens')) const AccountNotFound = lazy(() => import(/* webpackChunkName: "affiliate" */ 'pages/error/NotFound')) +const KitchenSink = lazy(() => import(/* webpackChunkName: "kitchen_sink" */ 'pages/KitchenSink')) + function createRedirectExternal(url: string) { return () => { window.location.replace(url) @@ -64,6 +66,7 @@ const lazyRoutes: LazyRouteProps[] = [ { route: RoutesEnum.PRIVACY_POLICY, element: }, { route: RoutesEnum.COOKIE_POLICY, element: }, { route: RoutesEnum.TERMS_CONDITIONS, element: }, + { route: RoutesEnum.KITCHEN_SINK, element: }, ] export function RoutesApp() { diff --git a/src/modules/application/containers/TradeWidgetLinks/index.tsx b/src/modules/application/containers/TradeWidgetLinks/index.tsx index 8e895d31d6..df22701ccc 100644 --- a/src/modules/application/containers/TradeWidgetLinks/index.tsx +++ b/src/modules/application/containers/TradeWidgetLinks/index.tsx @@ -3,8 +3,8 @@ import { Trans } from '@lingui/macro' import { useTradeRouteContext } from 'modules/trade/hooks/useTradeRouteContext' import { parameterizeTradeRoute } from 'modules/trade/utils/parameterizeTradeRoute' +import { Routes } from 'common/constants/routes' import { FeatureGuard } from 'common/containers/FeatureGuard' -import { Routes } from 'constants/routes' import * as styledEl from './styled' diff --git a/src/modules/application/containers/WithLDProvider.tsx b/src/modules/application/containers/WithLDProvider.tsx index 2ad7136131..3b54994308 100644 --- a/src/modules/application/containers/WithLDProvider.tsx +++ b/src/modules/application/containers/WithLDProvider.tsx @@ -2,7 +2,7 @@ import { PropsWithChildren } from 'react' import { withLDProvider } from 'launchdarkly-react-client-sdk' -import { LAUNCH_DARKLY_CLIENT_KEY } from 'constants/launchDarkly' +import { LAUNCH_DARKLY_CLIENT_KEY } from 'common/constants/launchDarkly' function InnerWithLDProvider({ children }: PropsWithChildren) { return <>{children} diff --git a/src/modules/limitOrders/containers/LimitOrdersWarnings/index.tsx b/src/modules/limitOrders/containers/LimitOrdersWarnings/index.tsx index 4729b9a17c..8900a57bbb 100644 --- a/src/modules/limitOrders/containers/LimitOrdersWarnings/index.tsx +++ b/src/modules/limitOrders/containers/LimitOrdersWarnings/index.tsx @@ -22,6 +22,7 @@ import { TradeFormValidation, useGetTradeFormValidation } from 'modules/tradeFor import { useTradeQuote } from 'modules/tradeQuote' import { useIsSafeViaWc, useWalletInfo } from 'modules/wallet' +import { HIGH_FEE_WARNING_PERCENTAGE } from 'common/constants/common' import { useShouldZeroApprove } from 'common/hooks/useShouldZeroApprove' import { BundleTxApprovalBanner, @@ -29,7 +30,6 @@ import { SmallVolumeWarningBanner, } from 'common/pure/InlineBanner/banners' import { ZeroApprovalWarning } from 'common/pure/ZeroApprovalWarning' -import { HIGH_FEE_WARNING_PERCENTAGE } from 'constants/common' import { isFractionFalsy } from 'utils/isFractionFalsy' import { calculatePercentageInRelationToReference } from 'utils/orderUtils/calculatePercentageInRelationToReference' diff --git a/src/modules/limitOrders/containers/RateInput/index.tsx b/src/modules/limitOrders/containers/RateInput/index.tsx index 9d0167b955..4689c0043d 100644 --- a/src/modules/limitOrders/containers/RateInput/index.tsx +++ b/src/modules/limitOrders/containers/RateInput/index.tsx @@ -16,10 +16,10 @@ import { limitRateAtom, updateLimitRateAtom } from 'modules/limitOrders/state/li import { toFraction } from 'modules/limitOrders/utils/toFraction' import { useWalletInfo } from 'modules/wallet' +import { ordersTableFeatures } from 'common/constants/featureFlags' import { ExecutionPrice } from 'common/pure/ExecutionPrice' import { TokenSymbol } from 'common/pure/TokenSymbol' import { getQuoteCurrency, getQuoteCurrencyByStableCoin } from 'common/services/getQuoteCurrency' -import { ordersTableFeatures } from 'constants/featureFlags' import { formatInputAmount } from 'utils/amountFormat' import { getAddress } from 'utils/getAddress' import { isFractionFalsy } from 'utils/isFractionFalsy' diff --git a/src/modules/limitOrders/pure/LimitOrdersDetails/index.tsx b/src/modules/limitOrders/pure/LimitOrdersDetails/index.tsx index 02ad06168f..b08b94b6ef 100644 --- a/src/modules/limitOrders/pure/LimitOrdersDetails/index.tsx +++ b/src/modules/limitOrders/pure/LimitOrdersDetails/index.tsx @@ -18,10 +18,10 @@ import { LimitRateState } from 'modules/limitOrders/state/limitRateAtom' import { PartiallyFillableOverrideDispatcherType } from 'modules/limitOrders/state/partiallyFillableOverride' import { calculateLimitOrdersDeadline } from 'modules/limitOrders/utils/calculateLimitOrdersDeadline' +import { ordersTableFeatures } from 'common/constants/featureFlags' +import { DEFAULT_DATE_FORMAT } from 'common/constants/intl' import { ExecutionPrice } from 'common/pure/ExecutionPrice' import { RateInfoParams } from 'common/pure/RateInfo' -import { ordersTableFeatures } from 'constants/featureFlags' -import { DEFAULT_DATE_FORMAT } from 'constants/intl' import { formatInputAmount } from 'utils/amountFormat' import * as styledEl from './styled' diff --git a/src/modules/mainMenu/constants/mainMenu.ts b/src/modules/mainMenu/constants/mainMenu.ts index 96756960b6..3d4b8ad83d 100644 --- a/src/modules/mainMenu/constants/mainMenu.ts +++ b/src/modules/mainMenu/constants/mainMenu.ts @@ -11,7 +11,7 @@ import IMAGE_TERMS_AND_CONDITIONS from 'legacy/assets/cow-swap/terms-and-conditi import IMAGE_TWITTER from 'legacy/assets/cow-swap/twitter.svg' import { CONTRACTS_CODE_LINK, DISCORD_LINK, DOCS_LINK, DUNE_DASHBOARD_LINK, TWITTER_LINK } from 'legacy/constants' -import { Routes } from 'constants/routes' +import { Routes } from 'common/constants/routes' import { BasicMenuLink, InternalLink, MainMenuItemId, MenuItemKind, MenuTreeItem } from '../types' diff --git a/src/modules/mainMenu/pure/MenuTree/index.tsx b/src/modules/mainMenu/pure/MenuTree/index.tsx index 9eb31e6a06..e264d44cd8 100644 --- a/src/modules/mainMenu/pure/MenuTree/index.tsx +++ b/src/modules/mainMenu/pure/MenuTree/index.tsx @@ -19,7 +19,7 @@ import { } from 'modules/mainMenu/types' import { parameterizeTradeRoute } from 'modules/trade/utils/parameterizeTradeRoute' -import { Routes } from 'constants/routes' +import { Routes } from 'common/constants/routes' // Assets diff --git a/src/modules/mainMenu/types.ts b/src/modules/mainMenu/types.ts index 918f44af74..156145fb70 100644 --- a/src/modules/mainMenu/types.ts +++ b/src/modules/mainMenu/types.ts @@ -1,6 +1,6 @@ import { TradeUrlParams } from 'modules/trade/types/TradeRawState' -import { Routes } from 'constants/routes' +import { Routes } from 'common/constants/routes' export enum MenuItemKind { DROP_DOWN = 'DROP_DOWN', diff --git a/src/modules/mainMenu/utils.ts b/src/modules/mainMenu/utils.ts index 7835aa5120..a0eba0c536 100644 --- a/src/modules/mainMenu/utils.ts +++ b/src/modules/mainMenu/utils.ts @@ -3,7 +3,7 @@ import cloneDeep from 'clone-deep' import { MAIN_MENU } from './constants/mainMenu' import { DropDownItem, MainMenuItemId, MenuItemKind, MenuTreeItem } from './types' -import { Routes } from '../../constants/routes' +import { Routes } from '../../common/constants/routes' const ADVANCED_ORDERS_MENU_TITLE = 'Advanced orders' diff --git a/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/EstimatedExecutionPrice.tsx b/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/EstimatedExecutionPrice.tsx index b0f063df51..01d725a782 100644 --- a/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/EstimatedExecutionPrice.tsx +++ b/src/modules/ordersTable/pure/OrdersTableContainer/OrderRow/EstimatedExecutionPrice.tsx @@ -10,9 +10,9 @@ import AlertTriangle from 'legacy/assets/cow-swap/alert.svg' import { MouseoverTooltipContent } from 'legacy/components/Tooltip' import { ZERO_FRACTION } from 'legacy/constants' +import { HIGH_FEE_WARNING_PERCENTAGE } from 'common/constants/common' import { calculateOrderExecutionStatus, ExecuteIndicator } from 'common/pure/OrderExecutionStatusList' import { SymbolElement, TokenAmount, TokenAmountProps } from 'common/pure/TokenAmount' -import { HIGH_FEE_WARNING_PERCENTAGE } from 'constants/common' import * as styledEl from './styled' diff --git a/src/modules/ordersTable/pure/OrdersTableContainer/OrdersTable.tsx b/src/modules/ordersTable/pure/OrdersTableContainer/OrdersTable.tsx index fca133bd5b..2f2e176587 100644 --- a/src/modules/ordersTable/pure/OrdersTableContainer/OrdersTable.tsx +++ b/src/modules/ordersTable/pure/OrdersTableContainer/OrdersTable.tsx @@ -25,11 +25,11 @@ import { import { LimitOrderActions } from 'modules/ordersTable/pure/OrdersTableContainer/types' import { BalancesAndAllowances } from 'modules/tokens' +import { ordersTableFeatures } from 'common/constants/featureFlags' import { OrderExecutionStatusList, RateTooltipHeader } from 'common/pure/OrderExecutionStatusList' import { InvertRateControl } from 'common/pure/RateInfo' import { CancellableOrder } from 'common/utils/isOrderCancellable' import { isOrderOffChainCancellable } from 'common/utils/isOrderOffChainCancellable' -import { ordersTableFeatures } from 'constants/featureFlags' import { ordersSorter } from 'utils/orderUtils/ordersSorter' import { ParsedOrder } from 'utils/orderUtils/parseOrder' diff --git a/src/modules/trade/hooks/useTradeTypeInfo.ts b/src/modules/trade/hooks/useTradeTypeInfo.ts index 766e99dff8..f921e48661 100644 --- a/src/modules/trade/hooks/useTradeTypeInfo.ts +++ b/src/modules/trade/hooks/useTradeTypeInfo.ts @@ -3,7 +3,7 @@ import { useMemo } from 'react' import { PathMatch } from '@remix-run/router' import { useMatch } from 'react-router-dom' -import { Routes } from 'constants/routes' +import { Routes } from 'common/constants/routes' export enum TradeType { SWAP, diff --git a/src/modules/trade/utils/parameterizeTradeRoute.ts b/src/modules/trade/utils/parameterizeTradeRoute.ts index 13d6786e32..c9fda931ba 100644 --- a/src/modules/trade/utils/parameterizeTradeRoute.ts +++ b/src/modules/trade/utils/parameterizeTradeRoute.ts @@ -1,6 +1,6 @@ import { TradeUrlParams } from 'modules/trade/types/TradeRawState' -import { Routes } from 'constants/routes' +import { Routes } from 'common/constants/routes' /** * When input currency is not set and user select output currency, we build a link like: diff --git a/src/modules/wallet/api/pure/WalletModal/index.tsx b/src/modules/wallet/api/pure/WalletModal/index.tsx index 8f92f851c9..a62e7897c3 100644 --- a/src/modules/wallet/api/pure/WalletModal/index.tsx +++ b/src/modules/wallet/api/pure/WalletModal/index.tsx @@ -12,9 +12,9 @@ import { PendingView } from 'modules/wallet/api/pure/PendingView' import { ZengoBanner } from 'modules/wallet/api/pure/ZengoBanner' import { ConnectWalletOptions, TryActivation } from 'modules/wallet/web3-react/connection' +import { Routes } from 'common/constants/routes' import { GpModal } from 'common/pure/Modal' import { HeaderRow, HoverText, CloseIcon, ContentWrapper } from 'common/pure/Modal' -import { Routes } from 'constants/routes' import { CloseColor, OptionGrid, TermsWrapper, UpperSection, Wrapper } from './styled' diff --git a/src/pages/About/index.tsx b/src/pages/About/index.tsx index 56d793adb7..0cca13a89f 100644 --- a/src/pages/About/index.tsx +++ b/src/pages/About/index.tsx @@ -13,7 +13,7 @@ import { ExternalLink as ExternalLinkTheme } from 'legacy/theme' import { PageTitle } from 'modules/application/containers/PageTitle' import { Page, Title, Content, GdocsListStyle } from 'modules/application/pure/Page' -import { Routes } from 'constants/routes' +import { Routes } from 'common/constants/routes' const ExternalLink = styled(ExternalLinkTheme)`` diff --git a/src/pages/Account/index.tsx b/src/pages/Account/index.tsx index 2c94ca0de0..645d6d2c90 100644 --- a/src/pages/Account/index.tsx +++ b/src/pages/Account/index.tsx @@ -9,7 +9,7 @@ import { Loading } from 'legacy/components/FlashingLoading' import { PageTitle } from 'modules/application/containers/PageTitle' import { Content, Title } from 'modules/application/pure/Page' -import { Routes as RoutesEnum } from 'constants/routes' +import { Routes as RoutesEnum } from 'common/constants/routes' import { AccountMenu } from './Menu' import { Container, CardsWrapper } from './styled' diff --git a/src/pages/AdvancedOrders/index.tsx b/src/pages/AdvancedOrders/index.tsx index a44807628b..6ac7580fdd 100644 --- a/src/pages/AdvancedOrders/index.tsx +++ b/src/pages/AdvancedOrders/index.tsx @@ -8,8 +8,8 @@ import { parameterizeTradeRoute } from 'modules/trade/utils/parameterizeTradeRou import { TradeFormValidationUpdater } from 'modules/tradeFormValidation' import { TwapFormWidget } from 'modules/twap' +import { Routes as RoutesEnum } from 'common/constants/routes' import { useIsAdvancedOrdersEnabled } from 'common/hooks/useIsAdvancedOrdersEnabled' -import { Routes as RoutesEnum } from 'constants/routes' export default function AdvancedOrdersPage() { const isAdvancedOrdersEnabled = useIsAdvancedOrdersEnabled() diff --git a/src/pages/Claim/ClaimingStatus.tsx b/src/pages/Claim/ClaimingStatus.tsx index 75aca4928a..c0a2af03bc 100644 --- a/src/pages/Claim/ClaimingStatus.tsx +++ b/src/pages/Claim/ClaimingStatus.tsx @@ -24,8 +24,8 @@ import { shortenAddress } from 'legacy/utils' import { useWalletInfo } from 'modules/wallet' import AddToMetamask from 'modules/wallet/web3-react/containers/AddToMetamask' +import { Routes } from 'common/constants/routes' import { TokenAmount } from 'common/pure/TokenAmount' -import { Routes } from 'constants/routes' import { ConfirmOrLoadingWrapper, ConfirmedIcon, diff --git a/src/pages/Faq/AffiliateFaq.tsx b/src/pages/Faq/AffiliateFaq.tsx index 48fc3983b9..e184ace30c 100644 --- a/src/pages/Faq/AffiliateFaq.tsx +++ b/src/pages/Faq/AffiliateFaq.tsx @@ -8,7 +8,7 @@ import { BARN_URL, PRODUCTION_URL } from 'legacy/constants' import { PageTitle } from 'modules/application/containers/PageTitle' import { Page, Content } from 'modules/application/pure/Page' -import { Routes } from 'constants/routes' +import { Routes } from 'common/constants/routes' import { useToC } from './hooks' import { FaqMenu } from './Menu' diff --git a/src/pages/Faq/TokenFaq.tsx b/src/pages/Faq/TokenFaq.tsx index 3483d33a59..9635586998 100644 --- a/src/pages/Faq/TokenFaq.tsx +++ b/src/pages/Faq/TokenFaq.tsx @@ -6,7 +6,7 @@ import { Trace } from 'legacy/components/AmplitudeAnalytics/Trace' import { PageTitle } from 'modules/application/containers/PageTitle' import { Page, Content } from 'modules/application/pure/Page' -import { Routes } from 'constants/routes' +import { Routes } from 'common/constants/routes' import { useToC } from './hooks' import { FaqMenu } from './Menu' diff --git a/src/pages/Faq/index.tsx b/src/pages/Faq/index.tsx index 6d6b3f5da5..28636aeda1 100644 --- a/src/pages/Faq/index.tsx +++ b/src/pages/Faq/index.tsx @@ -16,7 +16,7 @@ import { StyledInternalLink } from 'legacy/theme' import { PageTitle } from 'modules/application/containers/PageTitle' import { Page, Content } from 'modules/application/pure/Page' -import { Routes } from 'constants/routes' +import { Routes } from 'common/constants/routes' import { useToC } from './hooks' import { FaqMenu } from './Menu' diff --git a/src/pages/KitchenSink/index.tsx b/src/pages/KitchenSink/index.tsx new file mode 100644 index 0000000000..d5ffd16513 --- /dev/null +++ b/src/pages/KitchenSink/index.tsx @@ -0,0 +1,18 @@ +import { PinkTitle } from '@cowprotocol/ui' + +import styled from 'styled-components/macro' + +import { Page, Title, Content } from 'modules/application/pure/Page' + +const Wrapper = styled(Page)`` + +export default function KitchenSink() { + return ( + + Kitchen Sink + + pink title + + + ) +} diff --git a/src/pages/Swap/index.tsx b/src/pages/Swap/index.tsx index 4849f45eb7..8b546caf99 100644 --- a/src/pages/Swap/index.tsx +++ b/src/pages/Swap/index.tsx @@ -9,7 +9,7 @@ import { getDefaultTradeRawState } from 'modules/trade/types/TradeRawState' import { parameterizeTradeRoute } from 'modules/trade/utils/parameterizeTradeRoute' import { useWalletInfo } from 'modules/wallet' -import { Routes } from 'constants/routes' +import { Routes } from 'common/constants/routes' export function SwapPage() { const params = useParams() diff --git a/src/utils/amountFormat/index.ts b/src/utils/amountFormat/index.ts index 519efe0e84..f9b1921f3f 100644 --- a/src/utils/amountFormat/index.ts +++ b/src/utils/amountFormat/index.ts @@ -6,7 +6,7 @@ import { FractionLike, Nullish } from 'types' import { AMOUNT_PRECISION, FIAT_PRECISION, PERCENTAGE_PRECISION, ZERO_FRACTION } from 'legacy/constants' import { maxAmountSpend } from 'legacy/utils/maxAmountSpend' -import { INTL_NUMBER_FORMAT } from 'constants/intl' +import { INTL_NUMBER_FORMAT } from 'common/constants/intl' import { FractionUtils } from 'utils/fractionUtils' import { trimTrailingZeros } from 'utils/trimTrailingZeros' diff --git a/tools/scripts/publish.mjs b/tools/scripts/publish.mjs new file mode 100644 index 0000000000..e82a097be2 --- /dev/null +++ b/tools/scripts/publish.mjs @@ -0,0 +1,58 @@ +/** + * This is a minimal script to publish your package to "npm". + * This is meant to be used as-is or customize as you see fit. + * + * This script is executed on "dist/path/to/library" as "cwd" by default. + * + * You might need to authenticate with NPM before running this script. + */ + +import { execSync } from 'child_process' +import { readFileSync, writeFileSync } from 'fs' +import chalk from 'chalk' + +import devkit from '@nx/devkit' +const { readCachedProjectGraph } = devkit + +function invariant(condition, message) { + if (!condition) { + console.error(chalk.bold.red(message)) + process.exit(1) + } +} + +// Executing publish script: node path/to/publish.mjs {name} --version {version} --tag {tag} +// Default "tag" to "next" so we won't publish the "latest" tag by accident. +const [, , name, version, tag = 'next'] = process.argv + +// A simple SemVer validation to validate the version +const validVersion = /^\d+\.\d+\.\d+(-\w+\.\d+)?/ +invariant( + version && validVersion.test(version), + `No version provided or version did not match Semantic Versioning, expected: #.#.#-tag.# or #.#.#, got ${version}.` +) + +const graph = readCachedProjectGraph() +const project = graph.nodes[name] + +invariant(project, `Could not find project "${name}" in the workspace. Is the project.json configured correctly?`) + +const outputPath = project.data?.targets?.build?.options?.outputPath +invariant( + outputPath, + `Could not find "build.options.outputPath" of project "${name}". Is project.json configured correctly?` +) + +process.chdir(outputPath) + +// Updating the version in "package.json" before publishing +try { + const json = JSON.parse(readFileSync(`package.json`).toString()) + json.version = version + writeFileSync(`package.json`, JSON.stringify(json, null, 2)) +} catch (e) { + console.error(chalk.bold.red(`Error reading package.json file from library build output.`)) +} + +// Execute "npm publish" to publish +execSync(`npm publish --access public --tag ${tag}`) diff --git a/tsconfig.base.json b/tsconfig.base.json index c7dce16414..e341874f5d 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "baseUrl": "src", "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, @@ -21,7 +22,11 @@ "isolatedModules": true, "downlevelIteration": true, "allowSyntheticDefaultImports": true, - "types": ["react-spring", "jest"] + "types": ["react-spring", "jest"], + "paths": { + "@cowprotocol/ui": ["libs/ui/src/index.ts"], + "@cowprotocol/ui-utils": ["libs/ui-utils/src/index.ts"] + } }, "exclude": ["node_modules", "cypress"] } diff --git a/tsconfig.json b/tsconfig.json index f58c75d333..92d39058f7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,11 +5,6 @@ "noEmit": true, "baseUrl": "src" }, - "exclude": [ - "node_modules", - "cypress", - ], - "include": [ - "src/**/*" - ] + "exclude": ["node_modules", "cypress"], + "include": ["src/**/*"] } diff --git a/tsconfig.spec.json b/tsconfig.spec.json new file mode 100644 index 0000000000..cd2c387006 --- /dev/null +++ b/tsconfig.spec.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] +} diff --git a/yarn.lock b/yarn.lock index 15e35441f6..e5a26e0dc8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -61,6 +61,14 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + "@apideck/better-ajv-errors@^0.3.1": version "0.3.6" resolved "https://registry.yarnpkg.com/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz#957d4c28e886a64a8141f7522783be65733ff097" @@ -82,11 +90,23 @@ dependencies: "@babel/highlight" "^7.18.6" +"@babel/code-frame@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" + integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== + dependencies: + "@babel/highlight" "^7.22.5" + "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5": version "7.20.10" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.10.tgz#9d92fa81b87542fff50e848ed585b4212c1d34ec" integrity sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg== +"@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/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.7.2", "@babel/core@^7.8.0": version "7.20.12" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d" @@ -108,6 +128,27 @@ json5 "^2.2.2" semver "^6.3.0" +"@babel/core@^7.11.6", "@babel/core@^7.15.0", "@babel/core@^7.20.12", "@babel/core@^7.21.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.5.tgz#d67d9747ecf26ee7ecd3ebae1ee22225fe902a89" + integrity sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg== + 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/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@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" + "@babel/eslint-parser@^7.16.3": version "7.19.1" resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.19.1.tgz#4f68f6b0825489e00a24b41b6a1ae35414ecd2f4" @@ -126,6 +167,16 @@ "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" +"@babel/generator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.5.tgz#1e7bf768688acfb05cf30b2369ef855e82d984f7" + integrity sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA== + dependencies: + "@babel/types" "^7.22.5" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.16.0", "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -133,6 +184,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-annotate-as-pure@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" + integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" @@ -141,6 +199,13 @@ "@babel/helper-explode-assignable-expression" "^7.18.6" "@babel/types" "^7.18.9" +"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz#a3f4758efdd0190d8927fcffd261755937c71878" + integrity sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.0", "@babel/helper-compilation-targets@^7.20.7": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" @@ -152,6 +217,17 @@ lru-cache "^5.1.1" semver "^6.3.0" +"@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== + dependencies: + "@babel/compat-data" "^7.22.5" + "@babel/helper-validator-option" "^7.22.5" + browserslist "^4.21.3" + lru-cache "^5.1.1" + semver "^6.3.0" + "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.5", "@babel/helper-create-class-features-plugin@^7.20.7": version "7.20.12" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz#4349b928e79be05ed2d1643b20b99bb87c503819" @@ -166,6 +242,21 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/helper-split-export-declaration" "^7.18.6" +"@babel/helper-create-class-features-plugin@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.5.tgz#2192a1970ece4685fbff85b48da2c32fcb130b7c" + integrity sha512-xkb58MyOYIslxu3gKmVXmjTtUPvBU4odYzbiIQbWwLKIHCsx6UGZGX6F1IznMFVnDdirseUZopzN+ZRt8Xb33Q== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.5" + semver "^6.3.0" + "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.20.5": version "7.20.5" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz#5ea79b59962a09ec2acf20a963a01ab4d076ccca" @@ -174,6 +265,15 @@ "@babel/helper-annotate-as-pure" "^7.18.6" regexpu-core "^5.2.1" +"@babel/helper-create-regexp-features-plugin@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.5.tgz#bb2bf0debfe39b831986a4efbf4066586819c6e4" + integrity sha512-1VpEFOIbMRaXyDeUwUfmTIxExLwQ+zkW+Bh5zXpApA3oQedBx9v/updixWxnx/bZpKw7u8VxWjb/qWpIcmPq8A== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + regexpu-core "^5.3.1" + semver "^6.3.0" + "@babel/helper-define-polyfill-provider@^0.3.3": version "0.3.3" resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a" @@ -186,11 +286,28 @@ 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== + dependencies: + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-plugin-utils" "^7.16.7" + 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" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== +"@babel/helper-environment-visitor@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" + integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== + "@babel/helper-explode-assignable-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" @@ -206,6 +323,14 @@ "@babel/template" "^7.18.10" "@babel/types" "^7.19.0" +"@babel/helper-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" + integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== + dependencies: + "@babel/template" "^7.22.5" + "@babel/types" "^7.22.5" + "@babel/helper-hoist-variables@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" @@ -213,6 +338,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-member-expression-to-functions@^7.20.7": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz#a6f26e919582275a93c3aa6594756d71b0bb7f05" @@ -220,6 +352,13 @@ dependencies: "@babel/types" "^7.20.7" +"@babel/helper-member-expression-to-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz#0a7c56117cad3372fbf8d2fb4bf8f8d64a1e76b2" + integrity sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.16.0", "@babel/helper-module-imports@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" @@ -227,6 +366,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-module-imports@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" + integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11": version "7.20.11" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz#df4c7af713c557938c50ea3ad0117a7944b2f1b0" @@ -241,6 +387,20 @@ "@babel/traverse" "^7.20.10" "@babel/types" "^7.20.7" +"@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== + 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-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.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" @@ -248,11 +408,23 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-optimise-call-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" + integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": version "7.20.2" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== +"@babel/helper-plugin-utils@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + "@babel/helper-remap-async-to-generator@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" @@ -263,6 +435,16 @@ "@babel/helper-wrap-function" "^7.18.9" "@babel/types" "^7.18.9" +"@babel/helper-remap-async-to-generator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.5.tgz#14a38141a7bf2165ad38da61d61cf27b43015da2" + integrity sha512-cU0Sq1Rf4Z55fgz7haOakIyM7+x/uCFwXpLPaeRzfoUtAEAuUZjZvFPjL/rk5rW693dIgn2hng1W7xbT7lWT4g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-wrap-function" "^7.22.5" + "@babel/types" "^7.22.5" + "@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz#243ecd2724d2071532b2c8ad2f0f9f083bcae331" @@ -275,6 +457,18 @@ "@babel/traverse" "^7.20.7" "@babel/types" "^7.20.7" +"@babel/helper-replace-supers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.5.tgz#71bc5fb348856dea9fdc4eafd7e2e49f585145dc" + integrity sha512-aLdNM5I3kdI/V9xGNyKSF3X/gTyMUBohTZ+/3QdQKAA9vxIiy12E+8E2HoOP1/DjeqU+g6as35QHJNMDDYpuCg== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" + "@babel/helper-simple-access@^7.20.2": version "7.20.2" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" @@ -282,6 +476,13 @@ dependencies: "@babel/types" "^7.20.2" +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers@^7.20.0": version "7.20.0" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" @@ -289,6 +490,13 @@ dependencies: "@babel/types" "^7.20.0" +"@babel/helper-skip-transparent-expression-wrappers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" + integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-split-export-declaration@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" @@ -296,21 +504,43 @@ 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== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-string-parser@^7.19.4": version "7.19.4" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": version "7.19.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== +"@babel/helper-validator-identifier@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" + integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== + "@babel/helper-validator-option@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== +"@babel/helper-validator-option@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" + integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== + "@babel/helper-wrap-function@^7.18.9": version "7.20.5" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3" @@ -321,6 +551,16 @@ "@babel/traverse" "^7.20.5" "@babel/types" "^7.20.5" +"@babel/helper-wrap-function@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.5.tgz#44d205af19ed8d872b4eefb0d2fa65f45eb34f06" + integrity sha512-bYqLIBSEshYcYQyfks8ewYA8S30yaGSeRslcvKMvoUk6HHPySbxHq9YRi6ghhzEU+yhQv9bP/jXnygkStOcqZw== + dependencies: + "@babel/helper-function-name" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" + "@babel/helpers@^7.20.7": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.7.tgz#04502ff0feecc9f20ecfaad120a18f011a8e6dce" @@ -330,6 +570,15 @@ "@babel/traverse" "^7.20.7" "@babel/types" "^7.20.7" +"@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== + dependencies: + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" + "@babel/highlight@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" @@ -339,11 +588,25 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" + integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== + dependencies: + "@babel/helper-validator-identifier" "^7.22.5" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.11.5", "@babel/parser@^7.14.4", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.7.tgz#66fe23b3c8569220817d5feb8b9dcdc95bb4f71b" integrity sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg== +"@babel/parser@^7.22.5": + 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/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" @@ -351,6 +614,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz#87245a21cd69a73b0b81bcda98d443d6df08f05e" + integrity sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.9": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz#d9c85589258539a22a901033853101a6198d4ef1" @@ -360,6 +630,15 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/plugin-proposal-optional-chaining" "^7.20.7" +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz#fef09f9499b1f1c930da8a0c419db42167d792ca" + integrity sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.22.5" + "@babel/plugin-proposal-async-generator-functions@^7.20.1": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" @@ -370,7 +649,7 @@ "@babel/helper-remap-async-to-generator" "^7.18.9" "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-proposal-class-properties@^7.16.0", "@babel/plugin-proposal-class-properties@^7.18.6": +"@babel/plugin-proposal-class-properties@^7.14.5", "@babel/plugin-proposal-class-properties@^7.16.0", "@babel/plugin-proposal-class-properties@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== @@ -387,6 +666,17 @@ "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-class-static-block" "^7.14.5" +"@babel/plugin-proposal-decorators@^7.14.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.22.5.tgz#dc8cdda048e5aea947efda920e030199806b868d" + integrity sha512-h8hlezQ4dl6ixodgXkH8lUfcD7x+WAuIqPUjwGoItynrXOAv4a4Tci1zA/qjzQjjcl0v3QpLdc2LM6ZACQuY7A== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.5" + "@babel/plugin-syntax-decorators" "^7.22.5" + "@babel/plugin-proposal-decorators@^7.16.4": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.20.7.tgz#05d37453c2ce818f3e47bbeda9468c8de947eecc" @@ -482,6 +772,11 @@ "@babel/helper-create-class-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": + version "7.21.0-placeholder-for-preset-env.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" + integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== + "@babel/plugin-proposal-private-property-in-object@^7.18.6": version "7.20.5" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz#309c7668f2263f1c711aa399b5a9a6291eef6135" @@ -535,6 +830,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.19.0" +"@babel/plugin-syntax-decorators@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.22.5.tgz#329fe2907c73de184033775637dbbc507f09116a" + integrity sha512-avpUOBS7IU6al8MmF1XpAyj9QYeLPuSDJI5D4pVMSMdL7xQokKqJPYQC67RCT0aCTashUXPiGwMJ0DEXXCEmMA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-dynamic-import@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" @@ -563,7 +865,21 @@ dependencies: "@babel/helper-plugin-utils" "^7.19.0" -"@babel/plugin-syntax-import-meta@^7.8.3": +"@babel/plugin-syntax-import-assertions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz#07d252e2aa0bc6125567f742cd58619cb14dce98" + integrity sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-import-attributes@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz#ab840248d834410b829f569f5262b9e517555ecb" + integrity sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== @@ -584,6 +900,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-syntax-jsx@^7.22.5", "@babel/plugin-syntax-jsx@^7.7.2": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz#a6b68e84fb76e759fc3b93e901876ffabbe1d918" + integrity sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -647,6 +970,21 @@ dependencies: "@babel/helper-plugin-utils" "^7.19.0" +"@babel/plugin-syntax-typescript@^7.22.5", "@babel/plugin-syntax-typescript@^7.3.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz#aac8d383b062c5072c647a31ef990c1d0af90272" + integrity sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-unicode-sets-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" + integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-transform-arrow-functions@^7.18.6": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz#bea332b0e8b2dab3dafe55a163d8227531ab0551" @@ -654,6 +992,23 @@ dependencies: "@babel/helper-plugin-utils" "^7.20.2" +"@babel/plugin-transform-arrow-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz#e5ba566d0c58a5b2ba2a8b795450641950b71958" + integrity sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-async-generator-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.5.tgz#7336356d23380eda9a56314974f053a020dab0c3" + integrity sha512-gGOEvFzm3fWoyD5uZq7vVTD57pPJ3PczPUD/xCFGjzBpUosnklmXyKnGQbbbGs1NPNPskFex0j93yKbHt0cHyg== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.5" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-transform-async-to-generator@^7.18.6": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" @@ -663,6 +1018,15 @@ "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-remap-async-to-generator" "^7.18.9" +"@babel/plugin-transform-async-to-generator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz#c7a85f44e46f8952f6d27fe57c2ed3cc084c3775" + integrity sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ== + dependencies: + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.5" + "@babel/plugin-transform-block-scoped-functions@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" @@ -670,6 +1034,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-transform-block-scoped-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz#27978075bfaeb9fa586d3cb63a3d30c1de580024" + integrity sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-block-scoping@^7.20.2": version "7.20.11" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.11.tgz#9f5a3424bd112a3f32fe0cf9364fbb155cff262a" @@ -677,6 +1048,30 @@ dependencies: "@babel/helper-plugin-utils" "^7.20.2" +"@babel/plugin-transform-block-scoping@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz#8bfc793b3a4b2742c0983fadc1480d843ecea31b" + integrity sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-class-properties@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz#97a56e31ad8c9dc06a0b3710ce7803d5a48cca77" + integrity sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-class-static-block@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz#3e40c46f048403472d6f4183116d5e46b1bff5ba" + integrity sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-transform-classes@^7.20.2": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz#f438216f094f6bb31dc266ebfab8ff05aecad073" @@ -692,6 +1087,21 @@ "@babel/helper-split-export-declaration" "^7.18.6" globals "^11.1.0" +"@babel/plugin-transform-classes@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.5.tgz#635d4e98da741fad814984639f4c0149eb0135e1" + integrity sha512-2edQhLfibpWpsVBx2n/GKOz6JdGQvLruZQfGr9l1qes2KQaWswjBzhQF7UDUZMNaMMQeYnQzxwOMPsbYF7wqPQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.5" + globals "^11.1.0" + "@babel/plugin-transform-computed-properties@^7.18.9": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz#704cc2fd155d1c996551db8276d55b9d46e4d0aa" @@ -700,6 +1110,14 @@ "@babel/helper-plugin-utils" "^7.20.2" "@babel/template" "^7.20.7" +"@babel/plugin-transform-computed-properties@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz#cd1e994bf9f316bd1c2dafcd02063ec261bb3869" + integrity sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/plugin-transform-destructuring@^7.20.2": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz#8bda578f71620c7de7c93af590154ba331415454" @@ -707,6 +1125,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.20.2" +"@babel/plugin-transform-destructuring@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz#d3aca7438f6c26c78cdd0b0ba920a336001b27cc" + integrity sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" @@ -715,6 +1140,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-transform-dotall-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz#dbb4f0e45766eb544e193fb00e65a1dd3b2a4165" + integrity sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-duplicate-keys@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" @@ -722,6 +1155,21 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.9" +"@babel/plugin-transform-duplicate-keys@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz#b6e6428d9416f5f0bba19c70d1e6e7e0b88ab285" + integrity sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-dynamic-import@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz#d6908a8916a810468c4edff73b5b75bda6ad393e" + integrity sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-transform-exponentiation-operator@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" @@ -730,6 +1178,22 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-transform-exponentiation-operator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz#402432ad544a1f9a480da865fda26be653e48f6a" + integrity sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-export-namespace-from@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz#57c41cb1d0613d22f548fddd8b288eedb9973a5b" + integrity sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-transform-flow-strip-types@^7.16.0": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.19.0.tgz#e9e8606633287488216028719638cbbb2f2dde8f" @@ -745,6 +1209,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-transform-for-of@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz#ab1b8a200a8f990137aff9a084f8de4099ab173f" + integrity sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-function-name@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" @@ -754,6 +1225,23 @@ "@babel/helper-function-name" "^7.18.9" "@babel/helper-plugin-utils" "^7.18.9" +"@babel/plugin-transform-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz#935189af68b01898e0d6d99658db6b164205c143" + integrity sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg== + dependencies: + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-json-strings@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz#14b64352fdf7e1f737eed68de1a1468bd2a77ec0" + integrity sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-transform-literals@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" @@ -761,6 +1249,21 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.9" +"@babel/plugin-transform-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz#e9341f4b5a167952576e23db8d435849b1dd7920" + integrity sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-logical-assignment-operators@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz#66ae5f068fd5a9a5dc570df16f56c2a8462a9d6c" + integrity sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-transform-member-expression-literals@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" @@ -768,6 +1271,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-transform-member-expression-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz#4fcc9050eded981a468347dd374539ed3e058def" + integrity sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-modules-amd@^7.19.6": version "7.20.11" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz#3daccca8e4cc309f03c3a0c4b41dc4b26f55214a" @@ -776,6 +1286,14 @@ "@babel/helper-module-transforms" "^7.20.11" "@babel/helper-plugin-utils" "^7.20.2" +"@babel/plugin-transform-modules-amd@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz#4e045f55dcf98afd00f85691a68fc0780704f526" + integrity sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ== + dependencies: + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-modules-commonjs@^7.19.6": version "7.20.11" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz#8cb23010869bf7669fd4b3098598b6b2be6dc607" @@ -785,6 +1303,15 @@ "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-simple-access" "^7.20.2" +"@babel/plugin-transform-modules-commonjs@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz#7d9875908d19b8c0536085af7b053fd5bd651bfa" + integrity sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA== + dependencies: + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + "@babel/plugin-transform-modules-systemjs@^7.19.6": version "7.20.11" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz#467ec6bba6b6a50634eea61c9c232654d8a4696e" @@ -795,6 +1322,16 @@ "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-validator-identifier" "^7.19.1" +"@babel/plugin-transform-modules-systemjs@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz#18c31410b5e579a0092638f95c896c2a98a5d496" + integrity sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ== + dependencies: + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" + "@babel/plugin-transform-modules-umd@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" @@ -803,6 +1340,14 @@ "@babel/helper-module-transforms" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-transform-modules-umd@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz#4694ae40a87b1745e3775b6a7fe96400315d4f98" + integrity sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ== + dependencies: + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-named-capturing-groups-regex@^7.19.1": version "7.20.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8" @@ -811,6 +1356,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.20.5" "@babel/helper-plugin-utils" "^7.20.2" +"@babel/plugin-transform-named-capturing-groups-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz#67fe18ee8ce02d57c855185e27e3dc959b2e991f" + integrity sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-new-target@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" @@ -818,6 +1371,40 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-transform-new-target@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz#1b248acea54ce44ea06dfd37247ba089fcf9758d" + integrity sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-nullish-coalescing-operator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz#f8872c65776e0b552e0849d7596cddd416c3e381" + integrity sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-transform-numeric-separator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz#57226a2ed9e512b9b446517ab6fa2d17abb83f58" + integrity sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-transform-object-rest-spread@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz#9686dc3447df4753b0b2a2fae7e8bc33cdc1f2e1" + integrity sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ== + dependencies: + "@babel/compat-data" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.22.5" + "@babel/plugin-transform-object-super@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" @@ -826,6 +1413,31 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/helper-replace-supers" "^7.18.6" +"@babel/plugin-transform-object-super@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz#794a8d2fcb5d0835af722173c1a9d704f44e218c" + integrity sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" + +"@babel/plugin-transform-optional-catch-binding@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz#842080be3076703be0eaf32ead6ac8174edee333" + integrity sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-transform-optional-chaining@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.5.tgz#1003762b9c14295501beb41be72426736bedd1e0" + integrity sha512-AconbMKOMkyG+xCng2JogMCDcqW8wedQAqpVIL4cOSescZ7+iW8utC6YDZLMCSUIReEA733gzRSaOSXMAt/4WQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-transform-parameters@^7.20.1", "@babel/plugin-transform-parameters@^7.20.7": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz#0ee349e9d1bc96e78e3b37a7af423a4078a7083f" @@ -833,6 +1445,31 @@ dependencies: "@babel/helper-plugin-utils" "^7.20.2" +"@babel/plugin-transform-parameters@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz#c3542dd3c39b42c8069936e48717a8d179d63a18" + integrity sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-private-methods@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz#21c8af791f76674420a147ae62e9935d790f8722" + integrity sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-private-property-in-object@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz#07a77f28cbb251546a43d175a1dda4cf3ef83e32" + integrity sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-transform-property-literals@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" @@ -840,6 +1477,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-transform-property-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz#b5ddabd73a4f7f26cd0e20f5db48290b88732766" + integrity sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-react-constant-elements@^7.12.1": version "7.20.2" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.20.2.tgz#3f02c784e0b711970d7d8ccc96c4359d64e27ac7" @@ -847,6 +1491,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.20.2" +"@babel/plugin-transform-react-constant-elements@^7.21.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.22.5.tgz#6dfa7c1c37f7d7279e417ceddf5a04abb8bb9c29" + integrity sha512-BF5SXoO+nX3h5OhlN78XbbDrBOffv+AxPP2ENaJOVqjWCgBDeOY3WcaUcddutGSfoap+5NEQ/q/4I3WZIvgkXA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-react-display-name@^7.16.0", "@babel/plugin-transform-react-display-name@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz#8b1125f919ef36ebdfff061d664e266c666b9415" @@ -854,6 +1505,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-transform-react-display-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.22.5.tgz#3c4326f9fce31c7968d6cb9debcaf32d9e279a2b" + integrity sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-react-jsx-development@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz#dbe5c972811e49c7405b630e4d0d2e1380c0ddc5" @@ -861,6 +1519,27 @@ dependencies: "@babel/plugin-transform-react-jsx" "^7.18.6" +"@babel/plugin-transform-react-jsx-development@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz#e716b6edbef972a92165cd69d92f1255f7e73e87" + integrity sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.22.5" + +"@babel/plugin-transform-react-jsx-self@^7.18.6": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.22.5.tgz#ca2fdc11bc20d4d46de01137318b13d04e481d8e" + integrity sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-react-jsx-source@^7.19.6": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.22.5.tgz#49af1615bfdf6ed9d3e9e43e425e0b2b65d15b6c" + integrity sha512-yIiRO6yobeEIaI0RTbIr8iAK9FcBHLtZq0S89ZPjDLQXBA4xvghaKqI0etp/tF3htTM0sazJKKLz9oEiGRtu7w== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-react-jsx@^7.18.6": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.20.7.tgz#025d85a1935fd7e19dfdcb1b1d4df34d4da484f7" @@ -872,6 +1551,17 @@ "@babel/plugin-syntax-jsx" "^7.18.6" "@babel/types" "^7.20.7" +"@babel/plugin-transform-react-jsx@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.5.tgz#932c291eb6dd1153359e2a90cb5e557dcf068416" + integrity sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-jsx" "^7.22.5" + "@babel/types" "^7.22.5" + "@babel/plugin-transform-react-pure-annotations@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz#561af267f19f3e5d59291f9950fd7b9663d0d844" @@ -880,6 +1570,14 @@ "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-transform-react-pure-annotations@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.22.5.tgz#1f58363eef6626d6fa517b95ac66fe94685e32c0" + integrity sha512-gP4k85wx09q+brArVinTXhWiyzLl9UpmGva0+mWyKxk6JZequ05x3eUcIUE+FyttPKJFRRVtAvQaJ6YF9h1ZpA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-regenerator@^7.18.6": version "7.20.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz#57cda588c7ffb7f4f8483cc83bdcea02a907f04d" @@ -888,6 +1586,14 @@ "@babel/helper-plugin-utils" "^7.20.2" regenerator-transform "^0.15.1" +"@babel/plugin-transform-regenerator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz#cd8a68b228a5f75fa01420e8cc2fc400f0fc32aa" + integrity sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + regenerator-transform "^0.15.1" + "@babel/plugin-transform-reserved-words@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz#b1abd8ebf8edaa5f7fe6bbb8d2133d23b6a6f76a" @@ -895,6 +1601,25 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-transform-reserved-words@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz#832cd35b81c287c4bcd09ce03e22199641f964fb" + integrity sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-runtime@^7.15.0": + 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== + 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-transform-runtime@^7.16.4", "@babel/plugin-transform-runtime@^7.5.5": version "7.19.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz#9d2a9dbf4e12644d6f46e5e75bfbf02b5d6e9194" @@ -914,6 +1639,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-transform-shorthand-properties@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz#6e277654be82b5559fc4b9f58088507c24f0c624" + integrity sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-spread@^7.19.0": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" @@ -922,6 +1654,14 @@ "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" +"@babel/plugin-transform-spread@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz#6487fd29f229c95e284ba6c98d65eafb893fea6b" + integrity sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-transform-sticky-regex@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" @@ -929,6 +1669,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-transform-sticky-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz#295aba1595bfc8197abd02eae5fc288c0deb26aa" + integrity sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-template-literals@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" @@ -936,6 +1683,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.9" +"@babel/plugin-transform-template-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz#8f38cf291e5f7a8e60e9f733193f0bcc10909bff" + integrity sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-typeof-symbol@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" @@ -943,6 +1697,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.9" +"@babel/plugin-transform-typeof-symbol@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz#5e2ba478da4b603af8673ff7c54f75a97b716b34" + integrity sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-typescript@^7.18.6": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.7.tgz#673f49499cd810ae32a1ea5f3f8fab370987e055" @@ -952,6 +1713,16 @@ "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-typescript" "^7.20.0" +"@babel/plugin-transform-typescript@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.5.tgz#5c0f7adfc1b5f38c4dbc8f79b1f0f8074134bd7d" + integrity sha512-SMubA9S7Cb5sGSFFUlqxyClTA9zWJ8qGQrppNUm05LtFuN1ELRFNndkix4zUJrC9F+YivWwa1dHMSyo0e0N9dA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-typescript" "^7.22.5" + "@babel/plugin-transform-unicode-escapes@^7.18.10": version "7.18.10" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246" @@ -959,6 +1730,21 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.9" +"@babel/plugin-transform-unicode-escapes@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz#ce0c248522b1cb22c7c992d88301a5ead70e806c" + integrity sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-property-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz#098898f74d5c1e86660dc112057b2d11227f1c81" + integrity sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-unicode-regex@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" @@ -967,6 +1753,22 @@ "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-transform-unicode-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz#ce7e7bb3ef208c4ff67e02a22816656256d7a183" + integrity sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-sets-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz#77788060e511b708ffc7d42fdfbc5b37c3004e91" + integrity sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.1", "@babel/preset-env@^7.16.4": version "7.20.2" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.20.2.tgz#9b1642aa47bb9f43a86f9630011780dab7f86506" @@ -1048,6 +1850,92 @@ core-js-compat "^3.25.1" semver "^6.3.0" +"@babel/preset-env@^7.15.0", "@babel/preset-env@^7.20.2": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.5.tgz#3da66078b181f3d62512c51cf7014392c511504e" + integrity sha512-fj06hw89dpiZzGZtxn+QybifF07nNiZjZ7sazs2aVDcysAZVGjW7+7iFYxg6GLNM47R/thYfLdrXc+2f11Vi9A== + dependencies: + "@babel/compat-data" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.22.5" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.22.5" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.22.5" + "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.22.5" + "@babel/plugin-syntax-import-attributes" "^7.22.5" + "@babel/plugin-syntax-import-meta" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" + "@babel/plugin-transform-arrow-functions" "^7.22.5" + "@babel/plugin-transform-async-generator-functions" "^7.22.5" + "@babel/plugin-transform-async-to-generator" "^7.22.5" + "@babel/plugin-transform-block-scoped-functions" "^7.22.5" + "@babel/plugin-transform-block-scoping" "^7.22.5" + "@babel/plugin-transform-class-properties" "^7.22.5" + "@babel/plugin-transform-class-static-block" "^7.22.5" + "@babel/plugin-transform-classes" "^7.22.5" + "@babel/plugin-transform-computed-properties" "^7.22.5" + "@babel/plugin-transform-destructuring" "^7.22.5" + "@babel/plugin-transform-dotall-regex" "^7.22.5" + "@babel/plugin-transform-duplicate-keys" "^7.22.5" + "@babel/plugin-transform-dynamic-import" "^7.22.5" + "@babel/plugin-transform-exponentiation-operator" "^7.22.5" + "@babel/plugin-transform-export-namespace-from" "^7.22.5" + "@babel/plugin-transform-for-of" "^7.22.5" + "@babel/plugin-transform-function-name" "^7.22.5" + "@babel/plugin-transform-json-strings" "^7.22.5" + "@babel/plugin-transform-literals" "^7.22.5" + "@babel/plugin-transform-logical-assignment-operators" "^7.22.5" + "@babel/plugin-transform-member-expression-literals" "^7.22.5" + "@babel/plugin-transform-modules-amd" "^7.22.5" + "@babel/plugin-transform-modules-commonjs" "^7.22.5" + "@babel/plugin-transform-modules-systemjs" "^7.22.5" + "@babel/plugin-transform-modules-umd" "^7.22.5" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" + "@babel/plugin-transform-new-target" "^7.22.5" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.5" + "@babel/plugin-transform-numeric-separator" "^7.22.5" + "@babel/plugin-transform-object-rest-spread" "^7.22.5" + "@babel/plugin-transform-object-super" "^7.22.5" + "@babel/plugin-transform-optional-catch-binding" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.22.5" + "@babel/plugin-transform-parameters" "^7.22.5" + "@babel/plugin-transform-private-methods" "^7.22.5" + "@babel/plugin-transform-private-property-in-object" "^7.22.5" + "@babel/plugin-transform-property-literals" "^7.22.5" + "@babel/plugin-transform-regenerator" "^7.22.5" + "@babel/plugin-transform-reserved-words" "^7.22.5" + "@babel/plugin-transform-shorthand-properties" "^7.22.5" + "@babel/plugin-transform-spread" "^7.22.5" + "@babel/plugin-transform-sticky-regex" "^7.22.5" + "@babel/plugin-transform-template-literals" "^7.22.5" + "@babel/plugin-transform-typeof-symbol" "^7.22.5" + "@babel/plugin-transform-unicode-escapes" "^7.22.5" + "@babel/plugin-transform-unicode-property-regex" "^7.22.5" + "@babel/plugin-transform-unicode-regex" "^7.22.5" + "@babel/plugin-transform-unicode-sets-regex" "^7.22.5" + "@babel/preset-modules" "^0.1.5" + "@babel/types" "^7.22.5" + babel-plugin-polyfill-corejs2 "^0.4.3" + babel-plugin-polyfill-corejs3 "^0.8.1" + babel-plugin-polyfill-regenerator "^0.5.0" + core-js-compat "^3.30.2" + semver "^6.3.0" + "@babel/preset-modules@^0.1.5": version "0.1.5" resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" @@ -1071,6 +1959,29 @@ "@babel/plugin-transform-react-jsx-development" "^7.18.6" "@babel/plugin-transform-react-pure-annotations" "^7.18.6" +"@babel/preset-react@^7.18.6": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.22.5.tgz#c4d6058fbf80bccad02dd8c313a9aaa67e3c3dd6" + integrity sha512-M+Is3WikOpEJHgR385HbuCITPTaPRaNkibTEa9oiofmJvIsrceb4yp9RL9Kb+TE8LznmeyZqpP+Lopwcx59xPQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.22.5" + "@babel/plugin-transform-react-display-name" "^7.22.5" + "@babel/plugin-transform-react-jsx" "^7.22.5" + "@babel/plugin-transform-react-jsx-development" "^7.22.5" + "@babel/plugin-transform-react-pure-annotations" "^7.22.5" + +"@babel/preset-typescript@^7.15.0", "@babel/preset-typescript@^7.21.0": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.22.5.tgz#16367d8b01d640e9a507577ed4ee54e0101e51c8" + integrity sha512-YbPaal9LxztSGhmndR46FmAbkJ/1fAsw293tSU+I5E5h+cnJ3d4GTwyUgGYmOXJYdGA+uNePle4qbaRzj2NISQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.22.5" + "@babel/plugin-syntax-jsx" "^7.22.5" + "@babel/plugin-transform-modules-commonjs" "^7.22.5" + "@babel/plugin-transform-typescript" "^7.22.5" + "@babel/preset-typescript@^7.16.0": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz#ce64be3e63eddc44240c6358daefac17b3186399" @@ -1080,6 +1991,11 @@ "@babel/helper-validator-option" "^7.18.6" "@babel/plugin-transform-typescript" "^7.18.6" +"@babel/regjsgen@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" + integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== + "@babel/runtime@^7.0.0", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.0", "@babel/runtime@^7.17.2", "@babel/runtime@^7.20.7", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.7.tgz#fcb41a5a70550e04a7b708037c7c32f7f356d8fd" @@ -1087,6 +2003,13 @@ dependencies: regenerator-runtime "^0.13.11" +"@babel/runtime@^7.14.8", "@babel/runtime@^7.15.4": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.5.tgz#8564dd588182ce0047d55d7a75e93921107b57ec" + integrity sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA== + dependencies: + regenerator-runtime "^0.13.11" + "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" @@ -1096,6 +2019,31 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" +"@babel/template@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" + integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/traverse@^7.16.0", "@babel/traverse@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.5.tgz#44bd276690db6f4940fdb84e1cb4abd2f729ccd1" + integrity sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.5" + "@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/types" "^7.22.5" + debug "^4.1.0" + globals "^11.1.0" + "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.2": version "7.20.12" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.12.tgz#7f0f787b3a67ca4475adef1f56cb94f6abd4a4b5" @@ -1121,6 +2069,15 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@babel/types@^7.21.3", "@babel/types@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" + integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" + to-fast-properties "^2.0.0" + "@base2/pretty-print-object@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@base2/pretty-print-object/-/pretty-print-object-1.0.1.tgz#371ba8be66d556812dc7fb169ebc3c08378f69d4" @@ -1654,6 +2611,123 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46" integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== +"@esbuild/android-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz#bafb75234a5d3d1b690e7c2956a599345e84a2fd" + integrity sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA== + +"@esbuild/android-arm@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.19.tgz#5898f7832c2298bc7d0ab53701c57beb74d78b4d" + integrity sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A== + +"@esbuild/android-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.19.tgz#658368ef92067866d95fb268719f98f363d13ae1" + integrity sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww== + +"@esbuild/darwin-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz#584c34c5991b95d4d48d333300b1a4e2ff7be276" + integrity sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg== + +"@esbuild/darwin-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz#7751d236dfe6ce136cce343dce69f52d76b7f6cb" + integrity sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw== + +"@esbuild/freebsd-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz#cacd171665dd1d500f45c167d50c6b7e539d5fd2" + integrity sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ== + +"@esbuild/freebsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz#0769456eee2a08b8d925d7c00b79e861cb3162e4" + integrity sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ== + +"@esbuild/linux-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz#38e162ecb723862c6be1c27d6389f48960b68edb" + integrity sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg== + +"@esbuild/linux-arm@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz#1a2cd399c50040184a805174a6d89097d9d1559a" + integrity sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA== + +"@esbuild/linux-ia32@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz#e28c25266b036ce1cabca3c30155222841dc035a" + integrity sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ== + +"@esbuild/linux-loong64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz#0f887b8bb3f90658d1a0117283e55dbd4c9dcf72" + integrity sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ== + +"@esbuild/linux-mips64el@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz#f5d2a0b8047ea9a5d9f592a178ea054053a70289" + integrity sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A== + +"@esbuild/linux-ppc64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz#876590e3acbd9fa7f57a2c7d86f83717dbbac8c7" + integrity sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg== + +"@esbuild/linux-riscv64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz#7f49373df463cd9f41dc34f9b2262d771688bf09" + integrity sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA== + +"@esbuild/linux-s390x@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz#e2afd1afcaf63afe2c7d9ceacd28ec57c77f8829" + integrity sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q== + +"@esbuild/linux-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz#8a0e9738b1635f0c53389e515ae83826dec22aa4" + integrity sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw== + +"@esbuild/netbsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz#c29fb2453c6b7ddef9a35e2c18b37bda1ae5c462" + integrity sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q== + +"@esbuild/openbsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz#95e75a391403cb10297280d524d66ce04c920691" + integrity sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g== + +"@esbuild/sunos-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz#722eaf057b83c2575937d3ffe5aeb16540da7273" + integrity sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg== + +"@esbuild/win32-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz#9aa9dc074399288bdcdd283443e9aeb6b9552b6f" + integrity sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag== + +"@esbuild/win32-ia32@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz#95ad43c62ad62485e210f6299c7b2571e48d2b03" + integrity sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw== + +"@esbuild/win32-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz#8cfaf2ff603e9aabb910e9c0558c26cf32744061" + integrity sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA== + +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + "@eslint/eslintrc@^1.4.1": version "1.4.1" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.4.1.tgz#af58772019a2d271b7e2d4c23ff4ddcba3ccfb3e" @@ -2054,6 +3128,18 @@ resolved "https://registry.yarnpkg.com/@fontsource/inter/-/inter-4.5.14.tgz#3944b809aa7bf27dacd20bdc04a49fb738959724" integrity sha512-JDC9AocdPLuGsASkvWw9hS5gtHE7K9dOwL98XLrk5yjYqxy4uVnScG58NUvFMJDVJRl/7c8Wnap6PEs+7Zvj1Q== +"@hapi/hoek@^9.0.0": + version "9.3.0" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" + integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== + +"@hapi/topo@^5.0.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" + integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + dependencies: + "@hapi/hoek" "^9.0.0" + "@humanwhocodes/config-array@^0.11.8": version "0.11.8" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" @@ -2128,6 +3214,18 @@ jest-util "^28.1.3" slash "^3.0.0" +"@jest/console@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.5.0.tgz#593a6c5c0d3f75689835f1b3b4688c4f8544cb57" + integrity sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ== + dependencies: + "@jest/types" "^29.5.0" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^29.5.0" + jest-util "^29.5.0" + slash "^3.0.0" + "@jest/core@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" @@ -2162,6 +3260,40 @@ slash "^3.0.0" strip-ansi "^6.0.0" +"@jest/core@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.5.0.tgz#76674b96904484e8214614d17261cc491e5f1f03" + integrity sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ== + dependencies: + "@jest/console" "^29.5.0" + "@jest/reporters" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + ci-info "^3.2.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^29.5.0" + jest-config "^29.5.0" + jest-haste-map "^29.5.0" + jest-message-util "^29.5.0" + jest-regex-util "^29.4.3" + jest-resolve "^29.5.0" + jest-resolve-dependencies "^29.5.0" + jest-runner "^29.5.0" + jest-runtime "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" + jest-watcher "^29.5.0" + micromatch "^4.0.4" + pretty-format "^29.5.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + "@jest/environment@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" @@ -2172,6 +3304,16 @@ "@types/node" "*" jest-mock "^27.5.1" +"@jest/environment@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.5.0.tgz#9152d56317c1fdb1af389c46640ba74ef0bb4c65" + integrity sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ== + dependencies: + "@jest/fake-timers" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" + jest-mock "^29.5.0" + "@jest/expect-utils@^29.3.1": version "29.3.1" resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.3.1.tgz#531f737039e9b9e27c42449798acb5bba01935b6" @@ -2179,6 +3321,21 @@ dependencies: jest-get-type "^29.2.0" +"@jest/expect-utils@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.5.0.tgz#f74fad6b6e20f924582dc8ecbf2cb800fe43a036" + integrity sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg== + dependencies: + jest-get-type "^29.4.3" + +"@jest/expect@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.5.0.tgz#80952f5316b23c483fbca4363ce822af79c38fba" + integrity sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g== + dependencies: + expect "^29.5.0" + jest-snapshot "^29.5.0" + "@jest/fake-timers@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" @@ -2191,6 +3348,18 @@ jest-mock "^27.5.1" jest-util "^27.5.1" +"@jest/fake-timers@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.5.0.tgz#d4d09ec3286b3d90c60bdcd66ed28d35f1b4dc2c" + integrity sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg== + dependencies: + "@jest/types" "^29.5.0" + "@sinonjs/fake-timers" "^10.0.2" + "@types/node" "*" + jest-message-util "^29.5.0" + jest-mock "^29.5.0" + jest-util "^29.5.0" + "@jest/globals@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" @@ -2200,6 +3369,16 @@ "@jest/types" "^27.5.1" expect "^27.5.1" +"@jest/globals@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.5.0.tgz#6166c0bfc374c58268677539d0c181f9c1833298" + integrity sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ== + dependencies: + "@jest/environment" "^29.5.0" + "@jest/expect" "^29.5.0" + "@jest/types" "^29.5.0" + jest-mock "^29.5.0" + "@jest/reporters@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" @@ -2231,6 +3410,36 @@ terminal-link "^2.0.0" v8-to-istanbul "^8.1.0" +"@jest/reporters@^29.4.1", "@jest/reporters@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.5.0.tgz#985dfd91290cd78ddae4914ba7921bcbabe8ac9b" + integrity sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@jridgewell/trace-mapping" "^0.3.15" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^5.1.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-message-util "^29.5.0" + jest-util "^29.5.0" + jest-worker "^29.5.0" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" + "@jest/schemas@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905" @@ -2245,6 +3454,13 @@ dependencies: "@sinclair/typebox" "^0.24.1" +"@jest/schemas@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" + integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== + dependencies: + "@sinclair/typebox" "^0.25.16" + "@jest/source-map@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" @@ -2254,6 +3470,15 @@ graceful-fs "^4.2.9" source-map "^0.6.0" +"@jest/source-map@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.4.3.tgz#ff8d05cbfff875d4a791ab679b4333df47951d20" + integrity sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.15" + callsites "^3.0.0" + graceful-fs "^4.2.9" + "@jest/test-result@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" @@ -2274,6 +3499,16 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" +"@jest/test-result@^29.4.1", "@jest/test-result@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.5.0.tgz#7c856a6ca84f45cc36926a4e9c6b57f1973f1408" + integrity sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ== + dependencies: + "@jest/console" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + "@jest/test-sequencer@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" @@ -2284,6 +3519,16 @@ jest-haste-map "^27.5.1" jest-runtime "^27.5.1" +"@jest/test-sequencer@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz#34d7d82d3081abd523dbddc038a3ddcb9f6d3cc4" + integrity sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ== + dependencies: + "@jest/test-result" "^29.5.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.5.0" + slash "^3.0.0" + "@jest/transform@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" @@ -2305,6 +3550,27 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" +"@jest/transform@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.5.0.tgz#cf9c872d0965f0cbd32f1458aa44a2b1988b00f9" + integrity sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^29.5.0" + "@jridgewell/trace-mapping" "^0.3.15" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.5.0" + jest-regex-util "^29.4.3" + jest-util "^29.5.0" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.2" + "@jest/types@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" @@ -2351,6 +3617,18 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" +"@jest/types@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593" + integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog== + dependencies: + "@jest/schemas" "^29.4.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + "@jimp/bmp@^0.16.2": version "0.16.2" resolved "https://registry.yarnpkg.com/@jimp/bmp/-/bmp-0.16.2.tgz#3982879b10626fc8cf1b4ab8627158bad142ec9d" @@ -2490,6 +3768,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.13": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + "@jridgewell/trace-mapping@0.3.9": version "0.3.9" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" @@ -2498,6 +3781,14 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.17": + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + "@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.9": version "0.3.17" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" @@ -2677,6 +3968,48 @@ resolved "https://registry.yarnpkg.com/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz#af577b477c683fad17c619a78208cede06f9605c" integrity sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q== +"@microsoft/api-extractor-model@7.27.3": + version "7.27.3" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.27.3.tgz#a484e0b7e25c4fa85846281aed6b87da0411d96c" + integrity sha512-fSFvw7otYHduOkyshjTbapKKgwF8bgquVHvgF8VgeKtMYvqXkoaj7W6VcM7PNY7E2bbblhUgC4XNdqZLD4SJGw== + dependencies: + "@microsoft/tsdoc" "0.14.2" + "@microsoft/tsdoc-config" "~0.16.1" + "@rushstack/node-core-library" "3.59.4" + +"@microsoft/api-extractor@^7.33.5": + version "7.35.4" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.35.4.tgz#87478e1e52dcae7f5e7760e870a9a07e9e4292f1" + integrity sha512-E/DIIlgu1ZW+AD+Y0UuVe/30rO+Km0CRkDU1aOKQntwKtv/+FodtRAUvzU/vAxq5lQHSsy6jJErLiXR6G3fupA== + dependencies: + "@microsoft/api-extractor-model" "7.27.3" + "@microsoft/tsdoc" "0.14.2" + "@microsoft/tsdoc-config" "~0.16.1" + "@rushstack/node-core-library" "3.59.4" + "@rushstack/rig-package" "0.3.21" + "@rushstack/ts-command-line" "4.15.1" + colors "~1.2.1" + lodash "~4.17.15" + resolve "~1.22.1" + semver "~7.3.0" + source-map "~0.6.1" + typescript "~5.0.4" + +"@microsoft/tsdoc-config@~0.16.1": + version "0.16.2" + resolved "https://registry.yarnpkg.com/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz#b786bb4ead00d54f53839a458ce626c8548d3adf" + integrity sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw== + dependencies: + "@microsoft/tsdoc" "0.14.2" + ajv "~6.12.6" + jju "~1.4.0" + resolve "~1.19.0" + +"@microsoft/tsdoc@0.14.2": + version "0.14.2" + resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz#c3ec604a0b54b9a9b87e9735dfc59e1a5da6a5fb" + integrity sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug== + "@multiformats/base-x@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" @@ -2725,6 +4058,41 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@nrwl/devkit@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-16.3.2.tgz#b45393dfd62dcb75554ff0c2dff6715a907e3877" + integrity sha512-EiDwVIvh6AcClXv22Q7auQh7Iy/ONISEFWzTswy/J6ZmVGCQesbiwg4cGV0MKiScr+awdVzqyNey+wD6IR5Lkw== + dependencies: + "@nx/devkit" "16.3.2" + +"@nrwl/eslint-plugin-nx@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nrwl/eslint-plugin-nx/-/eslint-plugin-nx-16.3.2.tgz#575e634dd19ea9e13f89b1f63bab300b53cf1bea" + integrity sha512-E+X/L8b/GtbAGnwm+WOdAlXNwNYCTb6uodo5yxRrUkkMoVVMkzI7FLOHKGgYiqXH/xbuKE3yAg21EwP0epOi2Q== + dependencies: + "@nx/eslint-plugin" "16.3.2" + +"@nrwl/jest@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nrwl/jest/-/jest-16.3.2.tgz#5e7a3cd23861d7aa31b52262e6dabc6ee2fa9206" + integrity sha512-vhwrgjIn1XG3zDSlc6CSfCKBtgDEYQUWG69MdfaqrNInmmsiPkspv7eM99Xh8MGN5HMC2Epzy2todD3J2zZZuQ== + dependencies: + "@nx/jest" "16.3.2" + +"@nrwl/js@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nrwl/js/-/js-16.3.2.tgz#60268ebbb08a82ea3c1ce29b89a58bfbb96ccddd" + integrity sha512-UMmdA4vXy2/VWNMlpBDruT9XwGmLw/MpUaKoN2KLkai/fYN6MvB3mabc9WQ8qsNvDWshmOJ6TqAHReR25BjugQ== + dependencies: + "@nx/js" "16.3.2" + +"@nrwl/linter@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nrwl/linter/-/linter-16.3.2.tgz#b99eabfceab16dc404415d0d87fa3e380b6f859f" + integrity sha512-sUDQNlmRIGQnhdDmpQkJgpF9LZWKBoqXr2g9Y4yq0QlpTamxTbx8/GxMICotA52kayEx1cKbU1xvjJWPchSrlw== + dependencies: + "@nx/linter" "16.3.2" + "@nrwl/nx-cloud@16.0.5": version "16.0.5" resolved "https://registry.yarnpkg.com/@nrwl/nx-cloud/-/nx-cloud-16.0.5.tgz#c963480a71c4afa964fbbe9e4d6bbf222764e9cd" @@ -2732,6 +4100,13 @@ dependencies: nx-cloud "16.0.5" +"@nrwl/react@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nrwl/react/-/react-16.3.2.tgz#69387a4b8a555fa07f4bd6f70fbb0a40d841df8c" + integrity sha512-p9Y2pWgUnVOUB5wMiE9iIKCV6CzixSPc6p5GQIP8ysbtVdV7WZ9bNFiuR9pxNQ9Y6weJsMH9lr45pZ7G7JO4KA== + dependencies: + "@nx/react" "16.3.2" + "@nrwl/tao@16.3.2": version "16.3.2" resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-16.3.2.tgz#eefc1974342afbbe48e4e5351d6707ad2f9fb179" @@ -2739,6 +4114,114 @@ dependencies: nx "16.3.2" +"@nrwl/vite@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nrwl/vite/-/vite-16.3.2.tgz#37f3ea458f12e2c533be0e6dffd6cc502f7e1644" + integrity sha512-EK9lR/tpBDSxSVG9E4o4VMioUJQ9hm1wMUghwWgLaMXM4S1iTT/CwA2NdLOGbTx8QxKm6t1odWGosOU3NhoohA== + dependencies: + "@nx/vite" "16.3.2" + +"@nrwl/web@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nrwl/web/-/web-16.3.2.tgz#0ab518b1f8042937250cac4a2f47618837969af8" + integrity sha512-UJotencL5g+jyozfVPLevbzWoR10Mx/AURzGNzzGmddLbmXJlsHwnnKHtzGzc+QFG10jYMzk1WZSyBo/ZPrN+Q== + dependencies: + "@nx/web" "16.3.2" + +"@nrwl/workspace@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nrwl/workspace/-/workspace-16.3.2.tgz#3d3921dc9288fb6a9dfd8f1b05ca16b46930cdac" + integrity sha512-ORVzEEJIMOFYEOtOQHLU7N4vT4mYZ/JzKiwHZrHkCaVhgkiGBLoX3tOwVZjafKaa/24cGISv0J7WRtnfRKl2cA== + dependencies: + "@nx/workspace" "16.3.2" + +"@nx/devkit@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nx/devkit/-/devkit-16.3.2.tgz#95d58d104449c54bdc276fa1c9166fcad867cfa8" + integrity sha512-1ev3EDm2Sx/ibziZroL1SheqxDR7UgC49tkBgJz1GrQLQnfdhBYroCPSyBSWGPMLHjIuHb3+hyGSV1Bz+BIYOA== + dependencies: + "@nrwl/devkit" "16.3.2" + ejs "^3.1.7" + ignore "^5.0.4" + semver "7.3.4" + tmp "~0.2.1" + tslib "^2.3.0" + +"@nx/eslint-plugin@16.3.2", "@nx/eslint-plugin@^16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nx/eslint-plugin/-/eslint-plugin-16.3.2.tgz#085ea6a71003f4780ef52f13dd1f83133adc1280" + integrity sha512-9KMiDEvsHPlLm9wrG3qUl68veNFLbFglD5XGKmBXA07tHISWo5eqNIML5/Y5cwsRufUcQFe21V+6FxrbVQ24CQ== + dependencies: + "@nrwl/eslint-plugin-nx" "16.3.2" + "@nx/devkit" "16.3.2" + "@nx/js" "16.3.2" + "@typescript-eslint/type-utils" "^5.58.0" + "@typescript-eslint/utils" "^5.58.0" + chalk "^4.1.0" + confusing-browser-globals "^1.0.9" + semver "7.3.4" + +"@nx/jest@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nx/jest/-/jest-16.3.2.tgz#809297cb4aa45e277e8a57684e93f3953bb4bea5" + integrity sha512-aO8Rc+wwSXLh1jJYd2cxOT5R9BQfqjAXWZOPcvAQQonFNNfwMHrw0+YsqjWgiFtFrxzSX5RrhzVG44cOWpAdqQ== + dependencies: + "@jest/reporters" "^29.4.1" + "@jest/test-result" "^29.4.1" + "@nrwl/jest" "16.3.2" + "@nx/devkit" "16.3.2" + "@nx/js" "16.3.2" + "@phenomnomnominal/tsquery" "~5.0.1" + chalk "^4.1.0" + dotenv "~10.0.0" + identity-obj-proxy "3.0.0" + jest-config "^29.4.1" + jest-resolve "^29.4.1" + jest-util "^29.4.1" + resolve.exports "1.1.0" + tslib "^2.3.0" + +"@nx/js@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nx/js/-/js-16.3.2.tgz#1f2e6807439bfb525f24f983558e7b054d57c3f2" + integrity sha512-bumLGMduNm221Sh3/wkEMEkJOC1kTlqmpx6wamDSsPlAFq0ePgoaNJjoYqC9XH7n7wXtgy9bgKhHJPnek8NKow== + dependencies: + "@babel/core" "^7.15.0" + "@babel/plugin-proposal-class-properties" "^7.14.5" + "@babel/plugin-proposal-decorators" "^7.14.5" + "@babel/plugin-transform-runtime" "^7.15.0" + "@babel/preset-env" "^7.15.0" + "@babel/preset-typescript" "^7.15.0" + "@babel/runtime" "^7.14.8" + "@nrwl/js" "16.3.2" + "@nx/devkit" "16.3.2" + "@nx/workspace" "16.3.2" + "@phenomnomnominal/tsquery" "~5.0.1" + babel-plugin-const-enum "^1.0.1" + babel-plugin-macros "^2.8.0" + babel-plugin-transform-typescript-metadata "^0.3.1" + chalk "^4.1.0" + fast-glob "3.2.7" + fs-extra "^11.1.0" + ignore "^5.0.4" + js-tokens "^4.0.0" + minimatch "3.0.5" + semver "7.3.4" + source-map-support "0.5.19" + tslib "^2.3.0" + +"@nx/linter@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nx/linter/-/linter-16.3.2.tgz#85411007f9d64f2723f532735f96d1a17480e2e1" + integrity sha512-hVCU6ZIMd+yTMLrC3PbjaHuD3yU+sB/lABTaWuUx2klT0cqKhiTp0KnDLcFWtzQmnNtGEaUjfPKxvA92xon0CA== + dependencies: + "@nrwl/linter" "16.3.2" + "@nx/devkit" "16.3.2" + "@nx/js" "16.3.2" + "@phenomnomnominal/tsquery" "~5.0.1" + tmp "~0.2.1" + tslib "^2.3.0" + "@nx/nx-darwin-arm64@16.3.2": version "16.3.2" resolved "https://registry.yarnpkg.com/@nx/nx-darwin-arm64/-/nx-darwin-arm64-16.3.2.tgz#83b6e78b27d2d7da8f7626560f52070c8735d28a" @@ -2789,6 +4272,76 @@ resolved "https://registry.yarnpkg.com/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-16.3.2.tgz#2195faaf1fc465c7a89bfdd62323fdd2a5d91f15" integrity sha512-QC0sWrfQm0/WdvvM//7UAgm+otbak6bznZ0zawTeqmLBh1hLjNeweyzSVKQEtZtlzDMKpzCVuuwkJq+VKBLvmw== +"@nx/react@16.3.2", "@nx/react@^16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nx/react/-/react-16.3.2.tgz#43e880f20481373d9264ef67f3e6dadb467212b1" + integrity sha512-Maj2zva2nUlsGP5o5SDEsNErIiDK247EqXraTnLPUEQy8XQP2oOThu5FkKhT51RguLyRDFtFei4vANPl/WI6FQ== + dependencies: + "@nrwl/react" "16.3.2" + "@nx/devkit" "16.3.2" + "@nx/js" "16.3.2" + "@nx/linter" "16.3.2" + "@nx/web" "16.3.2" + "@phenomnomnominal/tsquery" "~5.0.1" + "@svgr/webpack" "^8.0.1" + chalk "^4.1.0" + file-loader "^6.2.0" + minimatch "3.0.5" + +"@nx/vite@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nx/vite/-/vite-16.3.2.tgz#28327c4d317f36a9fa85c56df3ee1b69b81c8731" + integrity sha512-3xbWsFdssfJqeqX3qOWxCZnrsi2ZrdUgnAMzdfp983hjzBzz28Wp/d9QGW+2GgCX3jhnxxYrh11oSSHsChUxWg== + dependencies: + "@nrwl/vite" "16.3.2" + "@nx/devkit" "16.3.2" + "@nx/js" "16.3.2" + "@phenomnomnominal/tsquery" "~5.0.1" + "@swc/helpers" "~0.5.0" + dotenv "~10.0.0" + enquirer "~2.3.6" + +"@nx/web@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nx/web/-/web-16.3.2.tgz#73a9630b28f17640f7c80a221837c95057445116" + integrity sha512-PzTkawQ+OgjX0mR5KwMGcdveoMNLKo/jYaW3UrPMIVUy0NpalW0ULJnDOUt+NRlrp7jOsUP7hUYZkYPNDI3Ivg== + dependencies: + "@nrwl/web" "16.3.2" + "@nx/devkit" "16.3.2" + "@nx/js" "16.3.2" + chalk "^4.1.0" + chokidar "^3.5.1" + detect-port "^1.5.1" + http-server "^14.1.0" + ignore "^5.0.4" + tslib "^2.3.0" + +"@nx/workspace@16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@nx/workspace/-/workspace-16.3.2.tgz#145d4ee7b909d5b430d7ac9a8043d688a00d017c" + integrity sha512-gFrJEv3+Jn2leu3RKFTakPHY8okI8hjOg8RO4OWA2ZemFXRyh9oIm/xsCsOyqYlGt06eqV2mD3GUun/05z1nhg== + dependencies: + "@nrwl/workspace" "16.3.2" + "@nx/devkit" "16.3.2" + "@parcel/watcher" "2.0.4" + chalk "^4.1.0" + chokidar "^3.5.1" + cli-cursor "3.1.0" + cli-spinners "2.6.1" + dotenv "~10.0.0" + figures "3.2.0" + flat "^5.0.2" + ignore "^5.0.4" + minimatch "3.0.5" + npm-run-path "^4.0.1" + nx "16.3.2" + open "^8.4.0" + rxjs "^7.8.0" + tmp "~0.2.1" + tslib "^2.3.0" + yargs "^17.6.2" + yargs-parser "21.1.1" + "@openzeppelin/contracts@3.4.1-solc-0.7-2": version "3.4.1-solc-0.7-2" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.1-solc-0.7-2.tgz#371c67ebffe50f551c3146a9eec5fe6ffe862e92" @@ -2812,6 +4365,13 @@ resolved "https://registry.yarnpkg.com/@pedrouid/environment/-/environment-1.0.1.tgz#858f0f8a057340e0b250398b75ead77d6f4342ec" integrity sha512-HaW78NszGzRZd9SeoI3JD11JqY+lubnaOx7Pewj5pfjqWXOEATpeKIFb9Z4t2WBUK2iryiXX3lzWwmYWgUL0Ug== +"@phenomnomnominal/tsquery@~5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz#a2a5abc89f92c01562a32806655817516653a388" + integrity sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA== + dependencies: + esquery "^1.4.0" + "@pmmmwh/react-refresh-webpack-plugin@^0.5.3": version "0.5.10" resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz#2eba163b8e7dbabb4ce3609ab5e32ab63dda3ef8" @@ -3100,11 +4660,59 @@ estree-walker "^1.0.1" picomatch "^2.2.2" +"@rollup/pluginutils@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d" + integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ== + dependencies: + estree-walker "^2.0.1" + picomatch "^2.2.2" + +"@rollup/pluginutils@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.0.2.tgz#012b8f53c71e4f6f9cb317e311df1404f56e7a33" + integrity sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA== + dependencies: + "@types/estree" "^1.0.0" + estree-walker "^2.0.2" + picomatch "^2.3.1" + "@rushstack/eslint-patch@^1.1.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz#8be36a1f66f3265389e90b5f9c9962146758f728" integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg== +"@rushstack/node-core-library@3.59.4", "@rushstack/node-core-library@^3.53.2": + version "3.59.4" + resolved "https://registry.yarnpkg.com/@rushstack/node-core-library/-/node-core-library-3.59.4.tgz#95f9eb3274627a5e84ee9443e65bb6edec1a5dc8" + integrity sha512-YAKJDC6Mz/KA1D7bvB88WaRX3knt/ZuLzkRu5G9QADGSjLtvTWzCNCytRF2PCSaaHOZaZsWul4F1KQdgFgUDqA== + dependencies: + colors "~1.2.1" + fs-extra "~7.0.1" + import-lazy "~4.0.0" + jju "~1.4.0" + resolve "~1.22.1" + semver "~7.3.0" + z-schema "~5.0.2" + +"@rushstack/rig-package@0.3.21": + version "0.3.21" + resolved "https://registry.yarnpkg.com/@rushstack/rig-package/-/rig-package-0.3.21.tgz#5bd48c2a890f7d892c40fa1780f98af29c8140a4" + integrity sha512-6KPBuZYP/b9U0Qwy1J4vjYtXvLavdmVT7mMelErfqqZ3P/ywoxlFITGr9ZbqD1zmfIVrIfC2jrM6gfm7OHPRhQ== + dependencies: + resolve "~1.22.1" + strip-json-comments "~3.1.1" + +"@rushstack/ts-command-line@4.15.1": + version "4.15.1" + resolved "https://registry.yarnpkg.com/@rushstack/ts-command-line/-/ts-command-line-4.15.1.tgz#8f2ebde6bb19deb2c5b65363854b84aea1bf59f0" + integrity sha512-EL4jxZe5fhb1uVL/P/wQO+Z8Rc8FMiWJ1G7VgnPDvdIt5GVjRfK7vwzder1CZQiX3x0PY6uxENYLNGTFd1InRQ== + dependencies: + "@types/argparse" "1.0.38" + argparse "~1.0.9" + colors "~1.2.1" + string-argv "~0.3.1" + "@safe-global/api-kit@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@safe-global/api-kit/-/api-kit-1.0.1.tgz#7176024cfeacccbf5a383e49f5b37d25cca97efc" @@ -3279,6 +4887,23 @@ "@sentry/cli" "^1.74.6" webpack-sources "^2.0.0 || ^3.0.0" +"@sideway/address@^4.1.3": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0" + integrity sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@sideway/formula@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" + integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== + +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + "@simbathesailor/babel-plugin-use-what-changed@^2.1.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@simbathesailor/babel-plugin-use-what-changed/-/babel-plugin-use-what-changed-2.1.0.tgz#2f64a1bedcb7652a72ff7f84cb8f2df297ec9f5f" @@ -3298,6 +4923,11 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" integrity sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA== +"@sinclair/typebox@^0.25.16": + version "0.25.24" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" + integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== + "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -3315,6 +4945,20 @@ dependencies: type-detect "4.0.8" +"@sinonjs/commons@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72" + integrity sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^10.0.2": + version "10.2.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.2.0.tgz#b3e322a34c5f26e3184e7f6115695f299c1b1194" + integrity sha512-OPwQlEdg40HAj5KNF8WW6q2KG4Z+cBCZb3m4ninfTZKaBmbIJodviQsDBoYMPHkOyJJMHnOJo5j2+LKDOhOACg== + dependencies: + "@sinonjs/commons" "^3.0.0" + "@sinonjs/fake-timers@^8.0.1": version "8.1.0" resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" @@ -3612,46 +5256,100 @@ magic-string "^0.25.0" string.prototype.matchall "^4.0.6" +"@svgr/babel-plugin-add-jsx-attribute@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz#4001f5d5dd87fa13303e36ee106e3ff3a7eb8b22" + integrity sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g== + "@svgr/babel-plugin-add-jsx-attribute@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz#81ef61947bb268eb9d50523446f9c638fb355906" integrity sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg== +"@svgr/babel-plugin-remove-jsx-attribute@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz#69177f7937233caca3a1afb051906698f2f59186" + integrity sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA== + "@svgr/babel-plugin-remove-jsx-attribute@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz#6b2c770c95c874654fd5e1d5ef475b78a0a962ef" integrity sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg== +"@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz#c2c48104cfd7dcd557f373b70a56e9e3bdae1d44" + integrity sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA== + "@svgr/babel-plugin-remove-jsx-empty-expression@^5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz#25621a8915ed7ad70da6cea3d0a6dbc2ea933efd" integrity sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA== +"@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz#8fbb6b2e91fa26ac5d4aa25c6b6e4f20f9c0ae27" + integrity sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ== + "@svgr/babel-plugin-replace-jsx-attribute-value@^5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz#0b221fc57f9fcd10e91fe219e2cd0dd03145a897" integrity sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ== +"@svgr/babel-plugin-svg-dynamic-title@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz#1d5ba1d281363fc0f2f29a60d6d936f9bbc657b0" + integrity sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og== + "@svgr/babel-plugin-svg-dynamic-title@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz#139b546dd0c3186b6e5db4fefc26cb0baea729d7" integrity sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg== +"@svgr/babel-plugin-svg-em-dimensions@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz#35e08df300ea8b1d41cb8f62309c241b0369e501" + integrity sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g== + "@svgr/babel-plugin-svg-em-dimensions@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz#6543f69526632a133ce5cabab965deeaea2234a0" integrity sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw== +"@svgr/babel-plugin-transform-react-native-svg@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.0.0.tgz#023cd0895b98521f566060d6bb92100b9fee3775" + integrity sha512-UKrY3860AQICgH7g+6h2zkoxeVEPLYwX/uAjmqo4PIq2FIHppwhIqZstIyTz0ZtlwreKR41O3W3BzsBBiJV2Aw== + "@svgr/babel-plugin-transform-react-native-svg@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz#00bf9a7a73f1cad3948cdab1f8dfb774750f8c80" integrity sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q== +"@svgr/babel-plugin-transform-svg-component@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz#013b4bfca88779711f0ed2739f3f7efcefcf4f7e" + integrity sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw== + "@svgr/babel-plugin-transform-svg-component@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz#583a5e2a193e214da2f3afeb0b9e8d3250126b4a" integrity sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ== +"@svgr/babel-preset@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-8.0.0.tgz#6d78100b3b6daf11c940b82d5bd8c3164b9c6ad9" + integrity sha512-KLcjiZychInVrhs86OvcYPLTFu9L5XV2vj0XAaE1HwE3J3jLmIzRY8ttdeAg/iFyp8nhavJpafpDZTt+1LIpkQ== + dependencies: + "@svgr/babel-plugin-add-jsx-attribute" "8.0.0" + "@svgr/babel-plugin-remove-jsx-attribute" "8.0.0" + "@svgr/babel-plugin-remove-jsx-empty-expression" "8.0.0" + "@svgr/babel-plugin-replace-jsx-attribute-value" "8.0.0" + "@svgr/babel-plugin-svg-dynamic-title" "8.0.0" + "@svgr/babel-plugin-svg-em-dimensions" "8.0.0" + "@svgr/babel-plugin-transform-react-native-svg" "8.0.0" + "@svgr/babel-plugin-transform-svg-component" "8.0.0" + "@svgr/babel-preset@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-5.5.0.tgz#8af54f3e0a8add7b1e2b0fcd5a882c55393df327" @@ -3666,6 +5364,17 @@ "@svgr/babel-plugin-transform-react-native-svg" "^5.4.0" "@svgr/babel-plugin-transform-svg-component" "^5.5.0" +"@svgr/core@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/core/-/core-8.0.0.tgz#e96829cdb0473345d5671568282ee0736e86ef12" + integrity sha512-aJKtc+Pie/rFYsVH/unSkDaZGvEeylNv/s2cP+ta9/rYWxRVvoV/S4Qw65Kmrtah4CBK5PM6ISH9qUH7IJQCng== + dependencies: + "@babel/core" "^7.21.3" + "@svgr/babel-preset" "8.0.0" + camelcase "^6.2.0" + cosmiconfig "^8.1.3" + snake-case "^3.0.4" + "@svgr/core@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@svgr/core/-/core-5.5.0.tgz#82e826b8715d71083120fe8f2492ec7d7874a579" @@ -3675,6 +5384,14 @@ camelcase "^6.2.0" cosmiconfig "^7.0.0" +"@svgr/hast-util-to-babel-ast@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz#6952fd9ce0f470e1aded293b792a2705faf4ffd4" + integrity sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q== + dependencies: + "@babel/types" "^7.21.3" + entities "^4.4.0" + "@svgr/hast-util-to-babel-ast@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz#5ee52a9c2533f73e63f8f22b779f93cd432a5461" @@ -3682,6 +5399,16 @@ dependencies: "@babel/types" "^7.12.6" +"@svgr/plugin-jsx@8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-8.0.1.tgz#b9495e06062cc0cac0e035751b69471ee328236b" + integrity sha512-bfCFb+4ZsM3UuKP2t7KmDwn6YV8qVn9HIQJmau6xeQb/iV65Rpi7NBNBWA2hcCd4GKoCqG8hpaaDk5FDR0eH+g== + dependencies: + "@babel/core" "^7.21.3" + "@svgr/babel-preset" "8.0.0" + "@svgr/hast-util-to-babel-ast" "8.0.0" + svg-parser "^2.0.4" + "@svgr/plugin-jsx@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz#1aa8cd798a1db7173ac043466d7b52236b369000" @@ -3692,6 +5419,15 @@ "@svgr/hast-util-to-babel-ast" "^5.5.0" svg-parser "^2.0.2" +"@svgr/plugin-svgo@8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-8.0.1.tgz#df0199313fdc88c3d7cd8e0dff16695e9718548c" + integrity sha512-29OJ1QmJgnohQHDAgAuY2h21xWD6TZiXji+hnx+W635RiXTAlHTbjrZDktfqzkN0bOeQEtNe+xgq73/XeWFfSg== + dependencies: + cosmiconfig "^8.1.3" + deepmerge "^4.3.1" + svgo "^3.0.2" + "@svgr/plugin-svgo@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz#02da55d85320549324e201c7b2e53bf431fcc246" @@ -3715,6 +5451,27 @@ "@svgr/plugin-svgo" "^5.5.0" loader-utils "^2.0.0" +"@svgr/webpack@^8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-8.0.1.tgz#a0e4a711daae347b515335449d198a275b3ab1e4" + integrity sha512-zSoeKcbCmfMXjA11uDuCJb+1LWNb3vy6Qw/VHj0Nfcl3UuqwuoZWknHsBIhCWvi4wU9vPui3aq054qjVyZqY4A== + dependencies: + "@babel/core" "^7.21.3" + "@babel/plugin-transform-react-constant-elements" "^7.21.3" + "@babel/preset-env" "^7.20.2" + "@babel/preset-react" "^7.18.6" + "@babel/preset-typescript" "^7.21.0" + "@svgr/core" "8.0.0" + "@svgr/plugin-jsx" "8.0.1" + "@svgr/plugin-svgo" "8.0.1" + +"@swc/helpers@~0.5.0": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.1.tgz#e9031491aa3f26bfcc974a67f48bd456c8a5357a" + integrity sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg== + dependencies: + tslib "^2.4.0" + "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" @@ -3736,18 +5493,18 @@ dependencies: defer-to-connect "^2.0.1" -"@testing-library/dom@^8.5.0": - version "8.19.1" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.19.1.tgz#0e2dafd281dedb930bb235eac1045470b4129d0e" - integrity sha512-P6iIPyYQ+qH8CvGauAqanhVnjrnRe0IZFSYCeGkSRW9q3u8bdVn2NPI+lasFyVsEQn1J/IFmp5Aax41+dAP9wg== +"@testing-library/dom@^9.0.0": + version "9.3.1" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.3.1.tgz#8094f560e9389fb973fe957af41bf766937a9ee9" + integrity sha512-0DGPd9AR3+iDTjGoMpxIkAsUihHZ3Ai6CneU6bRRrffXMgzCdlNk43jTrD2/5LT6CBb3MWTP8v510JzYtahD2w== dependencies: "@babel/code-frame" "^7.10.4" "@babel/runtime" "^7.12.5" "@types/aria-query" "^5.0.1" - aria-query "^5.0.0" + aria-query "5.1.3" chalk "^4.1.0" dom-accessibility-api "^0.5.9" - lz-string "^1.4.4" + lz-string "^1.5.0" pretty-format "^27.0.2" "@testing-library/jest-dom@^5.16.5": @@ -3773,13 +5530,13 @@ "@babel/runtime" "^7.12.5" react-error-boundary "^3.1.0" -"@testing-library/react@^13.4.0": - version "13.4.0" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-13.4.0.tgz#6a31e3bf5951615593ad984e96b9e5e2d9380966" - integrity sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw== +"@testing-library/react@14.0.0": + version "14.0.0" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-14.0.0.tgz#59030392a6792450b9ab8e67aea5f3cc18d6347c" + integrity sha512-S04gSNJbYE30TlIMLTzv6QCTzt9AqIF5y6s6SzVFILNcNvbV/jU96GeiTPillGQo+Ny64M/5PV7klNYYgv5Dfg== dependencies: "@babel/runtime" "^7.12.5" - "@testing-library/dom" "^8.5.0" + "@testing-library/dom" "^9.0.0" "@types/react-dom" "^18.0.0" "@testing-library/user-event@^13.5.0": @@ -3794,11 +5551,26 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== + "@trysound/sax@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== +"@ts-morph/common@~0.18.0": + version "0.18.1" + resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.18.1.tgz#ca40c3a62c3f9e17142e0af42633ad63efbae0ec" + integrity sha512-RVE+zSRICWRsfrkAw5qCAK+4ZH9kwEFv5h0+/YeHTLieWP7F4wWq4JsKFuNWG+fYh/KF+8rAtgdj5zb2mm+DVA== + dependencies: + fast-glob "^3.2.12" + minimatch "^5.1.0" + mkdirp "^1.0.4" + path-browserify "^1.0.1" + "@tsconfig/node10@^1.0.7": version "1.0.9" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" @@ -3827,6 +5599,11 @@ lodash "^4.17.15" ts-essentials "^7.0.1" +"@types/argparse@1.0.38": + version "1.0.38" + resolved "https://registry.yarnpkg.com/@types/argparse/-/argparse-1.0.38.tgz#a81fd8606d481f873a3800c6ebae4f1d768a56a9" + integrity sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA== + "@types/aria-query@^5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.1.tgz#3286741fb8f1e1580ac28784add4c7a1d49bdfbc" @@ -3924,6 +5701,18 @@ "@types/node" "*" "@types/responselike" "^1.0.0" +"@types/chai-subset@^1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@types/chai-subset/-/chai-subset-1.3.3.tgz#97893814e92abd2c534de422cb377e0e0bdaac94" + integrity sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw== + dependencies: + "@types/chai" "*" + +"@types/chai@*", "@types/chai@^4.3.5": + version "4.3.5" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.5.tgz#ae69bcbb1bebb68c4ac0b11e9d8ed04526b3562b" + integrity sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng== + "@types/clone-deep@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/clone-deep/-/clone-deep-4.0.1.tgz#7c488443ab9f571cd343d774551b78e9264ea990" @@ -4170,6 +5959,14 @@ "@types/estree" "*" "@types/json-schema" "*" +"@types/eslint@^8.4.5": + version "8.40.2" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.40.2.tgz#2833bc112d809677864a4b0e7d1de4f04d7dac2d" + integrity sha512-PRVjQ4Eh9z9pmmtaq8nTjZjQwKFk7YIHIud3lRoKRBgUQjgjRmoGxxGEPXQkF+lH7QkHJRNr5F4aBgYCW0lqpQ== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + "@types/estree@*": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" @@ -4185,6 +5982,11 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== +"@types/estree@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" + integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== + "@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.31": version "4.17.32" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.32.tgz#93dda387f5516af616d8d3f05f2c4c79d81e1b82" @@ -4209,7 +6011,7 @@ resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.10.tgz#6dfbf5ea17142f7f9a043809f1cd4c448cb68249" integrity sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA== -"@types/graceful-fs@^4.1.2": +"@types/graceful-fs@^4.1.2", "@types/graceful-fs@^4.1.3": version "4.1.6" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== @@ -4273,13 +6075,22 @@ expect "^29.0.0" pretty-format "^29.0.0" -"@types/jest@^27.5.2": - version "27.5.2" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.5.2.tgz#ec49d29d926500ffb9fd22b84262e862049c026c" - integrity sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA== +"@types/jest@^29.4.0": + version "29.5.2" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.2.tgz#86b4afc86e3a8f3005b297ed8a72494f89e6395b" + integrity sha512-mSoZVJF5YzGVCk+FsDxzDuH7s+SCkzrgKZzf0Z0T2WudhBUPoF6ktoTPC4R0ZoCPCV5xUvuU6ias5NvxcBcMMg== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + +"@types/jsdom@^20.0.0": + version "20.0.1" + resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-20.0.1.tgz#07c14bc19bd2f918c1929541cdaacae894744808" + integrity sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ== dependencies: - jest-matcher-utils "^27.0.0" - pretty-format "^27.0.0" + "@types/node" "*" + "@types/tough-cookie" "*" + parse5 "^7.0.0" "@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.11" @@ -4298,6 +6109,11 @@ dependencies: "@types/node" "*" +"@types/lodash@^4.14.175": + version "4.14.195" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.195.tgz#bafc975b252eb6cea78882ce8a7b6bf22a6de632" + integrity sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg== + "@types/long@^4.0.1": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" @@ -4345,6 +6161,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.9.1.tgz#0611b37db4246c937feef529ddcc018cf8e35708" integrity sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g== +"@types/node@18.14.2": + version "18.14.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.14.2.tgz#c076ed1d7b6095078ad3cf21dfeea951842778b1" + integrity sha512-1uEQxww3DaghA0RxqHx0O0ppVlo43pJhepY51OxuQIKHpjbnYLA7vcdwioNPzIqmC2u3I/dmylcqjlh0e7AyUA== + "@types/node@^10.12.18": version "10.17.60" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" @@ -4360,11 +6181,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.36.tgz#c414052cb9d43fab67d679d5f3c641be911f5835" integrity sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ== -"@types/node@^16.18.11": - version "16.18.11" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.11.tgz#cbb15c12ca7c16c85a72b6bdc4d4b01151bb3cae" - integrity sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA== - "@types/normalize-package-data@^2.4.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" @@ -4414,7 +6230,14 @@ dependencies: "@types/react" "*" -"@types/react-dom@^18.0.0", "@types/react-dom@^18.0.10": +"@types/react-dom@18.0.11": + version "18.0.11" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.11.tgz#321351c1459bc9ca3d216aefc8a167beec334e33" + integrity sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw== + dependencies: + "@types/react" "*" + +"@types/react-dom@^18.0.0": version "18.0.10" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.10.tgz#3b66dec56aa0f16a6cc26da9e9ca96c35c0b4352" integrity sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg== @@ -4468,7 +6291,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^18.0.26": +"@types/react@*": version "18.0.26" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.26.tgz#8ad59fc01fef8eaf5c74f4ea392621749f0b7917" integrity sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug== @@ -4477,6 +6300,15 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/react@18.0.28": + version "18.0.28" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.28.tgz#accaeb8b86f4908057ad629a26635fe641480065" + integrity sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/rebass@^4.0.10": version "4.0.10" resolved "https://registry.yarnpkg.com/@types/rebass/-/rebass-4.0.10.tgz#f7a819d039349c138f279d2104986d7604a85528" @@ -4590,6 +6422,11 @@ dependencies: "@types/jest" "*" +"@types/tough-cookie@*": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397" + integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw== + "@types/trusted-types@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756" @@ -4702,6 +6539,14 @@ "@typescript-eslint/types" "5.48.1" "@typescript-eslint/visitor-keys" "5.48.1" +"@typescript-eslint/scope-manager@5.60.0": + version "5.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.60.0.tgz#ae511967b4bd84f1d5e179bb2c82857334941c1c" + integrity sha512-hakuzcxPwXi2ihf9WQu1BbRj1e/Pd8ZZwVTG9kfbxAMZstKz8/9OoexIwnmLzShtsdap5U/CoQGRCWlSuPbYxQ== + dependencies: + "@typescript-eslint/types" "5.60.0" + "@typescript-eslint/visitor-keys" "5.60.0" + "@typescript-eslint/type-utils@5.48.1": version "5.48.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.48.1.tgz#5d94ac0c269a81a91ad77c03407cea2caf481412" @@ -4712,11 +6557,26 @@ debug "^4.3.4" tsutils "^3.21.0" +"@typescript-eslint/type-utils@^5.58.0": + version "5.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.60.0.tgz#69b09087eb12d7513d5b07747e7d47f5533aa228" + integrity sha512-X7NsRQddORMYRFH7FWo6sA9Y/zbJ8s1x1RIAtnlj6YprbToTiQnM6vxcMu7iYhdunmoC0rUWlca13D5DVHkK2g== + dependencies: + "@typescript-eslint/typescript-estree" "5.60.0" + "@typescript-eslint/utils" "5.60.0" + debug "^4.3.4" + tsutils "^3.21.0" + "@typescript-eslint/types@5.48.1": version "5.48.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.48.1.tgz#efd1913a9aaf67caf8a6e6779fd53e14e8587e14" integrity sha512-xHyDLU6MSuEEdIlzrrAerCGS3T7AA/L8Hggd0RCYBi0w3JMvGYxlLlXHeg50JI9Tfg5MrtsfuNxbS/3zF1/ATg== +"@typescript-eslint/types@5.60.0": + version "5.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.60.0.tgz#3179962b28b4790de70e2344465ec97582ce2558" + integrity sha512-ascOuoCpNZBccFVNJRSC6rPq4EmJ2NkuoKnd6LDNyAQmdDnziAtxbCGWCbefG1CNzmDvd05zO36AmB7H8RzKPA== + "@typescript-eslint/typescript-estree@5.48.1": version "5.48.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.1.tgz#9efa8ee2aa471c6ab62e649f6e64d8d121bc2056" @@ -4730,6 +6590,19 @@ semver "^7.3.7" tsutils "^3.21.0" +"@typescript-eslint/typescript-estree@5.60.0": + version "5.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.0.tgz#4ddf1a81d32a850de66642d9b3ad1e3254fb1600" + integrity sha512-R43thAuwarC99SnvrBmh26tc7F6sPa2B3evkXp/8q954kYL6Ro56AwASYWtEEi+4j09GbiNAHqYwNNZuNlARGQ== + dependencies: + "@typescript-eslint/types" "5.60.0" + "@typescript-eslint/visitor-keys" "5.60.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + "@typescript-eslint/utils@5.48.1", "@typescript-eslint/utils@^5.13.0": version "5.48.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.48.1.tgz#20f2f4e88e9e2a0961cbebcb47a1f0f7da7ba7f9" @@ -4744,6 +6617,20 @@ eslint-utils "^3.0.0" semver "^7.3.7" +"@typescript-eslint/utils@5.60.0", "@typescript-eslint/utils@^5.58.0": + version "5.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.60.0.tgz#4667c5aece82f9d4f24a667602f0f300864b554c" + integrity sha512-ba51uMqDtfLQ5+xHtwlO84vkdjrqNzOnqrnwbMHMRY8Tqeme8C2Q8Fc7LajfGR+e3/4LoYiWXUM6BpIIbHJ4hQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.60.0" + "@typescript-eslint/types" "5.60.0" + "@typescript-eslint/typescript-estree" "5.60.0" + eslint-scope "^5.1.1" + semver "^7.3.7" + "@typescript-eslint/visitor-keys@5.48.1": version "5.48.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.1.tgz#79fd4fb9996023ef86849bf6f904f33eb6c8fccb" @@ -4752,6 +6639,14 @@ "@typescript-eslint/types" "5.48.1" eslint-visitor-keys "^3.3.0" +"@typescript-eslint/visitor-keys@5.60.0": + version "5.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.0.tgz#b48b29da3f5f31dd1656281727004589d2722a66" + integrity sha512-wm9Uz71SbCyhUKgcaPRauBdTegUyY/ZWl8gLwD/i/ybJqscrrdVSFImpvUz16BLPChIeKBK5Fa9s6KDQjsjyWw== + dependencies: + "@typescript-eslint/types" "5.60.0" + eslint-visitor-keys "^3.3.0" + "@uniswap/default-token-list@^2.0.0": version "2.3.0" resolved "https://registry.yarnpkg.com/@uniswap/default-token-list/-/default-token-list-2.3.0.tgz#e5e522e775791999643aac9b0faf1ccfb4c49bd8" @@ -4968,6 +6863,167 @@ dependencies: "@use-gesture/core" "10.2.23" +"@verdaccio/commons-api@10.2.0": + version "10.2.0" + resolved "https://registry.yarnpkg.com/@verdaccio/commons-api/-/commons-api-10.2.0.tgz#3b684c31749837b0574375bb2e10644ecea9fcca" + integrity sha512-F/YZANu4DmpcEV0jronzI7v2fGVWkQ5Mwi+bVmV+ACJ+EzR0c9Jbhtbe5QyLUuzR97t8R5E/Xe53O0cc2LukdQ== + dependencies: + http-errors "2.0.0" + http-status-codes "2.2.0" + +"@verdaccio/config@6.0.0-6-next.71": + version "6.0.0-6-next.71" + resolved "https://registry.yarnpkg.com/@verdaccio/config/-/config-6.0.0-6-next.71.tgz#ab28f3ec3f7430af2816bca3c574dcc5380b1a42" + integrity sha512-PvXXVNw28I9JWw7TYCbjA5jkkwbliZTB+TNXzWaFVOpW6s+94WWQzBNUUvPG67iPW4Wgo1ciHVdde/zOeFNfYw== + dependencies: + "@verdaccio/core" "6.0.0-6-next.71" + "@verdaccio/utils" "6.0.0-6-next.39" + debug "4.3.4" + js-yaml "4.1.0" + lodash "4.17.21" + minimatch "3.1.2" + yup "0.32.11" + +"@verdaccio/core@6.0.0-6-next.71": + version "6.0.0-6-next.71" + resolved "https://registry.yarnpkg.com/@verdaccio/core/-/core-6.0.0-6-next.71.tgz#a421cd460e45aac07c228ef5ec206fce0c0222be" + integrity sha512-leREshFssUKy+yI+Y6r9uyfuOEluLbdEs47WpI0hlV6htXkgBjfWIi884i4V/SCm+UIU8Dhn2iHPRo06KlRvFQ== + dependencies: + ajv "8.12.0" + core-js "3.30.2" + http-errors "2.0.0" + http-status-codes "2.2.0" + process-warning "1.0.0" + semver "7.5.0" + +"@verdaccio/file-locking@10.3.1": + version "10.3.1" + resolved "https://registry.yarnpkg.com/@verdaccio/file-locking/-/file-locking-10.3.1.tgz#cfc2436e0715954e0965f97dfcd87381d116f749" + integrity sha512-oqYLfv3Yg3mAgw9qhASBpjD50osj2AX4IwbkUtyuhhKGyoFU9eZdrbeW6tpnqUnj6yBMtAPm2eGD4BwQuX400g== + dependencies: + lockfile "1.0.4" + +"@verdaccio/file-locking@11.0.0-6-next.7": + version "11.0.0-6-next.7" + resolved "https://registry.yarnpkg.com/@verdaccio/file-locking/-/file-locking-11.0.0-6-next.7.tgz#ef5891992747fb07dc0a499708f4fa9960b3c5f0" + integrity sha512-S0GNoe2oBOgB7fKJN2vZqnl5qDEvdnTfKAfa47InXweJROeul6kjlE2/NlbNbK3zZID01VR1HFrFehMQO0Jyfw== + dependencies: + lockfile "1.0.4" + +"@verdaccio/local-storage@10.3.3": + version "10.3.3" + resolved "https://registry.yarnpkg.com/@verdaccio/local-storage/-/local-storage-10.3.3.tgz#fc31eea9e3da2f27e0cfaf5fe713834ed1fab9e9" + integrity sha512-/n0FH+1hxVg80YhYBfJuW7F2AuvLY2fra8/DTCilWDll9Y5yZDxwntZfcKHJLerCA4atrbJtvaqpWkoV3Q9x8w== + dependencies: + "@verdaccio/commons-api" "10.2.0" + "@verdaccio/file-locking" "10.3.1" + "@verdaccio/streams" "10.2.1" + async "3.2.4" + debug "4.3.4" + lodash "4.17.21" + lowdb "1.0.0" + mkdirp "1.0.4" + +"@verdaccio/logger-7@6.0.0-6-next.16": + version "6.0.0-6-next.16" + resolved "https://registry.yarnpkg.com/@verdaccio/logger-7/-/logger-7-6.0.0-6-next.16.tgz#c68160537f6fa340a1d16d62cf7bc25d62ce083a" + integrity sha512-QElvJcICP3DiJgDPUP4JRjWuImh6RbLWeK83iubrb/y865OmvrOgCgnBAZn0/i/L6buPFa/Sh5A07PBRuYWHgw== + dependencies: + "@verdaccio/logger-commons" "6.0.0-6-next.39" + pino "7.11.0" + +"@verdaccio/logger-commons@6.0.0-6-next.39": + version "6.0.0-6-next.39" + resolved "https://registry.yarnpkg.com/@verdaccio/logger-commons/-/logger-commons-6.0.0-6-next.39.tgz#8d51beae011e5ead1c28700eadea037114724e54" + integrity sha512-jdk8nDu2u3307XV2RtBo+FrC30xXGuSvZN7pQqFWCB9dJo21LpsMPTCgj9eBEwaAT+/ICTJURjO0VBkMlvcbGQ== + dependencies: + "@verdaccio/core" "6.0.0-6-next.71" + "@verdaccio/logger-prettify" "6.0.0-6-next.10" + colorette "2.0.20" + debug "4.3.4" + +"@verdaccio/logger-prettify@6.0.0-6-next.10": + version "6.0.0-6-next.10" + resolved "https://registry.yarnpkg.com/@verdaccio/logger-prettify/-/logger-prettify-6.0.0-6-next.10.tgz#013c1cfb8e2b463c9a36c85c0303fd9e6d04fd35" + integrity sha512-G9woGojHXoRg3W4fE2ZlNy2c25f5faqLWHxVdnDFbgbH6dieG+GzlyNwiOcrRC4LEkh7dWcgwuNMx1NZFojqhg== + dependencies: + colorette "2.0.20" + dayjs "1.11.7" + lodash "4.17.21" + pino-abstract-transport "1.0.0" + sonic-boom "3.3.0" + +"@verdaccio/middleware@6.0.0-6-next.50": + version "6.0.0-6-next.50" + resolved "https://registry.yarnpkg.com/@verdaccio/middleware/-/middleware-6.0.0-6-next.50.tgz#f8552fc3fb5bd2ab1155ea1b762b9c8d8d57ef31" + integrity sha512-eWn1C3p4Tc2ijqrzM0YjSb48DyNkH30UURjh23WyUVrMC7sn7s0DR9DlrRlVC8OSi8oqyQzV1KihowkzFLDcag== + dependencies: + "@verdaccio/config" "6.0.0-6-next.71" + "@verdaccio/core" "6.0.0-6-next.71" + "@verdaccio/url" "11.0.0-6-next.37" + "@verdaccio/utils" "6.0.0-6-next.39" + debug "4.3.4" + express "4.18.2" + express-rate-limit "5.5.1" + lodash "4.17.21" + lru-cache "7.18.3" + mime "2.6.0" + +"@verdaccio/search@6.0.0-6-next.2": + version "6.0.0-6-next.2" + resolved "https://registry.yarnpkg.com/@verdaccio/search/-/search-6.0.0-6-next.2.tgz#af11a66207368648ab678aad504720c4b007cac1" + integrity sha512-5Hkcxoj7crPn6Zth59I54af6KO5Ho7bzvCHCDbEwcmjewKcQJB4Kst4cEtpN/xA1ao0hqOSruEObl7/mqCq8hg== + +"@verdaccio/signature@6.0.0-6-next.2": + version "6.0.0-6-next.2" + resolved "https://registry.yarnpkg.com/@verdaccio/signature/-/signature-6.0.0-6-next.2.tgz#2df445354e0857daa6dd7a6b744a36d570a4602d" + integrity sha512-aFvMbxxHzJCpPmqSgVuQYvYN2RP11CoSEcTXikkyb1zF4Uf3cOy53zUZ1Y7iOKCRYTgWrmet9KP7+24e44GHxg== + dependencies: + debug "4.3.4" + jsonwebtoken "9.0.0" + lodash "4.17.21" + +"@verdaccio/streams@10.2.1": + version "10.2.1" + resolved "https://registry.yarnpkg.com/@verdaccio/streams/-/streams-10.2.1.tgz#9443d24d4f17672b8f8c8e147690557918ed2bcb" + integrity sha512-OojIG/f7UYKxC4dYX8x5ax8QhRx1b8OYUAMz82rUottCuzrssX/4nn5QE7Ank0DUSX3C9l/HPthc4d9uKRJqJQ== + +"@verdaccio/tarball@11.0.0-6-next.40": + version "11.0.0-6-next.40" + resolved "https://registry.yarnpkg.com/@verdaccio/tarball/-/tarball-11.0.0-6-next.40.tgz#ac9a3c8e67f9626b0748b0e0be65f86bdbbba973" + integrity sha512-1470DzyV9fdEsjqFhjOQ/5kU5EEgHXV8tyqKyZK+AQ+2g6mpj6NfU5Q82fpmoyzNlPGjREygE75KBv/niRCgRA== + dependencies: + "@verdaccio/core" "6.0.0-6-next.71" + "@verdaccio/url" "11.0.0-6-next.37" + "@verdaccio/utils" "6.0.0-6-next.39" + debug "4.3.4" + lodash "4.17.21" + +"@verdaccio/ui-theme@6.0.0-6-next.71": + version "6.0.0-6-next.71" + resolved "https://registry.yarnpkg.com/@verdaccio/ui-theme/-/ui-theme-6.0.0-6-next.71.tgz#45e27e6060ebb0158cf02366c802af777d03ca85" + integrity sha512-HX9NY0pZSg/H1C4GHLGzt91Xo5Oq8+VyZYN3JocHKev/EIE6G2/UuInKGAJxxdSIkno6jUyfrGZi2t9Qhgwwnw== + +"@verdaccio/url@11.0.0-6-next.37": + version "11.0.0-6-next.37" + resolved "https://registry.yarnpkg.com/@verdaccio/url/-/url-11.0.0-6-next.37.tgz#a4e28a9c119c304e94b1e667e67f250a3c86ce76" + integrity sha512-bPEq/aS77IzMUv7H1Zq4fVJeM7IxIImCan+ydQzFWGJfoGXgAz8p5PBm1+fqCgtEyQ/TeK6EowdXitX9lAIGVQ== + dependencies: + "@verdaccio/core" "6.0.0-6-next.71" + debug "4.3.4" + lodash "4.17.21" + validator "13.9.0" + +"@verdaccio/utils@6.0.0-6-next.39": + version "6.0.0-6-next.39" + resolved "https://registry.yarnpkg.com/@verdaccio/utils/-/utils-6.0.0-6-next.39.tgz#b7a2adf5a57c76b3d1aaf5d7cb6652546abd9f4c" + integrity sha512-V4+pBaXxObgofHcAw7BZXv2RZwCi2KaWNIcpQNYz6AcF15gLT0C2/8e1M8nMLb7Qnips3fetpA26VNNvl5XRdw== + dependencies: + "@verdaccio/core" "6.0.0-6-next.71" + lodash "4.17.21" + minimatch "3.1.2" + semver "7.5.0" + "@vibrant/color@^3.2.1-alpha.1": version "3.2.1-alpha.1" resolved "https://registry.yarnpkg.com/@vibrant/color/-/color-3.2.1-alpha.1.tgz#1bcee4545d2276d36f9a1acb42ab3485a9b489ec" @@ -5056,6 +7112,74 @@ dependencies: "@vibrant/types" "^3.2.1-alpha.1" +"@vitejs/plugin-react@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-3.1.0.tgz#d1091f535eab8b83d6e74034d01e27d73c773240" + integrity sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g== + dependencies: + "@babel/core" "^7.20.12" + "@babel/plugin-transform-react-jsx-self" "^7.18.6" + "@babel/plugin-transform-react-jsx-source" "^7.19.6" + magic-string "^0.27.0" + react-refresh "^0.14.0" + +"@vitest/expect@0.31.4": + version "0.31.4" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-0.31.4.tgz#115c517404488bf3cb6ce4ac411c40d8e86891b8" + integrity sha512-tibyx8o7GUyGHZGyPgzwiaPaLDQ9MMuCOrc03BYT0nryUuhLbL7NV2r/q98iv5STlwMgaKuFJkgBW/8iPKwlSg== + dependencies: + "@vitest/spy" "0.31.4" + "@vitest/utils" "0.31.4" + chai "^4.3.7" + +"@vitest/runner@0.31.4": + version "0.31.4" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-0.31.4.tgz#e99abee89132a500d9726a53b58dfc9160db1078" + integrity sha512-Wgm6UER+gwq6zkyrm5/wbpXGF+g+UBB78asJlFkIOwyse0pz8lZoiC6SW5i4gPnls/zUcPLWS7Zog0LVepXnpg== + dependencies: + "@vitest/utils" "0.31.4" + concordance "^5.0.4" + p-limit "^4.0.0" + pathe "^1.1.0" + +"@vitest/snapshot@0.31.4": + version "0.31.4" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-0.31.4.tgz#59a42046fec4950a1ac70cf0ec64aada3b995559" + integrity sha512-LemvNumL3NdWSmfVAMpXILGyaXPkZbG5tyl6+RQSdcHnTj6hvA49UAI8jzez9oQyE/FWLKRSNqTGzsHuk89LRA== + dependencies: + magic-string "^0.30.0" + pathe "^1.1.0" + pretty-format "^27.5.1" + +"@vitest/spy@0.31.4": + version "0.31.4" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-0.31.4.tgz#fce8e348cea32deff79996d116c67893b19cc47d" + integrity sha512-3ei5ZH1s3aqbEyftPAzSuunGICRuhE+IXOmpURFdkm5ybUADk+viyQfejNk6q8M5QGX8/EVKw+QWMEP3DTJDag== + dependencies: + tinyspy "^2.1.0" + +"@vitest/ui@^0.31.0": + version "0.31.4" + resolved "https://registry.yarnpkg.com/@vitest/ui/-/ui-0.31.4.tgz#465a4762d7937e3322fe66f94c2378bacfeba1ac" + integrity sha512-sKM16ITX6HrNFF+lNZ2AQAen4/6Bx2i6KlBfIvkUjcTgc5YII/j2ltcX14oCUv4EA0OTWGQuGhO3zDoAsTENGA== + dependencies: + "@vitest/utils" "0.31.4" + fast-glob "^3.2.12" + fflate "^0.7.4" + flatted "^3.2.7" + pathe "^1.1.0" + picocolors "^1.0.0" + sirv "^2.0.3" + +"@vitest/utils@0.31.4": + version "0.31.4" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-0.31.4.tgz#5cfdcecfd604a7dbe3972cfe0f2b1e0af1246ad2" + integrity sha512-DobZbHacWznoGUfYU8XDPY78UubJxXfMNY1+SUdOp1NsI34eopSA6aZMeaGu10waSOeYwE8lxrd/pLfT0RMxjQ== + dependencies: + concordance "^5.0.4" + loupe "^2.3.6" + pretty-format "^27.5.1" + "@walletconnect/browser-utils@^1.8.0": version "1.8.0" resolved "https://registry.yarnpkg.com/@walletconnect/browser-utils/-/browser-utils-1.8.0.tgz#33c10e777aa6be86c713095b5206d63d32df0951" @@ -5707,6 +7831,11 @@ js-yaml "^3.10.0" tslib "^2.4.0" +"@zeit/schemas@2.29.0": + version "2.29.0" + resolved "https://registry.yarnpkg.com/@zeit/schemas/-/schemas-2.29.0.tgz#a59ae6ebfdf4ddc66a876872dd736baa58b6696c" + integrity sha512-g5QiLIfbg3pLuYUJPlisNKY+epQJTcMDsOnVNkscrDP1oi7vmJnzOANYJI/1pZcVJ6umUkBv3aFtlg1UvUHGzA== + "@zkochan/js-yaml@0.0.6": version "0.0.6" resolved "https://registry.yarnpkg.com/@zkochan/js-yaml/-/js-yaml-0.0.6.tgz#975f0b306e705e28b8068a07737fa46d3fc04826" @@ -5714,7 +7843,7 @@ dependencies: argparse "^2.0.1" -JSONStream@^1.0.4, JSONStream@^1.3.5: +JSONStream@1.3.5, JSONStream@^1.0.4, JSONStream@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== @@ -5722,7 +7851,7 @@ JSONStream@^1.0.4, JSONStream@^1.3.5: jsonparse "^1.2.0" through ">=2.2.7 <3" -abab@^2.0.3, abab@^2.0.5: +abab@^2.0.3, abab@^2.0.5, abab@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== @@ -5755,6 +7884,14 @@ acorn-globals@^6.0.0: acorn "^7.1.1" acorn-walk "^7.1.1" +acorn-globals@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3" + integrity sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q== + dependencies: + acorn "^8.1.0" + acorn-walk "^8.0.2" + acorn-import-assertions@^1.7.6: version "1.8.0" resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" @@ -5779,7 +7916,7 @@ acorn-walk@^7.0.0, acorn-walk@^7.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -acorn-walk@^8.0.0, acorn-walk@^8.1.1: +acorn-walk@^8.0.0, acorn-walk@^8.0.2, acorn-walk@^8.1.1, acorn-walk@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== @@ -5794,6 +7931,11 @@ acorn@^8.0.4, acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8 resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== +acorn@^8.1.0, acorn@^8.8.1, acorn@^8.8.2: + version "8.9.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.9.0.tgz#78a16e3b2bcc198c10822786fa6679e245db5b59" + integrity sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ== + address@^1.0.1, address@^1.1.2: version "1.2.2" resolved "https://registry.yarnpkg.com/address/-/address-1.2.2.tgz#2b5248dac5485a6390532c6a517fda2e3faac89e" @@ -5865,17 +8007,17 @@ ajv-keywords@^5.0.0: dependencies: fast-deep-equal "^3.1.3" -ajv@^6.10.0, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== +ajv@8.11.0: + version "8.11.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" + integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg== dependencies: fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.11.0, ajv@^8.6.0, ajv@^8.8.0: +ajv@8.12.0, ajv@^8.0.0, ajv@^8.11.0, ajv@^8.6.0, ajv@^8.8.0: version "8.12.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== @@ -5885,7 +8027,17 @@ ajv@^8.0.0, ajv@^8.11.0, ajv@^8.6.0, ajv@^8.8.0: require-from-string "^2.0.2" uri-js "^4.2.2" -ansi-align@^3.0.0: +ajv@^6.10.0, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5, ajv@~6.12.6: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-align@^3.0.0, ansi-align@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== @@ -5963,6 +8115,11 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + any-base@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/any-base/-/any-base-1.1.0.tgz#ae101a62bc08a597b4c9ab5b7089d456630549fe" @@ -5991,6 +8148,11 @@ anymatch@^3.0.3, anymatch@~3.1.1, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +apache-md5@1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/apache-md5/-/apache-md5-1.1.8.tgz#ea79c6feb03abfed42b2830dde06f75df5e3bbd9" + integrity sha512-FCAJojipPn0bXjuEpjOOOMN8FZDkxfWWp4JGN9mifU2IhxvKyXZYqpzPHdnTSUpmPDy+tsslB6Z1g+Vg6nVbYA== + aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -6009,17 +8171,17 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" +arg@5.0.2, arg@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== -arg@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" - integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== - -argparse@^1.0.7: +argparse@^1.0.7, argparse@~1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== @@ -6031,7 +8193,7 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-query@^5.0.0, aria-query@^5.1.3: +aria-query@5.1.3, aria-query@^5.0.0, aria-query@^5.1.3: version "5.1.3" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== @@ -6207,11 +8369,18 @@ async-retry@^1.3.1: dependencies: retry "0.13.1" -async@^3.0.0, async@^3.1.0, async@^3.2.0, async@^3.2.3: +async@3.2.4, async@^3.0.0, async@^3.1.0, async@^3.2.0, async@^3.2.3: version "3.2.4" resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== +async@^2.6.4: + version "2.6.4" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" + integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== + dependencies: + lodash "^4.17.14" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -6294,6 +8463,14 @@ axios@^0.21.0, axios@^0.21.1: dependencies: follow-redirects "^1.14.0" +axios@^0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== + dependencies: + follow-redirects "^1.14.9" + form-data "^4.0.0" + axios@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" @@ -6333,6 +8510,19 @@ babel-jest@^27.4.2, babel-jest@^27.5.1: graceful-fs "^4.2.9" slash "^3.0.0" +babel-jest@^29.4.1, babel-jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.5.0.tgz#3fe3ddb109198e78b1c88f9ebdecd5e4fc2f50a5" + integrity sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q== + dependencies: + "@jest/transform" "^29.5.0" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^29.5.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + babel-loader@^8.2.3: version "8.3.0" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.3.0.tgz#124936e841ba4fe8176786d6ff28add1f134d6a8" @@ -6350,6 +8540,15 @@ babel-messages@^6.23.0: dependencies: babel-runtime "^6.22.0" +babel-plugin-const-enum@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-const-enum/-/babel-plugin-const-enum-1.2.0.tgz#3d25524106f68f081e187829ba736b251c289861" + integrity sha512-o1m/6iyyFnp9MRsK1dHF3bneqyf3AlM2q3A/YbgQr2pCat6B6XJVDv2TXqzfY2RYUi4mak6WAksSBPlyYGx9dg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-typescript" "^7.3.3" + "@babel/traverse" "^7.16.0" + babel-plugin-emotion@^10.0.27: version "10.2.2" resolved "https://registry.yarnpkg.com/babel-plugin-emotion/-/babel-plugin-emotion-10.2.2.tgz#a1fe3503cff80abfd0bdda14abd2e8e57a79d17d" @@ -6387,7 +8586,17 @@ babel-plugin-jest-hoist@^27.5.1: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-plugin-macros@^2.0.0: +babel-plugin-jest-hoist@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz#a97db437936f441ec196990c9738d4b88538618a" + integrity sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.1.14" + "@types/babel__traverse" "^7.0.6" + +babel-plugin-macros@^2.0.0, babel-plugin-macros@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" integrity sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg== @@ -6419,6 +8628,15 @@ 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== + dependencies: + "@babel/compat-data" "^7.17.7" + "@babel/helper-define-polyfill-provider" "^0.4.0" + semver "^6.1.1" + babel-plugin-polyfill-corejs3@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a" @@ -6427,6 +8645,14 @@ 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== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.4.0" + core-js-compat "^3.30.1" + babel-plugin-polyfill-regenerator@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" @@ -6434,6 +8660,13 @@ 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== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.4.0" + "babel-plugin-styled-components@>= 1.12.0": version "2.0.7" resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.7.tgz#c81ef34b713f9da2b7d3f5550df0d1e19e798086" @@ -6455,6 +8688,13 @@ babel-plugin-transform-react-remove-prop-types@^0.4.24: resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz#f2edaf9b4c6a5fbe5c1d678bfb531078c1555f3a" integrity sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA== +babel-plugin-transform-typescript-metadata@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-typescript-metadata/-/babel-plugin-transform-typescript-metadata-0.3.2.tgz#7a327842d8c36ffe07ee1b5276434e56c297c9b7" + integrity sha512-mWEvCQTgXQf48yDqgN7CH50waTyYBeP2Lpqx4nNWab9sxEpdXVeKgfj1qYI2/TgUPQtNFZ85i3PemRtnXVYYJg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + babel-preset-current-node-syntax@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" @@ -6481,6 +8721,14 @@ babel-preset-jest@^27.5.1: babel-plugin-jest-hoist "^27.5.1" babel-preset-current-node-syntax "^1.0.0" +babel-preset-jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz#57bc8cc88097af7ff6a5ab59d1cd29d52a5916e2" + integrity sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg== + dependencies: + babel-plugin-jest-hoist "^29.5.0" + babel-preset-current-node-syntax "^1.0.0" + babel-preset-react-app@^10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz#ed6005a20a24f2c88521809fa9aea99903751584" @@ -6594,6 +8842,13 @@ base64id@1.0.0: resolved "https://registry.yarnpkg.com/base64id/-/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6" integrity sha512-rz8L+d/xByiB/vLVftPkyY215fqNrmasrcJsYkVcm4TgJNz+YXKrFaFAWibSaHkiKoSgMDCb+lipOIRQNGYesw== +basic-auth@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" + integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== + dependencies: + safe-buffer "5.1.2" + batch@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" @@ -6615,6 +8870,11 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +bcryptjs@2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb" + integrity sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ== + bech32@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" @@ -6730,11 +8990,16 @@ block-stream@*: dependencies: inherits "~2.0.0" -bluebird@^3.5.0, bluebird@^3.5.5, bluebird@^3.7.2: +bluebird@3.7.2, bluebird@^3.5.0, bluebird@^3.5.5, bluebird@^3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== +blueimp-md5@^2.10.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.19.0.tgz#b53feea5498dcb53dc6ec4b823adb84b729c4af0" + integrity sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w== + bmp-js@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.1.0.tgz#e05a63f796a6c1ff25f4771ec7adadc148c07233" @@ -6788,7 +9053,7 @@ body-parser@1.20.1: type-is "~1.6.18" unpipe "1.0.0" -body-parser@^1.16.0: +body-parser@1.20.2, body-parser@^1.16.0: version "1.20.2" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== @@ -6843,6 +9108,20 @@ borsh@^0.7.0: bs58 "^4.0.0" text-encoding-utf-8 "^1.0.2" +boxen@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-7.0.0.tgz#9e5f8c26e716793fc96edcf7cf754cdf5e3fbf32" + integrity sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg== + dependencies: + ansi-align "^3.0.1" + camelcase "^7.0.0" + chalk "^5.0.1" + cli-boxes "^3.0.0" + string-width "^5.1.2" + type-fest "^2.13.0" + widest-line "^4.0.1" + wrap-ansi "^8.0.1" + boxen@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" @@ -6968,6 +9247,23 @@ browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4 node-releases "^2.0.6" update-browserslist-db "^1.0.9" +browserslist@^4.21.5: + 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.30001503" + electron-to-chromium "^1.4.431" + node-releases "^2.0.12" + update-browserslist-db "^1.0.11" + +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + bs58@^4.0.0, bs58@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" @@ -7014,6 +9310,11 @@ buffer-crc32@~0.2.3: resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== + buffer-equal@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b" @@ -7122,6 +9423,11 @@ bytes@3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +cac@^6.7.14: + version "6.7.14" + resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + cacheable-lookup@^5.0.3: version "5.0.4" resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" @@ -7227,6 +9533,11 @@ camelcase@^6.0.0, camelcase@^6.2.0, camelcase@^6.2.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== +camelcase@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-7.0.1.tgz#f02e50af9fd7782bc8b88a3558c32fd3a388f048" + integrity sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw== + camelize@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.1.tgz#89b7e16884056331a35d6b5ad064332c91daa6c3" @@ -7247,6 +9558,11 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001426: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001445.tgz#cf2d4eb93f2bcdf0310de9dd6d18be271bc0b447" integrity sha512-8sdQIdMztYmzfTMO6KfLny878Ln9c2M0fc7EH60IjlP4Dc4PiCy7K2Vl3ITmWgOyPgVQKa5x+UP/KqFsxj4mBg== +caniuse-lite@^1.0.30001503: + version "1.0.30001504" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001504.tgz#eaf77e5c852dfa5f82c4924468c30602ac53744a" + integrity sha512-5uo7eoOp2mKbWyfMXnGO9rJWOGU8duvzEiYITW+wivukL7yHH4gX9yuRaobu6El4jPxo6jKZfG+N6fB621GD/Q== + case-sensitive-paths-webpack-plugin@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4" @@ -7275,6 +9591,18 @@ chai@^4.3.7: pathval "^1.1.1" type-detect "^4.0.5" +chalk-template@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/chalk-template/-/chalk-template-0.4.0.tgz#692c034d0ed62436b9062c1707fadcd0f753204b" + integrity sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg== + dependencies: + chalk "^4.1.2" + +chalk@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.0.1.tgz#ca57d71e82bb534a296df63bbacc4a1c22b2a4b6" + integrity sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w== + chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -7311,6 +9639,11 @@ chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^5.0.1: + version "5.2.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.2.0.tgz#249623b7d66869c673699fb66d65723e54dfcfb3" + integrity sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA== + char-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" @@ -7351,7 +9684,7 @@ check-error@^1.0.2: resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== -check-more-types@^2.24.0: +check-more-types@2.24.0, check-more-types@^2.24.0: version "2.24.0" resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" integrity sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA== @@ -7376,7 +9709,7 @@ chokidar@3.5.1: optionalDependencies: fsevents "~2.3.1" -chokidar@3.5.3, chokidar@^3.4.2, chokidar@^3.5.2, chokidar@^3.5.3: +chokidar@3.5.3, chokidar@^3.4.2, chokidar@^3.5.1, chokidar@^3.5.2, chokidar@^3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -7483,6 +9816,11 @@ cli-boxes@^2.2.0: resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== +cli-boxes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-3.0.0.tgz#71a10c716feeba005e4504f36329ef0b17cf3145" + integrity sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g== + cli-cursor@3.1.0, cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" @@ -7529,6 +9867,27 @@ cli-width@^3.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== +client-only@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" + integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== + +clipanion@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/clipanion/-/clipanion-3.2.0.tgz#ea55ce3fe27becb4ddd4915df6fb3de0f9e79a5c" + integrity sha512-XaPQiJQZKbyaaDbv5yR/cAt/ORfZfENkr4wGQj+Go/Uf/65ofTQBCPirgWFeJctZW24V3mxrFiEnEmqBflrJYA== + dependencies: + typanion "^3.8.0" + +clipboardy@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-3.0.0.tgz#f3876247404d334c9ed01b6f269c11d09a5e3092" + integrity sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg== + dependencies: + arch "^2.2.0" + execa "^5.1.1" + is-wsl "^2.2.0" + clipboardy@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-2.3.0.tgz#3c2903650c68e46a91b388985bc2774287dba290" @@ -7619,6 +9978,11 @@ coa@^2.0.2: chalk "^2.4.1" q "^1.1.2" +code-block-writer@^11.0.3: + version "11.0.3" + resolved "https://registry.yarnpkg.com/code-block-writer/-/code-block-writer-11.0.3.tgz#9eec2993edfb79bfae845fbc093758c0a0b73b76" + integrity sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw== + code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" @@ -7690,6 +10054,11 @@ colord@^2.9.1: resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== +colorette@2.0.20: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + colorette@^1.2.2: version "1.4.0" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" @@ -7710,6 +10079,11 @@ colors@^1.4.0: resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== +colors@~1.2.1: + version "1.2.5" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.2.5.tgz#89c7ad9a374bc030df8013241f68136ed8835afc" + integrity sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg== + combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -7747,6 +10121,11 @@ commander@7, commander@^7.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +commander@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + commander@^2.15.0, commander@^2.20.0, commander@^2.20.3: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -7827,7 +10206,7 @@ compressible@~2.0.16: dependencies: mime-db ">= 1.43.0 < 2" -compression@^1.7.4: +compression@1.7.4, compression@^1.7.4: version "1.7.4" resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== @@ -7855,6 +10234,20 @@ concat-stream@^2.0.0: readable-stream "^3.0.2" typedarray "^0.0.6" +concordance@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/concordance/-/concordance-5.0.4.tgz#9896073261adced72f88d60e4d56f8efc4bbbbd2" + integrity sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw== + dependencies: + date-time "^3.1.0" + esutils "^2.0.3" + fast-diff "^1.2.0" + js-string-escape "^1.0.1" + lodash "^4.17.15" + md5-hex "^3.0.1" + semver "^7.3.2" + well-known-symbols "^2.0.0" + configstore@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" @@ -7867,7 +10260,7 @@ configstore@^5.0.1: write-file-atomic "^3.0.0" xdg-basedir "^4.0.0" -confusing-browser-globals@^1.0.11: +confusing-browser-globals@^1.0.11, confusing-browser-globals@^1.0.9: version "1.0.11" resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== @@ -7882,6 +10275,11 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + integrity sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA== + content-disposition@0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" @@ -7942,6 +10340,11 @@ convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0, resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -7957,6 +10360,14 @@ cookie@0.5.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== +cookies@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.8.0.tgz#1293ce4b391740a8406e3c9870e828c4b54f3f90" + integrity sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow== + dependencies: + depd "~2.0.0" + keygrip "~1.1.0" + copy-to-clipboard@^3.2.0, copy-to-clipboard@^3.3.1: version "3.3.3" resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" @@ -7971,11 +10382,23 @@ core-js-compat@^3.25.1: dependencies: browserslist "^4.21.4" +core-js-compat@^3.30.1, core-js-compat@^3.30.2: + version "3.31.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.31.0.tgz#4030847c0766cc0e803dcdfb30055d7ef2064bf1" + integrity sha512-hM7YCu1cU6Opx7MXNu0NuumM0ezNeAeRKadixyiQELWY3vT3De9S4J5ZBMraWV2vZnrE1Cirl0GtFtDtMUXzPw== + dependencies: + browserslist "^4.21.5" + core-js-pure@^3.23.3: version "3.27.1" resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.27.1.tgz#ede4a6b8440585c7190062757069c01d37a19dca" integrity sha512-BS2NHgwwUppfeoqOXqi08mUqS5FiZpuRuJJpKsaME7kJz0xxuk0xkhDdfMIlP/zLa80krBqss1LtD7f889heAw== +core-js@3.30.2: + version "3.30.2" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.30.2.tgz#6528abfda65e5ad728143ea23f7a14f0dcf503fc" + integrity sha512-uBJiDmwqsbJCWHAwjrx3cvjbMXP7xD72Dmsn5LOJpiRmE3WbBbN5rCqQ2Qh6Ek6/eOrjlWngEynBWo4VxerQhg== + core-js@^2.4.0: version "2.6.12" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" @@ -7996,7 +10419,7 @@ core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== -cors@^2.8.1: +cors@2.8.5, cors@^2.8.1: version "2.8.5" resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== @@ -8004,6 +10427,11 @@ cors@^2.8.1: object-assign "^4" vary "^1" +corser@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/corser/-/corser-2.0.1.tgz#8eda252ecaab5840dcd975ceb90d9370c819ff87" + integrity sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ== + cosmiconfig-typescript-loader@^1.0.0: version "1.0.9" resolved "https://registry.yarnpkg.com/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-1.0.9.tgz#69c523f7e8c3d9f27f563d02bbeadaf2f27212d3" @@ -8057,6 +10485,16 @@ cosmiconfig@^8.0.0: parse-json "^5.0.0" path-type "^4.0.0" +cosmiconfig@^8.1.3: + version "8.2.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.2.0.tgz#f7d17c56a590856cd1e7cee98734dca272b0d8fd" + integrity sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ== + dependencies: + import-fresh "^3.2.1" + js-yaml "^4.1.0" + parse-json "^5.0.0" + path-type "^4.0.0" + craco-workbox@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/craco-workbox/-/craco-workbox-0.2.0.tgz#13e5c3c87c49a67d3d64ad6d636868c623cd7d89" @@ -8276,6 +10714,17 @@ css-select@^4.1.3: domutils "^2.8.0" nth-check "^2.0.1" +css-select@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" + integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== + dependencies: + boolbase "^1.0.0" + css-what "^6.1.0" + domhandler "^5.0.2" + domutils "^3.0.1" + nth-check "^2.0.1" + css-to-react-native@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.1.0.tgz#e783474149997608986afcff614405714a8fe1ac" @@ -8301,12 +10750,28 @@ css-tree@^1.1.2, css-tree@^1.1.3: mdn-data "2.0.14" source-map "^0.6.1" +css-tree@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20" + integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw== + dependencies: + mdn-data "2.0.30" + source-map-js "^1.0.1" + +css-tree@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.2.1.tgz#36115d382d60afd271e377f9c5f67d02bd48c032" + integrity sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA== + dependencies: + mdn-data "2.0.28" + source-map-js "^1.0.1" + css-what@^3.2.1: version "3.4.2" resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4" integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ== -css-what@^6.0.1: +css-what@^6.0.1, css-what@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== @@ -8382,11 +10847,23 @@ csso@^4.0.2, csso@^4.2.0: dependencies: css-tree "^1.1.2" +csso@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6" + integrity sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ== + dependencies: + css-tree "~2.2.0" + cssom@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== +cssom@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" + integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== + cssom@~0.3.6: version "0.3.8" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" @@ -8747,12 +11224,28 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +data-urls@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143" + integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ== + dependencies: + abab "^2.0.6" + whatwg-mimetype "^3.0.0" + whatwg-url "^11.0.0" + date-fns@^2.16.1, date-fns@^2.29.3: version "2.29.3" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8" integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA== -dayjs@^1.10.4: +date-time@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/date-time/-/date-time-3.1.0.tgz#0d1e934d170579f481ed8df1e2b8ff70ee845e1e" + integrity sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg== + dependencies: + time-zone "^1.0.0" + +dayjs@1.11.7, dayjs@^1.10.4: version "1.11.7" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2" integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ== @@ -8815,7 +11308,7 @@ decimal.js-light@^2.5.0: resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== -decimal.js@^10.2.1: +decimal.js@^10.2.1, decimal.js@^10.4.2: version "10.4.3" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== @@ -8889,6 +11382,11 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== +deepmerge@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + default-gateway@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" @@ -8958,7 +11456,7 @@ delimit-stream@0.1.0: resolved "https://registry.yarnpkg.com/delimit-stream/-/delimit-stream-0.1.0.tgz#9b8319477c0e5f8aeb3ce357ae305fc25ea1cd2b" integrity sha512-a02fiQ7poS5CnjiJBAsjGLPp5EwVoGHNeu9sziBd9huppRfsAFIpv5zNLv0V1gbop53ilngAf5Kf331AwcoRBQ== -depd@2.0.0: +depd@2.0.0, depd@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== @@ -9014,6 +11512,14 @@ detect-port-alt@^1.1.6: address "^1.0.1" debug "^2.6.0" +detect-port@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.5.1.tgz#451ca9b6eaf20451acb0799b8ab40dff7718727b" + integrity sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ== + dependencies: + address "^1.0.1" + debug "4" + detective@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.1.tgz#6af01eeda11015acb0e73f933242b70f24f91034" @@ -9038,6 +11544,11 @@ diff-sequences@^29.3.1: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.3.1.tgz#104b5b95fe725932421a9c6e5b4bef84c3f2249e" integrity sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ== +diff-sequences@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" + integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== + diff@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" @@ -9190,6 +11701,13 @@ domexception@^2.0.1: dependencies: webidl-conversions "^5.0.0" +domexception@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" + integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== + dependencies: + webidl-conversions "^7.0.0" + domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" @@ -9291,7 +11809,7 @@ duplexer3@^0.1.4: resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.5.tgz#0b5e4d7bad5de8901ea4440624c8e1d20099217e" integrity sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA== -duplexer@^0.1.1, duplexer@^0.1.2: +duplexer@^0.1.1, duplexer@^0.1.2, duplexer@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== @@ -9306,6 +11824,11 @@ duplexify@^4.1.2: readable-stream "^3.1.1" stream-shift "^1.0.0" +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -9314,6 +11837,13 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -9333,6 +11863,13 @@ ejs@^3.1.6: dependencies: jake "^10.8.5" +ejs@^3.1.7: + version "3.1.9" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361" + integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ== + dependencies: + jake "^10.8.5" + electron-fetch@^1.7.2: version "1.9.1" resolved "https://registry.yarnpkg.com/electron-fetch/-/electron-fetch-1.9.1.tgz#e28bfe78d467de3f2dec884b1d72b8b05322f30f" @@ -9345,6 +11882,11 @@ 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.431: + version "1.4.433" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.433.tgz#305ef5f8ea5fe65d252aae4b0e1088f9e4842533" + integrity sha512-MGO1k0w1RgrfdbLVwmXcDhHHuxCn2qRgR7dYsJvWFKDttvYPx6FNzCGG0c/fBBvzK2LDh3UV7Tt9awnHnvAAUQ== + elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" @@ -9368,6 +11910,11 @@ emittery@^0.10.2: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933" integrity sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw== +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== + emittery@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" @@ -9477,7 +12024,12 @@ entities@^4.2.0, entities@^4.3.0: resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174" integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA== -envinfo@^7.8.1: +entities@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + +envinfo@7.8.1, envinfo@^7.8.1: version "7.8.1" resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== @@ -9643,6 +12195,34 @@ es6-symbol@^3.1.1, es6-symbol@^3.1.3: d "^1.0.1" ext "^1.1.2" +esbuild@^0.17.5: + version "0.17.19" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.19.tgz#087a727e98299f0462a3d0bcdd9cd7ff100bd955" + integrity sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw== + optionalDependencies: + "@esbuild/android-arm" "0.17.19" + "@esbuild/android-arm64" "0.17.19" + "@esbuild/android-x64" "0.17.19" + "@esbuild/darwin-arm64" "0.17.19" + "@esbuild/darwin-x64" "0.17.19" + "@esbuild/freebsd-arm64" "0.17.19" + "@esbuild/freebsd-x64" "0.17.19" + "@esbuild/linux-arm" "0.17.19" + "@esbuild/linux-arm64" "0.17.19" + "@esbuild/linux-ia32" "0.17.19" + "@esbuild/linux-loong64" "0.17.19" + "@esbuild/linux-mips64el" "0.17.19" + "@esbuild/linux-ppc64" "0.17.19" + "@esbuild/linux-riscv64" "0.17.19" + "@esbuild/linux-s390x" "0.17.19" + "@esbuild/linux-x64" "0.17.19" + "@esbuild/netbsd-x64" "0.17.19" + "@esbuild/openbsd-x64" "0.17.19" + "@esbuild/sunos-x64" "0.17.19" + "@esbuild/win32-arm64" "0.17.19" + "@esbuild/win32-ia32" "0.17.19" + "@esbuild/win32-x64" "0.17.19" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -9736,6 +12316,27 @@ eslint-plugin-flowtype@^8.0.3: lodash "^4.17.21" string-natural-compare "^3.0.1" +eslint-plugin-import@2.27.5: + version "2.27.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65" + integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== + dependencies: + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + array.prototype.flatmap "^1.3.1" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.7" + eslint-module-utils "^2.7.4" + has "^1.0.3" + is-core-module "^2.11.0" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.values "^1.1.6" + resolve "^1.22.1" + semver "^6.3.0" + tsconfig-paths "^3.14.1" + eslint-plugin-import@^2.25.3: version "2.27.4" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.4.tgz#319c2f6f6580e1678d674a258ee5e981c10cc25b" @@ -9764,7 +12365,7 @@ eslint-plugin-jest@^25.3.0: dependencies: "@typescript-eslint/experimental-utils" "^5.0.0" -eslint-plugin-jsx-a11y@^6.5.1: +eslint-plugin-jsx-a11y@6.7.1, eslint-plugin-jsx-a11y@^6.5.1: version "6.7.1" resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz#fca5e02d115f48c9a597a6894d5bcec2f7a76976" integrity sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA== @@ -9786,11 +12387,32 @@ eslint-plugin-jsx-a11y@^6.5.1: object.fromentries "^2.0.6" semver "^6.3.0" -eslint-plugin-react-hooks@^4.3.0: +eslint-plugin-react-hooks@4.6.0, eslint-plugin-react-hooks@^4.3.0: version "4.6.0" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== +eslint-plugin-react@7.32.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== + dependencies: + array-includes "^3.1.6" + array.prototype.flatmap "^1.3.1" + array.prototype.tosorted "^1.1.1" + doctrine "^2.1.0" + estraverse "^5.3.0" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.1.2" + object.entries "^1.1.6" + object.fromentries "^2.0.6" + object.hasown "^1.1.2" + object.values "^1.1.6" + prop-types "^15.8.1" + resolve "^2.0.0-next.4" + semver "^6.3.0" + string.prototype.matchall "^4.0.8" + eslint-plugin-react@^7.27.1: version "7.32.0" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.32.0.tgz#d80f794a638c5770f952ba2ae793f0a516be7c09" @@ -9968,7 +12590,12 @@ estree-walker@^1.0.1: resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== -esutils@^2.0.2: +estree-walker@^2.0.1, estree-walker@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + +esutils@^2.0.2, esutils@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== @@ -10209,6 +12836,19 @@ ethjs-util@0.1.6, ethjs-util@^0.1.3: is-hex-prefixed "1.0.0" strip-hex-prefix "1.0.0" +event-stream@=3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" + integrity sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g== + dependencies: + duplexer "~0.1.1" + from "~0" + map-stream "~0.1.0" + pause-stream "0.0.11" + split "0.3" + stream-combiner "~0.0.4" + through "~2.3.1" + event-target-shim@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" @@ -10262,6 +12902,21 @@ execa@4.1.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execa@5.1.1, execa@^5.0.0, execa@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + execa@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" @@ -10288,21 +12943,6 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" - executable@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" @@ -10346,12 +12986,28 @@ expect@^29.0.0: jest-message-util "^29.3.1" jest-util "^29.3.1" +expect@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.5.0.tgz#68c0509156cb2a0adb8865d413b137eeaae682f7" + integrity sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg== + dependencies: + "@jest/expect-utils" "^29.5.0" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-util "^29.5.0" + exponential-backoff@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== -express@^4.14.0, express@^4.17.2, express@^4.17.3: +express-rate-limit@5.5.1: + version "5.5.1" + resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-5.5.1.tgz#110c23f6a65dfa96ab468eda95e71697bc6987a2" + integrity sha512-MTjE2eIbHv5DyfuFz4zLYWxpqVhEhkTiwFGuB74Q9CSou2WHO52nlE5y3Zlg6SIsiYUIPj6ifFxnkPz6O3sIUg== + +express@4.18.2, express@^4.14.0, express@^4.17.2, express@^4.17.3: version "4.18.2" resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== @@ -10455,6 +13111,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-diff@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" + integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== + fast-fifo@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.1.0.tgz#17d1a3646880b9891dfa0c54e69c5fef33cad779" @@ -10482,7 +13143,7 @@ fast-glob@^3.2.12, fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -10497,7 +13158,7 @@ fast-redact@^3.0.0: resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.1.2.tgz#d58e69e9084ce9fa4c1a6fa98a3e1ecf5d7839aa" integrity sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw== -fast-safe-stringify@^2.0.6, fast-safe-stringify@^2.0.8: +fast-safe-stringify@2.1.1, fast-safe-stringify@^2.0.6, fast-safe-stringify@^2.0.8: version "2.1.1" resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== @@ -10507,6 +13168,13 @@ fast-stable-stringify@^1.0.0: resolved "https://registry.yarnpkg.com/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz#5c5543462b22aeeefd36d05b34e51c78cb86d313" integrity sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag== +fast-url-parser@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" + integrity sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ== + dependencies: + punycode "^1.3.2" + fastq@^1.6.0: version "1.15.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" @@ -10535,6 +13203,11 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" +fflate@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.7.4.tgz#61587e5d958fdabb5a9368a302c25363f4f69f50" + integrity sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw== + figures@3.2.0, figures@^3.0.0, figures@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" @@ -10680,7 +13353,7 @@ flatmap@0.0.3: resolved "https://registry.yarnpkg.com/flatmap/-/flatmap-0.0.3.tgz#1f18a4d938152d495965f9c958d923ab2dd669b4" integrity sha512-OuR+o7kHVe+x9RtIujPay7Uw3bvDZBZFSBXClEphZuSDLmZTqMdclasf4vFSsogC8baDz0eaC2NdO/2dlXHBKQ== -flatted@^3.1.0: +flatted@^3.1.0, flatted@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== @@ -10699,7 +13372,7 @@ follow-redirects@1.5.10: dependencies: debug "=3.1.0" -follow-redirects@^1.0.0, follow-redirects@^1.14.0, follow-redirects@^1.15.0: +follow-redirects@^1.0.0, follow-redirects@^1.14.0, follow-redirects@^1.14.9, follow-redirects@^1.15.0: version "1.15.2" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== @@ -10794,6 +13467,11 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== +from@~0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" + integrity sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g== + fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" @@ -10808,7 +13486,7 @@ fs-extra@10.0.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^10.0.0: +fs-extra@^10.0.0, fs-extra@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== @@ -10835,7 +13513,7 @@ fs-extra@^4.0.2: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^7.0.0: +fs-extra@^7.0.0, fs-extra@~7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== @@ -11218,6 +13896,11 @@ globby@^11.0.4, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +globrex@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" + integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== + gopd@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" @@ -11283,6 +13966,11 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== +graceful-fs@^4.1.3: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + grapheme-splitter@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" @@ -11322,6 +14010,18 @@ handle-thing@^2.0.0: resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== +handlebars@4.7.7: + version "4.7.7" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" + integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.0" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -11504,6 +14204,13 @@ html-encoding-sniffer@^2.0.1: dependencies: whatwg-encoding "^1.0.5" +html-encoding-sniffer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" + integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== + dependencies: + whatwg-encoding "^2.0.0" + html-entities@^2.1.0, html-entities@^2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.3.tgz#117d7626bece327fc8baace8868fa6f5ef856e46" @@ -11617,6 +14324,15 @@ http-proxy-agent@^4.0.1: agent-base "6" debug "4" +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== + dependencies: + "@tootallnate/once" "2" + agent-base "6" + debug "4" + http-proxy-middleware@^2.0.1, http-proxy-middleware@^2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" @@ -11637,6 +14353,25 @@ http-proxy@^1.18.1: follow-redirects "^1.0.0" requires-port "^1.0.0" +http-server@^14.1.0: + version "14.1.1" + resolved "https://registry.yarnpkg.com/http-server/-/http-server-14.1.1.tgz#d60fbb37d7c2fdff0f0fbff0d0ee6670bd285e2e" + integrity sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A== + dependencies: + basic-auth "^2.0.1" + chalk "^4.1.2" + corser "^2.0.1" + he "^1.2.0" + html-encoding-sniffer "^3.0.0" + http-proxy "^1.18.1" + mime "^1.6.0" + minimist "^1.2.6" + opener "^1.5.1" + portfinder "^1.0.28" + secure-compare "3.0.1" + union "~0.5.0" + url-join "^4.0.1" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -11655,6 +14390,11 @@ http-signature@~1.3.6: jsprim "^2.0.2" sshpk "^1.14.1" +http-status-codes@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-2.2.0.tgz#bb2efe63d941dfc2be18e15f703da525169622be" + integrity sha512-feERVo9iWxvnejp3SEfm/+oNG517npqL2/PIA8ORjyOZjGC7TwCRQsZylciLS64i6pJ0wRYz3rkXLRwbtFa8Ng== + http2-wrapper@^1.0.0-beta.5.2: version "1.0.3" resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" @@ -11676,7 +14416,7 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg== -https-proxy-agent@^5.0.0: +https-proxy-agent@5.0.1, https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== @@ -11722,7 +14462,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.6, iconv-lite@^0.6.2, iconv-lite@^0.6.3: +iconv-lite@0.6, iconv-lite@0.6.3, iconv-lite@^0.6.2, iconv-lite@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -11739,7 +14479,7 @@ idb@^7.0.1: resolved "https://registry.yarnpkg.com/idb/-/idb-7.1.1.tgz#d910ded866d32c7ced9befc5bfdf36f572ced72b" integrity sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ== -identity-obj-proxy@^3.0.0: +identity-obj-proxy@3.0.0, identity-obj-proxy@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz#94d2bda96084453ef36fbc5aaec37e0f79f1fc14" integrity sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA== @@ -11795,6 +14535,11 @@ import-lazy@^2.1.0: resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" integrity sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A== +import-lazy@~4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" + integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== + import-local@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" @@ -12372,6 +15117,13 @@ is-circular@^1.0.2: resolved "https://registry.yarnpkg.com/is-circular/-/is-circular-1.0.2.tgz#2e0ab4e9835f4c6b0ea2b9855a84acd501b8366c" integrity sha512-YttjnrswnUYRVJvxCvu8z+PGMUSzC2JttP0OEXezlAEdp3EXzhf7IZ3j0gRAybJBQupedIZFhY61Tga6E0qASA== +is-core-module@^2.1.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" + integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== + dependencies: + has "^1.0.3" + is-core-module@^2.11.0, is-core-module@^2.5.0, is-core-module@^2.9.0: version "2.11.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" @@ -12574,11 +15326,21 @@ is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-port-reachable@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-port-reachable/-/is-port-reachable-4.0.0.tgz#dac044091ef15319c8ab2f34604d8794181f8c2d" + integrity sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig== + is-potential-custom-element-name@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== +is-promise@^2.1.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + is-pull-stream@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/is-pull-stream/-/is-pull-stream-0.0.0.tgz#a3bc3d1c6d3055151c46bde6f399efed21440ca9" @@ -12985,6 +15747,14 @@ jest-changed-files@^27.5.1: execa "^5.0.0" throat "^6.0.1" +jest-changed-files@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.5.0.tgz#e88786dca8bf2aa899ec4af7644e16d9dcf9b23e" + integrity sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag== + dependencies: + execa "^5.0.0" + p-limit "^3.1.0" + jest-circus@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" @@ -13010,6 +15780,32 @@ jest-circus@^27.5.1: stack-utils "^2.0.3" throat "^6.0.1" +jest-circus@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.5.0.tgz#b5926989449e75bff0d59944bae083c9d7fb7317" + integrity sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA== + dependencies: + "@jest/environment" "^29.5.0" + "@jest/expect" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + is-generator-fn "^2.0.0" + jest-each "^29.5.0" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-runtime "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" + p-limit "^3.1.0" + pretty-format "^29.5.0" + pure-rand "^6.0.0" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-cli@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" @@ -13028,6 +15824,24 @@ jest-cli@^27.5.1: prompts "^2.0.1" yargs "^16.2.0" +jest-cli@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.5.0.tgz#b34c20a6d35968f3ee47a7437ff8e53e086b4a67" + integrity sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw== + dependencies: + "@jest/core" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + import-local "^3.0.2" + jest-config "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" + prompts "^2.0.1" + yargs "^17.3.1" + jest-config@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" @@ -13054,7 +15868,35 @@ jest-config@^27.5.1: jest-validate "^27.5.1" micromatch "^4.0.4" parse-json "^5.2.0" - pretty-format "^27.5.1" + pretty-format "^27.5.1" + slash "^3.0.0" + strip-json-comments "^3.1.1" + +jest-config@^29.4.1, jest-config@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.5.0.tgz#3cc972faec8c8aaea9ae158c694541b79f3748da" + integrity sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.5.0" + "@jest/types" "^29.5.0" + babel-jest "^29.5.0" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-circus "^29.5.0" + jest-environment-node "^29.5.0" + jest-get-type "^29.4.3" + jest-regex-util "^29.4.3" + jest-resolve "^29.5.0" + jest-runner "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^29.5.0" slash "^3.0.0" strip-json-comments "^3.1.1" @@ -13078,6 +15920,16 @@ jest-diff@^29.3.1: jest-get-type "^29.2.0" pretty-format "^29.3.1" +jest-diff@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.5.0.tgz#e0d83a58eb5451dcc1fa61b1c3ee4e8f5a290d63" + integrity sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.4.3" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" + jest-docblock@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" @@ -13085,6 +15937,13 @@ jest-docblock@^27.5.1: dependencies: detect-newline "^3.0.0" +jest-docblock@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.4.3.tgz#90505aa89514a1c7dceeac1123df79e414636ea8" + integrity sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg== + dependencies: + detect-newline "^3.0.0" + jest-each@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" @@ -13096,6 +15955,17 @@ jest-each@^27.5.1: jest-util "^27.5.1" pretty-format "^27.5.1" +jest-each@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.5.0.tgz#fc6e7014f83eac68e22b7195598de8554c2e5c06" + integrity sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA== + dependencies: + "@jest/types" "^29.5.0" + chalk "^4.0.0" + jest-get-type "^29.4.3" + jest-util "^29.5.0" + pretty-format "^29.5.0" + jest-environment-jsdom@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" @@ -13109,6 +15979,20 @@ jest-environment-jsdom@^27.5.1: jest-util "^27.5.1" jsdom "^16.6.0" +jest-environment-jsdom@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.5.0.tgz#cfe86ebaf1453f3297b5ff3470fbe94739c960cb" + integrity sha512-/KG8yEK4aN8ak56yFVdqFDzKNHgF4BAymCx2LbPNPsUshUlfAl0eX402Xm1pt+eoG9SLZEUVifqXtX8SK74KCw== + dependencies: + "@jest/environment" "^29.5.0" + "@jest/fake-timers" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/jsdom" "^20.0.0" + "@types/node" "*" + jest-mock "^29.5.0" + jest-util "^29.5.0" + jsdom "^20.0.0" + jest-environment-node@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e" @@ -13121,6 +16005,18 @@ jest-environment-node@^27.5.1: jest-mock "^27.5.1" jest-util "^27.5.1" +jest-environment-node@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.5.0.tgz#f17219d0f0cc0e68e0727c58b792c040e332c967" + integrity sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw== + dependencies: + "@jest/environment" "^29.5.0" + "@jest/fake-timers" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" + jest-mock "^29.5.0" + jest-util "^29.5.0" + jest-fetch-mock@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz#31749c456ae27b8919d69824f1c2bd85fe0a1f3b" @@ -13144,6 +16040,11 @@ jest-get-type@^29.2.0: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.2.0.tgz#726646f927ef61d583a3b3adb1ab13f3a5036408" integrity sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA== +jest-get-type@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" + integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== + jest-haste-map@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" @@ -13164,6 +16065,25 @@ jest-haste-map@^27.5.1: optionalDependencies: fsevents "^2.3.2" +jest-haste-map@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.5.0.tgz#69bd67dc9012d6e2723f20a945099e972b2e94de" + integrity sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA== + dependencies: + "@jest/types" "^29.5.0" + "@types/graceful-fs" "^4.1.3" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^29.4.3" + jest-util "^29.5.0" + jest-worker "^29.5.0" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + jest-jasmine2@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" @@ -13195,7 +16115,15 @@ jest-leak-detector@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" -jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1: +jest-leak-detector@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz#cf4bdea9615c72bac4a3a7ba7e7930f9c0610c8c" + integrity sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow== + dependencies: + jest-get-type "^29.4.3" + pretty-format "^29.5.0" + +jest-matcher-utils@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== @@ -13215,6 +16143,16 @@ jest-matcher-utils@^29.3.1: jest-get-type "^29.2.0" pretty-format "^29.3.1" +jest-matcher-utils@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz#d957af7f8c0692c5453666705621ad4abc2c59c5" + integrity sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw== + dependencies: + chalk "^4.0.0" + jest-diff "^29.5.0" + jest-get-type "^29.4.3" + pretty-format "^29.5.0" + jest-message-util@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" @@ -13260,6 +16198,21 @@ jest-message-util@^29.3.1: slash "^3.0.0" stack-utils "^2.0.3" +jest-message-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.5.0.tgz#1f776cac3aca332ab8dd2e3b41625435085c900e" + integrity sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.5.0" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.5.0" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-mock@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" @@ -13268,6 +16221,15 @@ jest-mock@^27.5.1: "@jest/types" "^27.5.1" "@types/node" "*" +jest-mock@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.5.0.tgz#26e2172bcc71d8b0195081ff1f146ac7e1518aed" + integrity sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw== + dependencies: + "@jest/types" "^29.5.0" + "@types/node" "*" + jest-util "^29.5.0" + jest-pnp-resolver@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" @@ -13283,6 +16245,11 @@ jest-regex-util@^28.0.0: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-28.0.2.tgz#afdc377a3b25fb6e80825adcf76c854e5bf47ead" integrity sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw== +jest-regex-util@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.3.tgz#a42616141e0cae052cfa32c169945d00c0aa0bb8" + integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg== + jest-resolve-dependencies@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" @@ -13292,6 +16259,14 @@ jest-resolve-dependencies@^27.5.1: jest-regex-util "^27.5.1" jest-snapshot "^27.5.1" +jest-resolve-dependencies@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz#f0ea29955996f49788bf70996052aa98e7befee4" + integrity sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg== + dependencies: + jest-regex-util "^29.4.3" + jest-snapshot "^29.5.0" + jest-resolve@^27.4.2, jest-resolve@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" @@ -13308,6 +16283,21 @@ jest-resolve@^27.4.2, jest-resolve@^27.5.1: resolve.exports "^1.1.0" slash "^3.0.0" +jest-resolve@^29.4.1, jest-resolve@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.5.0.tgz#b053cc95ad1d5f6327f0ac8aae9f98795475ecdc" + integrity sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w== + dependencies: + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.5.0" + jest-pnp-resolver "^1.2.2" + jest-util "^29.5.0" + jest-validate "^29.5.0" + resolve "^1.20.0" + resolve.exports "^2.0.0" + slash "^3.0.0" + jest-runner@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" @@ -13335,6 +16325,33 @@ jest-runner@^27.5.1: source-map-support "^0.5.6" throat "^6.0.1" +jest-runner@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.5.0.tgz#6a57c282eb0ef749778d444c1d758c6a7693b6f8" + integrity sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ== + dependencies: + "@jest/console" "^29.5.0" + "@jest/environment" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.13.1" + graceful-fs "^4.2.9" + jest-docblock "^29.4.3" + jest-environment-node "^29.5.0" + jest-haste-map "^29.5.0" + jest-leak-detector "^29.5.0" + jest-message-util "^29.5.0" + jest-resolve "^29.5.0" + jest-runtime "^29.5.0" + jest-util "^29.5.0" + jest-watcher "^29.5.0" + jest-worker "^29.5.0" + p-limit "^3.1.0" + source-map-support "0.5.13" + jest-runtime@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" @@ -13363,6 +16380,34 @@ jest-runtime@^27.5.1: slash "^3.0.0" strip-bom "^4.0.0" +jest-runtime@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.5.0.tgz#c83f943ee0c1da7eb91fa181b0811ebd59b03420" + integrity sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw== + dependencies: + "@jest/environment" "^29.5.0" + "@jest/fake-timers" "^29.5.0" + "@jest/globals" "^29.5.0" + "@jest/source-map" "^29.4.3" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^29.5.0" + jest-message-util "^29.5.0" + jest-mock "^29.5.0" + jest-regex-util "^29.4.3" + jest-resolve "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" + slash "^3.0.0" + strip-bom "^4.0.0" + jest-serializer@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" @@ -13399,6 +16444,35 @@ jest-snapshot@^27.5.1: pretty-format "^27.5.1" semver "^7.3.2" +jest-snapshot@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.5.0.tgz#c9c1ce0331e5b63cd444e2f95a55a73b84b1e8ce" + integrity sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g== + dependencies: + "@babel/core" "^7.11.6" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/babel__traverse" "^7.0.6" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^29.5.0" + graceful-fs "^4.2.9" + jest-diff "^29.5.0" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-util "^29.5.0" + natural-compare "^1.4.0" + pretty-format "^29.5.0" + semver "^7.3.5" + jest-styled-components@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/jest-styled-components/-/jest-styled-components-7.1.1.tgz#faf19c733e0de4bbef1f9151955b99e839b7df48" @@ -13430,6 +16504,18 @@ jest-util@^28.1.3: graceful-fs "^4.2.9" picomatch "^2.2.3" +jest-util@^29.0.0, jest-util@^29.4.1, jest-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" + integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== + dependencies: + "@jest/types" "^29.5.0" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + jest-util@^29.3.1: version "29.3.1" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.3.1.tgz#1dda51e378bbcb7e3bc9d8ab651445591ed373e1" @@ -13466,6 +16552,18 @@ jest-validate@^27.5.1: leven "^3.1.0" pretty-format "^27.5.1" +jest-validate@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.5.0.tgz#8e5a8f36178d40e47138dc00866a5f3bd9916ffc" + integrity sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ== + dependencies: + "@jest/types" "^29.5.0" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^29.4.3" + leven "^3.1.0" + pretty-format "^29.5.0" + jest-watch-typeahead@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz#b4a6826dfb9c9420da2f7bc900de59dad11266a9" @@ -13506,6 +16604,20 @@ jest-watcher@^28.0.0: jest-util "^28.1.3" string-length "^4.0.1" +jest-watcher@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.5.0.tgz#cf7f0f949828ba65ddbbb45c743a382a4d911363" + integrity sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA== + dependencies: + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.13.1" + jest-util "^29.5.0" + string-length "^4.0.1" + jest-worker@^26.2.1: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" @@ -13533,6 +16645,16 @@ jest-worker@^28.0.2: merge-stream "^2.0.0" supports-color "^8.0.0" +jest-worker@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.5.0.tgz#bdaefb06811bd3384d93f009755014d8acb4615d" + integrity sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA== + dependencies: + "@types/node" "*" + jest-util "^29.5.0" + merge-stream "^2.0.0" + supports-color "^8.0.0" + jest@^27.4.3: version "27.5.1" resolved "https://registry.yarnpkg.com/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" @@ -13542,6 +16664,32 @@ jest@^27.4.3: import-local "^3.0.2" jest-cli "^27.5.1" +jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.5.0.tgz#f75157622f5ce7ad53028f2f8888ab53e1f1f24e" + integrity sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ== + dependencies: + "@jest/core" "^29.5.0" + "@jest/types" "^29.5.0" + import-local "^3.0.2" + jest-cli "^29.5.0" + +jju@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" + integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA== + +joi@^17.7.0: + version "17.9.2" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.9.2.tgz#8b2e4724188369f55451aebd1d0b1d9482470690" + integrity sha512-Itk/r+V4Dx0V3c7RLFdRh12IOjySm2/WGPMubBT92cQvRfYZhPM2W0hZlctjj72iES8jsRCwp7S/cRmWBnJ4nw== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@sideway/address" "^4.1.3" + "@sideway/formula" "^3.0.1" + "@sideway/pinpoint" "^2.0.0" + jotai@1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/jotai/-/jotai-1.7.0.tgz#b22e4cb995270a0215564990eeacc804025eb557" @@ -13567,6 +16715,11 @@ js-sha3@^0.5.7: resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" integrity sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g== +js-string-escape@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" + integrity sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -13635,6 +16788,38 @@ jsdom@^16.6.0: ws "^7.4.6" xml-name-validator "^3.0.0" +jsdom@^20.0.0, jsdom@~20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db" + integrity sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ== + dependencies: + abab "^2.0.6" + acorn "^8.8.1" + acorn-globals "^7.0.0" + cssom "^0.5.0" + cssstyle "^2.3.0" + data-urls "^3.0.2" + decimal.js "^10.4.2" + domexception "^4.0.0" + escodegen "^2.0.0" + form-data "^4.0.0" + html-encoding-sniffer "^3.0.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.1" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.2" + parse5 "^7.1.1" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^4.1.2" + w3c-xmlserializer "^4.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^2.0.0" + whatwg-mimetype "^3.0.0" + whatwg-url "^11.0.0" + ws "^8.11.0" + xml-name-validator "^4.0.0" + jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -13732,12 +16917,12 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -json5@^2.1.2, json5@^2.2.0, json5@^2.2.2: +json5@^2.1.2, json5@^2.2.0, json5@^2.2.2, json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -jsonc-parser@3.2.0: +jsonc-parser@3.2.0, jsonc-parser@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== @@ -13773,6 +16958,16 @@ jsonpointer@^5.0.0: resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559" integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== +jsonwebtoken@9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz#d0faf9ba1cc3a56255fe49c0961a67e520c1926d" + integrity sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw== + dependencies: + jws "^3.2.2" + lodash "^4.17.21" + ms "^2.1.1" + semver "^7.3.8" + jsprim@^1.2.2: version "1.4.2" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" @@ -13806,6 +17001,23 @@ just-performance@4.3.0: resolved "https://registry.yarnpkg.com/just-performance/-/just-performance-4.3.0.tgz#cc2bc8c9227f09e97b6b1df4cd0de2df7ae16db1" integrity sha512-L7RjvtJsL0QO8xFs5wEoDDzzJwoiowRw6Rn/GnvldlchS2JQr9wFYPiwZcDfrbbujEKqKN0tvENdbjXdYhDp5Q== +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + keccak@^3.0.0, keccak@^3.0.1: version "3.0.3" resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.3.tgz#4bc35ad917be1ef54ff246f904c2bbbf9ac61276" @@ -13815,6 +17027,13 @@ keccak@^3.0.0, keccak@^3.0.1: node-gyp-build "^4.2.0" readable-stream "^3.6.0" +keygrip@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226" + integrity sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ== + dependencies: + tsscmp "1.0.6" + keyv@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" @@ -13846,6 +17065,11 @@ klaw-sync@^6.0.0: dependencies: graceful-fs "^4.1.11" +kleur@4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780" + integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== + kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -13856,6 +17080,11 @@ klona@^2.0.4, klona@^2.0.5: resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc" integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ== +kolorist@^1.6.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/kolorist/-/kolorist-1.8.0.tgz#edddbbbc7894bc13302cdf740af6374d4a04743c" + integrity sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ== + language-subtag-registry@~0.3.2: version "0.3.22" resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d" @@ -13901,7 +17130,7 @@ launchdarkly-react-client-sdk@^3.0.4: launchdarkly-js-client-sdk "^3.1.2" lodash.camelcase "^4.3.0" -lazy-ass@^1.6.0: +lazy-ass@1.6.0, lazy-ass@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw== @@ -14013,6 +17242,11 @@ loader-utils@^3.2.0: resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.2.1.tgz#4fb104b599daafd82ef3e1a41fb9265f87e1f576" integrity sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw== +local-pkg@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.4.3.tgz#0ff361ab3ae7f1c19113d9bb97b98b905dbc4963" + integrity sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g== + locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -14043,6 +17277,18 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lockfile@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lockfile/-/lockfile-1.0.4.tgz#07f819d25ae48f87e538e6578b6964a4981a5609" + integrity sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA== + dependencies: + signal-exit "^3.0.2" + +lodash-es@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" + integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== + lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" @@ -14063,7 +17309,7 @@ lodash.get@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== -lodash.isequal@4.5.0: +lodash.isequal@4.5.0, lodash.isequal@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== @@ -14083,7 +17329,7 @@ lodash.kebabcase@^4.1.1: resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" integrity sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g== -lodash.memoize@^4.1.2: +lodash.memoize@4.x, lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== @@ -14128,7 +17374,7 @@ lodash.upperfirst@^4.3.1: resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce" integrity sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg== -lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.7.0: +lodash@4, lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.7.0, lodash@~4.17.15: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -14178,13 +17424,24 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" -loupe@^2.3.1: +loupe@^2.3.1, loupe@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53" integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== dependencies: get-func-name "^2.0.0" +lowdb@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lowdb/-/lowdb-1.0.0.tgz#5243be6b22786ccce30e50c9a33eac36b20c8064" + integrity sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ== + dependencies: + graceful-fs "^4.1.3" + is-promise "^2.1.0" + lodash "4" + pify "^3.0.0" + steno "^0.4.1" + lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" @@ -14207,6 +17464,11 @@ lowercase-keys@^3.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== +lru-cache@7.18.3: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + lru-cache@^4.0.1: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -14229,10 +17491,10 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lz-string@^1.4.4: - version "1.4.4" - resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" - integrity sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ== +lz-string@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" + integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== magic-string@^0.25.0, magic-string@^0.25.7: version "0.25.9" @@ -14241,6 +17503,20 @@ magic-string@^0.25.0, magic-string@^0.25.7: dependencies: sourcemap-codec "^1.4.8" +magic-string@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3" + integrity sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.13" + +magic-string@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.0.tgz#fd58a4748c5c4547338a424e90fa5dd17f4de529" + integrity sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.13" + make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -14248,7 +17524,7 @@ make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: dependencies: semver "^6.0.0" -make-error@^1.1.1: +make-error@1.x, make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== @@ -14292,6 +17568,18 @@ map-obj@^4.0.0: resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== +map-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" + integrity sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g== + +md5-hex@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/md5-hex/-/md5-hex-3.0.1.tgz#be3741b510591434b2784d79e556eefc2c9a8e5c" + integrity sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw== + dependencies: + blueimp-md5 "^2.10.0" + md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -14338,6 +17626,16 @@ mdn-data@2.0.14: resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== +mdn-data@2.0.28: + version "2.0.28" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba" + integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g== + +mdn-data@2.0.30: + version "2.0.30" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" + integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== + mdn-data@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" @@ -14528,6 +17826,18 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== +mime-db@~1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" + integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== + +mime-types@2.1.18: + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" + integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== + dependencies: + mime-db "~1.33.0" + mime-types@^2.1.12, mime-types@^2.1.16, mime-types@^2.1.27, mime-types@^2.1.30, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" @@ -14535,11 +17845,21 @@ mime-types@^2.1.12, mime-types@^2.1.16, mime-types@^2.1.27, mime-types@^2.1.30, dependencies: mime-db "1.52.0" -mime@1.6.0, mime@^1.3.4: +mime@1.6.0, mime@^1.3.4, mime@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== +mime@2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + +mime@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" + integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -14589,7 +17909,7 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== -"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: +"minimatch@2 || 3", minimatch@3.1.2, minimatch@^3.0.0, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -14617,6 +17937,13 @@ minimatch@^5.0.1: dependencies: brace-expansion "^2.0.1" +minimatch@^5.1.0: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -14639,6 +17966,11 @@ minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== +minimist@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + minipass@^2.6.0, minipass@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" @@ -14681,17 +18013,27 @@ mkdirp@*: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.0.tgz#758101231418bda24435c0888a91d9bd91f1372d" integrity sha512-7+JDnNsyCvZXoUJdkMR0oUE2AmAdsNXGTmRbiOjYIwQ6q+bL6NwrozGQdPcmYaNcrhH37F50HHBUzoaBV6FITQ== -"mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.1: +mkdirp@1.0.4, mkdirp@^1.0.3, mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +"mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@^0.5.6, mkdirp@~0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== dependencies: minimist "^1.2.6" -mkdirp@^1.0.3, mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +mlly@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.3.0.tgz#3184cb80c6437bda861a9f452ae74e3434ed9cd1" + integrity sha512-HT5mcgIQKkOrZecOjOX3DJorTikWXwsBfpcr/MGBkhfWcjiqvnaL/9ppxvIUXfjT6xt4DVIAsN9fMUz1ev4bIw== + dependencies: + acorn "^8.8.2" + pathe "^1.1.0" + pkg-types "^1.0.3" + ufo "^1.1.2" mnemonist@^0.38.3: version "0.38.5" @@ -14948,7 +18290,7 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -mv@~2: +mv@2.1.1, mv@~2: version "2.1.1" resolved "https://registry.yarnpkg.com/mv/-/mv-2.1.1.tgz#ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2" integrity sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg== @@ -14967,6 +18309,11 @@ nano-json-stream-parser@^0.1.2: resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" integrity sha512-9MqxMH/BSJC7dnLsEMPyfN5Dvoo49IsPFYMcHw3Bcfc2kN0lpHRBSzlMSVx4HGyJ7s9B31CyBTVehWJoQ8Ctew== +nanoclone@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4" + integrity sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA== + nanoid@3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" @@ -14977,6 +18324,11 @@ nanoid@^3.0.2, nanoid@^3.1.12, nanoid@^3.1.20, nanoid@^3.1.3, nanoid@^3.3.1, nan resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== +nanoid@^3.3.6: + version "3.3.6" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" + integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== + native-abort-controller@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/native-abort-controller/-/native-abort-controller-1.0.4.tgz#39920155cc0c18209ff93af5bc90be856143f251" @@ -15027,7 +18379,7 @@ negotiator@0.6.3: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -neo-async@^2.6.2: +neo-async@^2.6.0, neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== @@ -15074,7 +18426,7 @@ node-fetch@2, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.6, node-fetc dependencies: whatwg-url "^5.0.0" -node-fetch@2.6.7: +node-fetch@2.6.7, node-fetch@cjs: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== @@ -15120,6 +18472,11 @@ node-machine-id@^1.1.12: resolved "https://registry.yarnpkg.com/node-machine-id/-/node-machine-id-1.1.12.tgz#37904eee1e59b320bb9c5d6c0a59f3b469cb6267" integrity sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ== +node-releases@^2.0.12: + version "2.0.12" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.12.tgz#35627cc224a23bfb06fb3380f2b3afaaa7eb1039" + integrity sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ== + node-releases@^2.0.6: version "2.0.8" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae" @@ -15234,6 +18591,11 @@ nwsapi@^2.2.0: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== +nwsapi@^2.2.2: + version "2.2.5" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.5.tgz#a52744c61b3889dd44b0a158687add39b8d935e2" + integrity sha512-6xpotnECFy/og7tKSBVmUNft7J3jyXAka4XvG6AUhFWRz+Q/Ljus7znJAA3bxColfQLdS+XsjoodtJfCgeTEFQ== + nx-cloud@16.0.5, nx-cloud@latest: version "16.0.5" resolved "https://registry.yarnpkg.com/nx-cloud/-/nx-cloud-16.0.5.tgz#fa0b0185d254405ec47fcbcdbbd8b12ff1add096" @@ -15473,7 +18835,7 @@ open@~8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" -opener@^1.5.2: +opener@^1.5.1, opener@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== @@ -15598,13 +18960,20 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.0.2: +p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" +p-limit@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644" + integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ== + dependencies: + yocto-queue "^1.0.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -15790,6 +19159,13 @@ parse5@6.0.1: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== +parse5@^7.0.0, parse5@^7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" + integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== + dependencies: + entities "^4.4.0" + parseqs@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" @@ -15837,6 +19213,11 @@ patch-package@^6.5.1: tmp "^0.0.33" yaml "^1.10.2" +path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -15852,6 +19233,11 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== +path-is-inside@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== + path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" @@ -15862,7 +19248,7 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.7: +path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -15872,6 +19258,11 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== +path-to-regexp@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45" + integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ== + path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" @@ -15884,11 +19275,23 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pathe@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.1.tgz#1dd31d382b974ba69809adc9a7a347e65d84829a" + integrity sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q== + pathval@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== +pause-stream@0.0.11: + version "0.0.11" + resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + integrity sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A== + dependencies: + through "~2.3" + pbkdf2@^3.0.17, pbkdf2@^3.0.3: version "3.1.2" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" @@ -15955,6 +19358,14 @@ pify@^5.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== +pino-abstract-transport@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz#cc0d6955fffcadb91b7b49ef220a6cc111d48bb3" + integrity sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA== + dependencies: + readable-stream "^4.0.0" + split2 "^4.0.0" + pino-abstract-transport@v0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz#4b54348d8f73713bfd14e3dc44228739aa13d9c0" @@ -16004,6 +19415,15 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +pkg-types@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.0.3.tgz#988b42ab19254c01614d13f4f65a2cfc7880f868" + integrity sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A== + dependencies: + jsonc-parser "^3.2.0" + mlly "^1.2.0" + pathe "^1.1.0" + pkg-up@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" @@ -16011,6 +19431,11 @@ pkg-up@^3.1.0: dependencies: find-up "^3.0.0" +pkginfo@0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.4.1.tgz#b5418ef0439de5425fc4995042dced14fb2a84ff" + integrity sha512-8xCNE/aT/EXKenuMDZ+xTVwkT8gsoHN2z/Q29l80u0ppGEXVvsKRzNMbtKhg8LS8k1tJLAHHylf6p4VFmP6XUQ== + plurals-cldr@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/plurals-cldr/-/plurals-cldr-1.0.4.tgz#534e4784f80679d3b0b39b0fb6cc46645fcf5de8" @@ -16043,6 +19468,15 @@ popper-max-size-modifier@^0.2.0: resolved "https://registry.yarnpkg.com/popper-max-size-modifier/-/popper-max-size-modifier-0.2.0.tgz#1574744401296a488b4974909d130a85db94256f" integrity sha512-UerPt9pZfTFnpSpIBVJrR3ibHMuU1k5K01AyNLfMUWCr4z1MFH+dsayPlAF9ZeYExa02HPiQn5OIMqUSVtJEbg== +portfinder@^1.0.28: + version "1.0.32" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.32.tgz#2fe1b9e58389712429dc2bea5beb2146146c7f81" + integrity sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg== + dependencies: + async "^2.6.4" + debug "^3.2.7" + mkdirp "^0.5.6" + postcss-attribute-case-insensitive@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz#03d761b24afc04c09e757e92ff53716ae8ea2741" @@ -16595,6 +20029,15 @@ postcss@^8.3.5, postcss@^8.4.18, postcss@^8.4.19, postcss@^8.4.4: picocolors "^1.0.0" source-map-js "^1.0.2" +postcss@^8.4.23: + version "8.4.24" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.24.tgz#f714dba9b2284be3cc07dbd2fc57ee4dc972d2df" + integrity sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + preact@10.4.1: version "10.4.1" resolved "https://registry.yarnpkg.com/preact/-/preact-10.4.1.tgz#9b3ba020547673a231c6cf16f0fbaef0e8863431" @@ -16625,6 +20068,11 @@ prettier@^2.3.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.3.tgz#ab697b1d3dd46fb4626fbe2f543afe0cc98d8632" integrity sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw== +prettier@^2.6.2: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + pretty-bytes@^5.3.0, pretty-bytes@^5.4.1, pretty-bytes@^5.6.0: version "5.6.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" @@ -16648,7 +20096,7 @@ pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" -pretty-format@^27.0.0, pretty-format@^27.0.2, pretty-format@^27.5.1: +pretty-format@^27.0.2, pretty-format@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== @@ -16676,12 +20124,21 @@ pretty-format@^29.0.0, pretty-format@^29.3.1: ansi-styles "^5.0.0" react-is "^18.0.0" +pretty-format@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.5.0.tgz#283134e74f70e2e3e7229336de0e4fce94ccde5a" + integrity sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw== + dependencies: + "@jest/schemas" "^29.4.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -process-warning@^1.0.0: +process-warning@1.0.0, process-warning@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616" integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q== @@ -16730,6 +20187,11 @@ prop-types@^15.5.10, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" +property-expr@^2.0.4: + version "2.0.5" + resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.5.tgz#278bdb15308ae16af3e3b9640024524f4dc02cb4" + integrity sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA== + protobufjs@^6.10.2: version "6.11.3" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74" @@ -16782,6 +20244,13 @@ proxy-from-env@^1.1.0: resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== +ps-tree@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd" + integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA== + dependencies: + event-stream "=3.3.4" + pseudolocale@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pseudolocale/-/pseudolocale-1.2.0.tgz#787021d9a11abfdd8f084eabe0e59845ba571453" @@ -16836,6 +20305,11 @@ punycode@2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" integrity sha512-Yxz2kRwT90aPiWEMHVYnEf4+rhwF1tBmmZ4KepCP+Wkium9JxtWnUm1nqGwpiAHr/tnTSeHqr3wb++jgSkXjhA== +punycode@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== + punycode@^2.1.0, punycode@^2.1.1: version "2.2.0" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.2.0.tgz#2092cc57cd2582c38e4e7e8bb869dc8d3148bc74" @@ -16848,6 +20322,11 @@ pupa@^2.0.1: dependencies: escape-goat "^2.0.0" +pure-rand@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.2.tgz#a9c2ddcae9b68d736a8163036f088a2781c8b306" + integrity sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ== + q@^1.1.2, q@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -16873,6 +20352,13 @@ qs@6.11.0, qs@^6.10.3, qs@^6.5.1, qs@^6.9.1: dependencies: side-channel "^1.0.4" +qs@^6.4.0: + version "6.11.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== + dependencies: + side-channel "^1.0.4" + qs@~6.10.3: version "6.10.5" resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.5.tgz#974715920a80ff6a262264acd2c7e6c2a53282b4" @@ -16992,6 +20478,11 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" +range-parser@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + integrity sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A== + range-parser@^1.2.1, range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" @@ -17017,7 +20508,7 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" -rc@1.2.8, rc@^1.2.8: +rc@1.2.8, rc@^1.0.1, rc@^1.1.6, rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -17304,6 +20795,11 @@ react-refresh@^0.11.0: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A== +react-refresh@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e" + integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ== + react-remove-scroll-bar@^2.1.0: version "2.3.4" resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz#53e272d7a5cb8242990c7f144c44d8bd8ab5afd9" @@ -17518,6 +21014,16 @@ readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.1, readable string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@^4.0.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.4.0.tgz#55ce132d60a988c460d75c631e9ccf6a7229b468" + integrity sha512-kDMOq0qLtxV9f/SQv522h8cxZBqNZXuXNyjyezmfAAuribMyVXziljpQ/uQhfE1XLg2/TLTW2DsnoE4VAi/krg== + dependencies: + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + readable-stream@~1.1.9: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" @@ -17684,6 +21190,26 @@ regexpu-core@^5.2.1: unicode-match-property-ecmascript "^2.0.0" unicode-match-property-value-ecmascript "^2.1.0" +regexpu-core@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" + integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== + dependencies: + "@babel/regjsgen" "^0.8.0" + regenerate "^1.4.2" + regenerate-unicode-properties "^10.1.0" + regjsparser "^0.9.1" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" + +registry-auth-token@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20" + integrity sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ== + dependencies: + rc "^1.1.6" + safe-buffer "^5.0.1" + registry-auth-token@^4.0.0: version "4.2.2" resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.2.tgz#f02d49c3668884612ca031419491a13539e21fac" @@ -17691,6 +21217,13 @@ registry-auth-token@^4.0.0: dependencies: rc "1.2.8" +registry-url@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" + integrity sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA== + dependencies: + rc "^1.0.1" + registry-url@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" @@ -17747,7 +21280,7 @@ request-progress@^3.0.0: dependencies: throttleit "^1.0.0" -request@^2.79.0: +request@2.88.2, request@^2.79.0: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -17838,11 +21371,21 @@ resolve-url-loader@^4.0.0: postcss "^7.0.35" source-map "0.6.1" +resolve.exports@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" + integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== + resolve.exports@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== +resolve.exports@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" + integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== + resolve@^1.1.7, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.1: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" @@ -17861,6 +21404,23 @@ resolve@^2.0.0-next.4: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@~1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" + integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== + dependencies: + is-core-module "^2.1.0" + path-parse "^1.0.6" + +resolve@~1.22.1: + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== + dependencies: + is-core-module "^2.11.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + responselike@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" @@ -17954,13 +21514,20 @@ rollup-plugin-terser@^7.0.0: serialize-javascript "^4.0.0" terser "^5.0.0" -rollup@^2.43.1: +rollup@^2.43.1, rollup@^2.77.2: version "2.79.1" resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== optionalDependencies: fsevents "~2.3.2" +rollup@^3.21.0: + version "3.25.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.25.1.tgz#9fff79d22ff1a904b2b595a2fb9bc3793cb987d8" + integrity sha512-tywOR+rwIt5m2ZAWSe5AIJcTat8vGlnPFAv15ycCrw33t6iFsXZ6mzHVFh2psSjxQPmI+xgzMZZizUAukBI4aQ== + optionalDependencies: + fsevents "~2.3.2" + rpc-websockets@^7.5.0: version "7.5.0" resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.5.0.tgz#bbeb87572e66703ff151e50af1658f98098e2748" @@ -18005,6 +21572,13 @@ rxjs@^7.5.1: dependencies: tslib "^2.1.0" +rxjs@^7.8.0: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== + dependencies: + tslib "^2.1.0" + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -18076,6 +21650,13 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" +saxes@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" + integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== + dependencies: + xmlchars "^2.2.0" + scheduler@^0.23.0: version "0.23.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" @@ -18134,6 +21715,11 @@ secp256k1@^4.0.1: node-addon-api "^2.0.0" node-gyp-build "^4.2.0" +secure-compare@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/secure-compare/-/secure-compare-3.0.1.tgz#f1a0329b308b221fae37b9974f3d578d0ca999e3" + integrity sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw== + select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -18165,13 +21751,34 @@ semver@7.3.4: dependencies: lru-cache "^6.0.0" -semver@7.3.8, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: +semver@7.3.8, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@~7.3.0: version "7.3.8" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== dependencies: lru-cache "^6.0.0" +semver@7.5.0: + version "7.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" + integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== + dependencies: + lru-cache "^6.0.0" + +semver@7.5.1: + version "7.5.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.1.tgz#c90c4d631cf74720e46b21c1d37ea07edfab91ec" + integrity sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw== + dependencies: + lru-cache "^6.0.0" + +semver@7.x: + version "7.5.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.2.tgz#5b851e66d1be07c1cdaf37dfc856f543325a2beb" + integrity sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ== + dependencies: + lru-cache "^6.0.0" + semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" @@ -18217,6 +21824,20 @@ serialize-javascript@^6.0.0: dependencies: randombytes "^2.1.0" +serve-handler@6.1.5: + version "6.1.5" + resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-6.1.5.tgz#a4a0964f5c55c7e37a02a633232b6f0d6f068375" + integrity sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg== + dependencies: + bytes "3.0.0" + content-disposition "0.5.2" + fast-url-parser "1.1.3" + mime-types "2.1.18" + minimatch "3.1.2" + path-is-inside "1.0.2" + path-to-regexp "2.2.1" + range-parser "1.2.0" + serve-index@^1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" @@ -18240,6 +21861,23 @@ serve-static@1.15.0: parseurl "~1.3.3" send "0.18.0" +serve@^14.2.0: + version "14.2.0" + resolved "https://registry.yarnpkg.com/serve/-/serve-14.2.0.tgz#3d768e88fa13ad8644f2393599189707176e66b8" + integrity sha512-+HOw/XK1bW8tw5iBilBz/mJLWRzM8XM6MPxL4J/dKzdxq1vfdEWSwhaR7/yS8EJp5wzvP92p1qirysJvnEtjXg== + dependencies: + "@zeit/schemas" "2.29.0" + ajv "8.11.0" + arg "5.0.2" + boxen "7.0.0" + chalk "5.0.1" + chalk-template "0.4.0" + clipboardy "3.0.0" + compression "1.7.4" + is-port-reachable "4.0.0" + serve-handler "6.1.5" + update-check "1.5.4" + servify@^0.1.12: version "0.1.12" resolved "https://registry.yarnpkg.com/servify/-/servify-0.1.12.tgz#142ab7bee1f1d033b66d0707086085b17c06db95" @@ -18329,7 +21967,12 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: +siginfo@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" + integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== + +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -18376,6 +22019,15 @@ sirv@^1.0.7: mrmime "^1.0.0" totalist "^1.0.0" +sirv@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-2.0.3.tgz#ca5868b87205a74bef62a469ed0296abceccd446" + integrity sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA== + dependencies: + "@polka/url" "^1.0.0-next.20" + mrmime "^1.0.0" + totalist "^3.0.0" + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -18414,6 +22066,14 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" +snake-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" + integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + socket.io-adapter@~1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz#ab3f0d6f66b8fc7fca3959ab5991f82221789be9" @@ -18469,6 +22129,13 @@ sockjs@^0.3.24: uuid "^8.3.2" websocket-driver "^0.7.4" +sonic-boom@3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-3.3.0.tgz#cffab6dafee3b2bcb88d08d589394198bee1838c" + integrity sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g== + dependencies: + atomic-sleep "^1.0.0" + sonic-boom@^2.2.1: version "2.8.0" resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.8.0.tgz#c1def62a77425090e6ad7516aad8eb402e047611" @@ -18495,6 +22162,22 @@ source-map-loader@^3.0.0: iconv-lite "^0.6.3" source-map-js "^1.0.1" +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-support@0.5.19: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-support@^0.5.6, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -18608,6 +22291,13 @@ split2@^4.0.0: resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== +split@0.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" + integrity sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA== + dependencies: + through "2" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -18640,11 +22330,30 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" +stackback@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" + integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== + stackframe@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== +start-server-and-test@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-2.0.0.tgz#0644809d63036a8a001efb70582f3e37ebfdd33d" + integrity sha512-UqKLw0mJbfrsG1jcRLTUlvuRi9sjNuUiDOLI42r7R5fA9dsFoywAy9DoLXNYys9B886E4RCKb+qM1Gzu96h7DQ== + dependencies: + arg "^5.0.2" + bluebird "3.7.2" + check-more-types "2.24.0" + debug "4.3.4" + execa "5.1.1" + lazy-ass "1.6.0" + ps-tree "1.2.0" + wait-on "7.0.1" + stats-lite@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/stats-lite/-/stats-lite-2.2.0.tgz#278a5571fa1d2e8b1691295dccc0235282393bbf" @@ -18662,6 +22371,18 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== +std-env@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.3.3.tgz#a54f06eb245fdcfef53d56f3c0251f1d5c3d01fe" + integrity sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg== + +steno@^0.4.1: + version "0.4.4" + resolved "https://registry.yarnpkg.com/steno/-/steno-0.4.4.tgz#071105bdfc286e6615c0403c27e9d7b5dcb855cb" + integrity sha512-EEHMVYHNXFHfGtgjNITnka0aHhiAlo93F7z2/Pwd+g0teG9CnM3JIINM7hVVB5/rhw9voufD7Wukwgtw2uqh6w== + dependencies: + graceful-fs "^4.1.3" + stop-iteration-iterator@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" @@ -18682,6 +22403,13 @@ stream-browserify@^3.0.0: inherits "~2.0.4" readable-stream "^3.5.0" +stream-combiner@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" + integrity sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw== + dependencies: + duplexer "~0.1.1" + stream-http@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-3.2.0.tgz#1872dfcf24cb15752677e40e5c3f9cc1926028b5" @@ -18721,6 +22449,11 @@ strict-uri-encode@^2.0.0: resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ== +string-argv@~0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" + integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== + string-format@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/string-format/-/string-format-2.0.0.tgz#f2df2e7097440d3b65de31b6d40d54c96eaffb9b" @@ -18782,6 +22515,15 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string.prototype.matchall@^4.0.6, string.prototype.matchall@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3" @@ -18921,7 +22663,7 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" -strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1, strip-json-comments@~3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -18931,6 +22673,13 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== +strip-literal@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-1.0.1.tgz#0115a332710c849b4e46497891fb8d585e404bd2" + integrity sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q== + dependencies: + acorn "^8.8.2" + strong-log-transformer@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" @@ -18968,6 +22717,13 @@ styled-components@^5.3.0: shallowequal "^1.1.0" supports-color "^5.5.0" +styled-jsx@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.2.tgz#55020c50da54d2b225750bd7aaab57d511724060" + integrity sha512-FI5r0a5ED2/+DSdG2ZRz3a4FtNQnKPLadauU5v76a9QsscwZrWggQKOmyxGGP5EWKbyY3bsuWAJYzyKaDAVAcw== + dependencies: + client-only "0.0.1" + styled-system@^5.0.0, styled-system@^5.1.5: version "5.1.5" resolved "https://registry.yarnpkg.com/styled-system/-/styled-system-5.1.5.tgz#e362d73e1dbb5641a2fd749a6eba1263dc85075e" @@ -19039,7 +22795,7 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -svg-parser@^2.0.2: +svg-parser@^2.0.2, svg-parser@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== @@ -19076,6 +22832,18 @@ svgo@^2.7.0: picocolors "^1.0.0" stable "^0.1.8" +svgo@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-3.0.2.tgz#5e99eeea42c68ee0dc46aa16da093838c262fe0a" + integrity sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ== + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^5.1.0" + css-tree "^2.2.1" + csso "^5.0.5" + picocolors "^1.0.0" + swarm-js@^0.1.40: version "0.1.42" resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.42.tgz#497995c62df6696f6e22372f457120e43e727979" @@ -19341,7 +23109,7 @@ through2@~0.6.3: readable-stream ">=1.0.33-1 <1.1.0-0" xtend ">=4.0.0 <4.1.0-0" -"through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8: +through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8, through@~2.3, through@~2.3.1: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== @@ -19351,6 +23119,11 @@ thunky@^1.0.2: resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== +time-zone@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/time-zone/-/time-zone-1.0.0.tgz#99c5bf55958966af6d06d83bdf3800dc82faec5d" + integrity sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA== + timeago.js@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/timeago.js/-/timeago.js-4.0.2.tgz#724e8c8833e3490676c7bb0a75f5daf20e558028" @@ -19384,11 +23157,26 @@ tiny-warning@^1.0.3: resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== +tinybench@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.5.0.tgz#4711c99bbf6f3e986f67eb722fed9cddb3a68ba5" + integrity sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA== + tinycolor2@^1.4.1: version "1.5.2" resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.5.2.tgz#7d30b4584d8b7d62b9a94dacc505614a6516a95f" integrity sha512-h80m9GPFGbcLzZByXlNSEhp1gf8Dy+VX/2JCGUZsWLo7lV1mnE/XlxGYgRBoMLJh1lIDXP0EMC4RPTjlRaV+Bg== +tinypool@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.5.0.tgz#3861c3069bf71e4f1f5aa2d2e6b3aaacc278961e" + integrity sha512-paHQtnrlS1QZYKF/GnLoOM/DN9fqaGOFbCbxzAhwniySnzl9Ebk8w73/dd34DAhe/obUbPAOldTyYXQZxnPBPQ== + +tinyspy@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-2.1.1.tgz#9e6371b00c259e5c5b301917ca18c01d40ae558c" + integrity sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w== + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -19450,11 +23238,21 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +toposort@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" + integrity sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg== + totalist@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== +totalist@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" + integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== + tough-cookie@^4.0.0: version "4.1.2" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874" @@ -19465,6 +23263,16 @@ tough-cookie@^4.0.0: universalify "^0.2.0" url-parse "^1.5.3" +tough-cookie@^4.1.2: + version "4.1.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" + integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -19487,6 +23295,13 @@ tr46@^2.1.0: dependencies: punycode "^2.1.1" +tr46@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" + integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== + dependencies: + punycode "^2.1.1" + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -19541,6 +23356,20 @@ ts-essentials@^7.0.0, ts-essentials@^7.0.1: resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.3.tgz#686fd155a02133eedcc5362dc8b5056cde3e5a38" integrity sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ== +ts-jest@^29.1.0: + version "29.1.0" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.0.tgz#4a9db4104a49b76d2b368ea775b6c9535c603891" + integrity sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA== + dependencies: + bs-logger "0.x" + fast-json-stable-stringify "2.x" + jest-util "^29.0.0" + json5 "^2.2.3" + lodash.memoize "4.x" + make-error "1.x" + semver "7.x" + yargs-parser "^21.0.1" + ts-mockito@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/ts-mockito/-/ts-mockito-2.6.1.tgz#bc9ee2619033934e6fad1c4455aca5b5ace34e73" @@ -19548,7 +23377,15 @@ ts-mockito@^2.6.1: dependencies: lodash "^4.17.5" -ts-node@^10.7.0, ts-node@^10.8.1, ts-node@^10.9.1: +ts-morph@17.0.1: + version "17.0.1" + resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-17.0.1.tgz#d85df4fcf9a1fcda1b331d52c00655f381c932d1" + integrity sha512-10PkHyXmrtsTvZSL+cqtJLTgFXkU43Gd0JCc0Rw6GchWbqKe0Rwgt1v3ouobTZwQzF1mGhDeAlWYBMGRV7y+3g== + dependencies: + "@ts-morph/common" "~0.18.0" + code-block-writer "^11.0.3" + +ts-node@10.9.1, ts-node@^10.7.0, ts-node@^10.8.1, ts-node@^10.9.1: version "10.9.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== @@ -19567,6 +23404,11 @@ ts-node@^10.7.0, ts-node@^10.8.1, ts-node@^10.9.1: v8-compile-cache-lib "^3.0.1" yn "3.1.1" +tsconfck@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tsconfck/-/tsconfck-2.1.1.tgz#9b51603d2712d1f4740fa14748ca886a2e1893e5" + integrity sha512-ZPCkJBKASZBmBUNqGHmRhdhM8pJYDdOXp4nRgj/O0JwUwsMq50lCDRQP/M5GBNAA0elPrq4gAeu4dkaVCuKWww== + tsconfig-paths@^3.14.1: version "3.14.1" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" @@ -19601,6 +23443,11 @@ tslib@^2.3.0, tslib@^2.4.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== +tsscmp@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb" + integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -19625,6 +23472,11 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== +typanion@^3.8.0: + version "3.12.1" + resolved "https://registry.yarnpkg.com/typanion/-/typanion-3.12.1.tgz#d33deb130aba23ef6f2a3c69e7fb28148dd9089a" + integrity sha512-3SJF/czpzqq6G3lprGFLa6ps12yb1uQ1EmitNnep2fDMNh1aO/Zbq9sWY+3lem0zYb2oHJnQWyabTGUZ+L1ScQ== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -19679,6 +23531,11 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-fest@^2.13.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" + integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -19734,7 +23591,7 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -"typescript@^4.6.4 || ^5.0.0": +"typescript@^4.6.4 || ^5.0.0", typescript@~5.0.4: version "5.0.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== @@ -19764,6 +23621,16 @@ ua-parser-js@^1.0.32: resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.32.tgz#786bf17df97de159d5b1c9d5e8e9e89806f8a030" integrity sha512-dXVsz3M4j+5tTiovFVyVqssXBu5HM47//YSOeZ9fQkdDKkfzv2v3PP1jmH6FUyPW+yCSn7aBVK1fGGKNhowdDA== +ufo@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.1.2.tgz#d0d9e0fa09dece0c31ffd57bd363f030a35cfe76" + integrity sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ== + +uglify-js@^3.1.4: + version "3.17.4" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" + integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== + uid-number@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" @@ -19833,6 +23700,13 @@ unified@^9.0.0: trough "^1.0.0" vfile "^4.0.0" +union@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/union/-/union-0.5.0.tgz#b2c11be84f60538537b846edb9ba266ba0090075" + integrity sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA== + dependencies: + qs "^6.4.0" + unique-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" @@ -19889,6 +23763,11 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== +unix-crypt-td-js@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/unix-crypt-td-js/-/unix-crypt-td-js-1.1.4.tgz#4912dfad1c8aeb7d20fa0a39e4c31918c1d5d5dd" + integrity sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw== + unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -19909,6 +23788,14 @@ upath@^1.2.0: resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== +update-browserslist-db@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" + integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + update-browserslist-db@^1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" @@ -19917,6 +23804,14 @@ update-browserslist-db@^1.0.9: escalade "^3.1.1" picocolors "^1.0.0" +update-check@1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/update-check/-/update-check-1.5.4.tgz#5b508e259558f1ad7dbc8b4b0457d4c9d28c8743" + integrity sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ== + dependencies: + registry-auth-token "3.3.2" + registry-url "3.1.0" + update-notifier@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.3.tgz#be86ee13e8ce48fb50043ff72057b5bd598e1ea3" @@ -19943,6 +23838,11 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +url-join@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7" + integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA== + url-parse-lax@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" @@ -20115,6 +24015,15 @@ v8-to-istanbul@^8.1.0: convert-source-map "^1.6.0" source-map "^0.7.3" +v8-to-istanbul@^9.0.1: + version "9.1.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265" + integrity sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.12" + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -20130,6 +24039,11 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" +validator@13.9.0, validator@^13.7.0: + version "13.9.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.9.0.tgz#33e7b85b604f3bbce9bb1a05d5c3e22e1c2ff855" + integrity sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA== + varint@^5.0.0, varint@^5.0.2, varint@~5.0.0: version "5.0.2" resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" @@ -20145,6 +24059,76 @@ vary@^1, vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== +verdaccio-audit@11.0.0-6-next.34: + version "11.0.0-6-next.34" + resolved "https://registry.yarnpkg.com/verdaccio-audit/-/verdaccio-audit-11.0.0-6-next.34.tgz#2d4582842853a7a00bcc493be13a63ef5dcdc320" + integrity sha512-TF+gnJJveEI4TGJTsVBCNwbfx5WBvlP7QoaDDxSmJQlmhzrsJ2MjhagWgAA/OoLV7p45bJ7e00v391Frv0pwnw== + dependencies: + "@verdaccio/config" "6.0.0-6-next.71" + "@verdaccio/core" "6.0.0-6-next.71" + express "4.18.2" + https-proxy-agent "5.0.1" + node-fetch cjs + +verdaccio-htpasswd@11.0.0-6-next.41: + version "11.0.0-6-next.41" + resolved "https://registry.yarnpkg.com/verdaccio-htpasswd/-/verdaccio-htpasswd-11.0.0-6-next.41.tgz#2e6ce1e47a09025a76ec132e951e4267c4c33aff" + integrity sha512-HS1/3No2W7Dhl9DJ3tXUDgSOIL3do5tW2O2OvRVPc6aNKbqXFg22FIqjzpn1yG2sydTuBFKUSjMvmk/1oliKPg== + dependencies: + "@verdaccio/core" "6.0.0-6-next.71" + "@verdaccio/file-locking" "11.0.0-6-next.7" + apache-md5 "1.1.8" + bcryptjs "2.4.3" + core-js "3.30.2" + debug "4.3.4" + http-errors "2.0.0" + unix-crypt-td-js "1.1.4" + +verdaccio@^5.0.4: + version "5.25.0" + resolved "https://registry.yarnpkg.com/verdaccio/-/verdaccio-5.25.0.tgz#fefe45ca72b6e612a3ef79bb80138947871e8ca5" + integrity sha512-h/BDAudOZtwC52waErxCjZA+YKuUi7Ojt3haRGxZ1ZTL26BkbjaKkzt0Y72Z2bauRLxmwtGevJWm2LV7ZTeIug== + dependencies: + "@verdaccio/config" "6.0.0-6-next.71" + "@verdaccio/core" "6.0.0-6-next.71" + "@verdaccio/local-storage" "10.3.3" + "@verdaccio/logger-7" "6.0.0-6-next.16" + "@verdaccio/middleware" "6.0.0-6-next.50" + "@verdaccio/search" "6.0.0-6-next.2" + "@verdaccio/signature" "6.0.0-6-next.2" + "@verdaccio/streams" "10.2.1" + "@verdaccio/tarball" "11.0.0-6-next.40" + "@verdaccio/ui-theme" "6.0.0-6-next.71" + "@verdaccio/url" "11.0.0-6-next.37" + "@verdaccio/utils" "6.0.0-6-next.39" + JSONStream "1.3.5" + async "3.2.4" + body-parser "1.20.2" + clipanion "3.2.0" + compression "1.7.4" + cookies "0.8.0" + cors "2.8.5" + debug "^4.3.4" + envinfo "7.8.1" + express "4.18.2" + express-rate-limit "5.5.1" + fast-safe-stringify "2.1.1" + handlebars "4.7.7" + js-yaml "4.1.0" + jsonwebtoken "9.0.0" + kleur "4.1.5" + lodash "4.17.21" + lru-cache "7.18.3" + mime "3.0.0" + mkdirp "1.0.4" + mv "2.1.1" + pkginfo "0.4.1" + request "2.88.2" + semver "7.5.1" + validator "13.9.0" + verdaccio-audit "11.0.0-6-next.34" + verdaccio-htpasswd "11.0.0-6-next.41" + verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" @@ -20172,6 +24156,92 @@ vfile@^4.0.0: unist-util-stringify-position "^2.0.0" vfile-message "^2.0.0" +vite-node@0.31.4: + version "0.31.4" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-0.31.4.tgz#0437f76c35fa83f0a868d3fb5896ca9e164291f5" + integrity sha512-uzL377GjJtTbuc5KQxVbDu2xfU/x0wVjUtXQR2ihS21q/NK6ROr4oG0rsSkBBddZUVCwzfx22in76/0ZZHXgkQ== + dependencies: + cac "^6.7.14" + debug "^4.3.4" + mlly "^1.2.0" + pathe "^1.1.0" + picocolors "^1.0.0" + vite "^3.0.0 || ^4.0.0" + +vite-plugin-dts@~1.7.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/vite-plugin-dts/-/vite-plugin-dts-1.7.3.tgz#cf0c243fff9ae3fc1f103987b97439b3bf813f15" + integrity sha512-u3t45p6fTbzUPMkwYe0ESwuUeiRMlwdPfD3dRyDKUwLe2WmEYcFyVp2o9/ke2EMrM51lQcmNWdV9eLcgjD1/ng== + dependencies: + "@microsoft/api-extractor" "^7.33.5" + "@rollup/pluginutils" "^5.0.2" + "@rushstack/node-core-library" "^3.53.2" + debug "^4.3.4" + fast-glob "^3.2.12" + fs-extra "^10.1.0" + kolorist "^1.6.0" + ts-morph "17.0.1" + +vite-plugin-eslint@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/vite-plugin-eslint/-/vite-plugin-eslint-1.8.1.tgz#0381b8272e7f0fd8b663311b64f7608d55d8b04c" + integrity sha512-PqdMf3Y2fLO9FsNPmMX+//2BF5SF8nEWspZdgl4kSt7UvHDRHVVfHvxsD7ULYzZrJDGRxR81Nq7TOFgwMnUang== + dependencies: + "@rollup/pluginutils" "^4.2.1" + "@types/eslint" "^8.4.5" + rollup "^2.77.2" + +vite-tsconfig-paths@^4.0.2: + version "4.2.0" + resolved "https://registry.yarnpkg.com/vite-tsconfig-paths/-/vite-tsconfig-paths-4.2.0.tgz#bd2647d3eadafb65a10fc98a2ca565211f2eaf63" + integrity sha512-jGpus0eUy5qbbMVGiTxCL1iB9ZGN6Bd37VGLJU39kTDD6ZfULTTb1bcc5IeTWqWJKiWV5YihCaibeASPiGi8kw== + dependencies: + debug "^4.1.1" + globrex "^0.1.2" + tsconfck "^2.1.0" + +"vite@^3.0.0 || ^4.0.0", vite@^4.3.4: + version "4.3.9" + resolved "https://registry.yarnpkg.com/vite/-/vite-4.3.9.tgz#db896200c0b1aa13b37cdc35c9e99ee2fdd5f96d" + integrity sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg== + dependencies: + esbuild "^0.17.5" + postcss "^8.4.23" + rollup "^3.21.0" + optionalDependencies: + fsevents "~2.3.2" + +vitest@^0.31.0: + version "0.31.4" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-0.31.4.tgz#5abe02562675262949c10e40811f348a80f6b2a6" + integrity sha512-GoV0VQPmWrUFOZSg3RpQAPN+LPmHg2/gxlMNJlyxJihkz6qReHDV6b0pPDcqFLNEPya4tWJ1pgwUNP9MLmUfvQ== + dependencies: + "@types/chai" "^4.3.5" + "@types/chai-subset" "^1.3.3" + "@types/node" "*" + "@vitest/expect" "0.31.4" + "@vitest/runner" "0.31.4" + "@vitest/snapshot" "0.31.4" + "@vitest/spy" "0.31.4" + "@vitest/utils" "0.31.4" + acorn "^8.8.2" + acorn-walk "^8.2.0" + cac "^6.7.14" + chai "^4.3.7" + concordance "^5.0.4" + debug "^4.3.4" + local-pkg "^0.4.3" + magic-string "^0.30.0" + pathe "^1.1.0" + picocolors "^1.0.0" + std-env "^3.3.2" + strip-literal "^1.0.1" + tinybench "^2.5.0" + tinypool "^0.5.0" + vite "^3.0.0 || ^4.0.0" + vite-node "0.31.4" + why-is-node-running "^2.2.2" + w3c-hr-time@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" @@ -20186,7 +24256,25 @@ w3c-xmlserializer@^2.0.0: dependencies: xml-name-validator "^3.0.0" -walker@^1.0.7: +w3c-xmlserializer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" + integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw== + dependencies: + xml-name-validator "^4.0.0" + +wait-on@7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-7.0.1.tgz#5cff9f8427e94f4deacbc2762e6b0a489b19eae9" + integrity sha512-9AnJE9qTjRQOlTZIldAaf/da2eW0eSRSgcqq85mXQja/DW3MriHxkpODDSUEg+Gri/rKEcXUZHe+cevvYItaog== + dependencies: + axios "^0.27.2" + joi "^17.7.0" + lodash "^4.17.21" + minimist "^1.2.7" + rxjs "^7.8.0" + +walker@^1.0.7, walker@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== @@ -20589,6 +24677,11 @@ webidl-conversions@^6.1.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + webpack-bundle-analyzer@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.7.0.tgz#33c1c485a7fcae8627c547b5c3328b46de733c66" @@ -20755,6 +24848,11 @@ websocket@^1.0.32: utf-8-validate "^5.0.2" yaeti "^0.0.6" +well-known-symbols@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/well-known-symbols/-/well-known-symbols-2.0.0.tgz#e9c7c07dbd132b7b84212c8174391ec1f9871ba5" + integrity sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q== + whatwg-encoding@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" @@ -20762,6 +24860,13 @@ whatwg-encoding@^1.0.5: dependencies: iconv-lite "0.4.24" +whatwg-encoding@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" + integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== + dependencies: + iconv-lite "0.6.3" + whatwg-fetch@>=0.10.0, whatwg-fetch@^3.6.2: version "3.6.2" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" @@ -20772,6 +24877,19 @@ whatwg-mimetype@^2.3.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== + +whatwg-url@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" + integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== + dependencies: + tr46 "^3.0.0" + webidl-conversions "^7.0.0" + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -20850,6 +24968,14 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" +why-is-node-running@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.2.2.tgz#4185b2b4699117819e7154594271e7e344c9973e" + integrity sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA== + dependencies: + siginfo "^2.0.0" + stackback "0.0.2" + wide-align@^1.1.0: version "1.1.5" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" @@ -20864,6 +24990,13 @@ widest-line@^3.1.0: dependencies: string-width "^4.0.0" +widest-line@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-4.0.1.tgz#a0fc673aaba1ea6f0a0d35b3c2795c9a9cc2ebf2" + integrity sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig== + dependencies: + string-width "^5.0.1" + wildcard@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" @@ -20874,6 +25007,11 @@ word-wrap@^1.2.3, word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== + wordwrapjs@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.1.tgz#d9790bccfb110a0fc7836b5ebce0937b37a8b98f" @@ -21092,6 +25230,15 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^8.0.1: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -21107,6 +25254,14 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + ws@7.4.6: version "7.4.6" resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" @@ -21131,6 +25286,11 @@ ws@^7.3.1, ws@^7.4.0, ws@^7.4.5, ws@^7.4.6, ws@^7.5.1: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== +ws@^8.11.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== + ws@^8.4.2, ws@^8.5.0: version "8.12.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.0.tgz#485074cc392689da78e1828a9ff23585e06cddd8" @@ -21183,6 +25343,11 @@ xml-name-validator@^3.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== + xml-parse-from-string@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz#a9029e929d3dbcded169f3c6e28238d95a5d5a28" @@ -21256,7 +25421,7 @@ yargs-parser@20.2.4: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== -yargs-parser@21.1.1, yargs-parser@>=21.1.1, yargs-parser@^21.1.1: +yargs-parser@21.1.1, yargs-parser@>=21.1.1, yargs-parser@^21.0.1, yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== @@ -21358,7 +25523,7 @@ yargs@^17.0.0: y18n "^5.0.5" yargs-parser "^21.1.1" -yargs@^17.6.2: +yargs@^17.3.1, yargs@^17.6.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== @@ -21394,6 +25559,35 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== +yocto-queue@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" + integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== + +yup@0.32.11: + version "0.32.11" + resolved "https://registry.yarnpkg.com/yup/-/yup-0.32.11.tgz#d67fb83eefa4698607982e63f7ca4c5ed3cf18c5" + integrity sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg== + dependencies: + "@babel/runtime" "^7.15.4" + "@types/lodash" "^4.14.175" + lodash "^4.17.21" + lodash-es "^4.17.21" + nanoclone "^0.2.1" + property-expr "^2.0.4" + toposort "^2.0.2" + +z-schema@~5.0.2: + version "5.0.6" + resolved "https://registry.yarnpkg.com/z-schema/-/z-schema-5.0.6.tgz#46d6a687b15e4a4369e18d6cb1c7b8618fc256c5" + integrity sha512-+XR1GhnWklYdfr8YaZv/iu+vY+ux7V5DS5zH1DQf6bO5ufrt/5cgNhVO5qyhsjFXvsqQb/f08DWE9b6uPscyAg== + dependencies: + lodash.get "^4.4.2" + lodash.isequal "^4.5.0" + validator "^13.7.0" + optionalDependencies: + commander "^10.0.0" + zustand@^4.3.5: version "4.3.7" resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.3.7.tgz#501b1f0393a7f1d103332e45ab574be5747fedce" From b72b1901b24aff05e67bba2539fe3b34e8290b0a Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Thu, 22 Jun 2023 14:05:53 +0100 Subject: [PATCH 29/67] chore: Add `widget-lib` and publish to NPM (#2704) * chore: add widget lib * fix: fix build dir * feat: implement widget and tests * docs: improve docs * chore: publish to npm --- jest.config.app.ts | 7 +++ jest.config.ts | 4 +- project.json | 2 +- src/libs/ui-utils/package.json | 2 +- src/libs/widget-lib/.eslintrc.json | 18 ++++++ src/libs/widget-lib/README.md | 62 +++++++++++++++++++ src/libs/widget-lib/jest.config.ts | 10 +++ src/libs/widget-lib/package.json | 24 ++++++++ src/libs/widget-lib/project.json | 42 +++++++++++++ src/libs/widget-lib/src/JsonRpcManager.ts | 73 ++++++++++++++++++++++ src/libs/widget-lib/src/consts.ts | 11 ++++ src/libs/widget-lib/src/cowSwapWidget.ts | 55 +++++++++++++++++ src/libs/widget-lib/src/index.ts | 3 + src/libs/widget-lib/src/types.ts | 47 ++++++++++++++ src/libs/widget-lib/src/utils.spec.ts | 75 +++++++++++++++++++++++ src/libs/widget-lib/src/utils.ts | 43 +++++++++++++ src/libs/widget-lib/tsconfig.json | 22 +++++++ src/libs/widget-lib/tsconfig.lib.json | 10 +++ src/libs/widget-lib/tsconfig.spec.json | 9 +++ src/libs/widget-lib/vite.config.ts | 48 +++++++++++++++ tools/scripts/publish.mjs | 17 +++-- tsconfig.base.json | 3 +- 22 files changed, 576 insertions(+), 11 deletions(-) create mode 100644 jest.config.app.ts create mode 100644 src/libs/widget-lib/.eslintrc.json create mode 100644 src/libs/widget-lib/README.md create mode 100644 src/libs/widget-lib/jest.config.ts create mode 100644 src/libs/widget-lib/package.json create mode 100644 src/libs/widget-lib/project.json create mode 100644 src/libs/widget-lib/src/JsonRpcManager.ts create mode 100644 src/libs/widget-lib/src/consts.ts create mode 100644 src/libs/widget-lib/src/cowSwapWidget.ts create mode 100644 src/libs/widget-lib/src/index.ts create mode 100644 src/libs/widget-lib/src/types.ts create mode 100644 src/libs/widget-lib/src/utils.spec.ts create mode 100644 src/libs/widget-lib/src/utils.ts create mode 100644 src/libs/widget-lib/tsconfig.json create mode 100644 src/libs/widget-lib/tsconfig.lib.json create mode 100644 src/libs/widget-lib/tsconfig.spec.json create mode 100644 src/libs/widget-lib/vite.config.ts diff --git a/jest.config.app.ts b/jest.config.app.ts new file mode 100644 index 0000000000..9b66958ca6 --- /dev/null +++ b/jest.config.app.ts @@ -0,0 +1,7 @@ +import { getJestProjects } from '@nx/jest' + +const config = { + projects: getJestProjects(), +} + +export default config diff --git a/jest.config.ts b/jest.config.ts index 9b66958ca6..64f7210caf 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -1,7 +1,5 @@ import { getJestProjects } from '@nx/jest' -const config = { +export default { projects: getJestProjects(), } - -export default config diff --git a/project.json b/project.json index ced528c01b..8bf23e6375 100644 --- a/project.json +++ b/project.json @@ -33,7 +33,7 @@ "executor": "@nx/jest:jest", "outputs": ["{workspaceRoot}/coverage/{projectName}"], "options": { - "jestConfig": "jest.config.ts", + "jestConfig": "jest.config.app.ts", "passWithNoTests": true }, "configurations": { diff --git a/src/libs/ui-utils/package.json b/src/libs/ui-utils/package.json index c098259ef5..63666735b9 100644 --- a/src/libs/ui-utils/package.json +++ b/src/libs/ui-utils/package.json @@ -1,5 +1,5 @@ { "name": "@cowprotocol/ui-utils", - "version": "0.0.1", + "version": "0.0.3", "type": "commonjs" } diff --git a/src/libs/widget-lib/.eslintrc.json b/src/libs/widget-lib/.eslintrc.json new file mode 100644 index 0000000000..3456be9b90 --- /dev/null +++ b/src/libs/widget-lib/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/src/libs/widget-lib/README.md b/src/libs/widget-lib/README.md new file mode 100644 index 0000000000..521a7982f5 --- /dev/null +++ b/src/libs/widget-lib/README.md @@ -0,0 +1,62 @@ +# CoW Swap widget-lib + +## Use it + +Install dependency + +```bash +# Install the dependency using NPM +npm install @cowprotocol/widget-lib --save + +# ...or alternativelly use YARN +yarn add @cowprotocol/widget-lib +``` + +Create an empty div somewhere in your website: + +```html +
+``` + +Import the widget and initialise it: + +```js +import { cowSwapWidget } from '@cowprotocol/widget-lib' + +// Initialise the widget +const widgetContainer = document.getElementById('cowswap-widget') +cowSwapWidget({ + container: widgetContainer +}) + + +// Additionally, you can pass some additional params to customise your widget +provider = /* instantiate your own web3 provider */ +cowSwapWidget({ + container: widgetContainer, + width: 600 + height: 640 + urlParams: { + sell: { asset: 'DAI' }, + buy: { asset: 'USDC', amount: '0.1' } + }, + provider +}) +``` + +## Developers + +```bash +# Test +nx test widget-lib + +# Build the library +nx build widget-lib + +# Publish to NPM +nx publish widget-lib --ver 0.0.7 --tag latest +``` + +## Running unit tests + +Run `nx test widget-lib` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/src/libs/widget-lib/jest.config.ts b/src/libs/widget-lib/jest.config.ts new file mode 100644 index 0000000000..6602c1b24a --- /dev/null +++ b/src/libs/widget-lib/jest.config.ts @@ -0,0 +1,10 @@ +/* eslint-disable */ +export default { + displayName: 'widget-lib', + preset: '../../../jest.preset.js', + transform: { + '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../../coverage/libs/widget-lib', +} diff --git a/src/libs/widget-lib/package.json b/src/libs/widget-lib/package.json new file mode 100644 index 0000000000..4c63ac8c32 --- /dev/null +++ b/src/libs/widget-lib/package.json @@ -0,0 +1,24 @@ +{ + "name": "@cowprotocol/widget-lib", + "version": "0.0.5", + "type": "commonjs", + "description": "CoW Swap Widget Library. Allows you to easily embed a CoW Swap widget on your website.", + "main": "index.js", + "exports": { + "import": "./index.js", + "require": "./index.mjs" + }, + "license": "ISC", + "repository": { + "type": "git", + "url": "https://github.com/cowprotocol/cowswap" + }, + "keywords": [ + "cowprotocol", + "widget", + "cowswap", + "dex", + "widget-lib" + ], + "dependencies": {} +} diff --git a/src/libs/widget-lib/project.json b/src/libs/widget-lib/project.json new file mode 100644 index 0000000000..cbc42d9833 --- /dev/null +++ b/src/libs/widget-lib/project.json @@ -0,0 +1,42 @@ +{ + "name": "widget-lib", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "src/libs/widget-lib/src", + "projectType": "library", + + "targets": { + "build": { + "executor": "@nx/vite:build", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/widget-lib" + } + }, + "publish": { + "command": "node tools/scripts/publish.mjs widget-lib {args.ver} {args.tag}", + "dependsOn": ["build"] + }, + "lint": { + "executor": "@nx/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["src/libs/widget-lib/**/*.ts"] + } + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "src/libs/widget-lib/jest.config.ts", + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "codeCoverage": true + } + } + } + }, + "tags": [] +} diff --git a/src/libs/widget-lib/src/JsonRpcManager.ts b/src/libs/widget-lib/src/JsonRpcManager.ts new file mode 100644 index 0000000000..640357cc93 --- /dev/null +++ b/src/libs/widget-lib/src/JsonRpcManager.ts @@ -0,0 +1,73 @@ +import { EthereumProvider, JsonRpcRequest } from './types' + +const JSON_PRC_V = '2.0' +const TARGET_ORIGIN = '*' +const EVENTS = ['connect', 'disconnect', 'chainChanged', 'accountsChanged'] + +export class JsonRpcManager { + ethereumProvider: EthereumProvider | null = null + + requests: { [key: string]: JsonRpcRequest } = {} + + constructor(private contentWindow: Window) { + window.addEventListener('message', this.processEvent) + } + + disconnect() { + this.ethereumProvider = null + window.removeEventListener('message', this.processEvent) + } + + onConnect(ethereumProvider: EthereumProvider) { + this.ethereumProvider = ethereumProvider + + Object.keys(this.requests).forEach((key) => { + this.processRequest(this.requests[key]) + }) + + this.requests = {} + + EVENTS.forEach((event) => { + ethereumProvider.on(event, (params: unknown): void => { + this.postMessage({ method: event, params: [params] }) + }) + }) + } + + processRequest(request: JsonRpcRequest) { + if (!this.ethereumProvider) return + + const request$ = + request.method === 'enable' // + ? this.ethereumProvider.enable() + : this.ethereumProvider.request(request) + + request$ + .then((result) => { + this.postMessage({ id: request.id, result }) + }) + .catch((error) => { + this.postMessage({ id: request.id, error }) + }) + } + + private processEvent = (event: MessageEvent) => { + if (event.data.jsonrpc === '2.0') { + if (this.ethereumProvider) { + this.processRequest(event.data) + } else { + this.requests[event.data.id] = event.data + } + } + } + + private postMessage(params: { [key: string]: unknown }) { + this.contentWindow.postMessage( + { + jsonrpc: JSON_PRC_V, + ...params, + }, + TARGET_ORIGIN + ) + } +} diff --git a/src/libs/widget-lib/src/consts.ts b/src/libs/widget-lib/src/consts.ts new file mode 100644 index 0000000000..a1464e3e38 --- /dev/null +++ b/src/libs/widget-lib/src/consts.ts @@ -0,0 +1,11 @@ +import { CowSwapWidgetEnv } from './types' + +export const DEFAULT_CHAIN = 1 +export const DEFAULT_WIDTH = 400 +export const DEFAULT_HEIGHT = 640 + +export const COWSWAP_URLS: Record = { + local: 'http://localhost:3000', + barn: 'https://barn.cow.fi', + prod: 'http://swap.cow.fi', +} diff --git a/src/libs/widget-lib/src/cowSwapWidget.ts b/src/libs/widget-lib/src/cowSwapWidget.ts new file mode 100644 index 0000000000..bb003f93e9 --- /dev/null +++ b/src/libs/widget-lib/src/cowSwapWidget.ts @@ -0,0 +1,55 @@ +import { DEFAULT_HEIGHT, DEFAULT_WIDTH } from './consts' +import { JsonRpcManager } from './JsonRpcManager' +import { CowSwapWidgetParams, CowSwapWidgetUrlParams } from './types' +import { buildTradeAmountsQuery, buildWidgetPath, buildWidgetUrl } from './utils' + +const COW_SWAP_WIDGET_KEY = '@cowprotocol/widget-lib' + +type UpdateWidgetCallback = (params: CowSwapWidgetUrlParams) => void + +export function cowSwapWidget(params: CowSwapWidgetParams): UpdateWidgetCallback { + const { container, provider } = params + const iframe = createIframe(params) + + container.innerHTML = '' + container.appendChild(iframe) + + const { contentWindow } = iframe + + if (!contentWindow) throw new Error('Iframe does not contain a window!') + + if (provider) { + const jsonRpcManager = new JsonRpcManager(contentWindow) + + jsonRpcManager.onConnect(provider) + } + + return (params: CowSwapWidgetUrlParams) => updateWidget(params, contentWindow) +} + +function updateWidget(params: CowSwapWidgetUrlParams, contentWindow: Window) { + const pathname = buildWidgetPath(params) + const search = buildTradeAmountsQuery(params).toString() + + contentWindow.postMessage( + { + key: COW_SWAP_WIDGET_KEY, + pathname, + search, + }, + '*' + ) +} + +function createIframe(params: CowSwapWidgetParams): HTMLIFrameElement { + const { width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT } = params + + const iframe = document.createElement('iframe') + + iframe.src = buildWidgetUrl(params.urlParams ?? {}) + iframe.width = `${width}px` + iframe.height = `${height}px` + iframe.style.border = '0' + + return iframe +} diff --git a/src/libs/widget-lib/src/index.ts b/src/libs/widget-lib/src/index.ts new file mode 100644 index 0000000000..3a727f83fd --- /dev/null +++ b/src/libs/widget-lib/src/index.ts @@ -0,0 +1,3 @@ +export { cowSwapWidget } from './cowSwapWidget' +export { COWSWAP_URLS } from './consts' +export * from './types' diff --git a/src/libs/widget-lib/src/types.ts b/src/libs/widget-lib/src/types.ts new file mode 100644 index 0000000000..d0484fb97f --- /dev/null +++ b/src/libs/widget-lib/src/types.ts @@ -0,0 +1,47 @@ +declare global { + interface Window { + ethereum?: EthereumProvider + } +} + +export interface JsonRpcRequest { + id: number + method: string + params: unknown[] +} + +// https://eips.ethereum.org/EIPS/eip-1193 +export interface EthereumProvider { + on(event: string, args: unknown): void + request(params: JsonRpcRequest): Promise + enable(): Promise +} + +export type CowSwapWidgetEnv = 'local' | 'prod' | 'barn' + +export type CowSwapTheme = 'dark' | 'light' + +interface TradeAsset { + asset: string + amount?: string +} + +export interface TradeAssets { + sell: TradeAsset + buy: TradeAsset +} + +export interface CowSwapWidgetUrlParams { + chainId?: number + env?: CowSwapWidgetEnv + tradeAssets?: TradeAssets + theme?: CowSwapTheme +} + +export interface CowSwapWidgetParams { + container: HTMLElement + width?: number + height?: number + urlParams?: CowSwapWidgetUrlParams + provider?: EthereumProvider +} diff --git a/src/libs/widget-lib/src/utils.spec.ts b/src/libs/widget-lib/src/utils.spec.ts new file mode 100644 index 0000000000..46c9cd0c52 --- /dev/null +++ b/src/libs/widget-lib/src/utils.spec.ts @@ -0,0 +1,75 @@ +import { buildWidgetUrl } from './utils' + +describe('buildWidgetUrl', () => { + it('minimal config', () => { + const url = buildWidgetUrl({}) + expect(url).toEqual('http://swap.cow.fi/#/1/swap/widget/?') + }) + + describe('env', () => { + it('local', () => { + const url = buildWidgetUrl({ env: 'local' }) + expect(url).toEqual('http://localhost:3000/#/1/swap/widget/?') + }) + it('prod', () => { + const url = buildWidgetUrl({ env: 'prod' }) + expect(url).toEqual('http://swap.cow.fi/#/1/swap/widget/?') + }) + + it('barn', () => { + const url = buildWidgetUrl({ env: 'barn' }) + expect(url).toEqual('https://barn.cow.fi/#/1/swap/widget/?') + }) + }) + + describe('chainId', () => { + it('mainnet', () => { + const url = buildWidgetUrl({ chainId: 1 }) + expect(url).toEqual('http://swap.cow.fi/#/1/swap/widget/?') + }) + it('gnosis chain', () => { + const url = buildWidgetUrl({ chainId: 5 }) + expect(url).toEqual('http://swap.cow.fi/#/5/swap/widget/?') + }) + }) + + describe('theme', () => { + it('dark', () => { + const url = buildWidgetUrl({ theme: 'dark' }) + expect(url).toEqual('http://swap.cow.fi/#/1/swap/widget/?theme=dark') + }) + it('light', () => { + const url = buildWidgetUrl({ theme: 'light' }) + expect(url).toEqual('http://swap.cow.fi/#/1/swap/widget/?theme=light') + }) + }) + + describe('trade assets', () => { + it('without amounts', () => { + const url = buildWidgetUrl({ tradeAssets: { sell: { asset: 'WETH' }, buy: { asset: 'COW' } } }) + expect(url).toEqual('http://swap.cow.fi/#/1/swap/widget/WETH/COW?') + }) + + it('with sell amount', () => { + const url = buildWidgetUrl({ tradeAssets: { sell: { asset: 'DAI', amount: '0.1' }, buy: { asset: 'USDC' } } }) + expect(url).toEqual('http://swap.cow.fi/#/1/swap/widget/DAI/USDC?sellAmount=0.1') + }) + + it('with buy amount', () => { + const url = buildWidgetUrl({ tradeAssets: { sell: { asset: 'DAI' }, buy: { asset: 'USDC', amount: '0.1' } } }) + expect(url).toEqual('http://swap.cow.fi/#/1/swap/widget/DAI/USDC?buyAmount=0.1') + }) + }) + + describe('all config', () => { + it('dark', () => { + const url = buildWidgetUrl({ + env: 'barn', + chainId: 100, + theme: 'light', + tradeAssets: { sell: { asset: 'DAI', amount: '0.1' }, buy: { asset: 'USDC', amount: '0.1' } }, + }) + expect(url).toEqual('https://barn.cow.fi/#/100/swap/widget/DAI/USDC?sellAmount=0.1&buyAmount=0.1&theme=light') + }) + }) +}) diff --git a/src/libs/widget-lib/src/utils.ts b/src/libs/widget-lib/src/utils.ts new file mode 100644 index 0000000000..0cd02caad7 --- /dev/null +++ b/src/libs/widget-lib/src/utils.ts @@ -0,0 +1,43 @@ +import { COWSWAP_URLS, DEFAULT_CHAIN } from './consts' +import { CowSwapWidgetUrlParams } from './types' + +export function buildWidgetUrl(params: CowSwapWidgetUrlParams): string { + const host = COWSWAP_URLS[params.env ?? 'prod'] + const path = buildWidgetPath(params) + const query = buildTradeAmountsQuery(params) + + return host + '/#' + path + '?' + query +} + +export function buildWidgetPath(params: CowSwapWidgetUrlParams): string { + const { chainId = DEFAULT_CHAIN, tradeAssets } = params + + const assetsPath = tradeAssets + ? [tradeAssets.sell.asset, tradeAssets.buy.asset].map(encodeURIComponent).join('/') + : '' + + return `/${chainId}/swap/widget/${assetsPath}` +} + +export function buildTradeAmountsQuery(params: CowSwapWidgetUrlParams): URLSearchParams { + const { tradeAssets, theme } = params + const query = new URLSearchParams() + + if (tradeAssets) { + const { sell, buy } = tradeAssets + + if (sell.amount) { + query.append('sellAmount', sell.amount) + } + + if (buy.amount) { + query.append('buyAmount', buy.amount) + } + } + + if (theme) { + query.append('theme', theme) + } + + return query +} diff --git a/src/libs/widget-lib/tsconfig.json b/src/libs/widget-lib/tsconfig.json new file mode 100644 index 0000000000..8122543a9a --- /dev/null +++ b/src/libs/widget-lib/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/src/libs/widget-lib/tsconfig.lib.json b/src/libs/widget-lib/tsconfig.lib.json new file mode 100644 index 0000000000..4befa7f099 --- /dev/null +++ b/src/libs/widget-lib/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/src/libs/widget-lib/tsconfig.spec.json b/src/libs/widget-lib/tsconfig.spec.json new file mode 100644 index 0000000000..5a865ca8c0 --- /dev/null +++ b/src/libs/widget-lib/tsconfig.spec.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts", "src/lib/widget.ts"] +} diff --git a/src/libs/widget-lib/vite.config.ts b/src/libs/widget-lib/vite.config.ts new file mode 100644 index 0000000000..7155209003 --- /dev/null +++ b/src/libs/widget-lib/vite.config.ts @@ -0,0 +1,48 @@ +/// +import { joinPathFragments } from '@nx/devkit' +import { defineConfig } from 'vite' +import dts from 'vite-plugin-dts' +import viteTsConfigPaths from 'vite-tsconfig-paths' + +export default defineConfig({ + cacheDir: '../../../node_modules/.vite/widget-lib', + + plugins: [ + dts({ + entryRoot: 'src', + tsConfigFilePath: joinPathFragments(__dirname, 'tsconfig.lib.json'), + skipDiagnostics: true, + }), + + viteTsConfigPaths({ + root: '../../../', + }), + ], + + // Uncomment this if you are using workers. + // worker: { + // plugins: [ + // viteTsConfigPaths({ + // root: '../../../', + // }), + // ], + // }, + + // Configuration for building your library. + // See: https://vitejs.dev/guide/build.html#library-mode + build: { + lib: { + // Could also be a dictionary or array of multiple entry points. + entry: 'src/index.ts', + name: 'widget-lib', + fileName: 'index', + // Change this to the formats you want to support. + // Don't forgot to update your package.json as well. + formats: ['es', 'cjs'], + }, + rollupOptions: { + // External packages that should not be bundled into your library. + external: [], + }, + }, +}) diff --git a/tools/scripts/publish.mjs b/tools/scripts/publish.mjs index e82a097be2..a6214556eb 100644 --- a/tools/scripts/publish.mjs +++ b/tools/scripts/publish.mjs @@ -7,11 +7,12 @@ * You might need to authenticate with NPM before running this script. */ -import { execSync } from 'child_process' -import { readFileSync, writeFileSync } from 'fs' +import devkit from '@nx/devkit' import chalk from 'chalk' -import devkit from '@nx/devkit' +import { execSync } from 'child_process' +import { readFileSync, writeFileSync } from 'fs' +import path from 'path' const { readCachedProjectGraph } = devkit function invariant(condition, message) { @@ -23,7 +24,7 @@ function invariant(condition, message) { // Executing publish script: node path/to/publish.mjs {name} --version {version} --tag {tag} // Default "tag" to "next" so we won't publish the "latest" tag by accident. -const [, , name, version, tag = 'next'] = process.argv +const [, , name, version, tag] = process.argv // A simple SemVer validation to validate the version const validVersion = /^\d+\.\d+\.\d+(-\w+\.\d+)?/ @@ -43,6 +44,9 @@ invariant( `Could not find "build.options.outputPath" of project "${name}". Is project.json configured correctly?` ) +const rootLib = path.dirname(project.data.sourceRoot) +const copyReadmeCommand = `cp ${rootLib}/README.md ${outputPath}` +console.log(chalk.bold.greenBright(copyReadmeCommand)) process.chdir(outputPath) // Updating the version in "package.json" before publishing @@ -55,4 +59,7 @@ try { } // Execute "npm publish" to publish -execSync(`npm publish --access public --tag ${tag}`) +const publishCommand = `npm publish --access public --tag ${tag === 'undefined' ? 'next' : tag}` +console.log(chalk.bold.greenBright(publishCommand)) +execSync(publishCommand) +console.log('Published successfully 🎉') diff --git a/tsconfig.base.json b/tsconfig.base.json index e341874f5d..a8bde00a51 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -25,7 +25,8 @@ "types": ["react-spring", "jest"], "paths": { "@cowprotocol/ui": ["libs/ui/src/index.ts"], - "@cowprotocol/ui-utils": ["libs/ui-utils/src/index.ts"] + "@cowprotocol/ui-utils": ["libs/ui-utils/src/index.ts"], + "@cowprotocol/widget-lib": ["src/libs/widget-lib/src/index.ts"] } }, "exclude": ["node_modules", "cypress"] From 6354c9ad8aa898b5c3b23797a6c1b96af26efc63 Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Thu, 22 Jun 2023 19:10:12 +0600 Subject: [PATCH 30/67] chore: update embedded widget codebase (#2707) * chore: update embedded widget codebase * ci: vite config for dev server * chore: fix typings * feat(injected-widget): support widget mode features * Fix code style issues with Prettier * refactor(injected-widget): simplify postMessage events data --------- Co-authored-by: Lint Action --- package.json | 1 + src/common/constants/routes.ts | 65 ++++--- .../containers/ImportTokenModal/index.tsx | 21 +- .../updaters/ThemeFromUrlUpdater/index.tsx | 34 ++++ src/common/utils/isInjectedWidget.ts | 3 + .../components/AmplitudeAnalytics/index.ts | 4 +- src/legacy/components/Header/index.tsx | 51 +++-- src/legacy/components/Header/styled.tsx | 4 + src/legacy/state/connection/reducer.ts | 19 +- .../orders/middleware/appziMiddleware.test.ts | 2 +- src/libs/widget-lib/README.md | 4 + src/libs/widget-lib/project.json | 9 + src/libs/widget-lib/src/consts.ts | 7 +- src/libs/widget-lib/src/cowSwapWidget.ts | 37 ++-- src/libs/widget-lib/src/demo/ProviderMode.ts | 29 +++ src/libs/widget-lib/src/demo/Settings.ts | 45 +++++ src/libs/widget-lib/src/demo/index.html | 184 ++++++++++++++++++ src/libs/widget-lib/src/demo/index.ts | 39 ++++ src/libs/widget-lib/src/types.ts | 29 +-- src/libs/widget-lib/src/urlUtils.spec.ts | 85 ++++++++ .../widget-lib/src/{utils.ts => urlUtils.ts} | 8 +- src/libs/widget-lib/src/utils.spec.ts | 75 ------- src/libs/widget-lib/vite.config.ts | 94 +++++---- .../application/containers/App/RoutesApp.tsx | 7 +- .../application/containers/App/Updaters.tsx | 4 + .../application/containers/App/index.tsx | 14 +- .../application/containers/App/styled.ts | 4 + .../hooks/useInjectedWidgetParams.ts | 7 + src/modules/injectedWidget/index.ts | 2 + .../state/injectedWidgetParamsAtom.ts | 9 + .../updaters/InjectedWidgetUpdater.ts | 28 +++ src/modules/mainMenu/pure/MenuTree/index.tsx | 4 +- src/modules/mainMenu/types.ts | 4 +- src/modules/mainMenu/utils.ts | 4 +- src/modules/trade/hooks/useTradeTypeInfo.ts | 8 +- .../trade/utils/parameterizeTradeRoute.ts | 4 +- src/modules/wallet/api/types.ts | 1 + src/modules/wallet/api/utils/connection.ts | 2 + .../wallet/web3-react/connection/index.tsx | 5 + .../web3-react/connection/injectedWidget.tsx | 19 ++ .../Injected/IFrameEthereumProvider.ts | 11 ++ .../web3-react/connectors/Injected/index.tsx | 25 ++- .../web3-react/hooks/useEagerlyConnect.ts | 14 +- .../web3-react/hooks/useOrderedConnections.ts | 7 + yarn.lock | 7 + 45 files changed, 790 insertions(+), 249 deletions(-) create mode 100644 src/common/updaters/ThemeFromUrlUpdater/index.tsx create mode 100644 src/common/utils/isInjectedWidget.ts create mode 100644 src/libs/widget-lib/src/demo/ProviderMode.ts create mode 100644 src/libs/widget-lib/src/demo/Settings.ts create mode 100644 src/libs/widget-lib/src/demo/index.html create mode 100644 src/libs/widget-lib/src/demo/index.ts create mode 100644 src/libs/widget-lib/src/urlUtils.spec.ts rename src/libs/widget-lib/src/{utils.ts => urlUtils.ts} (81%) delete mode 100644 src/libs/widget-lib/src/utils.spec.ts create mode 100644 src/modules/injectedWidget/hooks/useInjectedWidgetParams.ts create mode 100644 src/modules/injectedWidget/index.ts create mode 100644 src/modules/injectedWidget/state/injectedWidgetParamsAtom.ts create mode 100644 src/modules/injectedWidget/updaters/InjectedWidgetUpdater.ts create mode 100644 src/modules/wallet/web3-react/connection/injectedWidget.tsx create mode 100644 src/modules/wallet/web3-react/connectors/Injected/IFrameEthereumProvider.ts diff --git a/package.json b/package.json index bc6658fa58..dc439aba80 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,7 @@ "@cowprotocol/ethflowcontract": "cowprotocol/ethflowcontract.git#v1.0.0-artifacts", "@davatar/react": "1.8.1", "@ethersproject/bignumber": "^5.7.0", + "@ethvault/iframe-provider": "^0.1.10", "@fontsource/ibm-plex-mono": "^4.5.1", "@fontsource/inter": "^4.5.1", "@ledgerhq/connect-kit-loader": "^1.0.2", diff --git a/src/common/constants/routes.ts b/src/common/constants/routes.ts index 058f7d386d..0054846fa3 100644 --- a/src/common/constants/routes.ts +++ b/src/common/constants/routes.ts @@ -1,30 +1,35 @@ -// ENUM with routes -export enum Routes { - HOME = '/', - SWAP = '/:chainId?/swap/:inputCurrencyId?/:outputCurrencyId?', - LIMIT_ORDER = '/:chainId?/limit-orders/:inputCurrencyId?/:outputCurrencyId?', - ADVANCED_ORDERS = '/:chainId?/advanced-orders/:inputCurrencyId?/:outputCurrencyId?', - SEND = '/send', - ACCOUNT = '/account', - ACCOUNT_TOKENS = '/account/tokens', - ACCOUNT_TOKENS_SINGLE = '/account/tokens/:address', - ACCOUNT_GOVERNANCE = '/account/governance', - ABOUT = '/about', - PRIVACY_POLICY = '/privacy-policy', - COOKIE_POLICY = '/cookie-policy', - TERMS_CONDITIONS = '/terms-and-conditions', - FAQ = '/faq', - FAQ_PROTOCOL = '/faq/protocol', - FAQ_TOKEN = '/faq/token', - FAQ_TRADING = '/faq/trading', - FAQ_LIMIT_ORDERS = '/faq/limit-order', - FAQ_ETH_FLOW = '/faq/sell-native', - PLAY_COWRUNNER = '/play/cow-runner', - PLAY_MEVSLICER = '/play/mev-slicer', - ANYSWAP_AFFECTED = '/anyswap-affected-users', - CHAT = '/chat', - DOCS = '/docs', - STATS = '/stats', - TWITTER = '/twitter', - KITCHEN_SINK = '/kitchen-sink', -} +import { isInjectedWidget } from 'common/utils/isInjectedWidget' + +export const TRADE_WIDGET_PREFIX = isInjectedWidget() ? '/widget' : '' + +export const Routes = { + HOME: '/', + SWAP: `/:chainId?${TRADE_WIDGET_PREFIX}/swap/:inputCurrencyId?/:outputCurrencyId?`, + LIMIT_ORDER: `/:chainId?${TRADE_WIDGET_PREFIX}/limit-orders/:inputCurrencyId?/:outputCurrencyId?`, + ADVANCED_ORDERS: `/:chainId?${TRADE_WIDGET_PREFIX}/advanced-orders/:inputCurrencyId?/:outputCurrencyId?`, + SEND: '/send', + ACCOUNT: '/account', + ACCOUNT_TOKENS: '/account/tokens', + ACCOUNT_TOKENS_SINGLE: '/account/tokens/:address', + ACCOUNT_GOVERNANCE: '/account/governance', + ABOUT: '/about', + PRIVACY_POLICY: '/privacy-policy', + COOKIE_POLICY: '/cookie-policy', + TERMS_CONDITIONS: '/terms-and-conditions', + FAQ: '/faq', + FAQ_PROTOCOL: '/faq/protocol', + FAQ_TOKEN: '/faq/token', + FAQ_TRADING: '/faq/trading', + FAQ_LIMIT_ORDERS: '/faq/limit-order', + FAQ_ETH_FLOW: '/faq/sell-native', + PLAY_COWRUNNER: '/play/cow-runner', + PLAY_MEVSLICER: '/play/mev-slicer', + ANYSWAP_AFFECTED: '/anyswap-affected-users', + CHAT: '/chat', + DOCS: '/docs', + STATS: '/stats', + TWITTER: '/twitter', +} as const + +export type RoutesKeys = keyof typeof Routes +export type RoutesValues = (typeof Routes)[RoutesKeys] diff --git a/src/common/containers/ImportTokenModal/index.tsx b/src/common/containers/ImportTokenModal/index.tsx index d84006a5a0..281043da7b 100644 --- a/src/common/containers/ImportTokenModal/index.tsx +++ b/src/common/containers/ImportTokenModal/index.tsx @@ -6,13 +6,17 @@ import { Token } from '@uniswap/sdk-core' import TokenWarningModal from 'legacy/components/TokenWarningModal' import { TOKEN_SHORTHANDS, WRAPPED_NATIVE_CURRENCY } from 'legacy/constants/tokens' import { useSearchInactiveTokenLists } from 'legacy/hooks/Tokens' +import useDebounce from 'legacy/hooks/useDebounce' import { Field } from 'legacy/state/swap/actions' +import { useAddUserToken } from 'legacy/state/user/hooks' import { supportedChainId } from 'legacy/utils/supportedChainId' import { tokensByAddressAtom, tokensBySymbolAtom } from 'modules/tokensList/state/tokensListAtom' import { useNavigateOnCurrencySelection } from 'modules/trade/hooks/useNavigateOnCurrencySelection' import { useTradeState } from 'modules/trade/hooks/useTradeState' +import { isInjectedWidget } from 'common/utils/isInjectedWidget' + export interface ImportTokenModalProps { chainId: number } @@ -21,6 +25,8 @@ export function ImportTokenModal(props: ImportTokenModalProps) { const { chainId } = props const { state } = useTradeState() + const addToken = useAddUserToken() + const loadedInputCurrency = useSearchInactiveTokenLists( state?.inputCurrencyId || undefined, 1, @@ -38,14 +44,15 @@ export function ImportTokenModal(props: ImportTokenModalProps) { const urlLoadedTokens: Token[] = useMemo( () => [loadedInputCurrency, loadedOutputCurrency]?.filter((c): c is Token => c?.isToken ?? false) ?? [], - [loadedInputCurrency, loadedOutputCurrency] + // eslint-disable-next-line react-hooks/exhaustive-deps + [loadedInputCurrency?.address, loadedOutputCurrency?.address] ) // dismiss warning if all imported tokens are in active lists const tokensByAddress = useAtomValue(tokensByAddressAtom) const tokensBySymbol = useAtomValue(tokensBySymbolAtom) // example: https://cowswap.dev.gnosisdev.com/#/swap?chain=mainnet&inputCurrency=0xe0b7927c4af23765cb51314a0e0521a9645f0e2a&outputCurrency=0x539F3615C1dBAfa0D008d87504667458acBd16Fa - const importTokensNotInDefault = useMemo(() => { + const _importTokensNotInDefault = useMemo(() => { // We should return an empty array until the defaultTokens are loaded // Otherwise WETH will be in urlLoadedTokens but defaultTokens will be empty // Fix for https://github.com/cowprotocol/cowswap/issues/534 @@ -77,6 +84,7 @@ export function ImportTokenModal(props: ImportTokenModalProps) { }) ) }, [chainId, tokensByAddress, tokensBySymbol, urlLoadedTokens]) + const importTokensNotInDefault = useDebounce(_importTokensNotInDefault, 200) const [dismissTokenWarning, setDismissTokenWarning] = useState(false) @@ -109,6 +117,15 @@ export function ImportTokenModal(props: ImportTokenModalProps) { } }, [importTokensLength]) + // Automatically import unknown tokens in Widget mode + useEffect(() => { + if (isInjectedWidget()) { + importTokensNotInDefault.forEach(addToken) + } + }, [addToken, importTokensNotInDefault]) + + if (isInjectedWidget()) return null + return ( 0 && !dismissTokenWarning} diff --git a/src/common/updaters/ThemeFromUrlUpdater/index.tsx b/src/common/updaters/ThemeFromUrlUpdater/index.tsx new file mode 100644 index 0000000000..683db0e494 --- /dev/null +++ b/src/common/updaters/ThemeFromUrlUpdater/index.tsx @@ -0,0 +1,34 @@ +import { useEffect, useRef } from 'react' + +import { useLocation } from 'react-router-dom' + +import { useAppDispatch } from 'legacy/state/hooks' +import { useIsDarkMode } from 'legacy/state/user/hooks' +import { updateUserDarkMode } from 'legacy/state/user/reducer' + +/** + * Switch theme depending on theme query parameter + */ +export function ThemeFromUrlUpdater() { + const darkMode = useIsDarkMode() + const darkModeRef = useRef(darkMode) + const dispatch = useAppDispatch() + const { search } = useLocation() + + darkModeRef.current = darkMode + + useEffect(() => { + const searchParams = new URLSearchParams(search) + const themeValue = searchParams.get('theme') + + if (!themeValue) return + + const isDarkMode = searchParams.get('theme') === 'dark' + + if (isDarkMode === darkModeRef.current) return + + dispatch(updateUserDarkMode({ userDarkMode: isDarkMode })) + }, [search, dispatch]) + + return null +} diff --git a/src/common/utils/isInjectedWidget.ts b/src/common/utils/isInjectedWidget.ts new file mode 100644 index 0000000000..8f17ad96a8 --- /dev/null +++ b/src/common/utils/isInjectedWidget.ts @@ -0,0 +1,3 @@ +export function isInjectedWidget(): boolean { + return window.location.hash.includes('/widget') +} diff --git a/src/legacy/components/AmplitudeAnalytics/index.ts b/src/legacy/components/AmplitudeAnalytics/index.ts index f056e58a21..d6533cc94d 100644 --- a/src/legacy/components/AmplitudeAnalytics/index.ts +++ b/src/legacy/components/AmplitudeAnalytics/index.ts @@ -7,9 +7,9 @@ import { Identify, identify, init, track } from '@amplitude/analytics-browser' * member of the organization on Amplitude to view details. */ export function initializeAnalytics(isDevEnvironment = process.env.NODE_ENV === 'development') { - const API_KEY = process.env.REACT_APP_AMPLITUDE_KEY - if (isDevEnvironment || !API_KEY) return + if (isDevEnvironment) return + const API_KEY = process.env.REACT_APP_AMPLITUDE_KEY if (typeof API_KEY === 'undefined') { throw new Error(`REACT_APP_AMPLITUDE_KEY must be a defined environment variable`) } diff --git a/src/legacy/components/Header/index.tsx b/src/legacy/components/Header/index.tsx index 8e08f4c30f..ac252c6d11 100644 --- a/src/legacy/components/Header/index.tsx +++ b/src/legacy/components/Header/index.tsx @@ -15,6 +15,7 @@ import { supportedChainId } from 'legacy/utils/supportedChainId' import { addBodyClass, removeBodyClass } from 'legacy/utils/toggleBodyClass' import { OrdersPanel } from 'modules/account/containers/OrdersPanel' +import { useInjectedWidgetParams } from 'modules/injectedWidget' import { MainMenuContext, useMenuItems } from 'modules/mainMenu' import { MenuTree } from 'modules/mainMenu/pure/MenuTree' import { useSwapRawState } from 'modules/swap/hooks/useSwapRawState' @@ -25,11 +26,13 @@ import { useWalletInfo, Web3Status } from 'modules/wallet' import { Routes } from 'common/constants/routes' import { TokenAmount } from 'common/pure/TokenAmount' +import { isInjectedWidget } from 'common/utils/isInjectedWidget' import MobileMenuIcon from './MobileMenuIcon' import { AccountElement, BalanceText, + CustomLogoImg, HeaderControls, HeaderElement, HeaderModWrapper, @@ -49,6 +52,8 @@ const CHAIN_CURRENCY_LABELS: { [chainId in ChainId]?: string } = { export default function Header() { const { account, chainId: connectedChainId } = useWalletInfo() const chainId = supportedChainId(connectedChainId) + const isInjectedWidgetMode = isInjectedWidget() + const injectedWidgetParams = useInjectedWidgetParams() const userEthBalance = useNativeCurrencyBalances(account ? [account] : [])?.[account ?? ''] const nativeToken = chainId && (CHAIN_CURRENCY_LABELS[chainId] || 'ETH') @@ -72,7 +77,7 @@ export default function Header() { const menuItems = useMenuItems() const navigate = useNavigate() - const handleBalanceButtonClick = () => navigate('/account') + const isUpToLarge = useMediaQuery(upToLarge) const isUpToMedium = useMediaQuery(upToMedium) const isUpToSmall = useMediaQuery(upToSmall) @@ -122,26 +127,36 @@ export default function Header() { - - <UniIcon> - <LogoImage isMobileMenuOpen={isMobileMenuOpen}> - <SVG src={cowSwapLogo(darkMode)} /> - </LogoImage> - </UniIcon> - - + {!injectedWidgetParams.hideLogo && ( + + <UniIcon> + <LogoImage isMobileMenuOpen={isMobileMenuOpen}> + {injectedWidgetParams.logoUrl ? ( + <CustomLogoImg src={injectedWidgetParams.logoUrl} alt="Logo" /> + ) : ( + <SVG src={cowSwapLogo(darkMode)} /> + )} + </LogoImage> + </UniIcon> + + )} + {!isInjectedWidgetMode && ( + + )} - + {!injectedWidgetParams.hideNetworkSelector && } - + {!isInjectedWidgetMode && ( + navigate('/account')} + account={account} + chainId={chainId} + isUpToSmall={isUpToSmall} + /> + )} {account && userEthBalance && chainId && ( @@ -154,7 +169,9 @@ export default function Header() { - {isUpToLarge && } + {isUpToLarge && !isInjectedWidgetMode && ( + + )} {isOrdersPanelOpen && } diff --git a/src/legacy/components/Header/styled.tsx b/src/legacy/components/Header/styled.tsx index 12babdf391..722d706963 100644 --- a/src/legacy/components/Header/styled.tsx +++ b/src/legacy/components/Header/styled.tsx @@ -493,6 +493,10 @@ export const UniIcon = styled.div` } ` +export const CustomLogoImg = styled.img` + height: 100%; +` + export const AccountElement = styled(AccountElementUni)<{ active: boolean }>` background-color: ${({ theme, active }) => (!active ? theme.bg1 : theme.bg1)}; border-radius: 21px; diff --git a/src/legacy/state/connection/reducer.ts b/src/legacy/state/connection/reducer.ts index 2db6d658bb..14ae1b7651 100644 --- a/src/legacy/state/connection/reducer.ts +++ b/src/legacy/state/connection/reducer.ts @@ -7,21 +7,10 @@ export interface ConnectionState { } export const initialState: ConnectionState = { - errorByConnectionType: { - [ConnectionType.INJECTED]: undefined, - [ConnectionType.FORTMATIC]: undefined, - [ConnectionType.WALLET_CONNECT]: undefined, - [ConnectionType.COINBASE_WALLET]: undefined, - [ConnectionType.NETWORK]: undefined, - [ConnectionType.GNOSIS_SAFE]: undefined, - [ConnectionType.ZENGO]: undefined, - [ConnectionType.AMBIRE]: undefined, - [ConnectionType.ALPHA]: undefined, - [ConnectionType.TALLY]: undefined, - [ConnectionType.TRUST]: undefined, - [ConnectionType.LEDGER]: undefined, - [ConnectionType.KEYSTONE]: undefined, - }, + errorByConnectionType: Object.keys(ConnectionType).reduce((acc, key) => { + acc[key as ConnectionType] = undefined + return acc + }, {} as Record), } const connectionSlice = createSlice({ diff --git a/src/legacy/state/orders/middleware/appziMiddleware.test.ts b/src/legacy/state/orders/middleware/appziMiddleware.test.ts index aca3231a30..d7cee50434 100644 --- a/src/legacy/state/orders/middleware/appziMiddleware.test.ts +++ b/src/legacy/state/orders/middleware/appziMiddleware.test.ts @@ -1,4 +1,5 @@ import { OrderClass } from '@cowprotocol/cow-sdk' +import { getOrderByIdFromState } from '../helpers' import { AnyAction, Dispatch, MiddlewareAPI } from 'redux' import { instance, mock, resetCalls, when } from 'ts-mockito' @@ -8,7 +9,6 @@ import { isOrderInPendingTooLong, openNpsAppziSometimes } from 'legacy/utils/app import { appziMiddleware } from './appziMiddleware' import { AppState } from '../../index' -import { getOrderByIdFromState } from '../helpers' jest.mock('legacy/utils/appzi') jest.mock('../helpers', () => { diff --git a/src/libs/widget-lib/README.md b/src/libs/widget-lib/README.md index 521a7982f5..46b1e77b69 100644 --- a/src/libs/widget-lib/README.md +++ b/src/libs/widget-lib/README.md @@ -47,6 +47,10 @@ cowSwapWidget({ ## Developers ```bash +# Run demo project locally +# Will open http://localhost:3012/src/demo/index.html +nx serve widget-lib + # Test nx test widget-lib diff --git a/src/libs/widget-lib/project.json b/src/libs/widget-lib/project.json index cbc42d9833..97fa6d0ad2 100644 --- a/src/libs/widget-lib/project.json +++ b/src/libs/widget-lib/project.json @@ -12,6 +12,15 @@ "outputPath": "dist/libs/widget-lib" } }, + "serve": { + "executor": "@nx/vite:dev-server", + "defaultConfiguration": "development", + "options": { + "buildTarget": "widget-lib:build", + "port": 3012 + }, + "configurations": {} + }, "publish": { "command": "node tools/scripts/publish.mjs widget-lib {args.ver} {args.tag}", "dependsOn": ["build"] diff --git a/src/libs/widget-lib/src/consts.ts b/src/libs/widget-lib/src/consts.ts index a1464e3e38..3789bc0713 100644 --- a/src/libs/widget-lib/src/consts.ts +++ b/src/libs/widget-lib/src/consts.ts @@ -1,11 +1,6 @@ import { CowSwapWidgetEnv } from './types' -export const DEFAULT_CHAIN = 1 -export const DEFAULT_WIDTH = 400 -export const DEFAULT_HEIGHT = 640 - export const COWSWAP_URLS: Record = { local: 'http://localhost:3000', - barn: 'https://barn.cow.fi', - prod: 'http://swap.cow.fi', + prod: 'https://swap.cow.fi', } diff --git a/src/libs/widget-lib/src/cowSwapWidget.ts b/src/libs/widget-lib/src/cowSwapWidget.ts index bb003f93e9..301a47461f 100644 --- a/src/libs/widget-lib/src/cowSwapWidget.ts +++ b/src/libs/widget-lib/src/cowSwapWidget.ts @@ -1,15 +1,14 @@ -import { DEFAULT_HEIGHT, DEFAULT_WIDTH } from './consts' import { JsonRpcManager } from './JsonRpcManager' -import { CowSwapWidgetParams, CowSwapWidgetUrlParams } from './types' -import { buildTradeAmountsQuery, buildWidgetPath, buildWidgetUrl } from './utils' +import { CowSwapWidgetParams, CowSwapWidgetSettings } from './types' +import { buildTradeAmountsQuery, buildWidgetPath, buildWidgetUrl } from './urlUtils' -const COW_SWAP_WIDGET_KEY = '@cowprotocol/widget-lib' +const COW_SWAP_WIDGET_EVENT_KEY = 'cowSwapWidget' -type UpdateWidgetCallback = (params: CowSwapWidgetUrlParams) => void +export type UpdateWidgetCallback = (params: CowSwapWidgetSettings) => void -export function cowSwapWidget(params: CowSwapWidgetParams): UpdateWidgetCallback { +export function cowSwapWidget(params: CowSwapWidgetParams, settings: CowSwapWidgetSettings): UpdateWidgetCallback { const { container, provider } = params - const iframe = createIframe(params) + const iframe = createIframe(params, settings) container.innerHTML = '' container.appendChild(iframe) @@ -24,29 +23,33 @@ export function cowSwapWidget(params: CowSwapWidgetParams): UpdateWidgetCallback jsonRpcManager.onConnect(provider) } - return (params: CowSwapWidgetUrlParams) => updateWidget(params, contentWindow) + return (params: CowSwapWidgetSettings) => updateWidget(params, contentWindow) } -function updateWidget(params: CowSwapWidgetUrlParams, contentWindow: Window) { - const pathname = buildWidgetPath(params) - const search = buildTradeAmountsQuery(params).toString() +function updateWidget(params: CowSwapWidgetSettings, contentWindow: Window) { + const pathname = buildWidgetPath(params.urlParams) + const search = buildTradeAmountsQuery(params.urlParams).toString() contentWindow.postMessage( { - key: COW_SWAP_WIDGET_KEY, - pathname, - search, + key: COW_SWAP_WIDGET_EVENT_KEY, + method: 'update', + urlParams: { + pathname, + search, + }, + appParams: params.appParams, }, '*' ) } -function createIframe(params: CowSwapWidgetParams): HTMLIFrameElement { - const { width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT } = params +function createIframe(params: CowSwapWidgetParams, settings: CowSwapWidgetSettings): HTMLIFrameElement { + const { width, height } = params const iframe = document.createElement('iframe') - iframe.src = buildWidgetUrl(params.urlParams ?? {}) + iframe.src = buildWidgetUrl(settings.urlParams) iframe.width = `${width}px` iframe.height = `${height}px` iframe.style.border = '0' diff --git a/src/libs/widget-lib/src/demo/ProviderMode.ts b/src/libs/widget-lib/src/demo/ProviderMode.ts new file mode 100644 index 0000000000..d64d76caa4 --- /dev/null +++ b/src/libs/widget-lib/src/demo/ProviderMode.ts @@ -0,0 +1,29 @@ +import { EthereumProvider } from '../types' + +export function ProviderMode(init: (ethereum?: EthereumProvider) => void) { + const injectedProvider = (window as any)['ethereum'] as EthereumProvider + + const connectedProviderButton = document.getElementById('connectedProviderButton') as HTMLButtonElement + const standaloneModeButton = document.getElementById('standaloneModeButton') as HTMLButtonElement + const toggleMode = () => { + connectedProviderButton.classList.toggle('active') + standaloneModeButton.classList.toggle('active') + } + + connectedProviderButton.addEventListener('click', () => { + init(injectedProvider) + toggleMode() + }) + + standaloneModeButton.addEventListener('click', () => { + init() + toggleMode() + }) + + if (!injectedProvider) { + toggleMode() + connectedProviderButton.style.display = 'none' + } else { + init(injectedProvider) + } +} diff --git a/src/libs/widget-lib/src/demo/Settings.ts b/src/libs/widget-lib/src/demo/Settings.ts new file mode 100644 index 0000000000..486c1d5793 --- /dev/null +++ b/src/libs/widget-lib/src/demo/Settings.ts @@ -0,0 +1,45 @@ +import { UpdateWidgetCallback } from '../cowSwapWidget' + +// eslint-disable-next-line max-lines-per-function +export function Settings(updateWidget: UpdateWidgetCallback) { + const appSettingsForm = document.getElementById('appSettingsForm') as HTMLFormElement + const tradeSettingsForm = document.getElementById('tradeSettingsForm') as HTMLFormElement + + function applySettings() { + const tradeSettingsState = Object.fromEntries(new FormData(tradeSettingsForm) as never) + const appSettingsState = Object.fromEntries(new FormData(appSettingsForm) as never) + + updateWidget({ + urlParams: { + env: tradeSettingsState['env'], + chainId: tradeSettingsState['chainId'], + theme: tradeSettingsState['theme'], + tradeType: tradeSettingsState['tradeType'], + tradeAssets: { + sell: { + asset: tradeSettingsState['tradeAssets.sell.asset'], + amount: tradeSettingsState['tradeAssets.sell.amount'], + }, + buy: { + asset: tradeSettingsState['tradeAssets.buy.asset'], + amount: tradeSettingsState['tradeAssets.buy.amount'], + }, + }, + }, + appParams: { + logoUrl: appSettingsState['logoUrl'], + hideLogo: appSettingsState['hideLogo'], + hideNetworkSelector: appSettingsState['hideNetworkSelector'], + }, + }) + } + + ;['submit', 'change'].forEach((eventName) => { + ;[tradeSettingsForm, appSettingsForm].forEach((form) => { + form.addEventListener(eventName, (event) => { + event.preventDefault() + applySettings() + }) + }) + }) +} diff --git a/src/libs/widget-lib/src/demo/index.html b/src/libs/widget-lib/src/demo/index.html new file mode 100644 index 0000000000..f239e0b9ef --- /dev/null +++ b/src/libs/widget-lib/src/demo/index.html @@ -0,0 +1,184 @@ + + + + + CowSwap Widget + + + + + + +
+
+

CowSwap widget

+
+ + + + + + + + + +
+
+
+
+


+
+ + + + +
+
+
+ + diff --git a/src/libs/widget-lib/src/demo/index.ts b/src/libs/widget-lib/src/demo/index.ts new file mode 100644 index 0000000000..63c6697953 --- /dev/null +++ b/src/libs/widget-lib/src/demo/index.ts @@ -0,0 +1,39 @@ +import { ProviderMode } from './ProviderMode' +import { Settings } from './Settings' + +import { cowSwapWidget, CowSwapWidgetAppParams, CowSwapWidgetUrlParams, EthereumProvider } from '../index' + +const urlParams: CowSwapWidgetUrlParams = { + env: 'local', + chainId: 1, + theme: 'light', + tradeType: 'swap', + tradeAssets: { + sell: { + // asset: '0x543ff227f64aa17ea132bf9886cab5db55dcaddf', + asset: 'COW', + amount: '1200', + }, + buy: { + asset: 'WETH', + }, + }, +} + +const appParams: CowSwapWidgetAppParams = {} + +function init(provider?: EthereumProvider) { + const updateWidget = cowSwapWidget( + { + width: 400, + height: 640, + container: document.getElementById('widgetContainer') as HTMLElement, + provider, + }, + { urlParams, appParams } + ) + + Settings(updateWidget) +} + +ProviderMode(init) diff --git a/src/libs/widget-lib/src/types.ts b/src/libs/widget-lib/src/types.ts index d0484fb97f..10f6257dcd 100644 --- a/src/libs/widget-lib/src/types.ts +++ b/src/libs/widget-lib/src/types.ts @@ -1,9 +1,3 @@ -declare global { - interface Window { - ethereum?: EthereumProvider - } -} - export interface JsonRpcRequest { id: number method: string @@ -17,7 +11,7 @@ export interface EthereumProvider { enable(): Promise } -export type CowSwapWidgetEnv = 'local' | 'prod' | 'barn' +export type CowSwapWidgetEnv = 'local' | 'prod' export type CowSwapTheme = 'dark' | 'light' @@ -32,16 +26,27 @@ export interface TradeAssets { } export interface CowSwapWidgetUrlParams { - chainId?: number - env?: CowSwapWidgetEnv + chainId: number + tradeType: string + env: CowSwapWidgetEnv tradeAssets?: TradeAssets theme?: CowSwapTheme } +export interface CowSwapWidgetAppParams { + logoUrl?: string + hideLogo?: boolean + hideNetworkSelector?: boolean +} + +export interface CowSwapWidgetSettings { + urlParams: CowSwapWidgetUrlParams + appParams: CowSwapWidgetAppParams +} + export interface CowSwapWidgetParams { + width: number + height: number container: HTMLElement - width?: number - height?: number - urlParams?: CowSwapWidgetUrlParams provider?: EthereumProvider } diff --git a/src/libs/widget-lib/src/urlUtils.spec.ts b/src/libs/widget-lib/src/urlUtils.spec.ts new file mode 100644 index 0000000000..66fab5bf29 --- /dev/null +++ b/src/libs/widget-lib/src/urlUtils.spec.ts @@ -0,0 +1,85 @@ +import { buildWidgetUrl } from './urlUtils' + +const defaultEnv = 'prod' +const chainId = 1 +const tradeType = 'swap' + +describe('buildWidgetUrl', () => { + describe('env', () => { + it('local', () => { + const url = buildWidgetUrl({ chainId, tradeType, env: 'local' }) + expect(url).toEqual('http://localhost:3000/#/1/widget/swap/?') + }) + it('prod', () => { + const url = buildWidgetUrl({ chainId, tradeType, env: 'prod' }) + expect(url).toEqual('https://swap.cow.fi/#/1/widget/swap/?') + }) + }) + + describe('chainId', () => { + it('mainnet', () => { + const url = buildWidgetUrl({ chainId: 1, tradeType, env: defaultEnv }) + expect(url).toEqual('https://swap.cow.fi/#/1/widget/swap/?') + }) + it('gnosis chain', () => { + const url = buildWidgetUrl({ chainId: 5, tradeType, env: defaultEnv }) + expect(url).toEqual('https://swap.cow.fi/#/5/widget/swap/?') + }) + }) + + describe('theme', () => { + it('dark', () => { + const url = buildWidgetUrl({ theme: 'dark', chainId, tradeType, env: defaultEnv }) + expect(url).toEqual('https://swap.cow.fi/#/1/widget/swap/?theme=dark') + }) + it('light', () => { + const url = buildWidgetUrl({ theme: 'light', chainId, tradeType, env: defaultEnv }) + expect(url).toEqual('https://swap.cow.fi/#/1/widget/swap/?theme=light') + }) + }) + + describe('trade assets', () => { + it('without amounts', () => { + const url = buildWidgetUrl({ + tradeAssets: { sell: { asset: 'WETH' }, buy: { asset: 'COW' } }, + chainId, + tradeType, + env: defaultEnv, + }) + expect(url).toEqual('https://swap.cow.fi/#/1/widget/swap/WETH/COW?') + }) + + it('with sell amount', () => { + const url = buildWidgetUrl({ + tradeAssets: { sell: { asset: 'DAI', amount: '0.1' }, buy: { asset: 'USDC' } }, + chainId, + tradeType, + env: defaultEnv, + }) + expect(url).toEqual('https://swap.cow.fi/#/1/widget/swap/DAI/USDC?sellAmount=0.1') + }) + + it('with buy amount', () => { + const url = buildWidgetUrl({ + tradeAssets: { sell: { asset: 'DAI' }, buy: { asset: 'USDC', amount: '0.1' } }, + chainId, + tradeType, + env: defaultEnv, + }) + expect(url).toEqual('https://swap.cow.fi/#/1/widget/swap/DAI/USDC?buyAmount=0.1') + }) + }) + + describe('all config', () => { + it('dark', () => { + const url = buildWidgetUrl({ + env: 'prod', + chainId: 100, + tradeType, + theme: 'light', + tradeAssets: { sell: { asset: 'DAI', amount: '0.1' }, buy: { asset: 'USDC', amount: '0.1' } }, + }) + expect(url).toEqual('https://swap.cow.fi/#/100/widget/swap/DAI/USDC?sellAmount=0.1&buyAmount=0.1&theme=light') + }) + }) +}) diff --git a/src/libs/widget-lib/src/utils.ts b/src/libs/widget-lib/src/urlUtils.ts similarity index 81% rename from src/libs/widget-lib/src/utils.ts rename to src/libs/widget-lib/src/urlUtils.ts index 0cd02caad7..5765c1fc5b 100644 --- a/src/libs/widget-lib/src/utils.ts +++ b/src/libs/widget-lib/src/urlUtils.ts @@ -1,8 +1,8 @@ -import { COWSWAP_URLS, DEFAULT_CHAIN } from './consts' +import { COWSWAP_URLS } from './consts' import { CowSwapWidgetUrlParams } from './types' export function buildWidgetUrl(params: CowSwapWidgetUrlParams): string { - const host = COWSWAP_URLS[params.env ?? 'prod'] + const host = COWSWAP_URLS[params.env] const path = buildWidgetPath(params) const query = buildTradeAmountsQuery(params) @@ -10,13 +10,13 @@ export function buildWidgetUrl(params: CowSwapWidgetUrlParams): string { } export function buildWidgetPath(params: CowSwapWidgetUrlParams): string { - const { chainId = DEFAULT_CHAIN, tradeAssets } = params + const { chainId, tradeAssets, tradeType } = params const assetsPath = tradeAssets ? [tradeAssets.sell.asset, tradeAssets.buy.asset].map(encodeURIComponent).join('/') : '' - return `/${chainId}/swap/widget/${assetsPath}` + return `/${chainId}/widget/${tradeType}/${assetsPath}` } export function buildTradeAmountsQuery(params: CowSwapWidgetUrlParams): URLSearchParams { diff --git a/src/libs/widget-lib/src/utils.spec.ts b/src/libs/widget-lib/src/utils.spec.ts deleted file mode 100644 index 46c9cd0c52..0000000000 --- a/src/libs/widget-lib/src/utils.spec.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { buildWidgetUrl } from './utils' - -describe('buildWidgetUrl', () => { - it('minimal config', () => { - const url = buildWidgetUrl({}) - expect(url).toEqual('http://swap.cow.fi/#/1/swap/widget/?') - }) - - describe('env', () => { - it('local', () => { - const url = buildWidgetUrl({ env: 'local' }) - expect(url).toEqual('http://localhost:3000/#/1/swap/widget/?') - }) - it('prod', () => { - const url = buildWidgetUrl({ env: 'prod' }) - expect(url).toEqual('http://swap.cow.fi/#/1/swap/widget/?') - }) - - it('barn', () => { - const url = buildWidgetUrl({ env: 'barn' }) - expect(url).toEqual('https://barn.cow.fi/#/1/swap/widget/?') - }) - }) - - describe('chainId', () => { - it('mainnet', () => { - const url = buildWidgetUrl({ chainId: 1 }) - expect(url).toEqual('http://swap.cow.fi/#/1/swap/widget/?') - }) - it('gnosis chain', () => { - const url = buildWidgetUrl({ chainId: 5 }) - expect(url).toEqual('http://swap.cow.fi/#/5/swap/widget/?') - }) - }) - - describe('theme', () => { - it('dark', () => { - const url = buildWidgetUrl({ theme: 'dark' }) - expect(url).toEqual('http://swap.cow.fi/#/1/swap/widget/?theme=dark') - }) - it('light', () => { - const url = buildWidgetUrl({ theme: 'light' }) - expect(url).toEqual('http://swap.cow.fi/#/1/swap/widget/?theme=light') - }) - }) - - describe('trade assets', () => { - it('without amounts', () => { - const url = buildWidgetUrl({ tradeAssets: { sell: { asset: 'WETH' }, buy: { asset: 'COW' } } }) - expect(url).toEqual('http://swap.cow.fi/#/1/swap/widget/WETH/COW?') - }) - - it('with sell amount', () => { - const url = buildWidgetUrl({ tradeAssets: { sell: { asset: 'DAI', amount: '0.1' }, buy: { asset: 'USDC' } } }) - expect(url).toEqual('http://swap.cow.fi/#/1/swap/widget/DAI/USDC?sellAmount=0.1') - }) - - it('with buy amount', () => { - const url = buildWidgetUrl({ tradeAssets: { sell: { asset: 'DAI' }, buy: { asset: 'USDC', amount: '0.1' } } }) - expect(url).toEqual('http://swap.cow.fi/#/1/swap/widget/DAI/USDC?buyAmount=0.1') - }) - }) - - describe('all config', () => { - it('dark', () => { - const url = buildWidgetUrl({ - env: 'barn', - chainId: 100, - theme: 'light', - tradeAssets: { sell: { asset: 'DAI', amount: '0.1' }, buy: { asset: 'USDC', amount: '0.1' } }, - }) - expect(url).toEqual('https://barn.cow.fi/#/100/swap/widget/DAI/USDC?sellAmount=0.1&buyAmount=0.1&theme=light') - }) - }) -}) diff --git a/src/libs/widget-lib/vite.config.ts b/src/libs/widget-lib/vite.config.ts index 7155209003..22e52d0352 100644 --- a/src/libs/widget-lib/vite.config.ts +++ b/src/libs/widget-lib/vite.config.ts @@ -4,45 +4,67 @@ import { defineConfig } from 'vite' import dts from 'vite-plugin-dts' import viteTsConfigPaths from 'vite-tsconfig-paths' -export default defineConfig({ - cacheDir: '../../../node_modules/.vite/widget-lib', +export default defineConfig(({ command }) => { + if (command === 'serve') { + const entryPoint = './src/demo/index.html' - plugins: [ - dts({ - entryRoot: 'src', - tsConfigFilePath: joinPathFragments(__dirname, 'tsconfig.lib.json'), - skipDiagnostics: true, - }), + return { + server: { + open: entryPoint, + fs: { + allow: ['..'], + }, + }, + build: { + rollupOptions: { + input: { + main: entryPoint, + }, + }, + }, + } + } - viteTsConfigPaths({ - root: '../../../', - }), - ], + return { + cacheDir: '../../../node_modules/.vite/widget-lib', - // Uncomment this if you are using workers. - // worker: { - // plugins: [ - // viteTsConfigPaths({ - // root: '../../../', - // }), - // ], - // }, + plugins: [ + dts({ + entryRoot: 'src', + tsConfigFilePath: joinPathFragments(__dirname, 'tsconfig.lib.json'), + skipDiagnostics: true, + }), - // Configuration for building your library. - // See: https://vitejs.dev/guide/build.html#library-mode - build: { - lib: { - // Could also be a dictionary or array of multiple entry points. - entry: 'src/index.ts', - name: 'widget-lib', - fileName: 'index', - // Change this to the formats you want to support. - // Don't forgot to update your package.json as well. - formats: ['es', 'cjs'], - }, - rollupOptions: { - // External packages that should not be bundled into your library. - external: [], + viteTsConfigPaths({ + root: '../../../', + }), + ], + + // Uncomment this if you are using workers. + // worker: { + // plugins: [ + // viteTsConfigPaths({ + // root: '../../../', + // }), + // ], + // }, + + // Configuration for building your library. + // See: https://vitejs.dev/guide/build.html#library-mode + build: { + lib: { + // Could also be a dictionary or array of multiple entry points. + entry: 'src/index.ts', + name: 'widget-lib', + fileName: 'index', + // Change this to the formats you want to support. + // Don't forgot to update your package.json as well. + formats: ['es', 'cjs'], + }, + rollupOptions: { + // External packages that should not be bundled into your library. + external: [], + }, }, - }, + } }) diff --git a/src/modules/application/containers/App/RoutesApp.tsx b/src/modules/application/containers/App/RoutesApp.tsx index 3182aa6f1a..36516dce01 100644 --- a/src/modules/application/containers/App/RoutesApp.tsx +++ b/src/modules/application/containers/App/RoutesApp.tsx @@ -8,7 +8,7 @@ import Loader from 'legacy/components/Loader' import { DISCORD_LINK, DOCS_LINK, DUNE_DASHBOARD_LINK, TWITTER_LINK } from 'legacy/constants' import { RedirectPathToSwapOnly } from 'legacy/pages/Swap/redirects' -import { Routes as RoutesEnum } from 'common/constants/routes' +import { Routes as RoutesEnum, RoutesValues } from 'common/constants/routes' import Account, { AccountOverview } from 'pages/Account' import AnySwapAffectedUsers from 'pages/error/AnySwapAffectedUsers' import { SwapPage } from 'pages/Swap' @@ -36,8 +36,6 @@ const EthFlowFaq = lazy(() => import(/* webpackChunkName: "eth_flow_faq" */ 'pag const AccountTokensOverview = lazy(() => import(/* webpackChunkName: "tokens_overview" */ 'pages/Account/Tokens')) const AccountNotFound = lazy(() => import(/* webpackChunkName: "affiliate" */ 'pages/error/NotFound')) -const KitchenSink = lazy(() => import(/* webpackChunkName: "kitchen_sink" */ 'pages/KitchenSink')) - function createRedirectExternal(url: string) { return () => { window.location.replace(url) @@ -45,7 +43,7 @@ function createRedirectExternal(url: string) { } } -type LazyRouteProps = { route: RoutesEnum; element: ReactNode; key?: number } +type LazyRouteProps = { route: RoutesValues; element: ReactNode; key?: number } function LazyRoute({ route, element, key }: LazyRouteProps) { return {element}} /> @@ -66,7 +64,6 @@ const lazyRoutes: LazyRouteProps[] = [ { route: RoutesEnum.PRIVACY_POLICY, element: }, { route: RoutesEnum.COOKIE_POLICY, element: }, { route: RoutesEnum.TERMS_CONDITIONS, element: }, - { route: RoutesEnum.KITCHEN_SINK, element: }, ] export function RoutesApp() { diff --git a/src/modules/application/containers/App/Updaters.tsx b/src/modules/application/containers/App/Updaters.tsx index f06d25787a..e7552ff273 100644 --- a/src/modules/application/containers/App/Updaters.tsx +++ b/src/modules/application/containers/App/Updaters.tsx @@ -17,10 +17,12 @@ import SentryUpdater from 'legacy/state/sentry/updater' import UserUpdater from 'legacy/state/user/updater' import { UploadToIpfsUpdater, AppDataUpdater } from 'modules/appData' +import { InjectedWidgetUpdater } from 'modules/injectedWidget' import { EthFlowSlippageUpdater, EthFlowDeadlineUpdater } from 'modules/swap/state/EthFlow/updaters' import { TokensListUpdater } from 'modules/tokensList/updaters/TokensListUpdater' import { WalletUpdater } from 'modules/wallet' +import { ThemeFromUrlUpdater } from 'common/updaters/ThemeFromUrlUpdater' import { MulticallUpdater } from 'lib/state/multicall' export function Updaters() { @@ -48,6 +50,8 @@ export function Updaters() { + + ) } diff --git a/src/modules/application/containers/App/index.tsx b/src/modules/application/containers/App/index.tsx index 958cfd7bf9..cd5f7727d4 100644 --- a/src/modules/application/containers/App/index.tsx +++ b/src/modules/application/containers/App/index.tsx @@ -10,6 +10,7 @@ import DarkModeQueryParamReader from 'legacy/theme' import { useInitializeUtm } from 'modules/utm' +import { isInjectedWidget } from 'common/utils/isInjectedWidget' import RedirectAnySwapAffectedUsers from 'pages/error/AnySwapAffectedUsers/RedirectAnySwapAffectedUsers' import { RoutesApp } from './RoutesApp' @@ -20,11 +21,14 @@ export function App() { useAnalyticsReporter() useInitializeUtm() + const isInjectedWidgetMode = isInjectedWidget() + return ( + @@ -35,9 +39,13 @@ export function App() { - -