Skip to content
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
10de804
chore: mark places where order summary is in use
shoom3301 Dec 25, 2025
405295e
refactor: simplify createActivityDescriptor
shoom3301 Dec 25, 2025
448ac0e
refactor(ActivityDetails): replace order.summary with computeOrderSum…
shoom3301 Dec 25, 2025
9fad34a
refactor(RequestCancellationModal): extract styled el to another file
shoom3301 Dec 25, 2025
533ac14
refactor(CancellationModal): use computeOrderSummary instead of order…
shoom3301 Dec 25, 2025
c7962be
refactor(RequestCancellationModal): get rid of render functions
shoom3301 Dec 24, 2025
cf33725
refactor: simplify computeOrderSummary
shoom3301 Dec 24, 2025
1ebc832
refactor: extract useGetSerializedBridgeOrder
shoom3301 Dec 24, 2025
0c70318
feat: support all kinds of order in computeOrderSummary
shoom3301 Dec 24, 2025
8f4a80e
feat: compute order summary taking bridge into account
shoom3301 Dec 25, 2025
9334831
fix: fix canceled bridge order displaying
shoom3301 Dec 25, 2025
f318fb9
chore: fix tests
shoom3301 Dec 25, 2025
50d1c28
chore: add tests for bridging orders
shoom3301 Dec 25, 2025
f4b727a
chore: simplify types
shoom3301 Dec 25, 2025
b9b5450
chore: remove excessive computeOrderSummary call
shoom3301 Dec 26, 2025
94df4d2
refactor: replace computeOrderSummary with OrderSummary
shoom3301 Dec 26, 2025
374b0b3
fix: generalize OrderNotification for any type of order
shoom3301 Dec 29, 2025
cbafb71
chore: remove dead code
shoom3301 Dec 29, 2025
279b507
Merge branch 'develop' into refactor/order-summary
shoom3301 Dec 30, 2025
6e0827c
Merge branch 'refactor/order-summary' of https://github.com/cowprotoc…
shoom3301 Dec 30, 2025
75a02ed
Merge branch 'develop' of https://github.com/cowprotocol/cowswap into…
shoom3301 Dec 30, 2025
93b8d91
Merge branch 'refactor/order-summary-1' of https://github.com/cowprot…
shoom3301 Dec 30, 2025
19c9474
Merge branch 'develop' of https://github.com/cowprotocol/cowswap into…
shoom3301 Dec 30, 2025
9771cea
Merge branch 'develop' of https://github.com/cowprotocol/cowswap into…
shoom3301 Dec 30, 2025
f8962f8
chore: update i18n
shoom3301 Dec 30, 2025
f1f7a1a
chore: delete irrelevant tests
shoom3301 Dec 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { useAtomValue } from 'jotai'
import { ReactNode, useMemo } from 'react'
import React, { ReactNode, useMemo } from 'react'

import { Command } from '@cowprotocol/types'

import { cancellationModalContextAtom } from 'common/hooks/useCancelOrder/state'
import { CancellationModal as Pure } from 'common/pure/CancellationModal'
import { OrderSummary } from 'common/pure/OrderSummary'

import { useUltimateOrder } from '../../hooks/useUltimateOrder'
import { computeOrderSummary } from '../../updaters/orders/utils'
import { getUltimateOrderTradeAmounts } from '../../updaters/orders/utils'

