Skip to content

Commit

Permalink
fix(orders-table): display fulfilled status before cancelling (#2770)
Browse files Browse the repository at this point in the history
  • Loading branch information
shoom3301 authored Jul 7, 2023
1 parent dc1c2aa commit bd9838b
Show file tree
Hide file tree
Showing 25 changed files with 311 additions and 170 deletions.
2 changes: 1 addition & 1 deletion src/common/pure/SafeWalletLink/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ export function SafeWalletLink(props: {
return null
}

return <ExternalLink href={safeUrl}>View Safe ↗</ExternalLink>
return <ExternalLink href={safeUrl}>View on Safe ↗</ExternalLink>
}
1 change: 1 addition & 0 deletions src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { OrderStatus } from 'legacy/state/orders/actions'
export type ComposableCowInfo = {
id?: string
parentId?: string
isVirtualPart?: boolean
status: OrderStatus
}

Expand Down
55 changes: 0 additions & 55 deletions src/legacy/state/claim/middleware.ts

This file was deleted.

3 changes: 3 additions & 0 deletions src/legacy/state/orders/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ export interface UpdatePresignGnosisSafeTxParams {
export type ExpireOrdersBatchParams = BatchOrdersUpdateParams
export type InvalidateOrdersBatchParams = BatchOrdersUpdateParams
export type CancelOrdersBatchParams = BatchOrdersUpdateParams
export type DeleteOrdersParams = BatchOrdersUpdateParams

export const addOrUpdateOrders = createAction<AddOrUpdateOrdersParams>('order/addOrUpdateOrders')

Expand All @@ -198,6 +199,8 @@ export const requestOrderCancellation = createAction<ChangeOrderStatusParams>('o

export const cancelOrdersBatch = createAction<CancelOrdersBatchParams>('order/cancelOrdersBatch')

export const deleteOrders = createAction<DeleteOrdersParams>('order/deleteOrders')

export const clearOrders = createAction<{ chainId: ChainId }>('order/clearOrders')

export const updateLastCheckedBlock = createAction<{ chainId: ChainId; lastCheckedBlock: number }>(
Expand Down
3 changes: 2 additions & 1 deletion src/legacy/state/orders/helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export function getOrderByIdFromState(orders: OrdersStateNetwork | undefined, id
return
}

const { pending, presignaturePending, fulfilled, expired, cancelled, creating, failed } = orders
const { pending, presignaturePending, fulfilled, expired, cancelled, creating, failed, scheduled } = orders

return (
pending?.[id] ||
Expand All @@ -116,6 +116,7 @@ export function getOrderByIdFromState(orders: OrdersStateNetwork | undefined, id
expired?.[id] ||
cancelled?.[id] ||
creating?.[id] ||
scheduled?.[id] ||
failed?.[id]
)
}
1 change: 1 addition & 0 deletions src/legacy/state/orders/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export const useOrder = ({ id, chainId }: Partial<GetRemoveOrderParams>): Order
orders?.presignaturePending[id] ||
orders?.cancelled[id] ||
orders?.creating[id] ||
orders?.scheduled[id] ||
orders?.failed[id]

return _deserializeOrder(serialisedOrder)
Expand Down
27 changes: 19 additions & 8 deletions src/legacy/state/orders/middleware/updateOrderPopup.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { OrderClass } from '@cowprotocol/cow-sdk'

import { MiddlewareAPI } from '@reduxjs/toolkit'
import { Dispatch } from 'redux'

Expand All @@ -17,13 +19,22 @@ export function updateOrderPopup(store: MiddlewareAPI<Dispatch, AppState>, paylo
// This was a presign order created hidden
// Trigger the popup if order is no longer hidden
if (!order.isHidden && orderObject) {
const popup = setPopupData(OrderTxTypes.METATXN, {
summary: orderObject.order.summary,
status: 'submitted',
id: order.id,
})
orderAnalytics('Posted', orderObject.order.class, 'Presign')

store.dispatch(addPopup(popup))
dispatchPresignedOrderPosted(store, order.id, orderObject.order.summary, orderObject.order.class)
}
}

export function dispatchPresignedOrderPosted(
store: MiddlewareAPI<Dispatch>,
orderId: string,
summary: string,
orderClass: OrderClass
) {
const popup = setPopupData(OrderTxTypes.METATXN, {
summary,
status: 'submitted',
id: orderId,
})
orderAnalytics('Posted', orderClass, 'Presign')

store.dispatch(addPopup(popup))
}
60 changes: 54 additions & 6 deletions src/legacy/state/orders/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import { createReducer, PayloadAction } from '@reduxjs/toolkit'
import { Writable } from 'types'

import { OrderID } from 'api/gnosisProtocol'
import { getIsComposableCowChildOrder } from 'utils/orderUtils/getIsComposableCowChildOrder'
import { getIsComposableCowParentOrder } from 'utils/orderUtils/getIsComposableCowParentOrder'

import {
addOrUpdateOrders,
addPendingOrder,
cancelOrdersBatch,
clearOrders,
CREATING_STATES,
deleteOrders,
expireOrdersBatch,
fulfillOrdersBatch,
invalidateOrdersBatch,
Expand Down Expand Up @@ -145,6 +148,7 @@ function getOrderById(state: Required<OrdersState>, chainId: ChainId, id: string
stateForChain.expired[id] ||
stateForChain.fulfilled[id] ||
stateForChain.creating[id] ||
stateForChain.scheduled[id] ||
stateForChain.failed[id]
)
}
Expand All @@ -158,6 +162,7 @@ function deleteOrderById(state: Required<OrdersState>, chainId: ChainId, id: str
delete stateForChain.cancelled[id]
delete stateForChain.creating[id]
delete stateForChain.failed[id]
delete stateForChain.scheduled[id]
}

function addOrderToState(
Expand Down Expand Up @@ -187,6 +192,17 @@ function getValidTo(apiAdditionalInfo: OrderInfoApi | undefined, order: Serializ
return (apiAdditionalInfo?.ethflowData?.userValidTo || order.validTo) as number
}

function cancelOrderInState(state: Required<OrdersState>, chainId: ChainId, orderObject: OrderObject) {
const id = orderObject.id

deleteOrderById(state, chainId, id)

orderObject.order.status = OrderStatus.CANCELLED
orderObject.order.isCancelling = false

addOrderToState(state, chainId, id, 'cancelled', orderObject.order)
}

const initialState: OrdersState = {}

export default createReducer(initialState, (builder) =>
Expand Down Expand Up @@ -262,18 +278,22 @@ export default createReducer(initialState, (builder) =>
popOrder(state, chainId, OrderStatus.FAILED, id)

const validTo = getValidTo(newOrder.apiAdditionalInfo, newOrder)
const isComposableCowOrder = !!newOrder?.composableCowInfo
const isComposableParentOrder = getIsComposableCowParentOrder(newOrder)
const isComposableChildOrder = getIsComposableCowChildOrder(newOrder)

// merge existing and new order objects
const order = orderObj
? {
...orderObj.order,
...(isComposableChildOrder ? newOrder : {}),
validTo,
creationTime: newOrder.creationTime,
composableCowInfo: newOrder.composableCowInfo,
apiAdditionalInfo: newOrder.apiAdditionalInfo,
// Don't reset isCancelling status for ComposableCow orders
// Because currently we don't have a backend for it
// And this status is stored only on Frontend
isCancelling: isComposableCowOrder ? !!orderObj.order.isCancelling : newOrder.isCancelling,
isCancelling: isComposableParentOrder ? !!orderObj.order.isCancelling : newOrder.isCancelling,
class: newOrder.class,
openSince: newOrder.openSince || orderObj.order.openSince,
status,
Expand Down Expand Up @@ -387,12 +407,31 @@ export default createReducer(initialState, (builder) =>
const orderObject = getOrderById(state, chainId, id)

if (orderObject) {
deleteOrderById(state, chainId, id)
cancelOrderInState(state, chainId, orderObject)

if (getIsComposableCowParentOrder(orderObject.order)) {
const ordersMap = state[chainId]
const allOrdersMap = {
...ordersMap.pending,
...ordersMap.presignaturePending,
...ordersMap.fulfilled,
...ordersMap.expired,
...ordersMap.cancelled,
...ordersMap.creating,
...ordersMap.failed,
...ordersMap.scheduled,
}

orderObject.order.status = OrderStatus.CANCELLED
orderObject.order.isCancelling = false
const children = Object.values(allOrdersMap).filter(
(item) => item?.order.composableCowInfo?.parentId === id
)

children.forEach((child) => {
if (!child) return

addOrderToState(state, chainId, id, 'cancelled', orderObject.order)
cancelOrderInState(state, chainId, child)
})
}
}
})
})
Expand Down Expand Up @@ -436,4 +475,13 @@ export default createReducer(initialState, (builder) =>
}
})
})
.addCase(deleteOrders, (state, action) => {
prefillState(state, action)

const { chainId, ids } = action.payload

ids.forEach((id) => {
deleteOrderById(state, chainId, id)
})
})
)
21 changes: 18 additions & 3 deletions src/legacy/state/orders/updaters/GpOrdersUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import { useWalletInfo } from 'modules/wallet'

