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

Commit

Permalink
Partial fills table surplus (#440)
Browse files Browse the repository at this point in the history
* Added new component SurplusComponent

* Added calculation for trade surplus getTradeSurplus

* Added surplus to fills table

* Refactored OrderSurplusDisplay to use SurplusComponent

* Fixed issue with surplus tooltip

* Removed buy amount value by mistake 😅
  • Loading branch information
alfetopito authored Apr 11, 2023
1 parent 31e5607 commit 052a822
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 68 deletions.
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
}

0 comments on commit 052a822

Please sign in to comment.