export type CancellationModalProps = {
isOpen: boolean
Expand All @@ -22,7 +23,12 @@ export function CancellationModal(props: CancellationModalProps): ReactNode {

const orderSummary = useMemo(() => {
if (!ultimateOrder) return undefined
return computeOrderSummary(ultimateOrder)

const { inputAmount, outputAmount } = getUltimateOrderTradeAmounts(ultimateOrder)

return (
<OrderSummary inputAmount={inputAmount} outputAmount={outputAmount} kind={ultimateOrder.orderFromStore.kind} />
)
}, [ultimateOrder])

return <Pure isOpen={isOpen} onDismiss={onDismiss} context={context} orderSummary={orderSummary} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { ReactElement, useMemo } from 'react'
import React, { ReactElement, ReactNode, useMemo } from 'react'

import { shortenOrderId } from '@cowprotocol/common-utils'
import { Command } from '@cowprotocol/types'
Expand All @@ -18,7 +18,7 @@ export type CancellationModalProps = {
isOpen: boolean
onDismiss: Command
context: CancellationModalContext
orderSummary: string | undefined
orderSummary: ReactNode | undefined
}

export function CancellationModal(props: CancellationModalProps): ReactElement | null {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { ReactNode } from 'react'

import { TokenWithLogo } from '@cowprotocol/common-const'
import { Command } from '@cowprotocol/types'
import type { BigNumber } from '@ethersproject/bignumber'

import { CancellationType } from '../../hooks/useCancelOrder/state'

export type RequestCancellationModalProps = {
summary?: string
summary?: ReactNode
shortId: string
defaultType: CancellationType
onDismiss: Command
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,42 +9,44 @@ import { CurrencyAmount } from '@uniswap/sdk-core'

import { BuyForAtMostTemplate, SellForAtLeastTemplate } from './summaryTemplates'

interface OrderSummaryProps {
actionTitle?: string
import { TradeAmounts } from '../../types'

interface TokensAndAmounts {
inputToken: TokenInfo
outputToken: TokenInfo
sellAmount: string
buyAmount: string
}

type OrderSummaryProps = {
actionTitle?: string
kind: OrderKind
srcChainData?: ChainInfo
dstChainData?: ChainInfo
children?: ReactElement | string
customTemplate?: typeof SellForAtLeastTemplate
}
} & (TradeAmounts | TokensAndAmounts)

export function OrderSummary(props: OrderSummaryProps): ReactNode {
const {
kind,
sellAmount,
buyAmount,
outputToken,
inputToken,
children,
customTemplate,
actionTitle,
srcChainData,
dstChainData,
} = props
const { kind, children, customTemplate, actionTitle, srcChainData, dstChainData } = props
const isSell = isSellOrder(kind)
const isBridgeOrder = srcChainData && dstChainData && srcChainData.id !== dstChainData.id

const inputAmount = useMemo(() => {
return CurrencyAmount.fromRawAmount(TokenWithLogo.fromToken(inputToken), sellAmount)
}, [inputToken, sellAmount])
if ('inputAmount' in props) {
return props.inputAmount
}

return CurrencyAmount.fromRawAmount(TokenWithLogo.fromToken(props.inputToken), props.sellAmount)
}, [props])

const outputAmount = useMemo(() => {
return CurrencyAmount.fromRawAmount(TokenWithLogo.fromToken(outputToken), buyAmount)
}, [buyAmount, outputToken])
if ('outputAmount' in props) {
return props.outputAmount
}

return CurrencyAmount.fromRawAmount(TokenWithLogo.fromToken(props.outputToken), props.buyAmount)
}, [props])

const inputAmountElement = <TokenAmount amount={inputAmount} tokenSymbol={inputAmount?.currency} />
const outputAmountElement = <TokenAmount amount={outputAmount} tokenSymbol={outputAmount?.currency} />
Expand Down
53 changes: 1 addition & 52 deletions apps/cowswap-frontend/src/common/updaters/orders/utils.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,16 @@
import {
areAddressesEqual,
formatSymbol,
formatTokenAmount,
isSellOrder,
shortenAddress,
} from '@cowprotocol/common-utils'
import { EnrichedOrder, SupportedChainId as ChainId } from '@cowprotocol/cow-sdk'
import { Currency, CurrencyAmount } from '@uniswap/sdk-core'

import { t } from '@lingui/core/macro'

import { Order, OrderStatus } from 'legacy/state/orders/actions'
import { classifyOrder, OrderTransitionStatus } from 'legacy/state/orders/utils'

import { getOrder } from 'api/cowProtocol'
import { getIsComposableCowChildOrder } from 'utils/orderUtils/getIsComposableCowChildOrder'
import { getUiOrderType, getUiOrderTypeTitles, UiOrderTypeParams } from 'utils/orderUtils/getUiOrderType'

import { UltimateOrderData } from '../../hooks/useUltimateOrder'
import { TradeAmounts } from '../../types'

export function computeOrderSummary(ultimateOrder: UltimateOrderData): string | undefined {
const { orderFromStore } = ultimateOrder
const orderFromApi = orderFromStore.apiAdditionalInfo
const genericOrder = orderFromApi ?? orderFromStore

const owner = genericOrder?.owner
const receiver = genericOrder?.receiver
const uiOrderType = getUiOrderType(genericOrder as UiOrderTypeParams)
const orderTitle = getUiOrderTypeTitles()[uiOrderType]

const recipientSuffix = receiver && !areAddressesEqual(receiver, owner) ? t`to` + ` ${shortenAddress(receiver)}` : ''

const summaryAmounts = getOrderSummary(genericOrder, getOrderTradeAmounts(ultimateOrder))

// ${Swap} ${10 DAI at least 10 USDC} ${to 0x000..aaa}
return `${orderTitle} ${summaryAmounts} ${recipientSuffix}`.trim()
}

function getOrderTradeAmounts({
export function getUltimateOrderTradeAmounts({
orderFromStore,
bridgeOrderFromStore,
bridgeOrderFromApi,
Expand Down Expand Up @@ -89,33 +61,10 @@ function getOrderTradeAmounts({
}
}

function getOrderSummary(genericOrder: EnrichedOrder | Order, { inputAmount, outputAmount }: TradeAmounts): string {
const { status, kind } = genericOrder

const isFulfilled = status === OrderStatus.FULFILLED

if (isFulfilled) {
return `${stringifyAmount(inputAmount)} ` + t`for` + ` ${stringifyAmount(outputAmount)}`
} else {
const isSell = isSellOrder(kind)

const inputPrefix = !isSell ? t`at most` + ` ` : ''
const outputPrefix = isSell ? t`at least` + ` ` : ''

return (
`${inputPrefix}${stringifyAmount(inputAmount)} ` + t`for` + ` ${outputPrefix}${stringifyAmount(outputAmount)}`
)
}
}

function stringToCurrency(amount: string, currency: Currency): CurrencyAmount<Currency> {
return CurrencyAmount.fromRawAmount(currency, amount)
}

function stringifyAmount(amount: CurrencyAmount<Currency>): string {
return `${formatTokenAmount(amount)} ${formatSymbol(amount.currency.symbol)}`
}

type PopupData = {
status: OrderTransitionStatus
order: EnrichedOrder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ export function useTransactionAdder(): TransactionAdder {
chainId,
...addTransactionParams,
nonce,
})
}),
)
} catch (e) {
console.error('Cannot add a transaction', e)
}
},
[dispatch, chainId, account, isSafeWallet, provider]
[dispatch, chainId, account, isSafeWallet, provider],
)
}

Expand Down Expand Up @@ -99,47 +99,3 @@ export function useTransactionsByHash({ hashes }: { hashes: string[] }): Enhance
}, {})
}, [allTxs, hashes])
}

// TODO: Add proper return type annotation
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function useAllClaimingTransactions() {
const transactionsMap = useAllTransactions()
const transactions = Object.values(transactionsMap)

return useMemo(() => {
return transactions.filter((tx) => !!tx.claim)
}, [transactions])
}

// TODO: Add proper return type annotation
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function useAllClaimingTransactionIndices() {
const claimingTransactions = useAllClaimingTransactions()
return useMemo(() => {
const flattenedClaimingTransactions = claimingTransactions.reduce<number[]>((acc, { claim, receipt }) => {
if (claim && claim.indices && !receipt) {
acc.push(...claim.indices)
}
return acc
}, [])

return new Set(flattenedClaimingTransactions)
}, [claimingTransactions])
}

// // watch for submissions to claim
// // return null if not done loading, return undefined if not found
// export function useUserHasSubmittedClaim(account?: string): {
// claimSubmitted: boolean
// claimTxn: EnhancedTransactionDetails | undefined
// } {
// const pendingClaims = useAllClaimingTransactions()
// const claimTxn = useMemo(
// () =>
// // find one that is both the user's claim, AND not mined
// pendingClaims.find((claim) => claim.claim?.recipient === account),
// [account, pendingClaims]
// )