import { useGpOrders } from 'api/gnosisProtocol/hooks'
import { OrderWithComposableCowInfo } from 'common/types'
import { getIsComposableCowChildOrder } from 'utils/orderUtils/getIsComposableCowChildOrder'

type OrderWithComposableCowInfoMap = { [id: string]: OrderWithComposableCowInfo }

function _getTokenFromMapping(
address: string,
Expand Down Expand Up @@ -43,7 +46,8 @@ const statusMapping: Record<OrderTransitionStatus, OrderStatus | undefined> = {
function _transformGpOrderToStoreOrder(
{ order, composableCowInfo }: OrderWithComposableCowInfo,
chainId: ChainId,
allTokens: { [address: string]: Token | null }
allTokens: { [address: string]: Token | null },
ordersMap: OrderWithComposableCowInfoMap
): Order | undefined {
const {
uid: id,
Expand All @@ -58,6 +62,9 @@ function _transformGpOrderToStoreOrder(
// Hack, because Swagger doesn't have isRefunded property and backend is going to delete it soon
const ethflowData: (EthflowData & { isRefunded?: boolean }) | undefined = ethflowDataRaw

const isComposableCowChildOrder = getIsComposableCowChildOrder({ composableCowInfo })
const parentOrder = isComposableCowChildOrder ? ordersMap[composableCowInfo!.parentId!] : null

const isEthFlow = Boolean(ethflowData)

const inputToken = _getInputToken(isEthFlow, chainId, sellToken, allTokens)
Expand All @@ -79,6 +86,8 @@ function _transformGpOrderToStoreOrder(
return
}

const isCancelling = (apiStatus === 'pending' && order.invalidated) || parentOrder?.order.invalidated

const storeOrder: Order = {
...order,
// TODO: for some reason executedSellAmountBeforeFees is zero for limit-orders
Expand All @@ -91,7 +100,7 @@ function _transformGpOrderToStoreOrder(
status,
receiver: receiver || '',
apiAdditionalInfo: order,
isCancelling: apiStatus === 'pending' && order.invalidated, // already cancelled in the API, not yet in the UI
isCancelling,
// EthFlow related
owner: onchainOrderData?.sender || owner,
validTo: ethflowData?.userValidTo || order.validTo,
Expand Down Expand Up @@ -166,8 +175,14 @@ function _filterOrders(
tokens: Record<string, Token | null>,
chainId: ChainId
): Order[] {
const ordersMap = orders.reduce<OrderWithComposableCowInfoMap>((acc, val) => {
acc[val.order.uid] = val

return acc
}, {})

return orders.reduce<Order[]>((acc, order) => {
const storeOrder = _transformGpOrderToStoreOrder(order, chainId, tokens)
const storeOrder = _transformGpOrderToStoreOrder(order, chainId, tokens, ordersMap)
if (storeOrder) {
acc.push(storeOrder)
}
Expand Down
2 changes: 1 addition & 1 deletion src/legacy/state/orders/updaters/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export async function fetchOrderPopupData(orderFromStore: Order, chainId: ChainI
return null
}
// Skip ComposableCow part orders
if (orderFromStore.composableCowInfo?.status) {
if (orderFromStore.composableCowInfo?.isVirtualPart) {
return null
}
// Get order from API
Expand Down
24 changes: 11 additions & 13 deletions src/legacy/utils/trade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,16 @@ export type UnsignedOrderAdditionalParams = PostOrderParams & {
orderCreationHash?: string
}

function _getSummary(params: PostOrderParams): string {
const {
kind,
account,
inputAmount,
outputAmount,
recipient,
recipientAddressOrName,
feeAmount,
sellToken,
buyToken,
} = params
export function getOrderSubmitSummary(
params: Pick<
PostOrderParams,
'kind' | 'account' | 'inputAmount' | 'outputAmount' | 'recipient' | 'recipientAddressOrName' | 'feeAmount'
>
): string {
const { kind, account, inputAmount, outputAmount, recipient, recipientAddressOrName, feeAmount } = params

const sellToken = inputAmount.currency
const buyToken = outputAmount.currency

const [inputQuantifier, outputQuantifier] = [
kind === OrderKind.BUY ? 'at most ' : '',
Expand Down Expand Up @@ -117,7 +115,7 @@ export function getOrderParams(params: PostOrderParams): {
const buyAmount = outputAmount.quotient.toString(RADIX_DECIMAL)

// Prepare order
const summary = _getSummary(params)
const summary = getOrderSubmitSummary(params)
const receiver = recipient

return {
Expand Down
Loading

0 comments on commit bd9838b

Please sign in to comment.