Commit bf50582d authored by cartcrom's avatar cartcrom Committed by GitHub

test: local activity tests (#6341)

* refactor: move MP files into subfolders

* refactor: consolidate MP subfolder file-naming scheme

* test: add tests for parseLocal.ts

* refactor: update existing parseLocal tests
parent 110e23d6
// jest unit tests for the parseLocalActivity function
import { SupportedChainId, Token, TradeType as MockTradeType } from '@uniswap/sdk-core' import { SupportedChainId, Token, TradeType as MockTradeType } from '@uniswap/sdk-core'
import { DAI as MockDAI, USDC_MAINNET as MockUSDC_MAINNET } from 'constants/tokens' import { PERMIT2_ADDRESS } from '@uniswap/universal-router-sdk'
import { DAI as MockDAI, nativeOnChain, USDC_MAINNET as MockUSDC_MAINNET } from 'constants/tokens'
import { TransactionStatus as MockTxStatus } from 'graphql/data/__generated__/types-and-hooks'
import { TokenAddressMap } from 'state/lists/hooks' import { TokenAddressMap } from 'state/lists/hooks'
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
import { import {
ExactInputSwapTransactionInfo, ExactInputSwapTransactionInfo,
ExactOutputSwapTransactionInfo, ExactOutputSwapTransactionInfo,
TransactionDetails, TransactionDetails,
TransactionType, TransactionInfo,
TransactionType as MockTxType,
} from 'state/transactions/types' } from 'state/transactions/types'
import { renderHook } from 'test-utils/render' import { renderHook } from 'test-utils/render'
import { parseLocalActivity, useLocalActivities } from './parseLocal' import { parseLocalActivity, useLocalActivities } from './parseLocal'
const oneUSDCRaw = '1000000'
const oneDAIRaw = '1000000000000000000'
function mockSwapInfo( function mockSwapInfo(
type: MockTradeType, type: MockTradeType,
inputCurrency: Token, inputCurrency: Token,
...@@ -26,7 +23,7 @@ function mockSwapInfo( ...@@ -26,7 +23,7 @@ function mockSwapInfo(
): ExactInputSwapTransactionInfo | ExactOutputSwapTransactionInfo { ): ExactInputSwapTransactionInfo | ExactOutputSwapTransactionInfo {
if (type === MockTradeType.EXACT_INPUT) { if (type === MockTradeType.EXACT_INPUT) {
return { return {
type: TransactionType.SWAP, type: MockTxType.SWAP,
tradeType: MockTradeType.EXACT_INPUT, tradeType: MockTradeType.EXACT_INPUT,
inputCurrencyId: inputCurrency.address, inputCurrencyId: inputCurrency.address,
inputCurrencyAmountRaw, inputCurrencyAmountRaw,
...@@ -36,7 +33,7 @@ function mockSwapInfo( ...@@ -36,7 +33,7 @@ function mockSwapInfo(
} }
} else { } else {
return { return {
type: TransactionType.SWAP, type: MockTxType.SWAP,
tradeType: MockTradeType.EXACT_OUTPUT, tradeType: MockTradeType.EXACT_OUTPUT,
inputCurrencyId: inputCurrency.address, inputCurrencyId: inputCurrency.address,
expectedInputCurrencyAmountRaw: inputCurrencyAmountRaw, expectedInputCurrencyAmountRaw: inputCurrencyAmountRaw,
...@@ -50,58 +47,195 @@ function mockSwapInfo( ...@@ -50,58 +47,195 @@ function mockSwapInfo(
const mockAccount1 = '0x000000000000000000000000000000000000000001' const mockAccount1 = '0x000000000000000000000000000000000000000001'
const mockAccount2 = '0x000000000000000000000000000000000000000002' const mockAccount2 = '0x000000000000000000000000000000000000000002'
const mockChainId = SupportedChainId.MAINNET const mockChainId = SupportedChainId.MAINNET
const mockSpenderAddress = PERMIT2_ADDRESS[mockChainId]
const mockCurrencyAmountRaw = '1000000000000000000'
const mockCurrencyAmountRawUSDC = '1000000'
function mockHash(id: string, status: MockTxStatus = MockTxStatus.Confirmed) {
return id + status
}
function mockCommonFields(id: string, account = mockAccount2, status: MockTxStatus) {
const hash = mockHash(id, status)
return {
hash,
from: account,
receipt:
status === MockTxStatus.Pending
? undefined
: {
transactionHash: hash,
status: status === MockTxStatus.Confirmed ? 1 : 0,
},
addedTime: 0,
}
}
function mockMultiStatus(info: TransactionInfo, id: string): [TransactionDetails, number][] {
// Mocks a transaction with multiple statuses
return [
[
{ info, ...mockCommonFields(id, mockAccount2, MockTxStatus.Pending) } as unknown as TransactionDetails,
mockChainId,
],
[
{ info, ...mockCommonFields(id, mockAccount2, MockTxStatus.Confirmed) } as unknown as TransactionDetails,
mockChainId,
],
[
{ info, ...mockCommonFields(id, mockAccount2, MockTxStatus.Failed) } as unknown as TransactionDetails,
mockChainId,
],
]
}
const mockTokenAddressMap: TokenAddressMap = {
[mockChainId]: {
[MockDAI.address]: { token: MockDAI },
[MockUSDC_MAINNET.address]: { token: MockUSDC_MAINNET },
} as TokenAddressMap[number],
}
jest.mock('../../../../state/lists/hooks', () => ({
useCombinedActiveList: () => mockTokenAddressMap,
}))
jest.mock('../../../../state/transactions/hooks', () => { jest.mock('../../../../state/transactions/hooks', () => {
return { return {
useMultichainTransactions: () => { useMultichainTransactions: (): [TransactionDetails, number][] => {
return [ return [
[ [
{ {
info: mockSwapInfo(MockTradeType.EXACT_INPUT, MockUSDC_MAINNET, oneUSDCRaw, MockDAI, oneDAIRaw), info: mockSwapInfo(
hash: '0x123', MockTradeType.EXACT_INPUT,
from: mockAccount1, MockUSDC_MAINNET,
mockCurrencyAmountRawUSDC,
MockDAI,
mockCurrencyAmountRaw
),
...mockCommonFields('0x123', mockAccount1, MockTxStatus.Confirmed),
} as TransactionDetails, } as TransactionDetails,
mockChainId, mockChainId,
], ],
[ ...mockMultiStatus(
mockSwapInfo(
MockTradeType.EXACT_OUTPUT,
MockUSDC_MAINNET,
mockCurrencyAmountRawUSDC,
MockDAI,
mockCurrencyAmountRaw
),
'0xswap_exact_input'
),
...mockMultiStatus(
mockSwapInfo(
MockTradeType.EXACT_INPUT,
MockUSDC_MAINNET,
mockCurrencyAmountRawUSDC,
MockDAI,
mockCurrencyAmountRaw
),
'0xswap_exact_output'
),
...mockMultiStatus(
{ {
info: mockSwapInfo(MockTradeType.EXACT_INPUT, MockUSDC_MAINNET, oneUSDCRaw, MockDAI, oneDAIRaw), type: MockTxType.APPROVAL,
hash: '0x456', tokenAddress: MockDAI.address,
from: mockAccount2, spender: mockSpenderAddress,
} as TransactionDetails, },
mockChainId, '0xapproval'
], ),
[ ...mockMultiStatus(
{ {
info: mockSwapInfo(MockTradeType.EXACT_INPUT, MockUSDC_MAINNET, oneUSDCRaw, MockDAI, oneDAIRaw), type: MockTxType.WRAP,
hash: '0x789', unwrapped: false,
from: mockAccount2, currencyAmountRaw: mockCurrencyAmountRaw,
} as TransactionDetails, chainId: mockChainId,
mockChainId, },
], '0xwrap'
),
...mockMultiStatus(
{
type: MockTxType.WRAP,
unwrapped: true,
currencyAmountRaw: mockCurrencyAmountRaw,
chainId: mockChainId,
},
'0xunwrap'
),
...mockMultiStatus(
{
type: MockTxType.ADD_LIQUIDITY_V3_POOL,
createPool: false,
baseCurrencyId: MockUSDC_MAINNET.address,
quoteCurrencyId: MockDAI.address,
feeAmount: 500,
expectedAmountBaseRaw: mockCurrencyAmountRawUSDC,
expectedAmountQuoteRaw: mockCurrencyAmountRaw,
},
'0xadd_liquidity_v3'
),
...mockMultiStatus(
{
type: MockTxType.REMOVE_LIQUIDITY_V3,
baseCurrencyId: MockUSDC_MAINNET.address,
quoteCurrencyId: MockDAI.address,
expectedAmountBaseRaw: mockCurrencyAmountRawUSDC,
expectedAmountQuoteRaw: mockCurrencyAmountRaw,
},
'0xremove_liquidity_v3'
),
...mockMultiStatus(
{
type: MockTxType.ADD_LIQUIDITY_V2_POOL,
baseCurrencyId: MockUSDC_MAINNET.address,
quoteCurrencyId: MockDAI.address,
expectedAmountBaseRaw: mockCurrencyAmountRawUSDC,
expectedAmountQuoteRaw: mockCurrencyAmountRaw,
},
'0xadd_liquidity_v2'
),
...mockMultiStatus(
{
type: MockTxType.COLLECT_FEES,
currencyId0: MockUSDC_MAINNET.address,
currencyId1: MockDAI.address,
expectedCurrencyOwed0: mockCurrencyAmountRawUSDC,
expectedCurrencyOwed1: mockCurrencyAmountRaw,
},
'0xcollect_fees'
),
...mockMultiStatus(
{
type: MockTxType.MIGRATE_LIQUIDITY_V3,
baseCurrencyId: MockUSDC_MAINNET.address,
quoteCurrencyId: MockDAI.address,
isFork: false,
},
'0xmigrate_v3_liquidity'
),
] ]
}, },
} }
}) })
function mockTokenAddressMap(...tokens: WrappedTokenInfo[]): TokenAddressMap {
return {
[SupportedChainId.MAINNET]: Object.fromEntries(tokens.map((token) => [token.address, { token }])),
}
}
describe('parseLocalActivity', () => { describe('parseLocalActivity', () => {
it('returns swap activity fields with known tokens, exact input', () => { it('returns swap activity fields with known tokens, exact input', () => {
const details = { const details = {
info: mockSwapInfo(MockTradeType.EXACT_INPUT, MockUSDC_MAINNET, oneUSDCRaw, MockDAI, oneDAIRaw), info: mockSwapInfo(
MockTradeType.EXACT_INPUT,
MockUSDC_MAINNET,
mockCurrencyAmountRawUSDC,
MockDAI,
mockCurrencyAmountRaw
),
receipt: { receipt: {
transactionHash: '0x123', transactionHash: '0x123',
status: 1, status: 1,
}, },
} as TransactionDetails } as TransactionDetails
const chainId = SupportedChainId.MAINNET const chainId = SupportedChainId.MAINNET
const tokens = mockTokenAddressMap(MockUSDC_MAINNET as WrappedTokenInfo, MockDAI as WrappedTokenInfo) expect(parseLocalActivity(details, chainId, mockTokenAddressMap)).toEqual({
expect(parseLocalActivity(details, chainId, tokens)).toEqual({
chainId: 1, chainId: 1,
currencies: [MockUSDC_MAINNET, MockDAI], currencies: [MockUSDC_MAINNET, MockDAI],
descriptor: '1.00 USDC for 1.00 DAI', descriptor: '1.00 USDC for 1.00 DAI',
...@@ -112,10 +246,10 @@ describe('parseLocalActivity', () => { ...@@ -112,10 +246,10 @@ describe('parseLocalActivity', () => {
type: 1, type: 1,
tradeType: MockTradeType.EXACT_INPUT, tradeType: MockTradeType.EXACT_INPUT,
inputCurrencyId: MockUSDC_MAINNET.address, inputCurrencyId: MockUSDC_MAINNET.address,
inputCurrencyAmountRaw: oneUSDCRaw, inputCurrencyAmountRaw: mockCurrencyAmountRawUSDC,
outputCurrencyId: MockDAI.address, outputCurrencyId: MockDAI.address,
expectedOutputCurrencyAmountRaw: oneDAIRaw, expectedOutputCurrencyAmountRaw: mockCurrencyAmountRaw,
minimumOutputCurrencyAmountRaw: oneDAIRaw, minimumOutputCurrencyAmountRaw: mockCurrencyAmountRaw,
}, },
receipt: { status: 1, transactionHash: '0x123' }, receipt: { status: 1, transactionHash: '0x123' },
status: 'CONFIRMED', status: 'CONFIRMED',
...@@ -129,43 +263,37 @@ describe('parseLocalActivity', () => { ...@@ -129,43 +263,37 @@ describe('parseLocalActivity', () => {
it('returns swap activity fields with known tokens, exact output', () => { it('returns swap activity fields with known tokens, exact output', () => {
const details = { const details = {
info: mockSwapInfo(MockTradeType.EXACT_OUTPUT, MockUSDC_MAINNET, oneUSDCRaw, MockDAI, oneDAIRaw), info: mockSwapInfo(
MockTradeType.EXACT_OUTPUT,
MockUSDC_MAINNET,
mockCurrencyAmountRawUSDC,
MockDAI,
mockCurrencyAmountRaw
),
receipt: { receipt: {
transactionHash: '0x123', transactionHash: '0x123',
status: 1, status: 1,
}, },
} as TransactionDetails } as TransactionDetails
const chainId = SupportedChainId.MAINNET const chainId = SupportedChainId.MAINNET
const tokens = mockTokenAddressMap(MockUSDC_MAINNET as WrappedTokenInfo, MockDAI as WrappedTokenInfo) expect(parseLocalActivity(details, chainId, mockTokenAddressMap)).toMatchObject({
expect(parseLocalActivity(details, chainId, tokens)).toEqual({
chainId: 1, chainId: 1,
currencies: [MockUSDC_MAINNET, MockDAI], currencies: [MockUSDC_MAINNET, MockDAI],
descriptor: '1.00 USDC for 1.00 DAI', descriptor: '1.00 USDC for 1.00 DAI',
hash: undefined,
receipt: {
id: '0x123',
info: {
type: 1,
tradeType: MockTradeType.EXACT_OUTPUT,
inputCurrencyId: MockUSDC_MAINNET.address,
expectedInputCurrencyAmountRaw: oneUSDCRaw,
maximumInputCurrencyAmountRaw: oneUSDCRaw,
outputCurrencyId: MockDAI.address,
outputCurrencyAmountRaw: oneDAIRaw,
},
receipt: { status: 1, transactionHash: '0x123' },
status: 'CONFIRMED',
transactionHash: '0x123',
},
status: 'CONFIRMED', status: 'CONFIRMED',
timestamp: NaN,
title: 'Swapped', title: 'Swapped',
}) })
}) })
it('returns swap activity fields with unknown tokens', () => { it('returns swap activity fields with unknown tokens', () => {
const details = { const details = {
info: mockSwapInfo(MockTradeType.EXACT_INPUT, MockUSDC_MAINNET, oneUSDCRaw, MockDAI, oneDAIRaw), info: mockSwapInfo(
MockTradeType.EXACT_INPUT,
MockUSDC_MAINNET,
mockCurrencyAmountRawUSDC,
MockDAI,
mockCurrencyAmountRaw
),
receipt: { receipt: {
transactionHash: '0x123', transactionHash: '0x123',
status: 1, status: 1,
...@@ -173,28 +301,11 @@ describe('parseLocalActivity', () => { ...@@ -173,28 +301,11 @@ describe('parseLocalActivity', () => {
} as TransactionDetails } as TransactionDetails
const chainId = SupportedChainId.MAINNET const chainId = SupportedChainId.MAINNET
const tokens = {} as TokenAddressMap const tokens = {} as TokenAddressMap
expect(parseLocalActivity(details, chainId, tokens)).toEqual({ expect(parseLocalActivity(details, chainId, tokens)).toMatchObject({
chainId: 1, chainId: 1,
currencies: [undefined, undefined], currencies: [undefined, undefined],
descriptor: 'Unknown for Unknown', descriptor: 'Unknown for Unknown',
hash: undefined,
receipt: {
id: '0x123',
info: {
type: 1,
tradeType: MockTradeType.EXACT_INPUT,
inputCurrencyId: MockUSDC_MAINNET.address,
inputCurrencyAmountRaw: oneUSDCRaw,
outputCurrencyId: MockDAI.address,
expectedOutputCurrencyAmountRaw: oneDAIRaw,
minimumOutputCurrencyAmountRaw: oneDAIRaw,
},
receipt: { status: 1, transactionHash: '0x123' },
status: 'CONFIRMED',
transactionHash: '0x123',
},
status: 'CONFIRMED', status: 'CONFIRMED',
timestamp: NaN,
title: 'Swapped', title: 'Swapped',
}) })
}) })
...@@ -204,6 +315,198 @@ describe('parseLocalActivity', () => { ...@@ -204,6 +315,198 @@ describe('parseLocalActivity', () => {
const account2Activites = renderHook(() => useLocalActivities(mockAccount2)).result.current const account2Activites = renderHook(() => useLocalActivities(mockAccount2)).result.current
expect(Object.values(account1Activites)).toHaveLength(1) expect(Object.values(account1Activites)).toHaveLength(1)
expect(Object.values(account2Activites)).toHaveLength(2) expect(Object.values(account2Activites)).toHaveLength(30)
})
it('Properly uses correct tense of activity title based on tx status', () => {
const activities = renderHook(() => useLocalActivities(mockAccount2)).result.current
expect(activities[mockHash('0xswap_exact_input', MockTxStatus.Pending)]?.title).toEqual('Swapping')
expect(activities[mockHash('0xswap_exact_input', MockTxStatus.Confirmed)]?.title).toEqual('Swapped')
expect(activities[mockHash('0xswap_exact_input', MockTxStatus.Failed)]?.title).toEqual('Swap failed')
})
it('Adapts Swap exact input to Activity type', () => {
const hash = mockHash('0xswap_exact_input')
const activity = renderHook(() => useLocalActivities(mockAccount2)).result.current[hash]
expect(activity).toMatchObject({
chainId: mockChainId,
currencies: [MockUSDC_MAINNET, MockDAI],
title: 'Swapped',
descriptor: `1.00 ${MockUSDC_MAINNET.symbol} for 1.00 ${MockDAI.symbol}`,
hash,
status: MockTxStatus.Confirmed,
receipt: {
id: hash,
status: MockTxStatus.Confirmed,
},
})
})
it('Adapts Swap exact output to Activity type', () => {
const hash = mockHash('0xswap_exact_output')
const activity = renderHook(() => useLocalActivities(mockAccount2)).result.current[hash]
expect(activity).toMatchObject({
chainId: mockChainId,
currencies: [MockUSDC_MAINNET, MockDAI],
title: 'Swapped',
descriptor: `1.00 ${MockUSDC_MAINNET.symbol} for 1.00 ${MockDAI.symbol}`,
hash,
status: MockTxStatus.Confirmed,
receipt: {
id: hash,
status: MockTxStatus.Confirmed,
},
})
})
it('Adapts Approval to Activity type', () => {
const hash = mockHash('0xapproval')
const activity = renderHook(() => useLocalActivities(mockAccount2)).result.current[hash]
expect(activity).toMatchObject({
chainId: mockChainId,
currencies: [MockDAI],
title: 'Approved',
descriptor: MockDAI.symbol,
hash,
status: MockTxStatus.Confirmed,
receipt: {
id: hash,
status: MockTxStatus.Confirmed,
},
})
})
it('Adapts Wrap to Activity type', () => {
const hash = mockHash('0xwrap')
const activity = renderHook(() => useLocalActivities(mockAccount2)).result.current[hash]
const native = nativeOnChain(mockChainId)
expect(activity).toMatchObject({
chainId: mockChainId,
currencies: [native, native.wrapped],
title: 'Wrapped',
descriptor: `1.00 ${native.symbol} for 1.00 ${native.wrapped.symbol}`,
hash,
status: MockTxStatus.Confirmed,
receipt: {
id: hash,
status: MockTxStatus.Confirmed,
},
})
})
it('Adapts Unwrap to Activity type', () => {
const hash = mockHash('0xunwrap')
const activity = renderHook(() => useLocalActivities(mockAccount2)).result.current[hash]
const native = nativeOnChain(mockChainId)
expect(activity).toMatchObject({
chainId: mockChainId,
currencies: [native.wrapped, native],
title: 'Unwrapped',
descriptor: `1.00 ${native.wrapped.symbol} for 1.00 ${native.symbol}`,
hash,
status: MockTxStatus.Confirmed,
receipt: {
id: hash,
status: MockTxStatus.Confirmed,
},
})
})
it('Adapts AddLiquidityV3 to Activity type', () => {
const hash = mockHash('0xadd_liquidity_v3')
const activity = renderHook(() => useLocalActivities(mockAccount2)).result.current[hash]
expect(activity).toMatchObject({
chainId: mockChainId,
currencies: [MockUSDC_MAINNET, MockDAI],
title: 'Added liquidity',
descriptor: `1.00 ${MockUSDC_MAINNET.symbol} and 1.00 ${MockDAI.symbol}`,
hash,
status: MockTxStatus.Confirmed,
receipt: {
id: hash,
status: MockTxStatus.Confirmed,
},
})
})
it('Adapts RemoveLiquidityV3 to Activity type', () => {
const hash = mockHash('0xremove_liquidity_v3')
const activity = renderHook(() => useLocalActivities(mockAccount2)).result.current[hash]
expect(activity).toMatchObject({
chainId: mockChainId,
currencies: [MockUSDC_MAINNET, MockDAI],
title: 'Removed liquidity',
descriptor: `1.00 ${MockUSDC_MAINNET.symbol} and 1.00 ${MockDAI.symbol}`,
hash,
status: MockTxStatus.Confirmed,
receipt: {
id: hash,
status: MockTxStatus.Confirmed,
},
})
})
it('Adapts RemoveLiquidityV2 to Activity type', () => {
const hash = mockHash('0xadd_liquidity_v2')
const activity = renderHook(() => useLocalActivities(mockAccount2)).result.current[hash]
expect(activity).toMatchObject({
chainId: mockChainId,
currencies: [MockUSDC_MAINNET, MockDAI],
title: 'Added V2 liquidity',
descriptor: `1.00 ${MockUSDC_MAINNET.symbol} and 1.00 ${MockDAI.symbol}`,
hash,
status: MockTxStatus.Confirmed,
receipt: {
id: hash,
status: MockTxStatus.Confirmed,
},
})
})
it('Adapts CollectFees to Activity type', () => {
const hash = mockHash('0xcollect_fees')
const activity = renderHook(() => useLocalActivities(mockAccount2)).result.current[hash]
expect(activity).toMatchObject({
chainId: mockChainId,
currencies: [MockUSDC_MAINNET, MockDAI],
title: 'Collected fees',
descriptor: `1.00 ${MockUSDC_MAINNET.symbol} and 1.00 ${MockDAI.symbol}`,
hash,
status: MockTxStatus.Confirmed,
receipt: {
id: hash,
status: MockTxStatus.Confirmed,
},
})
})
it('Adapts MigrateLiquidityV3 to Activity type', () => {
const hash = mockHash('0xmigrate_v3_liquidity')
const activity = renderHook(() => useLocalActivities(mockAccount2)).result.current[hash]
expect(activity).toMatchObject({
chainId: mockChainId,
currencies: [MockUSDC_MAINNET, MockDAI],
title: 'Migrated liquidity',
descriptor: `${MockUSDC_MAINNET.symbol} and ${MockDAI.symbol}`,
hash,
status: MockTxStatus.Confirmed,
receipt: {
id: hash,
status: MockTxStatus.Confirmed,
},
})
}) })
}) })
...@@ -122,7 +122,7 @@ function parseMigrateCreateV3( ...@@ -122,7 +122,7 @@ function parseMigrateCreateV3(
): Partial<Activity> { ): Partial<Activity> {
const baseCurrency = getCurrency(lp.baseCurrencyId, chainId, tokens) const baseCurrency = getCurrency(lp.baseCurrencyId, chainId, tokens)
const baseSymbol = baseCurrency?.symbol ?? t`Unknown` const baseSymbol = baseCurrency?.symbol ?? t`Unknown`
const quoteCurrency = getCurrency(lp.baseCurrencyId, chainId, tokens) const quoteCurrency = getCurrency(lp.quoteCurrencyId, chainId, tokens)
const quoteSymbol = quoteCurrency?.symbol ?? t`Unknown` const quoteSymbol = quoteCurrency?.symbol ?? t`Unknown`
const descriptor = t`${baseSymbol} and ${quoteSymbol}` const descriptor = t`${baseSymbol} and ${quoteSymbol}`
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment