Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/feat-cow-swap' into swap-titles
Browse files Browse the repository at this point in the history
  • Loading branch information
usame-algan committed May 6, 2024
2 parents 52c55a8 + 179be31 commit 25da062
Show file tree
Hide file tree
Showing 13 changed files with 109 additions and 44 deletions.
16 changes: 9 additions & 7 deletions public/images/common/swap.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 0 additions & 10 deletions public/images/sidebar/swap.svg

This file was deleted.

2 changes: 1 addition & 1 deletion src/components/balances/AssetsTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import AddFundsCTA from '@/components/common/AddFunds'
import { SWAP_EVENTS } from '@/services/analytics/events/swaps'
import { useRouter } from 'next/router'
import { AppRoutes } from '@/config/routes'
import SwapIcon from '@/public/images/sidebar/swap.svg'
import SwapIcon from '@/public/images/common/swap.svg'

const skeletonCells: EnhancedTableProps['rows'][0]['cells'] = {
asset: {
Expand Down
2 changes: 1 addition & 1 deletion src/components/dashboard/Overview/Overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import useSafeInfo from '@/hooks/useSafeInfo'
import { useVisibleBalances } from '@/hooks/useVisibleBalances'
import ArrowIconSE from '@/public/images/common/arrow-se.svg'
import ArrowIconNW from '@/public/images/common/arrow-top-right.svg'
import SwapIcon from '@/public/images/sidebar/swap.svg'
import SwapIcon from '@/public/images/common/swap.svg'
import { OVERVIEW_EVENTS, trackEvent } from '@/services/analytics'
import { useAppSelector } from '@/store'
import { selectCurrency } from '@/store/settingsSlice'
Expand Down
2 changes: 1 addition & 1 deletion src/components/sidebar/SidebarNavigation/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import TransactionIcon from '@/public/images/sidebar/transactions.svg'
import ABIcon from '@/public/images/sidebar/address-book.svg'
import AppsIcon from '@/public/images/apps/apps-icon.svg'
import SettingsIcon from '@/public/images/sidebar/settings.svg'
import SwapIcon from '@/public/images/sidebar/swap.svg'
import SwapIcon from '@/public/images/common/swap.svg'
import { SvgIcon } from '@mui/material'

export type NavItem = {
Expand Down
4 changes: 2 additions & 2 deletions src/components/transactions/TxDetails/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { POLLING_INTERVAL } from '@/config/constants'
import useIsExpiredSwap from '@/features/swap/hooks/useIsExpiredSwap'
import useIntervalCounter from '@/hooks/useIntervalCounter'
import React, { type ReactElement } from 'react'
import type { TransactionDetails, TransactionSummary } from '@safe-global/safe-gateway-typescript-sdk'
Expand All @@ -12,7 +13,6 @@ import useChainId from '@/hooks/useChainId'
import useAsync from '@/hooks/useAsync'
import {
isAwaitingExecution,
isExpiredSwap,
isModuleExecutionInfo,
isMultiSendTxInfo,
isMultisigDetailedExecutionInfo,
Expand Down Expand Up @@ -67,7 +67,7 @@ const TxDetailsBlock = ({ txSummary, txDetails }: TxDetailsProps): ReactElement
safeTxHash = txDetails.detailedExecutionInfo.safeTxHash
}

const expiredSwap = isExpiredSwap(txSummary.txInfo)
const expiredSwap = useIsExpiredSwap(txSummary.txInfo)

return (
<>
Expand Down
20 changes: 16 additions & 4 deletions src/features/swap/components/SwapOrderConfirmationView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ import { compareAsc } from 'date-fns'
import { Alert, Typography } from '@mui/material'
import { formatAmount } from '@/utils/formatNumber'
import { formatVisualAmount } from '@/utils/formatters'
import { getExecutionPrice, getLimitPrice, getSlippageInPercent, getSurplusPrice } from '@/features/swap/helpers/utils'
import {
getExecutionPrice,
getLimitPrice,
getOrderClass,
getSlippageInPercent,
getSurplusPrice,
} from '@/features/swap/helpers/utils'
import type { CowSwapConfirmationView } from '@safe-global/safe-gateway-typescript-sdk'
import SwapTokens from '@/features/swap/components/SwapTokens'
import AlertIcon from '@/public/images/common/alert.svg'
Expand All @@ -25,9 +31,11 @@ export const SwapOrderConfirmationView = ({ order, settlementContract }: SwapOrd

const { uid, owner, kind, validUntil, status, sellToken, buyToken, sellAmount, buyAmount, explorerUrl, receiver } =
order

const executionPrice = getExecutionPrice(order)
const limitPrice = getLimitPrice(order)
const surplusPrice = getSurplusPrice(order)
const orderClass = getOrderClass(order)
const expires = new Date(validUntil * 1000)
const now = new Date()

Expand Down Expand Up @@ -90,9 +98,13 @@ export const SwapOrderConfirmationView = ({ order, settlementContract }: SwapOrd
) : (
<></>
),
<DataRow key="Slippage" title="Slippage">
{slippage}%
</DataRow>,
orderClass !== 'limit' ? (
<DataRow key="Slippage" title="Slippage">
{slippage}%
</DataRow>
) : (
<></>
),
<DataRow key="Order ID" title="Order ID">
<OrderId orderId={uid} href={explorerUrl} />
</DataRow>,
Expand Down
33 changes: 33 additions & 0 deletions src/features/swap/helpers/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,5 +141,38 @@ describe('Swap helpers', () => {

expect(result).toEqual('0')
})

// eslint-disable-next-line no-only-tests/no-only-tests
it('returns the surplus amount for buy orders', () => {
const mockOrder = {
executedSellAmount: '10000000000000000000', //10
executedBuyAmount: '50',
buyToken: { decimals: 8 },
sellToken: { decimals: 18 },
sellAmount: '15000000000000000000', //15
buyAmount: '5000000000',
kind: 'buy',
} as unknown as SwapOrder

const result = getSurplusPrice(mockOrder)

expect(result).toEqual(5)
})

it('returns the surplus amount for sell orders', () => {
const mockOrder = {
executedSellAmount: '100000000000000000000',
executedBuyAmount: '10000000000', //100
buyToken: { decimals: 8 },
sellToken: { decimals: 18 },
sellAmount: '100000000000000000000',
buyAmount: '5000000000', //50
kind: 'sell',
} as unknown as SwapOrder

const result = getSurplusPrice(mockOrder)

expect(result).toEqual(50)
})
})
})
38 changes: 27 additions & 11 deletions src/features/swap/helpers/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ type Quantity = {
decimals: number
}

enum OrderKind {
SELL = 'sell',
BUY = 'buy',
}

function calculateDifference(amountA: string, amountB: string, decimals: number): number {
return asDecimal(BigInt(amountA), decimals) - asDecimal(BigInt(amountB), decimals)
}

function asDecimal(amount: number | bigint, decimals: number): number {
return Number(formatUnits(amount, decimals))
}
Expand Down Expand Up @@ -47,13 +56,20 @@ const calculateRatio = (a: Quantity, b: Quantity) => {
return asDecimal(BigInt(a.amount), a.decimals) / asDecimal(BigInt(b.amount), b.decimals)
}

export const getSurplusPrice = (order: Pick<SwapOrder, 'executedBuyAmount' | 'buyAmount' | 'buyToken'>): number => {
const { executedBuyAmount, buyAmount, buyToken } = order

const surplus =
asDecimal(BigInt(executedBuyAmount), buyToken.decimals) - asDecimal(BigInt(buyAmount), buyToken.decimals)

return surplus
export const getSurplusPrice = (
order: Pick<
SwapOrder,
'executedBuyAmount' | 'buyAmount' | 'buyToken' | 'executedSellAmount' | 'sellAmount' | 'sellToken' | 'kind'
>,
): number => {
const { kind, executedSellAmount, sellAmount, sellToken, executedBuyAmount, buyAmount, buyToken } = order
if (kind === OrderKind.BUY) {
return calculateDifference(sellAmount, executedSellAmount, sellToken.decimals)
} else if (kind === OrderKind.SELL) {
return calculateDifference(executedBuyAmount, buyAmount, buyToken.decimals)
} else {
return 0
}
}

export const getFilledPercentage = (
Expand All @@ -62,10 +78,10 @@ export const getFilledPercentage = (
let executed: number
let total: number

if (order.kind === 'buy') {
if (order.kind === OrderKind.BUY) {
executed = Number(order.executedBuyAmount)
total = Number(order.buyAmount)
} else if (order.kind === 'sell') {
} else if (order.kind === OrderKind.SELL) {
executed = Number(order.executedSellAmount)
total = Number(order.sellAmount)
} else {
Expand All @@ -78,9 +94,9 @@ export const getFilledPercentage = (
export const getFilledAmount = (
order: Pick<SwapOrder, 'kind' | 'executedBuyAmount' | 'executedSellAmount' | 'buyToken' | 'sellToken'>,
): string => {
if (order.kind === 'buy') {
if (order.kind === OrderKind.BUY) {
return formatUnits(order.executedBuyAmount, order.buyToken.decimals)
} else if (order.kind === 'sell') {
} else if (order.kind === OrderKind.SELL) {
return formatUnits(order.executedSellAmount, order.sellToken.decimals)
} else {
return '0'
Expand Down
4 changes: 2 additions & 2 deletions src/features/swap/hooks/useIsExpiredSwap.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useState } from 'react'
import type { TransactionInfo } from '@safe-global/safe-gateway-typescript-sdk'
import useInterval from '@/hooks/useInterval'
import { isSwapTxInfo } from '@/utils/transaction-guards'
import { isExpiredSwap as isSwapInfoExpired, isSwapTxInfo } from '@/utils/transaction-guards'

const INTERVAL_IN_MS = 10_000

Expand All @@ -16,7 +16,7 @@ const useIsExpiredSwap = (txInfo: TransactionInfo) => {
const isExpiredSwap = () => {
if (!isSwapTxInfo(txInfo)) return

setIsExpired(Date.now() > txInfo.validUntil * 1000)
setIsExpired(Date.now() > txInfo.validUntil * 1000 && isSwapInfoExpired(txInfo))
}

useInterval(isExpiredSwap, INTERVAL_IN_MS)
Expand Down
4 changes: 2 additions & 2 deletions src/features/swap/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const SwapWidget = ({ sell }: Params) => {
const swapParams = useAppSelector(selectSwapParams)
const { tradeType } = swapParams

const { safeAddress } = useSafeInfo()
const { safeAddress, safeLoading } = useSafeInfo()
const wallet = useWallet()
const { isConsentAccepted, onAccept } = useSwapConsent()

Expand Down Expand Up @@ -204,7 +204,7 @@ const SwapWidget = ({ sell }: Params) => {
if (iframeElement) {
iframeRef.current = iframeElement as HTMLIFrameElement
}
}, [params])
}, [params, isConsentAccepted, safeLoading])

useCustomAppCommunicator(iframeRef, appData, chain)

Expand Down
15 changes: 12 additions & 3 deletions src/hooks/__tests__/useInterval.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,31 @@ describe('useInterval', () => {
jest.useFakeTimers()
})

it('should run the callback function once immediately', () => {
let mockValue = 0
const mockCallback = jest.fn(() => mockValue++)

renderHook(() => useInterval(mockCallback, 100))

expect(mockValue).toEqual(1)
})

it('should run the callback function with every interval', async () => {
let mockValue = 0
const mockCallback = jest.fn(() => mockValue++)

renderHook(() => useInterval(mockCallback, 100))

expect(mockValue).toEqual(0)
expect(mockValue).toEqual(1)

act(() => {
jest.advanceTimersByTime(100)
})
expect(mockValue).toEqual(1)
expect(mockValue).toEqual(2)

act(() => {
jest.advanceTimersByTime(100)
})
expect(mockValue).toEqual(2)
expect(mockValue).toEqual(3)
})
})
3 changes: 3 additions & 0 deletions src/hooks/useInterval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ const useInterval = (callback: () => void, time: number) => {
useEffect(() => {
const interval = setInterval(() => callbackRef.current?.(), time)

// Call the function once initially
callbackRef.current?.()

return () => clearInterval(interval)
}, [time])
}
Expand Down

0 comments on commit 25da062

Please sign in to comment.