Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"apps/cowswap-frontend": "1.119.0",
"apps/cowswap-frontend": "1.119.1",
"apps/explorer": "2.55.0",
"libs/permit-utils": "1.0.0",
"libs/widget-lib": "0.20.0",
Expand Down
8 changes: 8 additions & 0 deletions apps/cowswap-frontend/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## [1.119.1](https://github.com/cowprotocol/cowswap/compare/cowswap-v1.119.0...cowswap-v1.119.1) (2025-10-03)


### Bug Fixes

* **account-modal:** prevent crash with unexpected empty appData.metadata ([#6335](https://github.com/cowprotocol/cowswap/issues/6335)) ([db263c7](https://github.com/cowprotocol/cowswap/commit/db263c7846f999266cecaf1acaea4766c29d73cc))
* **bridging:** disable Across bridging provider on prod like envs ([#6336](https://github.com/cowprotocol/cowswap/issues/6336)) ([f7e21fe](https://github.com/cowprotocol/cowswap/commit/f7e21fe17fc4ba7937b8a4680cce5bf90c966391))

## [1.119.0](https://github.com/cowprotocol/cowswap/compare/cowswap-v1.118.13...cowswap-v1.119.0) (2025-10-02)


Expand Down
2 changes: 1 addition & 1 deletion apps/cowswap-frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cowprotocol/cowswap",
"version": "1.119.0",
"version": "1.119.1",
"description": "CoW Swap",
"main": "index.js",
"author": "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ export function OrderHooksDetails({ appData, children, margin, isTradeConfirmati
if (isTradeConfirmation) mutate()
}, [isTradeConfirmation, mutate])

if (!appDataDoc) return null
// Not all versions of appData have metadata
if (!appDataDoc?.metadata) return null

const metadata = appDataDoc.metadata as cowAppDataLatestScheme.Metadata

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { BalancesCombinedUpdater } from 'modules/combinedBalances/updater/Balanc
import { InFlightOrderFinalizeUpdater } from 'modules/ethFlow'
import { CowEventsUpdater, InjectedWidgetUpdater, useInjectedWidgetParams } from 'modules/injectedWidget'
import { FinalizeTxUpdater } from 'modules/onchainTransactions'
import { ProgressBarExecutingOrdersUpdater } from 'modules/orderProgressBar'
import { OrderProgressEventsUpdater, ProgressBarExecutingOrdersUpdater } from 'modules/orderProgressBar'
import { OrdersNotificationsUpdater } from 'modules/orders'
import { useSourceChainId } from 'modules/tokensList'
import { TradeType, useTradeTypeInfo } from 'modules/trade'
Expand Down Expand Up @@ -91,6 +91,7 @@ export function Updaters(): ReactNode {
<UsdPricesUpdater />
<OrdersNotificationsUpdater />
<ProgressBarExecutingOrdersUpdater />
<OrderProgressEventsUpdater />
<SolversInfoUpdater />
<AnnouncementsUpdater />
<BridgingEnabledUpdater />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { getProgressBarStepName } from './useOrderProgressBarProps'

import { OrderProgressBarStepName } from '../constants'
import { OrderProgressBarState } from '../types'

const OPEN_STATUS = 'open' as OrderProgressBarState['backendApiStatus']
const EXECUTING_STATUS = 'executing' as OrderProgressBarState['backendApiStatus']

describe('getProgressBarStepName', () => {
function callGetProgressBarStepName({
isUnfillable = false,
backendApiStatus,
previousStepName = OrderProgressBarStepName.SOLVING,
previousBackendApiStatus,
}: {
isUnfillable?: boolean
backendApiStatus?: OrderProgressBarState['backendApiStatus']
previousStepName?: OrderProgressBarStepName | undefined
previousBackendApiStatus?: OrderProgressBarState['previousBackendApiStatus']
}): OrderProgressBarStepName {
return getProgressBarStepName(
isUnfillable,
false, // isCancelled
false, // isExpired
false, // isCancelling
undefined, // cancellationTriggered
false, // isConfirmed
null, // countdown
backendApiStatus,
previousBackendApiStatus,
previousStepName,
undefined, // bridgingStatus
false, // isBridgingTrade
)
}

it('keeps the solving animation when an order recovers from unfillable without backend status', () => {
const result = callGetProgressBarStepName({
previousStepName: OrderProgressBarStepName.UNFILLABLE,
})

expect(result).toBe(OrderProgressBarStepName.SOLVING)
})

it('keeps the solving animation when backend status is open after an unfillable recovery', () => {
const result = callGetProgressBarStepName({
backendApiStatus: OPEN_STATUS,
previousStepName: OrderProgressBarStepName.UNFILLABLE,
})

expect(result).toBe(OrderProgressBarStepName.SOLVING)
})

it('still transitions to executing when the backend reports progress', () => {
const result = callGetProgressBarStepName({
backendApiStatus: EXECUTING_STATUS,
previousStepName: OrderProgressBarStepName.UNFILLABLE,
previousBackendApiStatus: OPEN_STATUS,
})

expect(result).toBe(OrderProgressBarStepName.EXECUTING)
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ function useProgressBarStepNameUpdater(
setProgressBarStepName(orderId, name || DEFAULT_STEP_NAME)
}

let timer: NodeJS.Timeout
let timer: NodeJS.Timeout | undefined

const timeSinceLastChange = lastTimeChangedSteps ? Date.now() - lastTimeChangedSteps : 0

Expand All @@ -371,15 +371,15 @@ function useProgressBarStepNameUpdater(
}

return () => {
clearInterval(timer)
if (timer) clearTimeout(timer)
}
}, [orderId, stepName, lastTimeChangedSteps, setProgressBarStepName])
}

// TODO: Break down this large function into smaller functions
// TODO: Reduce function complexity by extracting logic
// eslint-disable-next-line complexity
function getProgressBarStepName(
export function getProgressBarStepName(
isUnfillable: boolean,
isCancelled: boolean,
isExpired: boolean,
Expand Down Expand Up @@ -438,6 +438,13 @@ function getProgressBarStepName(
} else if (isUnfillable) {
// out of market order
return OrderProgressBarStepName.UNFILLABLE
} else if (
(backendApiStatus == null || backendApiStatus === 'open' || backendApiStatus === 'scheduled') &&
previousStepName === OrderProgressBarStepName.UNFILLABLE
) {
// Order just recovered from being unfillable but backend has not progressed yet.
// Keep showing the solving animation so the favicon restarts instead of idling.
return OrderProgressBarStepName.SOLVING
} else if (backendApiStatus === 'active' && countdown === 0) {
// solving, but took longer than stipulated countdown
return OrderProgressBarStepName.DELAYED
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Updaters

export { ProgressBarExecutingOrdersUpdater } from './updaters/ProgressBarExecutingOrdersUpdater'
export { OrderProgressEventsUpdater } from './updaters/OrderProgressEventsUpdater'

// Containers
export { SurplusModalSetup } from './containers/SurplusModalSetup'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import type { OrderFillability } from 'common/hooks/usePendingOrdersFillability'

import { computeUnfillableOrderIds, getNewlyFillableOrderIds } from './OrderProgressEventsUpdater'

type TestOrder = {
id: string
isUnfillable?: boolean
}

const FILLABILITY_OK: OrderFillability = {
hasEnoughBalance: true,
hasEnoughAllowance: true,
hasPermit: false,
}

const FILLABILITY_LACKING_BALANCE: OrderFillability = {
hasEnoughBalance: false,
hasEnoughAllowance: true,
hasPermit: false,
}

const FILLABILITY_LACKING_ALLOWANCE: OrderFillability = {
hasEnoughBalance: true,
hasEnoughAllowance: false,
hasPermit: false,
}

describe('computeUnfillableOrderIds', () => {
it('includes orders flagged as unfillable by price', () => {
const orders: TestOrder[] = [{ id: '1', isUnfillable: true }]

const result = computeUnfillableOrderIds(orders, {})

expect(result).toEqual(['1'])
})

it('includes orders lacking balance or allowance', () => {
const orders: TestOrder[] = [{ id: '2' }]

const result = computeUnfillableOrderIds(orders, {
'2': FILLABILITY_LACKING_BALANCE,
})

expect(result).toEqual(['2'])
})

it('deduplicates orders flagged by both sources', () => {
const orders: TestOrder[] = [{ id: '3', isUnfillable: true }]

const result = computeUnfillableOrderIds(orders, {
'3': FILLABILITY_LACKING_ALLOWANCE,
})

expect(result).toEqual(['3'])
})

it('ignores orders that are fillable and not flagged', () => {
const orders: TestOrder[] = [{ id: '4', isUnfillable: false }, { id: '5' }]

const result = computeUnfillableOrderIds(orders, {
'4': FILLABILITY_OK,
'5': FILLABILITY_OK,
})

expect(result).toEqual([])
})
})

describe('getNewlyFillableOrderIds', () => {
it('returns orders that were previously unfillable but not in the current set', () => {
const previous = new Set(['1', '2', '3'])
const current = new Set(['2', '3', '4'])

const result = getNewlyFillableOrderIds(previous, current)

expect(result).toEqual(['1'])
})

it('returns an empty list when nothing recovered', () => {
const previous = new Set(['1'])
const current = new Set(['1'])

const result = getNewlyFillableOrderIds(previous, current)

expect(result).toEqual([])
})

it('handles an empty previous set', () => {
const previous = new Set<string>()
const current = new Set(['1'])

const result = getNewlyFillableOrderIds(previous, current)

expect(result).toEqual([])
})
})
Loading
Loading