Skip to content

Commit

Permalink
test(fiat-amount): test UsdPricesUpdater (#3091)
Browse files Browse the repository at this point in the history
  • Loading branch information
shoom3301 authored Sep 1, 2023
1 parent fc9d43c commit 91bc835
Show file tree
Hide file tree
Showing 8 changed files with 423 additions and 119 deletions.
2 changes: 1 addition & 1 deletion apps/cowswap-frontend/src/legacy/state/swap/hooks.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { parse } from 'qs'

import { Field } from './actions'
import { queryParametersToSwapState } from './hooks'
import { queryParametersToSwapState } from './utils'

jest.mock('legacy/components/analytics/hooks/useAnalyticsReporter.ts')

Expand Down
70 changes: 0 additions & 70 deletions apps/cowswap-frontend/src/legacy/state/swap/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { SupportedChainId } from '@cowprotocol/cow-sdk'
import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'

import { t } from '@lingui/macro'
import { ParsedQs } from 'qs'

import { changeSwapAmountAnalytics, switchTokensAnalytics } from 'legacy/components/analytics'
import { FEE_SIZE_THRESHOLD } from 'legacy/constants'
Expand Down Expand Up @@ -34,46 +33,13 @@ import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { formatSymbol } from 'utils/format'

import { Field, setRecipient, switchCurrencies, typeInput } from './actions'
import { SwapState } from './reducer'

import { TOKEN_SHORTHANDS } from '../../constants/tokens'

export const BAD_RECIPIENT_ADDRESSES: { [address: string]: true } = {
'0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f': true, // v2 factory
'0xf164fC0Ec4E93095b804a4795bBe1e041497b92a': true, // v2 router 01
'0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D': true, // v2 router 02
}

export function parseCurrencyFromURLParameter(urlParam: ParsedQs[string]): string {
if (typeof urlParam === 'string') {
const valid = isAddress(urlParam)
if (valid) return valid
const upper = urlParam.toUpperCase()
if (upper === 'ETH') return 'ETH'
if (upper in TOKEN_SHORTHANDS) return upper
}
return ''
}

export function parseTokenAmountURLParameter(urlParam: any): string {
return typeof urlParam === 'string' && !isNaN(parseFloat(urlParam)) ? urlParam : ''
}

export function parseIndependentFieldURLParameter(urlParam: any): Field {
return typeof urlParam === 'string' && urlParam.toLowerCase() === 'output' ? Field.OUTPUT : Field.INPUT
}

const ENS_NAME_REGEX = /^[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)?$/
const ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/
export function validatedRecipient(recipient: any): string | null {
if (typeof recipient !== 'string') return null
const address = isAddress(recipient)
if (address) return address
if (ENS_NAME_REGEX.test(recipient)) return recipient
if (ADDRESS_REGEX.test(recipient)) return recipient
return null
}

export function useSwapState(): AppState['swap'] {
const isProviderNetworkUnsupported = useIsProviderNetworkUnsupported()

Expand Down Expand Up @@ -402,42 +368,6 @@ export function useDerivedSwapInfo(): DerivedSwapInfo {
)
}

export function queryParametersToSwapState(
parsedQs: ParsedQs,
defaultInputCurrency = '',
chainId: number | null
): SwapState {
let inputCurrency = parseCurrencyFromURLParameter(parsedQs.inputCurrency)
let outputCurrency = parseCurrencyFromURLParameter(parsedQs.outputCurrency)
const typedValue = parseTokenAmountURLParameter(parsedQs.exactAmount)
const independentField = parseIndependentFieldURLParameter(parsedQs.exactField)

if (inputCurrency === '' && outputCurrency === '' && typedValue === '' && independentField === Field.INPUT) {
// Defaults to having the wrapped native currency selected
inputCurrency = defaultInputCurrency // 'ETH' // mod
} else if (inputCurrency === outputCurrency) {
// clear output if identical
outputCurrency = ''
}

const recipient = validatedRecipient(parsedQs.recipient)
const recipientAddress = validatedRecipient(parsedQs.recipientAddress)

return {
chainId: chainId || null,
[Field.INPUT]: {
currencyId: inputCurrency === '' ? null : inputCurrency ?? null,
},
[Field.OUTPUT]: {
currencyId: outputCurrency === '' ? null : outputCurrency ?? null,
},
typedValue,
independentField,
recipient,
recipientAddress,
}
}

export function useIsFeeGreaterThanInput({
address,
chainId,
Expand Down
3 changes: 2 additions & 1 deletion apps/cowswap-frontend/src/legacy/state/swap/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ import {
switchCurrencies,
typeInput,
} from 'legacy/state/swap/actions'
import { queryParametersToSwapState } from 'legacy/state/swap/hooks'

import { getIsNativeToken } from 'utils/getIsNativeToken'
import { getIsWrapOrUnwrap } from 'utils/getIsWrapOrUnwrap'

import { queryParametersToSwapState } from './utils'

export interface SwapState {
// Mod: added chainId
chainId: number | null
Expand Down
113 changes: 71 additions & 42 deletions apps/cowswap-frontend/src/legacy/state/swap/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { SupportedChainId } from '@cowprotocol/cow-sdk'
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { Currency } from '@uniswap/sdk-core'

import { WRAPPED_NATIVE_CURRENCY } from 'legacy/constants/tokens'
import { ParsedQs } from 'qs'

import TradeGp from './TradeGp'
import { TOKEN_SHORTHANDS, WRAPPED_NATIVE_CURRENCY } from 'legacy/constants/tokens'
import { isAddress } from 'legacy/utils'

import { Field } from './actions'
import { SwapState } from './reducer'

export function isWrappingTrade(
sellCurrency: Currency | null | undefined,
Expand All @@ -20,44 +24,69 @@ export function isWrappingTrade(
)
}

export function logTradeDetails(trade: TradeGp | undefined, allowedSlippage: Percent) {
// don't do anything outside of dev env
if (!trade || process.env.NODE_ENV !== 'development') return

const exactIn = trade.tradeType === TradeType.EXACT_INPUT

// Log Exact In Trade info
if (exactIn) {
console.debug(
`[SwapMod::[SELL] Trade Constructed]`,
`
Type: SELL
==========
Input Amount: ${trade.inputAmount.toExact()}
Output Amount: ${trade.outputAmount.toExact()}
==========
Fee Amount [as SELL]: ${trade.fee?.feeAsCurrency?.toExact()} ${trade.inputAmount.currency.symbol}
Fee Amount [as BUY]: ${
trade.outputAmountWithoutFee && trade.outputAmountWithoutFee.subtract(trade.outputAmount).toExact()
} ${trade.outputAmount.currency.symbol}
==========
Minimum Received: ${trade.minimumAmountOut(allowedSlippage).toExact()}
`
)
} else {
// Log Exact Out Trade info
console.debug(
`[SwapMod::[BUY] Trade Constructed]`,
`
Type: BUY
=========
Input Amount [w/FEE]: ${trade.inputAmountWithFee.toExact()}
Output Amount: ${trade.outputAmount.toExact()}
=========
Fee Amount [as SELL]: ${trade.fee?.feeAsCurrency?.toExact()} ${trade.inputAmount.currency.symbol}
=========
Maximum Sold: ${trade.fee?.feeAsCurrency && trade.maximumAmountIn(allowedSlippage).toExact()}
`
)
function parseIndependentFieldURLParameter(urlParam: any): Field {
return typeof urlParam === 'string' && urlParam.toLowerCase() === 'output' ? Field.OUTPUT : Field.INPUT
}

export function parseCurrencyFromURLParameter(urlParam: ParsedQs[string]): string {
if (typeof urlParam === 'string') {
const valid = isAddress(urlParam)
if (valid) return valid
const upper = urlParam.toUpperCase()
if (upper === 'ETH') return 'ETH'
if (upper in TOKEN_SHORTHANDS) return upper
}
return ''
}

export function parseTokenAmountURLParameter(urlParam: any): string {
return typeof urlParam === 'string' && !isNaN(parseFloat(urlParam)) ? urlParam : ''
}

const ENS_NAME_REGEX = /^[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)?$/
const ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/

export function validatedRecipient(recipient: any): string | null {
if (typeof recipient !== 'string') return null
const address = isAddress(recipient)
if (address) return address
if (ENS_NAME_REGEX.test(recipient)) return recipient
if (ADDRESS_REGEX.test(recipient)) return recipient
return null
}

export function queryParametersToSwapState(
parsedQs: ParsedQs,
defaultInputCurrency = '',
chainId: number | null
): SwapState {
let inputCurrency = parseCurrencyFromURLParameter(parsedQs.inputCurrency)
let outputCurrency = parseCurrencyFromURLParameter(parsedQs.outputCurrency)
const typedValue = parseTokenAmountURLParameter(parsedQs.exactAmount)
const independentField = parseIndependentFieldURLParameter(parsedQs.exactField)

if (inputCurrency === '' && outputCurrency === '' && typedValue === '' && independentField === Field.INPUT) {
// Defaults to having the wrapped native currency selected
inputCurrency = defaultInputCurrency // 'ETH' // mod
} else if (inputCurrency === outputCurrency) {
// clear output if identical
outputCurrency = ''
}

const recipient = validatedRecipient(parsedQs.recipient)
const recipientAddress = validatedRecipient(parsedQs.recipientAddress)

return {
chainId: chainId || null,
[Field.INPUT]: {
currencyId: inputCurrency === '' ? null : inputCurrency ?? null,
},
[Field.OUTPUT]: {
currencyId: outputCurrency === '' ? null : outputCurrency ?? null,
},
typedValue,
independentField,
recipient,
recipientAddress,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { createStore } from 'jotai/vanilla'
import { ReactNode } from 'react'

import { CurrencyAmount } from '@uniswap/sdk-core'

import { renderHook } from '@testing-library/react-hooks'
import { JotaiTestProvider } from 'test-utils'

import { WETH_GNOSIS_CHAIN } from 'legacy/utils/gnosis_chain/constants'

import { useUsdAmount } from './useUsdAmount'

import { usdRawPricesAtom, UsdRawPriceState } from '../state/usdRawPricesAtom'

const WETH_RAW_PRICE_STATE: UsdRawPriceState = {
updatedAt: Date.now(),
price: 1650,
currency: WETH_GNOSIS_CHAIN,
isLoading: false,
}

function getWrapper() {
const store = createStore()
const initialValues = [[usdRawPricesAtom, { [WETH_GNOSIS_CHAIN.address.toLowerCase()]: WETH_RAW_PRICE_STATE }]]

return {
store,
TestComponent: function ({ children }: { children: ReactNode }) {
return (
<JotaiTestProvider store={store} initialValues={initialValues}>
{children}
</JotaiTestProvider>
)
},
}
}

describe('useUsdAmount', () => {
const ONE_WETH = CurrencyAmount.fromRawAmount(WETH_GNOSIS_CHAIN, 1 * 10 ** WETH_GNOSIS_CHAIN.decimals)

it('USD amount for 1 WETH should be 1650', async () => {
const { TestComponent } = getWrapper()
const { result } = renderHook(
() => {
return useUsdAmount(ONE_WETH)
},
{ wrapper: TestComponent }
)

expect(result.current.value?.toExact()).toBe(WETH_RAW_PRICE_STATE.price?.toString())
})
})
Loading

0 comments on commit 91bc835

Please sign in to comment.