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

[Batch Viewer] Show the receiver #120

Merged
merged 4 commits into from
Jun 22, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ export default class ElementsBuilder {
this._countEdgeDirection.set(idDirection, count + 1)
}

_createNodeElement = (node: Node, parent?: string): ElementDefinition => {
_createNodeElement = (node: Node, parent?: string, hideLabel?: boolean): ElementDefinition => {
this._increaseCountNodeType(node.type)
return {
group: 'nodes',
data: {
id: `${node.type}:${node.id}`,
label: node.entity.alias,
label: !hideLabel ? node.entity.alias : '',
type: node.type,
parent: parent ? `${TypeNodeOnTx.NetworkNode}:${parent}` : undefined,
},
Expand All @@ -45,7 +45,8 @@ export default class ElementsBuilder {
}

node(node: Node, parent?: string): this {
this._nodes.push(this._createNodeElement(node, parent))
const GROUP_NODE_NAME = 'group'
this._nodes.push(this._createNodeElement(node, parent, node.id.includes(GROUP_NODE_NAME)))
return this
}

Expand Down
30 changes: 27 additions & 3 deletions src/apps/explorer/components/TransanctionBatchGraph/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ const WrapperCytoscape = styled(CytoscapeComponent)`
`
const iconDice = [faDiceOne, faDiceTwo, faDiceThree, faDiceFour, faDiceFive]

function getTypeNode(account: Account): TypeNodeOnTx {
function getTypeNode(account: Account & { owner?: string }): TypeNodeOnTx {
let type = TypeNodeOnTx.Dex
if (account.alias === ALIAS_TRADER_NAME) {
if (account.alias === ALIAS_TRADER_NAME || account.owner) {
type = TypeNodeOnTx.Trader
} else if (account.alias === PROTOCOL_NAME) {
type = TypeNodeOnTx.CowProtocol
Expand Down Expand Up @@ -98,13 +98,37 @@ function getNodes(
const builder = new ElementsBuilder(heightSize)
builder.node({ type: TypeNodeOnTx.NetworkNode, entity: networkNode, id: networkNode.alias })

const groupNodes: string[] = []
for (const key in txSettlement.accounts) {
const account = txSettlement.accounts[key]
const parentNodeName = getNetworkParentNode(account, networkNode.alias)
let parentNodeName = getNetworkParentNode(account, networkNode.alias)

const receiverNode = { alias: `${abbreviateString(account.owner || key, 4, 4)}-group` }

if (account.owner && account.owner !== key) {
if (!groupNodes.includes(receiverNode.alias)) {
builder.node({ type: TypeNodeOnTx.NetworkNode, entity: receiverNode, id: receiverNode.alias })
groupNodes.push(receiverNode.alias)
}
parentNodeName = receiverNode.alias
}

if (getTypeNode(account) === TypeNodeOnTx.CowProtocol) {
builder.center({ type: TypeNodeOnTx.CowProtocol, entity: account, id: key }, parentNodeName)
} else {
const receivers = Object.keys(txSettlement.accounts).reduce(
(acc, key) => (txSettlement.accounts?.[key].owner ? [...acc, txSettlement.accounts?.[key].owner] : acc),
[],
)

if (receivers.includes(key) && account.owner !== key) {
if (!groupNodes.includes(receiverNode.alias)) {
builder.node({ type: TypeNodeOnTx.NetworkNode, entity: receiverNode, id: receiverNode.alias })
groupNodes.push(receiverNode.alias)
}
parentNodeName = receiverNode.alias
}

builder.node(
{
id: key,
Expand Down
84 changes: 67 additions & 17 deletions src/hooks/useTxBatchTrades.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useState, useCallback, useEffect } from 'react'

import { Network } from 'types'
import { getTradesAccount, getTradesAndTransfers, Trade, Transfer, Account } from 'api/tenderly'
import { getTradesAccount, getTradesAndTransfers, Trade, Transfer, Account, ALIAS_TRADER_NAME } from 'api/tenderly'
import { useMultipleErc20 } from './useErc20'
import { SingleErc20State } from 'state/erc20'
import { Order } from 'api/operator'
import BigNumber from 'bignumber.js'
import { usePrevious } from './usePrevious'

interface TxBatchTrades {
Expand All @@ -14,7 +15,8 @@ interface TxBatchTrades {

type Dict<T> = Record<string, T>

type Accounts = Dict<Account> | undefined
type AccountWithReceiver = Account & { owner?: string; uids?: string[] }
type Accounts = Dict<AccountWithReceiver> | undefined

export interface Settlement {
tokens: Dict<SingleErc20State>
Expand All @@ -29,6 +31,26 @@ export type GetTxBatchTradesResult = {
isLoading: boolean
}

const getGroupedByTransfers = (arr: Transfer[]): Transfer[] => {
return [
...arr
.reduce((r, t) => {
const key = `${t.token}-${t.from}-${t.to}`

const item =
r.get(key) ||
Object.assign({}, t, {
value: new BigNumber(0),
})

item.value = BigNumber.sum(item.value, new BigNumber(t.value))

return r.set(key, item)
}, new Map())
.values(),
]
}

export function useTxBatchTrades(
networkId: Network | undefined,
txHash: string,
Expand All @@ -38,41 +60,69 @@ export function useTxBatchTrades(
const [error, setError] = useState('')
const [txBatchTrades, setTxBatchTrades] = useState<TxBatchTrades>({ trades: [], transfers: [] })
const [accounts, setAccounts] = useState<Accounts>()
const txOrders = usePrevious(JSON.stringify(orders?.map((o) => ({ owner: o.owner, kind: o.kind })))) // We need to do a deep comparison here to avoid useEffect to be called twice (Orders array is populated partially from different places)
const txOrders = usePrevious(JSON.stringify(orders?.map((o) => ({ owner: o.owner, kind: o.kind, receiver: o.receiver })))) // We need to do a deep comparison here to avoid useEffect to be called twice (Orders array is populated partially from different places)
const [erc20Addresses, setErc20Addresses] = useState<string[]>([])
const { value: valueErc20s, isLoading: areErc20Loading } = useMultipleErc20({ networkId, addresses: erc20Addresses })

const _fetchTxTrades = useCallback(async (network: Network, _txHash: string, orders: Order[]): Promise<void> => {
setIsLoading(true)
setError('')

try {
const { transfers, trades } = await getTradesAndTransfers(network, _txHash)
const _accounts = Object.fromEntries(await getTradesAccount(network, _txHash, trades, transfers))
const orderIds = orders.map((order) => order.owner) || []
const transfersWithKind: Transfer[] = transfers.reduce(
(acc, transfer) =>
!orderIds.includes(transfer.from) && !orderIds.includes(transfer.to) ? [...acc, transfer] : acc,
[],
const _accounts: Accounts = Object.fromEntries(await getTradesAccount(network, _txHash, trades, transfers))
const filteredOrders = orders?.filter((order) => _accounts[order.owner])
const orderOwnersReceivers = [
...(filteredOrders?.map((order) => order.owner) || []),
...(filteredOrders?.map((order) => order.receiver) || []),
]
const groupedByTransfers = getGroupedByTransfers(transfers)
const transfersWithKind: Transfer[] = groupedByTransfers.filter(
(transfer) => !orderOwnersReceivers.includes(transfer.from) && !orderOwnersReceivers.includes(transfer.to),
)
filteredOrders?.forEach((order) => {
const { owner, kind, receiver } = order
if (!orderOwnersReceivers.includes(owner)) return
transfersWithKind.push(
...groupedByTransfers
.filter((t) => [t.from, t.to].includes(owner))
.map((transfer) => ({ ...transfer, kind })),
)

orders.forEach((order) => {
const { owner, kind } = order
transfersWithKind.push(
...transfers.filter((t) => [t.from, t.to].includes(owner)).map((transfer) => ({ ...transfer, kind })),
...groupedByTransfers
.filter((t) => [t.from, t.to].includes(receiver))
.map((transfer) => ({ ...transfer, kind })),
)
orderOwnersReceivers.splice(orderOwnersReceivers.indexOf(owner), 1)
orderOwnersReceivers.splice(orderOwnersReceivers.indexOf(receiver), 1)
})
setErc20Addresses(transfers.map((transfer: Transfer): string => transfer.token))
setTxBatchTrades({ trades, transfers })
setAccounts(_accounts)

const accountsWithReceiver = _accounts
filteredOrders?.forEach((order) => {
if (!(order.receiver in _accounts)) {
accountsWithReceiver[order.receiver] = {
alias: ALIAS_TRADER_NAME,
}
}
accountsWithReceiver[order.receiver] = {
...accountsWithReceiver[order.receiver],
owner: order.owner,
}
})

setErc20Addresses(transfersWithKind.map((transfer: Transfer): string => transfer.token))
setTxBatchTrades({ trades, transfers: transfersWithKind })
setAccounts(accountsWithReceiver)
} catch (e) {
const msg = `Failed to fetch tx batch trades`
console.error(msg, e)
setError(msg)
} finally {
setIsLoading(false)
}
}, [])
},
[orders],
)

useEffect(() => {
if (!networkId || !txOrders) {
Expand Down