From 886532e2a24e2ae9afe8df276ff58c87297cc203 Mon Sep 17 00:00:00 2001 From: Alfetopito Date: Mon, 30 Dec 2024 17:57:02 +0000 Subject: [PATCH 1/5] chore: bump cow-sdk to latest RC version --- package.json | 4 ++-- yarn.lock | 20 ++++++++++++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 6d26d07d41..a5f73fb00f 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "@cowprotocol/cms": "^0.9.0", "@cowprotocol/contracts": "^1.3.1", "@cowprotocol/cow-runner-game": "^0.2.9", - "@cowprotocol/cow-sdk": "^5.8.0", + "@cowprotocol/cow-sdk": "^5.10.0-RC.0", "@cowprotocol/ethflowcontract": "cowprotocol/ethflowcontract.git#main-artifacts", "@davatar/react": "1.8.1", "@emotion/react": "^11.11.1", @@ -343,4 +343,4 @@ "vite-tsconfig-paths": "~4.3.2", "vitest": "~0.32.0" } -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index cd5acc9218..47bd897c45 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2414,6 +2414,17 @@ json-stringify-deterministic "^1.0.8" multiformats "^9.6.4" +"@cowprotocol/app-data@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@cowprotocol/app-data/-/app-data-2.4.0.tgz#326e20065161e06308cf0ff429fe9dd2908c1c30" + integrity sha512-aG3CicUdR7jpY5/linxXmpL4axmiUvEwiHlOM0qKO/QdbNSntKNXjSu3r4QtHZ7BUiF1VUkcDVvvFW4D2MA0Rw== + dependencies: + ajv "^8.11.0" + cross-fetch "^3.1.5" + ipfs-only-hash "^4.0.0" + json-stringify-deterministic "^1.0.8" + multiformats "^9.6.4" + "@cowprotocol/cms@^0.9.0": version "0.9.0" resolved "https://registry.yarnpkg.com/@cowprotocol/cms/-/cms-0.9.0.tgz#f668256f7cc8ebe344cbfef018a82a2b9c8eb9dd" @@ -2436,11 +2447,12 @@ resolved "https://registry.yarnpkg.com/@cowprotocol/cow-runner-game/-/cow-runner-game-0.2.9.tgz#3f94b3f370bd114f77db8b1d238cba3ef4e9d644" integrity sha512-rX7HnoV+HYEEkBaqVUsAkGGo0oBrExi+d6Io+8nQZYwZk+IYLmS9jdcIObsLviM2h4YX8+iin6NuKl35AaiHmg== -"@cowprotocol/cow-sdk@^5.8.0": - version "5.8.0" - resolved "https://registry.yarnpkg.com/@cowprotocol/cow-sdk/-/cow-sdk-5.8.0.tgz#f5a4533a02e8055087a47fae6917819283c021c8" - integrity sha512-rNfkJ9wf1/x9R5L/sfTPWc8i5aAmodVb2whnlrb7JNl0BYviE1V0rtPmT8b2vwB6JWMIF41gFd9dXEWeqlavJA== +"@cowprotocol/cow-sdk@^5.10.0-RC.0": + version "5.10.0-RC.0" + resolved "https://registry.yarnpkg.com/@cowprotocol/cow-sdk/-/cow-sdk-5.10.0-RC.0.tgz#6e33a60cd0e028d9d1236f0822755727712f75dc" + integrity sha512-KneqcG7esS3hqUxHc45IQ95koThIZ6aKzBu/5YvgGq77OSFgmKjMDYayM647eAufByHCJkCt5I3TnFK8sjB9wA== dependencies: + "@cowprotocol/app-data" "^2.4.0" "@cowprotocol/contracts" "^1.6.0" "@ethersproject/abstract-signer" "^5.7.0" "@openzeppelin/merkle-tree" "^1.0.5" From 58ea52b16cc69e6ee072738526ac7dd8eff3d822 Mon Sep 17 00:00:00 2001 From: Alfetopito Date: Mon, 30 Dec 2024 17:27:41 +0000 Subject: [PATCH 2/5] feat: replace executedSurplusFee with executedSuplus Also use totalFee where applicable --- .../src/legacy/state/orders/actions.ts | 11 +++++-- .../src/legacy/state/orders/reducer.ts | 17 +++++----- .../pure/ReceiptModal/FeeField.tsx | 33 +++++++++++++++---- .../src/utils/orderUtils/parseOrder.ts | 12 +++++-- 4 files changed, 52 insertions(+), 21 deletions(-) diff --git a/apps/cowswap-frontend/src/legacy/state/orders/actions.ts b/apps/cowswap-frontend/src/legacy/state/orders/actions.ts index 4630be0d51..89bd865995 100644 --- a/apps/cowswap-frontend/src/legacy/state/orders/actions.ts +++ b/apps/cowswap-frontend/src/legacy/state/orders/actions.ts @@ -107,7 +107,9 @@ export type OrderInfoApi = Pick< | 'executedSellAmount' | 'executedSellAmountBeforeFees' | 'executedFeeAmount' - | 'executedSurplusFee' + | 'executedFee' + | 'executedFeeToken' + | 'totalFee' | 'invalidated' | 'ethflowData' | 'onchainOrderData' @@ -139,6 +141,7 @@ export interface AddPendingOrderParams { order: SerializedOrder isSafeWallet: boolean } + export type ChangeOrderStatusParams = { id: UID; chainId: ChainId } export type SetOrderCancellationHashParams = ChangeOrderStatusParams & { hash: string } @@ -177,11 +180,13 @@ export interface BatchOrdersUpdateParams { } export type PresignedOrdersParams = BatchOrdersUpdateParams + export interface UpdatePresignGnosisSafeTxParams { orderId: UID chainId: ChainId safeTransaction: SafeMultisigTransactionResponse } + export type ExpireOrdersBatchParams = BatchOrdersUpdateParams export type InvalidateOrdersBatchParams = BatchOrdersUpdateParams export type CancelOrdersBatchParams = BatchOrdersUpdateParams @@ -196,7 +201,7 @@ export const fulfillOrdersBatch = createAction('order/ export const preSignOrders = createAction('order/presignOrders') export const updatePresignGnosisSafeTx = createAction( - 'order/updatePresignGnosisSafeTx' + 'order/updatePresignGnosisSafeTx', ) export const expireOrdersBatch = createAction('order/expireOrdersBatch') @@ -214,7 +219,7 @@ export const deleteOrders = createAction('order/deleteOrders export const clearOrders = createAction<{ chainId: ChainId }>('order/clearOrders') export const updateLastCheckedBlock = createAction<{ chainId: ChainId; lastCheckedBlock: number }>( - 'order/updateLastCheckedBlock' + 'order/updateLastCheckedBlock', ) export const clearOrdersStorage = createAction('order/clearOrdersStorage') diff --git a/apps/cowswap-frontend/src/legacy/state/orders/reducer.ts b/apps/cowswap-frontend/src/legacy/state/orders/reducer.ts index 4e32f45fd1..f3068c7554 100644 --- a/apps/cowswap-frontend/src/legacy/state/orders/reducer.ts +++ b/apps/cowswap-frontend/src/legacy/state/orders/reducer.ts @@ -121,7 +121,7 @@ export function getDefaultNetworkState(chainId: ChainId): OrdersStateNetwork { // makes sure there's always an object at state[chainId], state[chainId].pending | .fulfilled function prefillState( state: Writable, - { payload: { chainId } }: PayloadAction + { payload: { chainId } }: PayloadAction, ): asserts state is Required { const stateAtChainId = state[chainId] @@ -174,7 +174,7 @@ function addOrderToState( id: string, status: OrderTypeKeys, order: SerializedOrder, - isSafeWallet: boolean + isSafeWallet: boolean, ): void { // Attempt to fix `TypeError: Cannot add property , object is not extensible` // seen on https://user-images.githubusercontent.com/34510341/138450105-bb94a2d1-656e-4e15-ae99-df9fb33c8ca4.png @@ -200,7 +200,7 @@ function cancelOrderInState( state: Required, chainId: ChainId, orderObject: OrderObject, - isSafeWallet: boolean + isSafeWallet: boolean, ) { const id = orderObject.id @@ -368,12 +368,13 @@ export default createReducer(initialState, (builder) => orderObject.order.apiAdditionalInfo = { creationDate: order.creationDate, - availableBalance: order.availableBalance, executedBuyAmount: order.executedBuyAmount, executedSellAmount: order.executedSellAmount, executedSellAmountBeforeFees: order.executedSellAmountBeforeFees, executedFeeAmount: order.executedFeeAmount, - executedSurplusFee: order.executedSurplusFee, + executedFee: order.executedFee, + executedFeeToken: order.executedFeeToken, + totalFee: order.totalFee, invalidated: order.invalidated, ethflowData: order.ethflowData, onchainOrderData: order.onchainOrderData, @@ -458,7 +459,7 @@ export default createReducer(initialState, (builder) => const allOrdersMap = flatOrdersStateNetwork(state[chainId]) const children = Object.values(allOrdersMap).filter( - (item) => item?.order.composableCowInfo?.parentId === id + (item) => item?.order.composableCowInfo?.parentId === id, ) children.forEach((child) => { @@ -544,12 +545,12 @@ export default createReducer(initialState, (builder) => orderListByChain[status] = ordersCleaned }) }) - }) + }), ) function reClassifyOrder( newOrder: SerializedOrder, - existingOrder: OrderObject | undefined + existingOrder: OrderObject | undefined, ): { status: OrderStatus; isCancelling: boolean | undefined } { // Onchain cancellations are considered final // Still, the order classification at apps/cowswap-frontend/src/legacy/state/orders/utils.ts can't tell diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/ReceiptModal/FeeField.tsx b/apps/cowswap-frontend/src/modules/ordersTable/pure/ReceiptModal/FeeField.tsx index eed9c96389..4e3edc6e8b 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/ReceiptModal/FeeField.tsx +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/ReceiptModal/FeeField.tsx @@ -7,15 +7,34 @@ import * as styledEl from './styled' export type Props = { order: ParsedOrder } +// TODO: get rid of this once https://github.com/cowprotocol/services/pull/3184 is complete +const HAS_BACKEND_MIGRATED = false + +function getFeeToken(order: ParsedOrder) { + if (!HAS_BACKEND_MIGRATED) { + return order.inputToken + } + + const { inputToken, outputToken } = order + const { executedFeeToken } = order.executionData + + if (inputToken?.address.toLowerCase() === executedFeeToken?.toLowerCase()) { + return inputToken + } + if (outputToken?.address.toLowerCase() === executedFeeToken?.toLowerCase()) { + return outputToken + } + return undefined +} + export function FeeField({ order }: Props): JSX.Element | null { - const { inputToken } = order - const { executedFeeAmount, executedSurplusFee } = order.executionData + const { totalFee } = order.executionData + const feeToken = getFeeToken(order) - if (!inputToken) return + if (!feeToken) return - // TODO: use the value from SDK - const totalFee = CurrencyAmount.fromRawAmount(inputToken, (executedSurplusFee ?? executedFeeAmount) || 0) - const quoteSymbol = inputToken.symbol + const totalFeeAmount = CurrencyAmount.fromRawAmount(feeToken, totalFee || 0) + const quoteSymbol = feeToken.symbol return ( @@ -23,7 +42,7 @@ export function FeeField({ order }: Props): JSX.Element | null { - ) : ( - + )} diff --git a/apps/cowswap-frontend/src/utils/orderUtils/parseOrder.ts b/apps/cowswap-frontend/src/utils/orderUtils/parseOrder.ts index 8031e391f3..f6a3d0b4e2 100644 --- a/apps/cowswap-frontend/src/utils/orderUtils/parseOrder.ts +++ b/apps/cowswap-frontend/src/utils/orderUtils/parseOrder.ts @@ -24,7 +24,9 @@ export interface ParsedOrderExecutionData { surplusAmount: BigNumber surplusPercentage: BigNumber executedFeeAmount: string | undefined - executedSurplusFee: string | null + executedFee: string | null + executedFeeToken: string | null + totalFee: string | null filledPercentDisplay: string executedPrice: Price | null activityId: string | undefined @@ -60,7 +62,9 @@ export const parseOrder = (order: Order): ParsedOrder => { const { executedBuyAmount, executedSellAmount } = getOrderExecutedAmounts(order) const expirationTime = new Date(Number(order.validTo) * 1000) const executedFeeAmount = order.apiAdditionalInfo?.executedFeeAmount - const executedSurplusFee = order.apiAdditionalInfo?.executedSurplusFee || null + const executedFee = order.apiAdditionalInfo?.executedFee || null + const executedFeeToken = order.apiAdditionalInfo?.executedFeeToken || null + const totalFee = order.apiAdditionalInfo?.totalFee || null const creationTime = new Date(order.creationTime) const fullyFilled = isOrderFilled(order) const partiallyFilled = isPartiallyFilled(order) @@ -80,6 +84,7 @@ export const parseOrder = (order: Order): ParsedOrder => { const activityTitle = showCreationTxLink ? 'Creation transaction' : 'Order ID' const executionData: ParsedOrderExecutionData = { + executedFeeToken, executedBuyAmount, executedSellAmount, filledAmount, @@ -88,7 +93,8 @@ export const parseOrder = (order: Order): ParsedOrder => { surplusAmount, surplusPercentage, executedFeeAmount, - executedSurplusFee, + executedFee, + totalFee, executedPrice, fullyFilled, partiallyFilled, From 1c5417add3ea1d73abd1aecab181b6c2b370feba Mon Sep 17 00:00:00 2001 From: Alfetopito Date: Mon, 30 Dec 2024 17:35:24 +0000 Subject: [PATCH 3/5] feat: do same as previous, but on Explorer --- apps/explorer/src/api/operator/types.ts | 14 +++++++++++--- apps/explorer/src/test/data/operator.ts | 4 ++-- apps/explorer/src/utils/operator.ts | 4 ++-- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/apps/explorer/src/api/operator/types.ts b/apps/explorer/src/api/operator/types.ts index 8ef8bf3dba..54240f124e 100644 --- a/apps/explorer/src/api/operator/types.ts +++ b/apps/explorer/src/api/operator/types.ts @@ -18,7 +18,15 @@ export type RawOrder = EnrichedOrder */ export type Order = Pick< RawOrder, - 'owner' | 'uid' | 'appData' | 'kind' | 'partiallyFillable' | 'signature' | 'class' | 'fullAppData' + | 'owner' + | 'uid' + | 'appData' + | 'kind' + | 'partiallyFillable' + | 'signature' + | 'class' + | 'fullAppData' + | 'executedFeeToken' > & { receiver: string txHash?: string @@ -35,7 +43,7 @@ export type Order = Pick< executedSellAmount: BigNumber feeAmount: BigNumber executedFeeAmount: BigNumber - executedSurplusFee: BigNumber | null + executedFee: BigNumber | null totalFee: BigNumber cancelled: boolean status: OrderStatus @@ -60,7 +68,7 @@ export type Trade = Pick Date: Tue, 31 Dec 2024 09:59:39 +0000 Subject: [PATCH 4/5] refactor: moved getFeeToken out and improved logic as suggested --- .../pure/OrdersTableContainer/orders.mock.ts | 13 +++++++++++ .../pure/ReceiptModal/FeeField.tsx | 22 ++----------------- .../modules/ordersTable/utils/getFeeToken.ts | 14 ++++++++++++ 3 files changed, 29 insertions(+), 20 deletions(-) create mode 100644 apps/cowswap-frontend/src/modules/ordersTable/utils/getFeeToken.ts diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/orders.mock.ts b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/orders.mock.ts index 46c3abdb6d..b6d8259577 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/orders.mock.ts +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/OrdersTableContainer/orders.mock.ts @@ -100,6 +100,19 @@ export const ordersMock: ParsedOrder[] = [ signingScheme: SigningScheme.EIP712, class: OrderClass.MARKET, kind: OrderKind.SELL, + apiAdditionalInfo: { + executedFeeAmount: '1', + executedFee: '1', + executedFeeToken: USDC[chainId].address, + totalFee: '1', + creationDate: '2022-11-11T13:15:13.551Z', + executedBuyAmount: '23000000000000', + executedSellAmount: '5000300000000000', + executedSellAmountBeforeFees: '5000300000000000', + invalidated: false, + class: OrderClass.LIMIT, + signingScheme: SigningScheme.EIP712, + }, }, { id: '5', diff --git a/apps/cowswap-frontend/src/modules/ordersTable/pure/ReceiptModal/FeeField.tsx b/apps/cowswap-frontend/src/modules/ordersTable/pure/ReceiptModal/FeeField.tsx index 4e3edc6e8b..4d348db3ff 100644 --- a/apps/cowswap-frontend/src/modules/ordersTable/pure/ReceiptModal/FeeField.tsx +++ b/apps/cowswap-frontend/src/modules/ordersTable/pure/ReceiptModal/FeeField.tsx @@ -1,32 +1,14 @@ import { TokenAmount } from '@cowprotocol/ui' import { CurrencyAmount } from '@uniswap/sdk-core' +import { getFeeToken } from 'modules/ordersTable/utils/getFeeToken' + import { ParsedOrder } from 'utils/orderUtils/parseOrder' import * as styledEl from './styled' export type Props = { order: ParsedOrder } -// TODO: get rid of this once https://github.com/cowprotocol/services/pull/3184 is complete -const HAS_BACKEND_MIGRATED = false - -function getFeeToken(order: ParsedOrder) { - if (!HAS_BACKEND_MIGRATED) { - return order.inputToken - } - - const { inputToken, outputToken } = order - const { executedFeeToken } = order.executionData - - if (inputToken?.address.toLowerCase() === executedFeeToken?.toLowerCase()) { - return inputToken - } - if (outputToken?.address.toLowerCase() === executedFeeToken?.toLowerCase()) { - return outputToken - } - return undefined -} - export function FeeField({ order }: Props): JSX.Element | null { const { totalFee } = order.executionData const feeToken = getFeeToken(order) diff --git a/apps/cowswap-frontend/src/modules/ordersTable/utils/getFeeToken.ts b/apps/cowswap-frontend/src/modules/ordersTable/utils/getFeeToken.ts new file mode 100644 index 0000000000..a41a094bd4 --- /dev/null +++ b/apps/cowswap-frontend/src/modules/ordersTable/utils/getFeeToken.ts @@ -0,0 +1,14 @@ +import { ParsedOrder } from 'utils/orderUtils/parseOrder' + +export function getFeeToken(order: ParsedOrder) { + const { inputToken, outputToken } = order + const { executedFeeToken } = order.executionData + + const feeTokenAddress = executedFeeToken?.toLowerCase() + + if (!feeTokenAddress) { + return inputToken + } + + return [inputToken, outputToken].find((token) => token?.address.toLowerCase() === feeTokenAddress) +} From 4e351ceb1eddeb63fa76adc831dbdabd1542e0a1 Mon Sep 17 00:00:00 2001 From: Alfetopito Date: Tue, 31 Dec 2024 09:59:46 +0000 Subject: [PATCH 5/5] test: add unit tests --- .../ordersTable/utils/getFeeToken.test.ts | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 apps/cowswap-frontend/src/modules/ordersTable/utils/getFeeToken.test.ts diff --git a/apps/cowswap-frontend/src/modules/ordersTable/utils/getFeeToken.test.ts b/apps/cowswap-frontend/src/modules/ordersTable/utils/getFeeToken.test.ts new file mode 100644 index 0000000000..0eba3524c7 --- /dev/null +++ b/apps/cowswap-frontend/src/modules/ordersTable/utils/getFeeToken.test.ts @@ -0,0 +1,37 @@ +import { getFeeToken } from './getFeeToken' // Adjust the import path as necessary + +import { ordersMock } from '../pure/OrdersTableContainer/orders.mock' + +const BASE_ORDER = ordersMock[3] + +describe('getFeeToken', () => { + it("should return inputToken when that's the fee token", () => { + const input = BASE_ORDER + const expectedOutput = BASE_ORDER.inputToken + + const result = getFeeToken(input) + + expect(result).toEqual(expectedOutput) + }) + + it("should return outputToken when that's the fee token", () => { + const input = { + ...BASE_ORDER, + executionData: { ...BASE_ORDER.executionData, executedFeeToken: BASE_ORDER.outputToken.address }, + } + const expectedOutput = BASE_ORDER.outputToken + + const result = getFeeToken(input) + + expect(result).toEqual(expectedOutput) + }) + + it('should return inputToken when there is no fee token', () => { + const input = { ...BASE_ORDER, executionData: { ...BASE_ORDER.executionData, executedFeeToken: null } } + const expectedOutput = BASE_ORDER.inputToken + + const result = getFeeToken(input) + + expect(result).toEqual(expectedOutput) + }) +})