// return { claimSubmitted: !!claimTxn, claimTxn }
// }
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import { isPending } from 'common/hooks/useCategorizeRecentActivity'
import { useEnhancedActivityDerivedState } from 'common/hooks/useEnhancedActivityDerivedState'
import { useGetSurplusData } from 'common/hooks/useGetSurplusFiatValue'
import { useSwapAndBridgeContext } from 'common/hooks/useSwapAndBridgeContext'
import { useUltimateOrder } from 'common/hooks/useUltimateOrder'
import { CurrencyLogoPair } from 'common/pure/CurrencyLogoPair'
import { CustomRecipientWarningBanner } from 'common/pure/CustomRecipientWarningBanner'
import { IconSpinner } from 'common/pure/IconSpinner'
Expand All @@ -46,7 +45,6 @@ import {
useIsReceiverWalletBannerHidden,
} from 'common/state/receiverWalletBannerVisibility'
import { ActivityDerivedState, ActivityStatus } from 'common/types/activity'
import { computeOrderSummary } from 'common/updaters/orders/utils'
import { getIsBridgeOrder } from 'common/utils/getIsBridgeOrder'
import { getIsCustomRecipient } from 'utils/orderUtils/getIsCustomRecipient'
import { getUiOrderType } from 'utils/orderUtils/getUiOrderType'
Expand Down Expand Up @@ -245,7 +243,6 @@ export function ActivityDetails(props: {
const { isPartialApproveEnabled } = useFeatureFlags()
const [isPartialApproveEnabledBySettings] = useSwapPartialApprovalToggleState(isPartialApproveEnabled)
const getShowCancellationModal = useCancelOrder()
const ultimateOrder = useUltimateOrder(chainId, order?.id)

const isSwap = order && getUiOrderType(order) === UiOrderType.SWAP

Expand Down Expand Up @@ -490,6 +487,7 @@ export function ActivityDetails(props: {
<SummaryInner>
<b>{activityName}</b>
{isOrder ? (
// Order
<>
{order && !skipBridgingDisplay && isBridgeOrder ? (
<BridgeActivitySummary
Expand Down Expand Up @@ -590,10 +588,7 @@ export function ActivityDetails(props: {
</>
) : (
// Transaction
(activityDerivedState.summary ??
// Order
(ultimateOrder ? computeOrderSummary(ultimateOrder) : null) ??
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

computeOrderSummary was never called here, lol

id)
(activityDerivedState.summary ?? id)
)}

{activityLinkUrl && enhancedTransaction?.replacementType !== 'replaced' && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,57 +5,26 @@ import { ExternalLink } from '@cowprotocol/ui'

import { t } from '@lingui/core/macro'
import { Trans } from '@lingui/react/macro'
import { useBridgeOrderData } from 'entities/bridgeOrders'

import { getUiOrderType } from 'utils/orderUtils/getUiOrderType'
import type { OrderSummaryTemplateProps } from 'common/pure/OrderSummary/summaryTemplates'

import { OrderNotification } from '../OrderNotification'
import { mapBridgingResultToOrderInfo } from '../OrderNotification/utils'

import type { OrderSummaryTemplateProps } from '../../pure/OrderSummary/summaryTemplates'

function summaryTemplate({
inputAmount,
outputAmount,
srcChainData,
dstChainData,
}: OrderSummaryTemplateProps): ReactNode {
return (
<>
<Trans>Sell</Trans> {inputAmount}{' '}
{srcChainData && (
<>
({srcChainData.label}) <Trans>for a total of</Trans> {outputAmount}{' '}
{dstChainData && <> ({dstChainData.label})</>}
</>
)}
</>
)
}

interface BridgingSuccessNotificationProps {
payload: OnBridgingSuccessPayload
}

export function BridgingSuccessNotification({ payload }: BridgingSuccessNotificationProps): ReactNode {
const { chainId, order } = payload
const bridgingOrder = useBridgeOrderData(order.uid)

if (!bridgingOrder) return null

const orderInfo = mapBridgingResultToOrderInfo(payload, bridgingOrder)

return (
<OrderNotification
title={<Trans>Bridging succeeded</Trans>}
actionTitle={t`Bridge`}
skipExplorerLink
chainId={chainId}
orderInfo={orderInfo}
customTemplate={summaryTemplate}
orderType={getUiOrderType(order)}
orderUid={order.uid}
receiver={bridgingOrder.recipient}
messageType={ToastMessageType.ORDER_FULFILLED}
bottomContent={
payload.explorerUrl ? (
Expand All @@ -70,3 +39,22 @@ export function BridgingSuccessNotification({ payload }: BridgingSuccessNotifica
/>
)
}

function summaryTemplate({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can improve this component or its documentation so that it's clear if srcChainData or dstChainData is ever undefined.

inputAmount,
outputAmount,
srcChainData,
dstChainData,
}: OrderSummaryTemplateProps): ReactNode {
return (
<>
<Trans>Sell</Trans> {inputAmount}{' '}
{srcChainData && (
<>
({srcChainData.label}) <Trans>for a total of</Trans> {outputAmount}{' '}
{dstChainData && <> ({dstChainData.label})</>}
</>
)}
</>
)
}
Loading
Loading