Skip to content
This repository has been archived by the owner on Feb 2, 2024. It is now read-only.

Execution time for trades table #425

Merged
merged 12 commits into from
Apr 6, 2023
2 changes: 1 addition & 1 deletion src/api/operator/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export type Trade = Pick<RawTrade, 'blockNumber' | 'logIndex' | 'owner' | 'txHas
buyTokenAddress: string
sellToken?: TokenErc20 | null
sellTokenAddress: string
executionTime: Date
executionTime: Date | null
surplusAmount?: BigNumber
surplusPercentage?: BigNumber
}
Expand Down
12 changes: 8 additions & 4 deletions src/components/orders/OrderDetails/FillsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import Icon from 'components/Icon'
import { calculatePrice, TokenErc20 } from '@gnosis.pm/dex-js'
import { TEN_BIG_NUMBER } from 'const'
import BigNumber from 'bignumber.js'
import ShimmerBar from 'apps/explorer/components/common/ShimmerBar'

const Wrapper = styled(StyledUserDetailsTable)`
> thead {
Expand Down Expand Up @@ -63,7 +64,7 @@ const Wrapper = styled(StyledUserDetailsTable)`
}
> thead > tr,
> tbody > tr {
grid-template-columns: 4fr 2fr 3fr 3fr 3fr 4fr 4fr;
grid-template-columns: 4fr 2fr 3fr 3fr 3.5fr 3fr 4fr;
}
> tbody > tr > td:nth-child(8),
> thead > tr > th:nth-child(8) {
Expand Down Expand Up @@ -182,6 +183,11 @@ const StyledLinkButton = styled(LinkButton)`
}
`

const StyledShimmerBar = styled(ShimmerBar)`
min-height: 20px;
min-width: 100px;
`

export type Props = StyledUserDetailsTableProps & {
trades: Trade[] | undefined
order: Order | null
Expand Down Expand Up @@ -224,8 +230,6 @@ const RowFill: React.FC<RowProps> = ({ trade, isPriceInversed }) => {
const buyToken = tokens[buyTokenAddress]
const sellToken = tokens[sellTokenAddress]

const executionTimeFormatted =
executionTime instanceof Date && !isNaN(Date.parse(executionTime.toString())) ? executionTime : new Date()
const executionPrice = calculateExecutionPrice(isPriceInversed, sellAmount, buyAmount, sellToken, buyToken)
const executionToken = isPriceInversed ? buyToken : sellToken

Expand Down Expand Up @@ -272,7 +276,7 @@ const RowFill: React.FC<RowProps> = ({ trade, isPriceInversed }) => {
</td>
<td>
<HeaderTitle>Execution time</HeaderTitle>
<HeaderValue>{<DateDisplay date={executionTimeFormatted} showIcon={true} />}</HeaderValue>
<HeaderValue>{executionTime ? <DateDisplay date={executionTime} showIcon={true} /> : <StyledShimmerBar />}</HeaderValue>
</td>
<td>
<HeaderTitle></HeaderTitle>
Expand Down
110 changes: 54 additions & 56 deletions src/hooks/useOperatorTrades.ts
Original file line number Diff line number Diff line change
@@ -1,68 +1,33 @@
import { Trade as TradeMetaData } from '@cowprotocol/cow-sdk'
import { getTrades, Order, Trade } from 'api/operator'
import { getTrades, Order, RawTrade, Trade } from 'api/operator'
import { useCallback, useEffect, useState } from 'react'
import { useNetworkId } from 'state/network'
import { Network, UiError } from 'types'
import { transformTrade } from 'utils'

type Params = {
owner?: string
orderId?: string
}
import { web3 } from 'apps/explorer/api'

type Result = {
trades: Trade[]
error?: UiError
isLoading: boolean
}

/**
* Fetches trades for given filters
* When no filter is given, fetches all trades for current network
*/
export function useTrades(params: Params): Result {
const { owner, orderId } = params
type TradesTimestamps = { [txHash: string]: number }

const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<UiError>()
const [trades, setTrades] = useState<Trade[]>([])
async function fetchTradesTimestamps(rawTrades: RawTrade[]): Promise<TradesTimestamps> {
const requests = rawTrades.map(({ txHash, blockNumber }) => {
return web3.eth.getBlock(blockNumber).then(res => ({
txHash,
timestamp: +res.timestamp
}))
})
shoom3301 marked this conversation as resolved.
Show resolved Hide resolved

// Here we assume that we are already in the right network
// contrary to useOrder hook, where it searches all networks for a given orderId
const networkId = useNetworkId()

const fetchTrades = useCallback(async (networkId: Network, owner?: string, orderId?: string): Promise<void> => {
setIsLoading(true)

try {
let trades: TradeMetaData[] = []
if (orderId) {
trades = await getTrades({ networkId, orderId })
} else if (owner) {
trades = await getTrades({ networkId, owner })
}

// TODO: fetch buy/sellToken objects
setTrades(trades.map((trade) => transformTrade(trade)))
setError(undefined)
} catch (e) {
const msg = `Failed to fetch trades`
console.error(msg, e)
setError({ message: msg, type: 'error' })
} finally {
setIsLoading(false)
}
}, [])

useEffect(() => {
if (!networkId) {
return
}
const data = await Promise.all(requests)

fetchTrades(networkId, owner, orderId)
}, [fetchTrades, networkId, orderId, owner])
return data.reduce((acc, val) => {
if (val.txHash) acc[val.txHash] = val.timestamp

return { trades, error, isLoading }
return acc
}, {} as TradesTimestamps)
}

/**
Expand All @@ -72,44 +37,77 @@ export function useOrderTrades(order: Order | null): Result {
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<UiError>()
const [trades, setTrades] = useState<Trade[]>([])
const [rawTrades, setRawTrades] = useState<RawTrade[]>([])
const [tradesTimestamps, setTradesTimestamps] = useState<TradesTimestamps>({})

// Here we assume that we are already in the right network
// contrary to useOrder hook, where it searches all networks for a given orderId
const networkId = useNetworkId()

const fetchTrades = useCallback(
async (controller: AbortController, _networkId: Network, _order: Order): Promise<void> => {
async (controller: AbortController, _networkId: Network): Promise<void> => {
if (!order) return

setIsLoading(true)

const { uid: orderId, buyToken, sellToken } = _order
const { uid: orderId } = order

try {
const _trades = await getTrades({ networkId: _networkId, orderId })
const trades = await getTrades({ networkId: _networkId, orderId })

if (controller.signal.aborted) return

setTrades(_trades.map((trade) => ({ ...transformTrade(trade), buyToken, sellToken })))
setRawTrades(trades)
setError(undefined)
} catch (e) {
const msg = `Failed to fetch trades`
console.error(msg, e)

setError({ message: msg, type: 'error' })
} finally {
setIsLoading(false)
}
},
[],
[order],
)

// Fetch blocks timestamps for trades
useEffect(() => {
setTradesTimestamps({})

fetchTradesTimestamps(rawTrades).then(setTradesTimestamps).catch(error => {
shoom3301 marked this conversation as resolved.
Show resolved Hide resolved
console.error('Trades timestamps fetching error: ', error)

setTradesTimestamps({})
})
}, [rawTrades])

// Transform trades adding tokens and timestamps
useEffect(() => {
if (!order) return

const { buyToken, sellToken } = order

const trades = rawTrades.map((trade) => {
const timestamp = trade.txHash ? tradesTimestamps[trade.txHash] : undefined

return { ...transformTrade(trade, timestamp), buyToken, sellToken }
})

setTrades(trades)
}, [order, rawTrades, tradesTimestamps])

const executedSellAmount = order?.executedSellAmount.toString()
const executedBuyAmount = order?.executedBuyAmount.toString()

useEffect(() => {
if (!networkId || !order?.uid) {
return
}

const controller = new AbortController()

fetchTrades(controller, networkId, order)
fetchTrades(controller, networkId)
return (): void => controller.abort()
// Depending on order UID to avoid re-fetching when obj changes but ID remains the same
// Depending on `executedBuy/SellAmount`s string to force a refetch when there are new trades
Expand Down
7 changes: 3 additions & 4 deletions src/utils/operator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,21 +351,20 @@ export function transformOrder(rawOrder: RawOrder): Order {
filledPercentage,
surplusAmount,
surplusPercentage,
}
} as Order
}

/**
* Transforms a RawTrade into a Trade object
*/
export function transformTrade(rawTrade: TradeMetaData & { executionTime?: string }): Trade {
export function transformTrade(rawTrade: TradeMetaData, executionTimestamp?: number): Trade {
const {
orderUid,
buyAmount,
sellAmount,
sellAmountBeforeFees,
buyToken,
sellToken,
executionTime = '',
...rest
} = rawTrade

Expand All @@ -377,6 +376,6 @@ export function transformTrade(rawTrade: TradeMetaData & { executionTime?: strin
sellAmountBeforeFees: new BigNumber(sellAmountBeforeFees),
buyTokenAddress: buyToken,
sellTokenAddress: sellToken,
executionTime: new Date(executionTime) || null,
executionTime: executionTimestamp ? new Date(executionTimestamp * 1000) : null,
}
}