From 22ca5e0a9aa156e124cf6c34858e90c65cb0175c Mon Sep 17 00:00:00 2001 From: Chakravarthy7102 Date: Mon, 5 Aug 2024 18:39:57 +0530 Subject: [PATCH 01/13] fix: total surplus tooltip cannot be open in iOS mobile device (#4756) --- libs/ui/src/pure/Popover/index.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libs/ui/src/pure/Popover/index.tsx b/libs/ui/src/pure/Popover/index.tsx index 4511374ab1..b0a9e1f07a 100644 --- a/libs/ui/src/pure/Popover/index.tsx +++ b/libs/ui/src/pure/Popover/index.tsx @@ -56,7 +56,10 @@ export default function Popover(props: PopoverProps) { className={className} show={show} ref={setPopperElement as any} - style={styles.popper} + style={{ + ...styles.popper, + zIndex: 999999, + }} {...attributes.popper} bgColor={bgColor} color={color} From 9a73f8569f9384b0f8b8456403b77a364606ade7 Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Mon, 5 Aug 2024 16:15:24 +0100 Subject: [PATCH 02/13] feat: add post-hook builder (#4754) * feat: add post-hook builder * fix: fix build --- .../hooksStore/dapps/BuildHookApp/index.tsx | 34 +++++++++++-------- .../src/modules/hooksStore/hookRegistry.tsx | 16 ++------- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/apps/cowswap-frontend/src/modules/hooksStore/dapps/BuildHookApp/index.tsx b/apps/cowswap-frontend/src/modules/hooksStore/dapps/BuildHookApp/index.tsx index 8ffd6d9a32..6cd7c3ad37 100644 --- a/apps/cowswap-frontend/src/modules/hooksStore/dapps/BuildHookApp/index.tsx +++ b/apps/cowswap-frontend/src/modules/hooksStore/dapps/BuildHookApp/index.tsx @@ -8,18 +8,18 @@ import styled from 'styled-components/macro' import { HookDappContext } from '../../context' import buildImg from '../../images/build.png' -const TITLE = 'Build your own Pre-hook' -const DESCRIPTION = 'Add an arbitrary calldata to be executed before your hook' - -export const PRE_BUILD: HookDappInternal = { - name: TITLE, - description: DESCRIPTION, +const getAppDetails = (isPreHook: boolean): HookDappInternal => ({ + name: `Build your own ${isPreHook ? 'Pre' : 'Post'}-hook`, + description: `Add an arbitrary calldata to be executed ${isPreHook ? 'before' : 'after'} your hook`, type: HookDappType.INTERNAL, path: '/hooks-dapps/pre/build', image: buildImg, - component: , + component: , version: 'v0.1.0', -} +}) + +export const PRE_BUILD = getAppDetails(true) +export const POST_BUILD = getAppDetails(false) const Wrapper = styled.div` display: flex; @@ -80,7 +80,11 @@ const Row = styled.div` } ` -export function ClaimGnoHookApp() { +export interface BuildHookAppProps { + isPreHook: boolean +} + +export function BuildHookApp({ isPreHook }: BuildHookAppProps) { const hookDappContext = useContext(HookDappContext) const [hook, setHook] = useState({ target: '', @@ -88,6 +92,8 @@ export function ClaimGnoHookApp() { gasLimit: '', }) + const dapp = isPreHook ? PRE_BUILD : POST_BUILD + const clickOnAddHook = useCallback(() => { const { callData, gasLimit, target } = hook if (!hookDappContext || !callData || !gasLimit || !target) { @@ -97,10 +103,10 @@ export function ClaimGnoHookApp() { hookDappContext.addHook( { hook: hook, - dapp: PRE_BUILD, + dapp, outputTokens: undefined, // TODO: Simulate and extract the output tokens }, - true + isPreHook ) }, [hook, hookDappContext]) @@ -111,8 +117,8 @@ export function ClaimGnoHookApp() { return (
- {TITLE} -

{DESCRIPTION}

+ {dapp.name} +

{dapp.description}

@@ -141,7 +147,7 @@ export function ClaimGnoHookApp() { /> - +Add Pre-hook + +Add {isPreHook ? 'Pre' : 'Post'}-hook { e.preventDefault() diff --git a/apps/cowswap-frontend/src/modules/hooksStore/hookRegistry.tsx b/apps/cowswap-frontend/src/modules/hooksStore/hookRegistry.tsx index 089ed89421..ce7540ad8b 100644 --- a/apps/cowswap-frontend/src/modules/hooksStore/hookRegistry.tsx +++ b/apps/cowswap-frontend/src/modules/hooksStore/hookRegistry.tsx @@ -1,10 +1,9 @@ import { SupportedChainId } from '@cowprotocol/cow-sdk' import { HookDapp, HookDappIframe, HookDappType } from '@cowprotocol/types' -import { PRE_BUILD } from './dapps/BuildHookApp' +import { PRE_BUILD, POST_BUILD } from './dapps/BuildHookApp' import { PRE_CLAIM_GNO } from './dapps/ClaimGnoHookApp' import bridgeImg from './images/bridge.svg' -import buildImg from './images/build.png' import cowAMM from './images/cowAMM.png' import curveImg from './images/curve.svg' import daiImg from './images/dai.svg' @@ -74,15 +73,6 @@ const POST_MAKER: HookDappIframe = { version: FAKE_VERSION, } -const POST_BUILD: HookDappIframe = { - name: 'Build your own Post-hook', - description: 'Add an arbitrary calldata to be executed after your hook', - type: HookDappType.IFRAME, - url: FAKE_URL, - image: buildImg, - version: FAKE_VERSION, -} - const POST_HOOK_DAPPS_ALL = [POST_BRIDGE, POST_MAKER, POST_BUILD] export const PRE_HOOK_REGISTRY: Record = { @@ -95,6 +85,6 @@ export const PRE_HOOK_REGISTRY: Record = { export const POST_HOOK_REGISTRY: Record = { [SupportedChainId.MAINNET]: POST_HOOK_DAPPS_ALL, [SupportedChainId.GNOSIS_CHAIN]: POST_HOOK_DAPPS_ALL, - [SupportedChainId.SEPOLIA]: [], - [SupportedChainId.ARBITRUM_ONE]: [], + [SupportedChainId.SEPOLIA]: [POST_BUILD], + [SupportedChainId.ARBITRUM_ONE]: [POST_BUILD], } From f84d6b5cfe0710e33c02a692bf7f8b1e2464eb06 Mon Sep 17 00:00:00 2001 From: Chakravarthy7102 Date: Tue, 6 Aug 2024 15:15:34 +0530 Subject: [PATCH 03/13] fix: cancel modal does not pop-up when press on Cancel button in mobile view (#4757) * fix: cancel modal does not pop-up when press on Cancel button in mobile view * fix: remove super high z-index values in mobile view account panel * fix: layer z-index to fit the screen just fine and avoid super high z-index values * fix: reset unrelated changes --- .../src/modules/account/containers/OrdersPanel/index.tsx | 2 +- libs/ui/src/pure/MenuBar/styled.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/cowswap-frontend/src/modules/account/containers/OrdersPanel/index.tsx b/apps/cowswap-frontend/src/modules/account/containers/OrdersPanel/index.tsx index acb08e1d04..050d1c350e 100644 --- a/apps/cowswap-frontend/src/modules/account/containers/OrdersPanel/index.tsx +++ b/apps/cowswap-frontend/src/modules/account/containers/OrdersPanel/index.tsx @@ -45,7 +45,7 @@ const SideBar = styled.div` height: 100%; max-width: 100%; border-radius: ${({ theme }) => (theme.isInjectedWidgetMode ? '24px' : '0')}; - z-index: 10000; + z-index: 10; } ` diff --git a/libs/ui/src/pure/MenuBar/styled.ts b/libs/ui/src/pure/MenuBar/styled.ts index b704a6c948..01afaea824 100644 --- a/libs/ui/src/pure/MenuBar/styled.ts +++ b/libs/ui/src/pure/MenuBar/styled.ts @@ -47,7 +47,7 @@ export const MenuBarWrapper = styled.div<{ display: flex; width: 100%; padding: ${({ padding }) => padding || '10px'}; - z-index: 10; + z-index: 9; position: sticky; top: 0; color: var(--color); From 75ea3edafd8d4dd0b989c6d95b3169ea769abe7c Mon Sep 17 00:00:00 2001 From: Leandro Date: Wed, 7 Aug 2024 15:11:20 +0100 Subject: [PATCH 04/13] chore: remove outdated legacy price sources (#4770) * chore: remove outdated legacy price sources * chore: lint fix --- .../src/legacy/utils/priceLegacy.ts | 57 ++----------------- .../hooks/useProcessUnsupportedTokenError.ts | 2 +- 2 files changed, 5 insertions(+), 54 deletions(-) diff --git a/apps/cowswap-frontend/src/legacy/utils/priceLegacy.ts b/apps/cowswap-frontend/src/legacy/utils/priceLegacy.ts index bf6c37a92e..93580d648c 100644 --- a/apps/cowswap-frontend/src/legacy/utils/priceLegacy.ts +++ b/apps/cowswap-frontend/src/legacy/utils/priceLegacy.ts @@ -15,11 +15,6 @@ import { } from 'api/1inch' import { getQuote } from 'api/cowProtocol' import QuoteApiError, { QuoteApiErrorCodes } from 'api/cowProtocol/errors/QuoteError' -import { - getPriceQuote as getPriceQuoteMatcha, - MatchaPriceQuote, - toPriceInformation as toPriceInformationMatcha, -} from 'api/matcha-0x' import { LegacyPriceInformationWithSource, @@ -74,9 +69,6 @@ function _filterWinningPrice(params: FilterWinningPriceParams) { export type QuoteResult = [PromiseSettledResult, PromiseSettledResult] export type AllPricesResult = { - gpPriceResult: PromiseSettledResult - paraSwapPriceResult: PromiseSettledResult - matcha0xPriceResult: PromiseSettledResult oneInchPriceResult: PromiseSettledResult } @@ -84,19 +76,12 @@ export type AllPricesResult = { * Return all price estimations from all price sources */ async function getAllPrices(params: LegacyPriceQuoteParams): Promise { - const matchaPricePromise = withTimeout(getPriceQuoteMatcha(params), PRICE_API_TIMEOUT_MS, 'Matcha(0x): Get Price API') - const oneInchPricePromise = withTimeout(getPriceQuote1inch(params), PRICE_API_TIMEOUT_MS, '1inch: Get Price API') // Get results from API queries - const [matchaPrice, oneInchPrice] = await Promise.allSettled([matchaPricePromise, oneInchPricePromise]) + const [oneInchPrice] = await Promise.allSettled([oneInchPricePromise]) return { - // Warning! - // /markets endpoint was deleted, so we just skip it - gpPriceResult: { status: 'fulfilled', value: null }, - paraSwapPriceResult: { status: 'fulfilled', value: null }, - matcha0xPriceResult: matchaPrice, oneInchPriceResult: oneInchPrice, } } @@ -106,35 +91,12 @@ async function getAllPrices(params: LegacyPriceQuoteParams): Promise, - paraSwapPriceResult: PromiseSettledResult, - matchaPriceResult: PromiseSettledResult, oneInchPriceResult: PromiseSettledResult ): [Array, Array] { // Prepare an array with all successful estimations const priceQuotes: Array = [] const errorsGetPrice: Array = [] - if (isPromiseFulfilled(gpPriceResult)) { - const gpPrice = gpPriceResult.value - if (gpPrice) { - priceQuotes.push({ ...gpPrice, source: 'gnosis-protocol' }) - } - } else { - errorsGetPrice.push({ ...gpPriceResult, source: 'gnosis-protocol' }) - } - - if (isPromiseFulfilled(matchaPriceResult)) { - const matchaPrice = toPriceInformationMatcha(matchaPriceResult.value, kind) - if (matchaPrice) { - priceQuotes.push({ ...matchaPrice, source: 'matcha-0x', data: matchaPriceResult.value }) - } - } else { - errorsGetPrice.push({ ...matchaPriceResult, source: 'matcha-0x' }) - } - if (isPromiseFulfilled(oneInchPriceResult)) { const oneInchPrice = toPriceInformation1inch(oneInchPriceResult.value) if (oneInchPrice) { @@ -172,17 +134,10 @@ export async function getBestPrice( options?: GetBestPriceOptions ): Promise { // Get all prices - const { gpPriceResult, paraSwapPriceResult, matcha0xPriceResult, oneInchPriceResult } = await getAllPrices(params) + const { oneInchPriceResult } = await getAllPrices(params) // Aggregate successful and error prices - const [priceQuotes, errorsGetPrice] = _extractPriceAndErrorPromiseValues( - // we pass the kind of trade here as matcha doesn't have an easy way to differentiate - params.kind, - gpPriceResult, - paraSwapPriceResult, - matcha0xPriceResult, - oneInchPriceResult - ) + const [priceQuotes, errorsGetPrice] = _extractPriceAndErrorPromiseValues(oneInchPriceResult) // Print prices who failed to be fetched if (errorsGetPrice.length > 0) { @@ -199,11 +154,7 @@ export async function getBestPrice( return _filterWinningPrice({ ...options, kind: params.kind, amounts, priceQuotes }) } else { // It was not possible to get a price estimation - const priceQuoteError = new LegacyPriceQuoteError('Error querying price from APIs', params, [ - gpPriceResult, - paraSwapPriceResult, - matcha0xPriceResult, - ]) + const priceQuoteError = new LegacyPriceQuoteError('Error querying price from APIs', params, [oneInchPriceResult]) const sentryError = new Error() Object.assign(sentryError, priceQuoteError, { diff --git a/apps/cowswap-frontend/src/modules/tradeQuote/hooks/useProcessUnsupportedTokenError.ts b/apps/cowswap-frontend/src/modules/tradeQuote/hooks/useProcessUnsupportedTokenError.ts index f30f87a10f..ed4e90acc1 100644 --- a/apps/cowswap-frontend/src/modules/tradeQuote/hooks/useProcessUnsupportedTokenError.ts +++ b/apps/cowswap-frontend/src/modules/tradeQuote/hooks/useProcessUnsupportedTokenError.ts @@ -1,10 +1,10 @@ import { useCallback } from 'react' import { getQuoteUnsupportedToken } from '@cowprotocol/common-utils' +import { SupportedChainId } from '@cowprotocol/cow-sdk' import { useAddUnsupportedToken } from '@cowprotocol/tokens' import QuoteApiError from 'api/cowProtocol/errors/QuoteError' -import { SupportedChainId } from '@cowprotocol/cow-sdk' export function useProcessUnsupportedTokenError() { const addGpUnsupportedToken = useAddUnsupportedToken() From 5111b3919401d320ca01a6ec6bee07fb9855eb93 Mon Sep 17 00:00:00 2001 From: Leandro Date: Thu, 8 Aug 2024 08:30:44 +0100 Subject: [PATCH 05/13] feat(pending-notification): remove pending notification (#4771) --- .../containers/TradeConfirmModal/index.tsx | 12 +- .../FollowPendingTxPopupUI.tsx | 139 ------------------ .../containers/FollowPendingTxPopup/index.tsx | 79 ---------- .../wallet/containers/Web3Status/index.tsx | 5 +- .../hooks/useSetShowFollowPendingTxPopup.ts | 7 - .../wallet/pure/Web3StatusInner/index.tsx | 11 +- .../wallet/state/followPendingTxPopupAtom.ts | 34 ----- 7 files changed, 8 insertions(+), 279 deletions(-) delete mode 100644 apps/cowswap-frontend/src/modules/wallet/containers/FollowPendingTxPopup/FollowPendingTxPopupUI.tsx delete mode 100644 apps/cowswap-frontend/src/modules/wallet/containers/FollowPendingTxPopup/index.tsx delete mode 100644 apps/cowswap-frontend/src/modules/wallet/hooks/useSetShowFollowPendingTxPopup.ts delete mode 100644 apps/cowswap-frontend/src/modules/wallet/state/followPendingTxPopupAtom.ts diff --git a/apps/cowswap-frontend/src/modules/trade/containers/TradeConfirmModal/index.tsx b/apps/cowswap-frontend/src/modules/trade/containers/TradeConfirmModal/index.tsx index a8ecc79e41..98aec92994 100644 --- a/apps/cowswap-frontend/src/modules/trade/containers/TradeConfirmModal/index.tsx +++ b/apps/cowswap-frontend/src/modules/trade/containers/TradeConfirmModal/index.tsx @@ -1,5 +1,3 @@ -import { useCallback } from 'react' - import { SupportedChainId } from '@cowprotocol/cow-sdk' import { Command } from '@cowprotocol/types' import { UI } from '@cowprotocol/ui' @@ -15,7 +13,6 @@ import { OrderSubmittedContent } from 'common/pure/OrderSubmittedContent' import { TransactionErrorContent } from 'common/pure/TransactionErrorContent' import { TradeAmounts } from 'common/types' -import { useSetShowFollowPendingTxPopup } from '../../../wallet/hooks/useSetShowFollowPendingTxPopup' import { useTradeConfirmActions } from '../../hooks/useTradeConfirmActions' import { useTradeConfirmState } from '../../hooks/useTradeConfirmState' @@ -43,15 +40,9 @@ export function TradeConfirmModal(props: TradeConfirmModalProps) { const isSafeWallet = useIsSafeWallet() const { permitSignatureState, pendingTrade, transactionHash, error } = useTradeConfirmState() const { onDismiss } = useTradeConfirmActions() - const setShowFollowPendingTxPopup = useSetShowFollowPendingTxPopup() const order = useOrder({ chainId, id: transactionHash || undefined }) - const dismissConfirmation = useCallback(() => { - setShowFollowPendingTxPopup(true) - onDismiss() - }, [onDismiss, setShowFollowPendingTxPopup]) - if (!account) return null return ( @@ -63,7 +54,7 @@ export function TradeConfirmModal(props: TradeConfirmModalProps) { title={title} pendingTrade={pendingTrade} transactionHash={transactionHash} - onDismiss={dismissConfirmation} + onDismiss={onDismiss} permitSignatureState={permitSignatureState} isSafeWallet={isSafeWallet} submittedContent={submittedContent} @@ -89,6 +80,7 @@ type InnerComponentProps = { submittedContent?: CustomSubmittedContent order?: Order } + function InnerComponent(props: InnerComponentProps) { const { account, diff --git a/apps/cowswap-frontend/src/modules/wallet/containers/FollowPendingTxPopup/FollowPendingTxPopupUI.tsx b/apps/cowswap-frontend/src/modules/wallet/containers/FollowPendingTxPopup/FollowPendingTxPopupUI.tsx deleted file mode 100644 index afe5fde4e9..0000000000 --- a/apps/cowswap-frontend/src/modules/wallet/containers/FollowPendingTxPopup/FollowPendingTxPopupUI.tsx +++ /dev/null @@ -1,139 +0,0 @@ -import React from 'react' - -import { Command } from '@cowprotocol/types' -import { Media, Tooltip, TooltipProps, UI } from '@cowprotocol/ui' - -import { X } from 'react-feather' -import { Text } from 'rebass' -import styled from 'styled-components/macro' - -import { AutoColumn } from 'legacy/components/Column' - -interface PopupContentProps { - onCheck: Command - onClose: Command -} -type FollowingTxPopupProps = Omit & PopupContentProps - -const IconClose = styled(X)` - position: absolute; - right: 10px; - top: 10px; - color: inherit; - opacity: 0.7; - transition: opacity ${UI.ANIMATION_DURATION} ease-in-out; - - &:hover { - opacity: 1; - cursor: pointer; - } - - svg { - stroke: currentColor; - } -` - -const TooltipWrapper = styled(Tooltip)` - > .arrow- { - z-index: 1; - } - - > div { - max-width: 370px; - } - - ${Media.upToLarge()} { - padding-right: 0.8rem; - } - ${Media.upToExtraSmall()} { - padding-left: 0; - padding-right: 0.5rem; - } -` - -const BodyWrapper = styled(AutoColumn)` - display: flex; - gap: 1rem; - padding-top: 0.3rem; - - > div:nth-child(2) { - padding-top: 0.5rem; - font-size: 18px; - } - - ${Media.upToLarge()} { - gap: 0.8rem; - } - ${Media.upToSmall()} { - gap: 0.6rem; - padding-top: 0.5rem; - padding-top: auto; - } - ${Media.upToExtraSmall()} { - gap: 0.4rem; - } -` - -const AutoColumnWrapper = styled(AutoColumn)` - min-width: 21rem; - * input { - margin-left: 0; - } - ${Media.upToSmall()} { - max-width: 9rem; - min-width: auto; - } -` - -const StyledClose = styled(IconClose)` - top: 0.5rem; - ${Media.upToExtraSmall()} { - right: 0.5rem; - } -` - -const PopupContent = ({ onCheck, onClose }: PopupContentProps) => { - const _onCheckout = (event: React.ChangeEvent) => { - event.stopPropagation() - onCheck() - } - - return ( - ) => e.stopPropagation()}> - -
💡
- - - Follow your pending transactions here! - - - - - -
- ) -} - -export function FollowPendingTxPopupUI({ - show, - children, - onCheck, - onClose, - ...rest -}: FollowingTxPopupProps): JSX.Element { - return ( - } - {...rest} - > -
- {children} -
-
- ) -} diff --git a/apps/cowswap-frontend/src/modules/wallet/containers/FollowPendingTxPopup/index.tsx b/apps/cowswap-frontend/src/modules/wallet/containers/FollowPendingTxPopup/index.tsx deleted file mode 100644 index bd33661487..0000000000 --- a/apps/cowswap-frontend/src/modules/wallet/containers/FollowPendingTxPopup/index.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { useAtomValue, useSetAtom } from 'jotai' -import { selectAtom } from 'jotai/utils' -import React, { PropsWithChildren, useCallback, useEffect, useMemo, useRef } from 'react' - -import { Command } from '@cowprotocol/types' - -import { useRecentActivityLastPendingOrder } from 'legacy/hooks/useRecentActivity' -import { Order } from 'legacy/state/orders/actions' - -import { FollowPendingTxPopupUI } from './FollowPendingTxPopupUI' - -import { - followPendingTxPopupAtom, - handleCloseOrderPopupAtom, - handleFollowPendingTxPopupAtom, - handleHidePopupPermanentlyAtom, - showFollowTxPopupAtom, -} from '../../state/followPendingTxPopupAtom' - -export function useLastPendingOrder(): { lastPendingOrder: Order | null; onClose: Command } { - const setShowFollowPendingTxPopup = useSetAtom(handleFollowPendingTxPopupAtom) - const setLastOrderClosed = useSetAtom(handleCloseOrderPopupAtom) - const lastPendingOrder = useRecentActivityLastPendingOrder() - - const onClose = useCallback(() => { - lastPendingOrder && setLastOrderClosed(lastPendingOrder.id) - setShowFollowPendingTxPopup(false) - }, [lastPendingOrder, setLastOrderClosed, setShowFollowPendingTxPopup]) - - return useMemo(() => ({ lastPendingOrder, onClose }), [lastPendingOrder, onClose]) -} - -// Set pop up closed if it has not been closed and not fulfill a condition such as not pending tx -export function useCloseFollowTxPopupIfNotPendingOrder() { - const showingPopup = useAtomValue(showFollowTxPopupAtom) - const { lastPendingOrder, onClose } = useLastPendingOrder() - const onCloseRef = useRef() - - useEffect(() => { - if (lastPendingOrder && showingPopup) { - onCloseRef.current = onClose - } else if (!lastPendingOrder && showingPopup && onCloseRef.current) { - onCloseRef.current() - } - }, [lastPendingOrder, onClose, showingPopup]) -} - -const useShowingPopupFirstTime = (orderId: string | undefined) => { - const showingPopup = useAtomValue(showFollowTxPopupAtom) - const _firstTimePopupOrderAppears = useMemo( - () => - selectAtom(followPendingTxPopupAtom, ({ lastOrderPopupClosed }) => { - if (!orderId) return false - - return lastOrderPopupClosed !== orderId - }), - [orderId] - ) - - const firstTimePopupOrderAppears = useAtomValue(_firstTimePopupOrderAppears) - - return useMemo(() => ({ showPopup: firstTimePopupOrderAppears && showingPopup, firstTimePopupOrderAppears }), [firstTimePopupOrderAppears, showingPopup]) -} - -export const FollowPendingTxPopup: React.FC = ({ children }): JSX.Element => { - const setHidePendingTxPopupPermanently = useSetAtom(handleHidePopupPermanentlyAtom) - const { lastPendingOrder, onClose } = useLastPendingOrder() - const { showPopup: showFollowPendingTxPopup } = useShowingPopupFirstTime(lastPendingOrder?.id) - - return ( - setHidePendingTxPopupPermanently(true)} - onClose={onClose} - > - {children} - - ) -} diff --git a/apps/cowswap-frontend/src/modules/wallet/containers/Web3Status/index.tsx b/apps/cowswap-frontend/src/modules/wallet/containers/Web3Status/index.tsx index 59d8aa9b12..73df792457 100644 --- a/apps/cowswap-frontend/src/modules/wallet/containers/Web3Status/index.tsx +++ b/apps/cowswap-frontend/src/modules/wallet/containers/Web3Status/index.tsx @@ -1,11 +1,10 @@ -import { useWalletDetails, useWalletInfo, useConnectionType } from '@cowprotocol/wallet' +import { useConnectionType, useWalletDetails, useWalletInfo } from '@cowprotocol/wallet' import { useToggleWalletModal } from 'legacy/state/application/hooks' import { Web3StatusInner } from '../../pure/Web3StatusInner' import { Wrapper } from '../../pure/Web3StatusInner/styled' import { AccountSelectorModal } from '../AccountSelectorModal' -import { useCloseFollowTxPopupIfNotPendingOrder } from '../FollowPendingTxPopup' import { WalletModal } from '../WalletModal' export interface Web3StatusProps { @@ -13,13 +12,13 @@ export interface Web3StatusProps { className?: string onClick?: () => void } + export function Web3Status({ pendingActivities, className, onClick }: Web3StatusProps) { const connectionType = useConnectionType() const { account } = useWalletInfo() const { ensName } = useWalletDetails() const toggleWalletModal = useToggleWalletModal() - useCloseFollowTxPopupIfNotPendingOrder() return ( diff --git a/apps/cowswap-frontend/src/modules/wallet/hooks/useSetShowFollowPendingTxPopup.ts b/apps/cowswap-frontend/src/modules/wallet/hooks/useSetShowFollowPendingTxPopup.ts deleted file mode 100644 index 575c233e82..0000000000 --- a/apps/cowswap-frontend/src/modules/wallet/hooks/useSetShowFollowPendingTxPopup.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { useSetAtom } from 'jotai/index' - -import { handleFollowPendingTxPopupAtom } from '../state/followPendingTxPopupAtom' - -export function useSetShowFollowPendingTxPopup() { - return useSetAtom(handleFollowPendingTxPopupAtom) -} diff --git a/apps/cowswap-frontend/src/modules/wallet/pure/Web3StatusInner/index.tsx b/apps/cowswap-frontend/src/modules/wallet/pure/Web3StatusInner/index.tsx index f68e801a1b..f73b7c5f94 100644 --- a/apps/cowswap-frontend/src/modules/wallet/pure/Web3StatusInner/index.tsx +++ b/apps/cowswap-frontend/src/modules/wallet/pure/Web3StatusInner/index.tsx @@ -7,11 +7,10 @@ import { Trans } from '@lingui/macro' import ICON_WALLET from 'assets/icon/wallet.svg' import SVG from 'react-inlinesvg' -import { upToTiny, upToExtraSmall, useMediaQuery } from 'legacy/hooks/useMediaQuery' +import { upToExtraSmall, upToTiny, useMediaQuery } from 'legacy/hooks/useMediaQuery' import { Text, Web3StatusConnect, Web3StatusConnected } from './styled' -import { FollowPendingTxPopup } from '../../containers/FollowPendingTxPopup' import { StatusIcon } from '../StatusIcon' export interface Web3StatusInnerProps { @@ -34,11 +33,9 @@ export function Web3StatusInner(props: Web3StatusInnerProps) { {hasPendingTransactions ? ( - - - {pendingCount} Pending - {' '} - + + {pendingCount} Pending + {' '} ) : ( diff --git a/apps/cowswap-frontend/src/modules/wallet/state/followPendingTxPopupAtom.ts b/apps/cowswap-frontend/src/modules/wallet/state/followPendingTxPopupAtom.ts deleted file mode 100644 index 8920fd994f..0000000000 --- a/apps/cowswap-frontend/src/modules/wallet/state/followPendingTxPopupAtom.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { atom } from 'jotai' -import { atomWithStorage, selectAtom } from 'jotai/utils' - -type FollowPendingTxPopup = { - showPopup: boolean - lastOrderPopupClosed: string | undefined - hidePopupPermanently: boolean -} - -/** - * Base atom that store the popup state that indicate how to follow a pending tx - */ -export const followPendingTxPopupAtom = atomWithStorage('followPendingTxPopup', { - showPopup: false, - lastOrderPopupClosed: undefined, - hidePopupPermanently: false, -}) - -export const handleFollowPendingTxPopupAtom = atom(null, (_get, set, showPopup: boolean) => { - set(followPendingTxPopupAtom, (prev) => ({ ...prev, showPopup })) -}) - -export const handleHidePopupPermanentlyAtom = atom(null, (_get, set, hidePopupPermanently: boolean) => { - set(followPendingTxPopupAtom, (prev) => ({ ...prev, hidePopupPermanently })) -}) - -export const handleCloseOrderPopupAtom = atom(null, (_get, set, orderIdClosed: string) => { - set(followPendingTxPopupAtom, (prev) => ({ ...prev, lastOrderPopupClosed: orderIdClosed })) -}) - -export const showFollowTxPopupAtom = selectAtom( - followPendingTxPopupAtom, - ({ showPopup, hidePopupPermanently }) => showPopup && !hidePopupPermanently -) From 61907c7733ea8919ecf30a20ba1020062390d628 Mon Sep 17 00:00:00 2001 From: fairlight <31534717+fairlighteth@users.noreply.github.com> Date: Fri, 9 Aug 2024 18:21:24 +0100 Subject: [PATCH 06/13] feat: add cow amm banner (#4780) --- .../src/common/pure/CoWAMMBanner/index.tsx | 224 ++++++++++++++++++ .../application/containers/App/index.tsx | 5 +- 2 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 apps/cowswap-frontend/src/common/pure/CoWAMMBanner/index.tsx diff --git a/apps/cowswap-frontend/src/common/pure/CoWAMMBanner/index.tsx b/apps/cowswap-frontend/src/common/pure/CoWAMMBanner/index.tsx new file mode 100644 index 0000000000..c19fb1a8b4 --- /dev/null +++ b/apps/cowswap-frontend/src/common/pure/CoWAMMBanner/index.tsx @@ -0,0 +1,224 @@ +import { Media } from '@cowprotocol/ui' +import { ClosableBanner } from '@cowprotocol/ui' + +import { X } from 'react-feather' +import styled from 'styled-components/macro' + +import { cowAnalytics } from 'modules/analytics' + +const BannerWrapper = styled.div` + --darkGreen: #194d05; + --lightGreen: #bcec79; + + position: fixed; + top: 76px; + right: 10px; + z-index: 3; + width: 400px; + height: 327px; + border-radius: 24px; + background-color: var(--darkGreen); + color: var(--lightGreen); + padding: 24px; + display: flex; + flex-flow: column wrap; + align-items: center; + justify-content: center; + gap: 24px; + overflow: hidden; + + ${Media.upToSmall()} { + width: 100%; + height: auto; + left: 0; + right: 0; + margin: 0 auto; + bottom: 57px; + top: initial; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + box-shadow: 0 0 0 100vh rgb(0 0 0 / 40%); + z-index: 10; + } + + > i { + position: absolute; + top: -30px; + left: -30px; + width: 166px; + height: 42px; + border: 1px solid var(--lightGreen); + border-radius: 16px; + border-left: 0; + animation: bounceLeftRight 7s infinite; + animation-delay: 2s; + } + + &::before { + content: ''; + position: absolute; + top: 100px; + left: -23px; + width: 56px; + height: 190px; + border: 1px solid var(--lightGreen); + border-radius: 16px; + border-left: 0; + animation: bounceUpDown 7s infinite; + } + + &::after { + content: ''; + position: absolute; + bottom: -21px; + right: 32px; + width: 76px; + height: 36px; + border: 1px solid var(--lightGreen); + border-radius: 16px; + border-bottom: 0; + animation: bounceLeftRight 7s infinite; + animation-delay: 1s; + } + + > div { + display: flex; + flex-flow: column wrap; + gap: 24px; + width: 100%; + max-width: 75%; + margin: 0 auto; + } + + @keyframes bounceUpDown { + 0%, + 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-7px); + } + } + + @keyframes bounceLeftRight { + 0%, + 100% { + transform: translateX(0); + } + 50% { + transform: translateX(7px); + } + } +` + +const Title = styled.h2` + font-size: 34px; + font-weight: bold; + margin: 0; + + ${Media.upToSmall()} { + font-size: 26px; + } +` + +const Description = styled.p` + font-size: 17px; + line-height: 1.5; + margin: 0; + + ${Media.upToSmall()} { + font-size: 15px; + } +` + +const CTAButton = styled.button` + background-color: var(--lightGreen); + color: var(--darkGreen); + border: none; + border-radius: 56px; + padding: 12px 24px; + font-size: 18px; + font-weight: bold; + cursor: pointer; + width: 100%; + max-width: 75%; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + transition: border-radius 0.2s ease-in-out; + + &:hover { + border-radius: 16px; + + > i { + transform: rotate(45deg); + } + } + + > i { + font-size: 22px; + font-weight: bold; + font-style: normal; + line-height: 1; + margin: 3px 0 0; + transition: transform 0.2s ease-in-out; + animation: spin 6s infinite; + } + + @keyframes spin { + 0% { + transform: rotate(0deg); + } + 20% { + transform: rotate(360deg); + } + 100% { + transform: rotate(360deg); + } + } +` + +const CloseButton = styled(X)` + position: absolute; + top: 16px; + right: 16px; + cursor: pointer; + color: var(--lightGreen); + opacity: 0.6; + transition: opacity 0.2s ease-in-out; + + &:hover { + opacity: 1; + } +` + +export function CoWAmmBanner() { + const handleCTAClick = () => { + cowAnalytics.sendEvent({ + category: 'CoW Swap', + action: 'CoW AMM Banner CTA Clicked', + }) + + window.open( + 'https://balancer.fi/pools/cow?utm_source=swap.cow.fi&utm_medium=web&utm_content=cow_amm_banner', + '_blank' + ) + } + + return ClosableBanner('cow_amm_banner', (close) => ( + + + +
+ The first MEV-capturing AMM + + CoW AMM shields you from LVR, so you can provide liquidity with less risk and more rewards. + +
+ + LP on CoW AMM ↗ + +
+ )) +} diff --git a/apps/cowswap-frontend/src/modules/application/containers/App/index.tsx b/apps/cowswap-frontend/src/modules/application/containers/App/index.tsx index 5385a9e74a..d69e96defb 100644 --- a/apps/cowswap-frontend/src/modules/application/containers/App/index.tsx +++ b/apps/cowswap-frontend/src/modules/application/containers/App/index.tsx @@ -24,6 +24,7 @@ import { useInitializeUtm } from 'modules/utm' import { InvalidLocalTimeWarning } from 'common/containers/InvalidLocalTimeWarning' import { useCategorizeRecentActivity } from 'common/hooks/useCategorizeRecentActivity' import { useMenuItems } from 'common/hooks/useMenuItems' +import { CoWAmmBanner } from 'common/pure/CoWAMMBanner' import { LoadingApp } from 'common/pure/LoadingApp' import { CoWDAOFonts } from 'common/styles/CoWDAOFonts' import RedirectAnySwapAffectedUsers from 'pages/error/AnySwapAffectedUsers/RedirectAnySwapAffectedUsers' @@ -31,7 +32,6 @@ import RedirectAnySwapAffectedUsers from 'pages/error/AnySwapAffectedUsers/Redir import { ADDITIONAL_FOOTER_CONTENT, NAV_ITEMS, PRODUCT_VARIANT } from './menuConsts' import * as styledEl from './styled' - const RoutesApp = lazy(() => import('./RoutesApp').then((module) => ({ default: module.RoutesApp }))) const GlobalStyles = GlobalCoWDAOStyles(CoWDAOFonts, 'transparent') @@ -128,6 +128,9 @@ export function App() { /> )} + {/* CoW AMM banner */} + {!isInjectedWidgetMode && } + From a10df31010c34c90cffdad34b69a320a4167e119 Mon Sep 17 00:00:00 2001 From: Leandro Date: Wed, 14 Aug 2024 11:40:16 +0100 Subject: [PATCH 07/13] fix: replace cloudflare-ipfs.com with ipfs.io (#4786) --- apps/explorer/.env.example | 2 +- apps/explorer/src/const.ts | 2 +- libs/common-utils/src/environments.test.ts | 4 ++-- libs/common-utils/src/uriToHttp.test.ts | 6 +----- libs/common-utils/src/uriToHttp.ts | 7 ++----- 5 files changed, 7 insertions(+), 14 deletions(-) diff --git a/apps/explorer/.env.example b/apps/explorer/.env.example index e3ffb9a9c4..f24d674468 100644 --- a/apps/explorer/.env.example +++ b/apps/explorer/.env.example @@ -5,7 +5,7 @@ MOCK=true AUTOCONNECT=true # Public IPFS gateway -REACT_APP_IPFS_READ_URI=https://cloudflare-ipfs.com/ipfs +REACT_APP_IPFS_READ_URI=https://ipfs.io/ipfs # Sentry #REACT_APP_EXPLORER_SENTRY_DSN='https://' diff --git a/apps/explorer/src/const.ts b/apps/explorer/src/const.ts index 2d5c5b3ff7..9d9bf8d878 100644 --- a/apps/explorer/src/const.ts +++ b/apps/explorer/src/const.ts @@ -129,7 +129,7 @@ export const NATIVE_TOKEN_PER_NETWORK: Record = { } export const TENDERLY_API_URL = 'https://api.tenderly.co/api/v1/public-contract' -export const DEFAULT_IPFS_READ_URI = process.env.REACT_APP_IPFS_READ_URI || 'https://cloudflare-ipfs.com/ipfs' +export const DEFAULT_IPFS_READ_URI = process.env.REACT_APP_IPFS_READ_URI || 'https://ipfs.io/ipfs' export const IPFS_INVALID_APP_IDS = [ '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000001', diff --git a/libs/common-utils/src/environments.test.ts b/libs/common-utils/src/environments.test.ts index c61f7810b2..1e3bb07b8f 100644 --- a/libs/common-utils/src/environments.test.ts +++ b/libs/common-utils/src/environments.test.ts @@ -45,8 +45,8 @@ describe('Detect environments using host and path', () => { ).toEqual(isEns) }) - it('cloudflare-ipfs.com/ipfs/', () => { - expect(checkEnvironment('cloudflare-ipfs.com', '/ipfs/whatever')).toEqual(isEns) + it('ipfs.io/ipfs/', () => { + expect(checkEnvironment('ipfs.io', '/ipfs/whatever')).toEqual(isEns) }) it('gateway.pinata.cloud/ipfs/', () => { diff --git a/libs/common-utils/src/uriToHttp.test.ts b/libs/common-utils/src/uriToHttp.test.ts index 482fb4720a..2caa48622a 100644 --- a/libs/common-utils/src/uriToHttp.test.ts +++ b/libs/common-utils/src/uriToHttp.test.ts @@ -12,15 +12,11 @@ describe('uriToHttp', () => { }) it('returns ipfs gateways for ipfs:// urls', () => { expect(uriToHttp('ipfs://QmV8AfDE8GFSGQvt3vck8EwAzsPuNTmtP8VcQJE3qxRPaZ')).toEqual([ - 'https://cloudflare-ipfs.com/ipfs/QmV8AfDE8GFSGQvt3vck8EwAzsPuNTmtP8VcQJE3qxRPaZ/', 'https://ipfs.io/ipfs/QmV8AfDE8GFSGQvt3vck8EwAzsPuNTmtP8VcQJE3qxRPaZ/', ]) }) it('returns ipns gateways for ipns:// urls', () => { - expect(uriToHttp('ipns://app.uniswap.org')).toEqual([ - 'https://cloudflare-ipfs.com/ipns/app.uniswap.org/', - 'https://ipfs.io/ipns/app.uniswap.org/', - ]) + expect(uriToHttp('ipns://app.uniswap.org')).toEqual(['https://ipfs.io/ipns/app.uniswap.org/']) }) it('returns empty array for invalid scheme', () => { expect(uriToHttp('blah:test')).toEqual([]) diff --git a/libs/common-utils/src/uriToHttp.ts b/libs/common-utils/src/uriToHttp.ts index 2edb001d35..3d6c5fd858 100644 --- a/libs/common-utils/src/uriToHttp.ts +++ b/libs/common-utils/src/uriToHttp.ts @@ -17,15 +17,12 @@ export function uriToHttp(uri: string): string[] { case 'http': return ['https' + uri.substr(4), uri] case 'ipfs': - // eslint-disable-next-line no-case-declarations const hash = uri.match(/^ipfs:(\/\/)?(ipfs\/)?(.*)$/i)?.[3] // TODO: probably a bug on original code - return [`https://cloudflare-ipfs.com/ipfs/${hash}/`, `https://ipfs.io/ipfs/${hash}/`] + return [`https://ipfs.io/ipfs/${hash}/`] case 'ipns': - // eslint-disable-next-line no-case-declarations const name = uri.match(/^ipns:(\/\/)?(.*)$/i)?.[2] - return [`https://cloudflare-ipfs.com/ipns/${name}/`, `https://ipfs.io/ipns/${name}/`] + return [`https://ipfs.io/ipns/${name}/`] case 'ar': - // eslint-disable-next-line no-case-declarations const tx = uri.match(/^ar:(\/\/)?(.*)$/i)?.[2] return [`https://arweave.net/${tx}`] default: From 5755ca0b60707319c88d210c32ee8ec0b2e198cd Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Wed, 14 Aug 2024 11:41:51 +0100 Subject: [PATCH 08/13] feat: add analytics for notifications (#4785) * feat: add analytics for notifications * feat: add id and label for the event * fix: fix issues * fix: lint issues --- .../Header/AccountElement/index.tsx | 19 ++++++++++++++++++- .../src/modules/analytics/events.ts | 10 ++++++++++ .../containers/NotificationBell.tsx | 9 +++------ .../containers/NotificationsList/index.tsx | 3 +++ 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/apps/cowswap-frontend/src/legacy/components/Header/AccountElement/index.tsx b/apps/cowswap-frontend/src/legacy/components/Header/AccountElement/index.tsx index 3764770f64..a60eb2862a 100644 --- a/apps/cowswap-frontend/src/legacy/components/Header/AccountElement/index.tsx +++ b/apps/cowswap-frontend/src/legacy/components/Header/AccountElement/index.tsx @@ -8,10 +8,14 @@ import { useWalletInfo } from '@cowprotocol/wallet' import ReactDOM from 'react-dom' + + import { upToLarge, useMediaQuery } from 'legacy/hooks/useMediaQuery' import { useToggleAccountModal } from 'modules/account' +import { clickNotifications } from 'modules/analytics' import { NotificationBell, NotificationSidebar } from 'modules/notifications' +import { useUnreadNotifications } from 'modules/notifications/hooks/useUnreadNotifications' import { Web3Status } from 'modules/wallet/containers/Web3Status' import { useIsProviderNetworkUnsupported } from 'common/hooks/useIsProviderNetworkUnsupported' @@ -33,6 +37,9 @@ export function AccountElement({ className, standaloneMode, pendingActivities }: const isUpToLarge = useMediaQuery(upToLarge) const { isNotificationsFeedEnabled } = useFeatureFlags() + const unreadNotifications = useUnreadNotifications() + const unreadNotificationsCount = Object.keys(unreadNotifications).length + const [isSidebarOpen, setSidebarOpen] = useState(false) return ( @@ -44,7 +51,17 @@ export function AccountElement({ className, standaloneMode, pendingActivities }: )} account && toggleAccountModal()} /> - {account && isNotificationsFeedEnabled && setSidebarOpen(true)} />} + {account && isNotificationsFeedEnabled && ( + { + clickNotifications( + unreadNotificationsCount === 0 ? 'click-bell' : 'click-bell-with-pending-notifications' + ) + setSidebarOpen(true) + }} + /> + )}
{ReactDOM.createPortal( diff --git a/apps/cowswap-frontend/src/modules/analytics/events.ts b/apps/cowswap-frontend/src/modules/analytics/events.ts index 71d842e614..4d72a1c519 100644 --- a/apps/cowswap-frontend/src/modules/analytics/events.ts +++ b/apps/cowswap-frontend/src/modules/analytics/events.ts @@ -24,6 +24,7 @@ enum Category { TWAP = 'TWAP', COW_FORTUNE = 'CoWFortune', SURPLUS_MODAL = 'Surplus Modal', + NOTIFICATIONS = 'Notifications', } export function shareFortuneTwitterAnalytics() { @@ -321,3 +322,12 @@ export function shareSurplusOnTwitter() { action: `Share on Twitter`, }) } + +export function clickNotifications(event: string, notificationId?: number, title?: string) { + cowAnalytics.sendEvent({ + category: Category.NOTIFICATIONS, + action: event, + value: notificationId, + label: title, + }) +} diff --git a/apps/cowswap-frontend/src/modules/notifications/containers/NotificationBell.tsx b/apps/cowswap-frontend/src/modules/notifications/containers/NotificationBell.tsx index 82c359f819..67823bee82 100644 --- a/apps/cowswap-frontend/src/modules/notifications/containers/NotificationBell.tsx +++ b/apps/cowswap-frontend/src/modules/notifications/containers/NotificationBell.tsx @@ -7,7 +7,6 @@ import { UI } from '@cowprotocol/ui' import SVG from 'react-inlinesvg' import styled from 'styled-components/macro' -import { useUnreadNotifications } from '../hooks/useUnreadNotifications' const Icon = styled.div<{ hasNotification?: boolean }>` --size: 18px; @@ -59,14 +58,12 @@ const Icon = styled.div<{ hasNotification?: boolean }>` interface NotificationBellProps { onClick: Command + unreadCount: number } -export function NotificationBell({ onClick }: NotificationBellProps) { - const unreadNotifications = useUnreadNotifications() - const unreadNotificationsCount = Object.keys(unreadNotifications).length - +export function NotificationBell({ onClick, unreadCount }: NotificationBellProps) { return ( - 0} onClick={onClick}> + 0} onClick={onClick}> ) diff --git a/apps/cowswap-frontend/src/modules/notifications/containers/NotificationsList/index.tsx b/apps/cowswap-frontend/src/modules/notifications/containers/NotificationsList/index.tsx index 38915f4fa4..a3f5f76b30 100644 --- a/apps/cowswap-frontend/src/modules/notifications/containers/NotificationsList/index.tsx +++ b/apps/cowswap-frontend/src/modules/notifications/containers/NotificationsList/index.tsx @@ -1,6 +1,8 @@ import { useSetAtom } from 'jotai/index' import React, { ReactNode, useEffect, useMemo } from 'react' +import { clickNotifications } from 'modules/analytics' + import { ListWrapper, NoNotifications, NotificationCard, NotificationsListWrapper, NotificationThumb } from './styled' import { useAccountNotifications } from '../../hooks/useAccountNotifications' @@ -50,6 +52,7 @@ export function NotificationsList({ children }: { children: ReactNode }) { target={target} noImage={!thumbnail} rel={target === '_blank' ? 'noopener noreferrer' : ''} + onClick={() => clickNotifications('click-notification-card', id, title)} > {thumbnail && ( From df1235b20ab9a888e085d1d90a9e5ab73da54d13 Mon Sep 17 00:00:00 2001 From: Leandro Date: Wed, 14 Aug 2024 14:29:07 +0100 Subject: [PATCH 09/13] fix: replace gateway.ipfs.io with ipfs.io (#4788) --- apps/widget-configurator/src/app/configurator/consts.ts | 4 ++-- libs/tokens/src/const/tokensLists.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/widget-configurator/src/app/configurator/consts.ts b/apps/widget-configurator/src/app/configurator/consts.ts index af1d838865..b635a86844 100644 --- a/apps/widget-configurator/src/app/configurator/consts.ts +++ b/apps/widget-configurator/src/app/configurator/consts.ts @@ -1,5 +1,5 @@ import { CowEventListeners, CowEvents, ToastMessageType } from '@cowprotocol/events' -import { TradeType, TokenInfo, CowSwapWidgetPaletteParams } from '@cowprotocol/widget-lib' +import { CowSwapWidgetPaletteParams, TokenInfo, TradeType } from '@cowprotocol/widget-lib' import { TokenListItem } from './types' @@ -31,7 +31,7 @@ export const DEFAULT_TOKEN_LISTS: TokenListItem[] = [ { url: 'https://raw.githubusercontent.com/SetProtocol/uniswap-tokenlist/main/set.tokenlist.json', enabled: false }, { url: 'https://synths.snx.eth.link', enabled: false }, { url: 'https://testnet.tokenlist.eth.link', enabled: false }, - { url: 'https://gateway.ipfs.io/ipns/tokens.uniswap.org', enabled: false }, + { url: 'https://ipfs.io/ipns/tokens.uniswap.org', enabled: false }, { url: 'https://wrapped.tokensoft.eth.link', enabled: false }, ] // TODO: Move default palette to a new lib that only exposes the palette colors. diff --git a/libs/tokens/src/const/tokensLists.ts b/libs/tokens/src/const/tokensLists.ts index 35a7667728..d30a403e82 100644 --- a/libs/tokens/src/const/tokensLists.ts +++ b/libs/tokens/src/const/tokensLists.ts @@ -4,7 +4,7 @@ import { ListsSourcesByNetwork } from '../types' export const DEFAULT_TOKENS_LISTS: ListsSourcesByNetwork = tokensList -export const UNISWAP_TOKENS_LIST = 'https://gateway.ipfs.io/ipns/tokens.uniswap.org' +export const UNISWAP_TOKENS_LIST = 'https://ipfs.io/ipns/tokens.uniswap.org' export const GNOSIS_UNISWAP_TOKENS_LIST = 'https://raw.githubusercontent.com/cowprotocol/token-lists/main/src/public/GnosisUniswapTokensList.json' From b70b27cf6d25e76ae79b3906693095a2e92e64d9 Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Wed, 14 Aug 2024 14:38:40 +0100 Subject: [PATCH 10/13] fix: fix wrong quote in explorer (#4791) * fix: fix wrong quote in explorer * fix: don't return unused data Co-authored-by: Leandro --------- Co-authored-by: Leandro --- apps/explorer/src/utils/format.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/explorer/src/utils/format.ts b/apps/explorer/src/utils/format.ts index b7c3ea1ebf..78efe6cfef 100644 --- a/apps/explorer/src/utils/format.ts +++ b/apps/explorer/src/utils/format.ts @@ -245,7 +245,7 @@ export function formatCalculatedPriceToDisplay( const buySymbol = safeTokenName(buyToken) const sellSymbol = safeTokenName(sellToken) - const [quoteSymbol] = isPriceInverted ? [sellSymbol, buySymbol] : [buySymbol, sellSymbol] + const quoteSymbol = isPriceInverted ? buySymbol : sellSymbol return `${formattedPrice} ${quoteSymbol}` } From 87ee5aac719db1fcaecd17d06bddecda172e8a95 Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Wed, 14 Aug 2024 14:51:31 +0100 Subject: [PATCH 11/13] feat: improve finding tokens in other networks (#4794) --- .../tokens/src/hooks/tokens/useSearchToken.ts | 2 +- libs/tokens/src/services/searchTokensInApi.ts | 103 ++++++++++++++++-- 2 files changed, 94 insertions(+), 11 deletions(-) diff --git a/libs/tokens/src/hooks/tokens/useSearchToken.ts b/libs/tokens/src/hooks/tokens/useSearchToken.ts index 67fc126c35..fde44d8eac 100644 --- a/libs/tokens/src/hooks/tokens/useSearchToken.ts +++ b/libs/tokens/src/hooks/tokens/useSearchToken.ts @@ -159,7 +159,7 @@ function useSearchTokensInApi(input: string | undefined, isTokenAlreadyFoundByAd return null } - return searchTokensInApi(input).then((result) => parseTokensFromApi(result, chainId)) + return searchTokensInApi(chainId, input).then((result) => parseTokensFromApi(result, chainId)) }) } diff --git a/libs/tokens/src/services/searchTokensInApi.ts b/libs/tokens/src/services/searchTokensInApi.ts index f21e5707eb..9b49250caa 100644 --- a/libs/tokens/src/services/searchTokensInApi.ts +++ b/libs/tokens/src/services/searchTokensInApi.ts @@ -7,11 +7,6 @@ type Address = `0x${string}` type Chain = 'ARBITRUM' | 'ETHEREUM' | 'ETHEREUM_SEPOLIA' | 'OPTIMISM' | 'POLYGON' | 'CELO' | 'BNB' | 'UNKNOWN_CHAIN' -const CHAIN_TO_CHAIN_ID: { [key: string]: SupportedChainId } = { - ETHEREUM: SupportedChainId.MAINNET, - ETHEREUM_SEPOLIA: SupportedChainId.SEPOLIA, -} - interface FetchTokensResult { id: string decimals: number @@ -22,7 +17,12 @@ interface FetchTokensResult { symbol: string project: { id: string + name: string logoUrl: string + logo: { + id: string + url: string + } safetyLevel: string } } @@ -36,8 +36,9 @@ export interface TokenSearchFromApiResult extends FetchTokensResult { } const SEARCH_TOKENS = gql` - query SearchTokens($searchQuery: String!) { - searchTokens(searchQuery: $searchQuery) { + query SearchTokensWeb($searchQuery: String!, $chains: [Chain!]) { + searchTokens(searchQuery: $searchQuery, chains: $chains) { + ...SimpleTokenDetails id decimals name @@ -45,28 +46,110 @@ const SEARCH_TOKENS = gql` standard address symbol + market(currency: USD) { + id + price { + id + value + currency + __typename + } + pricePercentChange(duration: DAY) { + id + value + __typename + } + volume24H: volume(duration: DAY) { + id + value + currency + __typename + } + __typename + } project { id - logoUrl + name + logo { + id + url + __typename + } safetyLevel + logoUrl + isSpam + __typename + } + __typename + } + } + + fragment SimpleTokenDetails on Token { + id + address + chain + symbol + name + decimals + standard + project { + id + name + logo { + id + url __typename } + safetyLevel + logoUrl + isSpam __typename } + __typename } ` const BASE_URL = `${BFF_BASE_URL}/proxies/tokens` const GQL_CLIENT = new GraphQLClient(BASE_URL) -export async function searchTokensInApi(searchQuery: string): Promise { +const CHAIN_NAMES: Record = { + [SupportedChainId.MAINNET]: 'ETHEREUM', + [SupportedChainId.ARBITRUM_ONE]: 'ARBITRUM', + [SupportedChainId.SEPOLIA]: null, + [SupportedChainId.GNOSIS_CHAIN]: null, +} + +const CHAIN_IDS = Object.entries(CHAIN_NAMES).reduce((acc, [supportedChainId, chain]) => { + if (chain) { + acc[chain] = parseInt(supportedChainId) + } + + return acc +}, {} as Record) + +export async function searchTokensInApi( + chainId: SupportedChainId, + searchQuery: string +): Promise { + const chain = CHAIN_NAMES[chainId] + + if (!chain) { + return [] + } + return await GQL_CLIENT.request(SEARCH_TOKENS, { searchQuery, + chains: [chain], }).then((result) => { if (!result?.searchTokens?.length) { return [] } - return result.searchTokens.map((token) => ({ ...token, chainId: CHAIN_TO_CHAIN_ID[token.chain] })) + return result.searchTokens.map((token) => { + return { + ...token, + chainId: CHAIN_IDS[token.chain], + } + }) }) } From 46add78c41e8612986514ffa946cb60d1c180a33 Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Wed, 14 Aug 2024 14:53:29 +0100 Subject: [PATCH 12/13] feat: add analytics event for closing CoW AMM banner (#4790) --- .../src/common/pure/CoWAMMBanner/index.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/cowswap-frontend/src/common/pure/CoWAMMBanner/index.tsx b/apps/cowswap-frontend/src/common/pure/CoWAMMBanner/index.tsx index 72505167f2..883652f690 100644 --- a/apps/cowswap-frontend/src/common/pure/CoWAMMBanner/index.tsx +++ b/apps/cowswap-frontend/src/common/pure/CoWAMMBanner/index.tsx @@ -209,7 +209,16 @@ export function CoWAmmBanner() { return ClosableBanner('cow_amm_banner', (close) => ( - + { + cowAnalytics.sendEvent({ + category: 'CoW Swap', + action: 'CoW AMM Banner CTA Closed', + }) + close() + }} + />
Now live: the first MEV-capturing AMM From 63c0f55c3a5e6565484708fc0bb27251843cf1ee Mon Sep 17 00:00:00 2001 From: Anxo Rodriguez Date: Wed, 14 Aug 2024 14:53:49 +0100 Subject: [PATCH 13/13] fix: fix issue with the swap label don't showing (#4789) --- .../trade/containers/TradeWidgetLinks/index.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/cowswap-frontend/src/modules/trade/containers/TradeWidgetLinks/index.tsx b/apps/cowswap-frontend/src/modules/trade/containers/TradeWidgetLinks/index.tsx index fb8bb55242..cb7c298f86 100644 --- a/apps/cowswap-frontend/src/modules/trade/containers/TradeWidgetLinks/index.tsx +++ b/apps/cowswap-frontend/src/modules/trade/containers/TradeWidgetLinks/index.tsx @@ -87,22 +87,22 @@ export function TradeWidgetLinks({ location.pathname, highlightedBadgeText, highlightedBadgeType, - handleMenuItemClick + handleMenuItemClick, ]) const singleMenuItem = menuItemsElements.length === 1 + const selectedMenuItem = menuItemsElements.find((item) => item.props.isActive) || menuItemsElements[0] + return isDropdown ? ( <> !singleMenuItem && setDropdownVisible(!isDropdownVisible)} isDropdownVisible={isDropdownVisible} > - item.props.isActive)?.props.routePath || '#'}> - - {menuItemsElements.find((item) => item.props.isActive)?.props.item.label} - {!singleMenuItem ? : null} - + + {selectedMenuItem.props.item.label} + {!singleMenuItem ? : null}