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

Partial fills table surplus #440

Merged
merged 6 commits into from
Apr 11, 2023
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
46 changes: 46 additions & 0 deletions src/components/common/SurplusComponent/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from 'react'
import styled, { css, FlattenSimpleInterpolation } from 'styled-components'
import { formatPercentage, Surplus } from 'utils'
import { TokenErc20 } from '@gnosis.pm/dex-js'
import { TokenAmount } from 'components/token/TokenAmount'

const Percentage = styled.span`
color: ${({ theme }): string => theme.green};
`

const Amount = styled.span<{ showHiddenSection: boolean; strechHiddenSection?: boolean }>`
display: ${({ showHiddenSection }): string => (showHiddenSection ? 'flex' : 'none')};
${({ strechHiddenSection }): FlattenSimpleInterpolation | false | undefined =>
strechHiddenSection &&
css`
width: 3.4rem;
display: inline-block;
justify-content: end;
`}
`

export type SurplusComponentProps = {
surplus: Surplus | null
token: TokenErc20 | null
showHidden?: boolean
className?: string
}

export const SurplusComponent: React.FC<SurplusComponentProps> = (props) => {
const { surplus, token, showHidden, className } = props

if (!surplus || !token) {
return null
}

const { percentage, amount } = surplus

return (
<div className={className}>
<Percentage>{formatPercentage(percentage)}</Percentage>
<Amount showHiddenSection={!!showHidden}>
<TokenAmount amount={amount} token={token} />
</Amount>
</div>
)
}
21 changes: 18 additions & 3 deletions src/components/orders/OrderDetails/FillsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { abbreviateString } from 'utils'
import { useMultipleErc20 } from 'hooks/useErc20'

import StyledUserDetailsTable, {
StyledUserDetailsTableProps,
EmptyItemWrapper,
StyledUserDetailsTableProps,
} from '../../common/StyledUserDetailsTable'

import { media } from 'theme/styles/media'
Expand All @@ -25,6 +25,8 @@ 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'
import { SurplusComponent } from 'components/common/SurplusComponent'
import { OrderKind } from '@cowprotocol/cow-sdk'

const Wrapper = styled(StyledUserDetailsTable)`
> thead {
Expand Down Expand Up @@ -249,7 +251,16 @@ function calculateExecutionPrice(

const RowFill: React.FC<RowProps> = ({ trade, isPriceInverted, invertButton }) => {
const network = useNetworkId() || undefined
const { txHash, sellAmount, buyAmount, sellTokenAddress, buyTokenAddress, executionTime } = trade
const {
txHash,
sellAmount,
buyAmount,
sellTokenAddress,
buyTokenAddress,
executionTime,
surplusAmount,
surplusPercentage,
} = trade
const { value: tokens } = useMultipleErc20({
networkId: network,
addresses: [sellTokenAddress, buyTokenAddress],
Expand All @@ -263,6 +274,8 @@ const RowFill: React.FC<RowProps> = ({ trade, isPriceInverted, invertButton }) =
if (!network || !txHash) {
return null
}
const surplus = surplusAmount && surplusPercentage ? { amount: surplusAmount, percentage: surplusPercentage } : null
const surplusToken = trade.kind === OrderKind.BUY ? sellToken : buyToken

return (
<tr key={txHash}>
Expand All @@ -281,7 +294,9 @@ const RowFill: React.FC<RowProps> = ({ trade, isPriceInverted, invertButton }) =
</td>
<td>
<HeaderTitle>Surplus</HeaderTitle>
<HeaderValue>-</HeaderValue>
<HeaderValue>
{surplus ? <SurplusComponent surplus={surplus} token={surplusToken} showHidden /> : '-'}
</HeaderValue>
</td>
<td>
<HeaderTitle>Buy amount</HeaderTitle>
Expand Down
70 changes: 14 additions & 56 deletions src/components/orders/OrderSurplusDisplay/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,15 @@ import styled, { css, useTheme, FlattenSimpleInterpolation } from 'styled-compon

import { Order } from 'api/operator'

import {
formatSmart,
formatSmartMaxPrecision,
safeTokenName,
formattingAmountPrecision,
FormatAmountPrecision,
} from 'utils'

import { LOW_PRECISION_DECIMALS, PERCENTAGE_PRECISION } from 'apps/explorer/const'
import { BaseIconTooltipOnHover } from 'components/Tooltip'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faArrowAltCircleUp as faIcon } from '@fortawesome/free-regular-svg-icons'
import BigNumber from 'bignumber.js'
import { TokenErc20 } from '@gnosis.pm/dex-js'
import { SurplusComponent } from 'components/common/SurplusComponent'
import { TokenAmount } from 'components/token/TokenAmount'

const Wrapper = styled.div`
const Wrapper = styled(SurplusComponent)`
display: flex;
& > * {
margin-right: 0.25rem;
Expand All @@ -27,19 +22,15 @@ const Wrapper = styled.div`
}
`

const Surplus = styled.span`
color: ${({ theme }): string => theme.green};
`

// const UsdAmount = styled.span`
// color: ${({ theme }): string => theme.textPrimary1};
// opacity: 0.5;
// `

export type Props = { order: Order; amountSmartFormatting?: boolean } & React.HTMLAttributes<HTMLDivElement>
type SurplusText = { amount: string; percentage: string; formattedSmartAmount: string }
type OrderSurplus = { amount: BigNumber; percentage: BigNumber; surplusToken: TokenErc20 }

function useGetSurplus(order: Order): SurplusText | null {
function useGetSurplus(order: Order): OrderSurplus | null {
const { kind, buyToken, sellToken, surplusAmount, surplusPercentage } = order

const surplusToken = kind === 'buy' ? sellToken : buyToken
Expand All @@ -51,44 +42,15 @@ function useGetSurplus(order: Order): SurplusText | null {
return null
}

const formattedSurplusPercentage = formatSmart({
amount: surplusPercentage.toString(10),
precision: PERCENTAGE_PRECISION,
decimals: LOW_PRECISION_DECIMALS,
})
const formattedSurplusAmountMaxPrecision = formatSmartMaxPrecision(surplusAmount, surplusToken)
const formattedSurplusAmount = formattingAmountPrecision(
surplusAmount,
surplusToken,
FormatAmountPrecision.highPrecision,
)

const tokenSymbol = safeTokenName(surplusToken)
// const formattedUsdAmount = formatSmart({
// amount: usdAmount,
// precision: NO_ADJUSTMENT_NEEDED_PRECISION,
// decimals: LOW_PRECISION_DECIMALS,
// })

return {
amount: `${formattedSurplusAmountMaxPrecision} ${tokenSymbol}`,
formattedSmartAmount: `${formattedSurplusAmount} ${tokenSymbol}`,
percentage: `+${formattedSurplusPercentage}%`,
}
return { amount: surplusAmount, percentage: surplusPercentage, surplusToken }
}

export function OrderSurplusDisplay(props: Props): JSX.Element | null {
const surplus = useGetSurplus(props.order)

if (!surplus) return null

return (
<Wrapper className={props.className}>
<Surplus>{surplus.percentage}</Surplus>
<span>{props.amountSmartFormatting ? surplus.formattedSmartAmount : surplus.amount}</span>
{/* <UsdAmount>(~${formattedUsdAmount})</UsdAmount> */}
</Wrapper>
)
return <Wrapper surplus={surplus} token={surplus.surplusToken} showHidden />
}

const IconWrapper = styled(FontAwesomeIcon)`
Expand All @@ -107,14 +69,13 @@ const HiddenSection = styled.span<{ showHiddenSection: boolean; strechHiddenSect
strechHiddenSection &&
css`
width: 3.4rem;
display: 'inline-block';
display: inline-block;
justify-content: end;
`}
`

export function OrderSurplusTooltipDisplay({
order,
amountSmartFormatting,
showHiddenSection = false,
defaultWhenNoSurplus,
strechWhenNoSurplus = false,
Expand All @@ -135,15 +96,12 @@ export function OrderSurplusTooltipDisplay({

return (
<BaseIconTooltipOnHover
tooltip={surplus.amount}
tooltip={<TokenAmount amount={surplus.amount} token={surplus.surplusToken} />}
targetContent={
<span>
<>
<IconWrapper icon={faIcon} color={theme.green} />
<Surplus>{surplus.percentage}</Surplus>
<HiddenSection showHiddenSection={showHiddenSection}>
{amountSmartFormatting ? surplus.formattedSmartAmount : surplus.amount}
</HiddenSection>
</span>
<SurplusComponent surplus={surplus} token={surplus.surplusToken} showHidden={showHiddenSection} />
</>
}
/>
)
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useOperatorTrades.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export function useOrderTrades(order: Order | null): Result {
const trades = rawTrades.map((trade) => {
const timestamp = trade.txHash ? tradesTimestamps[trade.txHash] : undefined

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

setTrades(trades)
Expand Down
40 changes: 32 additions & 8 deletions src/utils/operator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
import BigNumber from 'bignumber.js'

import { calculatePrice, invertPrice, TokenErc20 } from '@gnosis.pm/dex-js'
import { Trade as TradeMetaData } from '@cowprotocol/cow-sdk'
import { OrderKind, Trade as TradeMetaData } from '@cowprotocol/cow-sdk'

import { FILLED_ORDER_EPSILON, ONE_BIG_NUMBER, ZERO_BIG_NUMBER } from 'const'

import { Order, OrderStatus, RawOrder, Trade } from 'api/operator/types'

import { formattingAmountPrecision, formatSmartMaxPrecision } from 'utils'
import { formatSmartMaxPrecision, formattingAmountPrecision } from 'utils'
import { PENDING_ORDERS_BUFFER } from 'apps/explorer/const'

function isOrderFilled(order: RawOrder): boolean {
Expand Down Expand Up @@ -100,7 +100,7 @@ export function getOrderFilledAmount(order: RawOrder): { amount: BigNumber; perc
return { amount: executedAmount, percentage: executedAmount.div(totalAmount) }
}

type Surplus = {
export type Surplus = {
amount: BigNumber
percentage: BigNumber
}
Expand Down Expand Up @@ -134,8 +134,15 @@ function _getFillOrKillSellSurplus(order: RawOrder): Surplus | null {
return { amount, percentage }
}

function _getPartialFillSellSurplus(order: RawOrder): Surplus | null {
const { buyAmount, sellAmount, executedSellAmountBeforeFees, executedBuyAmount } = order
type PartialFillSurplusParams = {
buyAmount: string | BigNumber
sellAmount: string | BigNumber
executedSellAmountBeforeFees: string
executedBuyAmount: string
}

function _getPartialFillSellSurplus(params: PartialFillSurplusParams): Surplus | null {
const { buyAmount, sellAmount, executedSellAmountBeforeFees, executedBuyAmount } = params

const sellAmountBigNumber = new BigNumber(sellAmount)
const executedSellAmountBigNumber = new BigNumber(executedSellAmountBeforeFees)
Expand Down Expand Up @@ -187,8 +194,8 @@ function _getFillOrKillBuySurplus(order: RawOrder): Surplus | null {
return { amount, percentage }
}

function _getPartialFillBuySurplus(order: RawOrder): Surplus | null {
const { buyAmount, sellAmount, executedSellAmountBeforeFees, executedBuyAmount } = order
function _getPartialFillBuySurplus(params: PartialFillSurplusParams): Surplus | null {
const { buyAmount, sellAmount, executedSellAmountBeforeFees, executedBuyAmount } = params

const sellAmountBigNumber = new BigNumber(sellAmount)
const executedSellAmountBigNumber = new BigNumber(executedSellAmountBeforeFees)
Expand Down Expand Up @@ -411,17 +418,34 @@ export function transformOrder(rawOrder: RawOrder): Order {
/**
* Transforms a RawTrade into a Trade object
*/
export function transformTrade(rawTrade: TradeMetaData, executionTimestamp?: number): Trade {
export function transformTrade(rawTrade: TradeMetaData, order: Order, executionTimestamp?: number): Trade {
const { orderUid, buyAmount, sellAmount, sellAmountBeforeFees, buyToken, sellToken, ...rest } = rawTrade
const { amount, percentage } = getTradeSurplus(rawTrade, order)

return {
...rest,
orderId: orderUid,
kind: order.kind,
buyAmount: new BigNumber(buyAmount),
sellAmount: new BigNumber(sellAmount),
sellAmountBeforeFees: new BigNumber(sellAmountBeforeFees),
buyTokenAddress: buyToken,
sellTokenAddress: sellToken,
surplusAmount: amount,
surplusPercentage: percentage,
executionTime: executionTimestamp ? new Date(executionTimestamp * 1000) : null,
}
}

export function getTradeSurplus(rawTrade: TradeMetaData, order: Order): Surplus {
const params: PartialFillSurplusParams = {
sellAmount: order.sellAmount,
buyAmount: order.buyAmount,
executedSellAmountBeforeFees: rawTrade.sellAmountBeforeFees,
executedBuyAmount: rawTrade.buyAmount,
}

const surplus = order.kind === OrderKind.SELL ? _getPartialFillSellSurplus(params) : _getPartialFillBuySurplus(params)

return surplus || ZERO_SURPLUS
}