Skip to content

Commit

Permalink
feat: implement Sentry for state tracking (Uniswap#6015)
Browse files Browse the repository at this point in the history
* feat: add sentry middleware

* chore: remove file

* chore: reorg

* chore: update txt

* chore: fix types

* chore

* chore: normalize

* add todo

* Update src/state/logging.ts

Co-authored-by: Zach Pomerantz <[email protected]>

* Update src/state/logging.ts

Co-authored-by: Zach Pomerantz <[email protected]>

* chore: update type

* nits

---------

Co-authored-by: Zach Pomerantz <[email protected]>
  • Loading branch information
grabbou and zzmp authored Mar 8, 2023
1 parent 7b7e4e6 commit 4ebc467
Show file tree
Hide file tree
Showing 15 changed files with 125 additions and 45 deletions.
4 changes: 2 additions & 2 deletions src/graphql/thegraph/apollo.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ApolloClient, ApolloLink, concat, HttpLink, InMemoryCache } from '@apollo/client'
import { SupportedChainId } from 'constants/chains'

import store, { AppState } from '../../state/index'
import store from '../../state/index'

const CHAIN_SUBGRAPH_URL: Record<number, string> = {
[SupportedChainId.MAINNET]: 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3',
Expand All @@ -21,7 +21,7 @@ const httpLink = new HttpLink({ uri: CHAIN_SUBGRAPH_URL[SupportedChainId.MAINNET
// For more information: https://www.apollographql.com/docs/react/networking/advanced-http-networking/
const authMiddleware = new ApolloLink((operation, forward) => {
// add the authorization to the headers
const chainId = (store.getState() as AppState).application.chainId
const chainId = store.getState().application.chainId

operation.setContext(() => ({
uri:
Expand Down
7 changes: 7 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ if (isSentryEnabled()) {
Sentry.init({
dsn: process.env.REACT_APP_SENTRY_DSN,
release: process.env.REACT_APP_GIT_COMMIT_HASH,
/**
* TODO(INFRA-143)
* According to Sentry, this shouldn't be necessary, as they default to `3` when not set.
* Unfortunately, that doesn't work right now, so we workaround it by explicitly setting
* the `normalizeDepth` to `10`. This should be removed once the issue is fixed.
*/
normalizeDepth: 10,
})
}

Expand Down
2 changes: 1 addition & 1 deletion src/state/application/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { DEFAULT_TXN_DISMISS_MS } from 'constants/misc'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useAppDispatch, useAppSelector } from 'state/hooks'

import { AppState } from '../index'
import { AppState } from '../types'
import {
addPopup,
ApplicationModal,
Expand Down
2 changes: 1 addition & 1 deletion src/state/burn/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { useAppDispatch, useAppSelector } from 'state/hooks'
import { useTotalSupply } from '../../hooks/useTotalSupply'
import { useV2Pair } from '../../hooks/useV2Pairs'
import { useTokenBalances } from '../connection/hooks'
import { AppState } from '../index'
import { AppState } from '../types'
import { Field, typeInput } from './actions'

export function useBurnState(): AppState['burn'] {
Expand Down
2 changes: 1 addition & 1 deletion src/state/burn/v3/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { useAppDispatch, useAppSelector } from 'state/hooks'
import { PositionDetails } from 'types/position'
import { unwrappedToken } from 'utils/unwrappedToken'

import { AppState } from '../../index'
import { AppState } from '../../types'
import { selectPercent } from './actions'

export function useBurnV3State(): AppState['burnV3'] {
Expand Down
7 changes: 4 additions & 3 deletions src/state/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import { AppDispatch, AppState } from 'state'

export const useAppDispatch = () => useDispatch<AppDispatch>()
export const useAppSelector: TypedUseSelectorHook<AppState> = useSelector
import store from './index'

export const useAppDispatch = () => useDispatch<typeof store.dispatch>()
export const useAppSelector: TypedUseSelectorHook<ReturnType<typeof store.getState>> = useSelector
36 changes: 4 additions & 32 deletions src/state/index.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,18 @@
import { configureStore } from '@reduxjs/toolkit'
import { setupListeners } from '@reduxjs/toolkit/query/react'
import multicall from 'lib/state/multicall'
import { load, save } from 'redux-localstorage-simple'
import { isTestEnv } from 'utils/env'

import application from './application/reducer'
import burn from './burn/reducer'
import burnV3 from './burn/v3/reducer'
import connection from './connection/reducer'
import { updateVersion } from './global/actions'
import lists from './lists/reducer'
import logs from './logs/slice'
import mint from './mint/reducer'
import mintV3 from './mint/v3/reducer'
import { sentryEnhancer } from './logging'
import reducer from './reducer'
import { routingApi } from './routing/slice'
import swap from './swap/reducer'
import transactions from './transactions/reducer'
import user from './user/reducer'
import wallets from './wallets/reducer'

const PERSISTED_KEYS: string[] = ['user', 'transactions', 'lists']

const store = configureStore({
reducer: {
application,
user,
connection,
transactions,
wallets,
swap,
mint,
mintV3,
burn,
burnV3,
multicall: multicall.reducer,
lists,
logs,
[routingApi.reducerPath]: routingApi.reducer,
},
reducer,
enhancers: (defaultEnhancers) => defaultEnhancers.concat(sentryEnhancer),
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({ thunk: true })
.concat(routingApi.middleware)
Expand All @@ -50,6 +25,3 @@ store.dispatch(updateVersion())
setupListeners(store.dispatch)

export default store

export type AppState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
2 changes: 1 addition & 1 deletion src/state/lists/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useAppSelector } from 'state/hooks'
import sortByListPriority from 'utils/listSort'

import BROKEN_LIST from '../../constants/tokenLists/broken.tokenlist.json'
import { AppState } from '../index'
import { AppState } from '../types'
import { DEFAULT_ACTIVE_LIST_URLS, UNSUPPORTED_LIST_URLS } from './../../constants/lists'

export type TokenAddressMap = ChainTokenMap
Expand Down
58 changes: 58 additions & 0 deletions src/state/logging.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import * as Sentry from '@sentry/react'

import { AppState } from './types'

/* Utility type to mark all properties of a type as optional */
type DeepPartial<T> = T extends object
? {
[P in keyof T]?: DeepPartial<T[P]>
}
: T

/**
* This enhancer will automatically store the latest state in Sentry's scope, so that it will be available
* in the Sentry dashboard when an exception happens.
*/
export const sentryEnhancer = Sentry.createReduxEnhancer({
/**
* We don't want to store actions as breadcrumbs in Sentry, so we return null to disable the default behavior.
*/
actionTransformer: () => null,
/**
* We only want to store a subset of the state in Sentry, containing only the relevant parts for debugging.
* Note: This function runs on every state update, so we're keeping it as fast as possible by avoiding any function
* calls and deep object traversals.
*/
stateTransformer: (state: AppState): DeepPartial<AppState> => {
const { application, user, connection, transactions } = state
return {
application: {
fiatOnramp: application.fiatOnramp,
chainId: application.chainId,
openModal: application.openModal,
popupList: application.popupList,
},
user: {
fiatOnrampAcknowledgments: user.fiatOnrampAcknowledgments,
selectedWallet: user.selectedWallet,
lastUpdateVersionTimestamp: user.lastUpdateVersionTimestamp,
matchesDarkMode: user.matchesDarkMode,
userDarkMode: user.userDarkMode,
userLocale: user.userLocale,
userExpertMode: user.userExpertMode,
userClientSideRouter: user.userClientSideRouter,
userHideClosedPositions: user.userHideClosedPositions,
userSlippageTolerance: user.userSlippageTolerance,
userSlippageToleranceHasBeenMigratedToAuto: user.userSlippageToleranceHasBeenMigratedToAuto,
userDeadline: user.userDeadline,
timestamp: user.timestamp,
URLWarningVisible: user.URLWarningVisible,
showSurveyPopup: user.showSurveyPopup,
},
connection: {
errorByConnectionType: connection.errorByConnectionType,
},
transactions,
}
},
})
2 changes: 1 addition & 1 deletion src/state/mint/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { useAppDispatch, useAppSelector } from 'state/hooks'
import { useTotalSupply } from '../../hooks/useTotalSupply'
import { PairState, useV2Pair } from '../../hooks/useV2Pairs'
import { useCurrencyBalances } from '../connection/hooks'
import { AppState } from '../index'
import { AppState } from '../types'
import { Field, typeInput } from './actions'

const ZERO = JSBI.BigInt(0)
Expand Down
2 changes: 1 addition & 1 deletion src/state/mint/v3/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { replaceURLParam } from 'utils/routes'
import { BIG_INT_ZERO } from '../../../constants/misc'
import { PoolState } from '../../../hooks/usePools'
import { useCurrencyBalances } from '../../connection/hooks'
import { AppState } from '../../index'
import { AppState } from '../../types'
import {
Bound,
Field,
Expand Down
32 changes: 32 additions & 0 deletions src/state/reducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import multicall from 'lib/state/multicall'

import application from './application/reducer'
import burn from './burn/reducer'
import burnV3 from './burn/v3/reducer'
import connection from './connection/reducer'
import lists from './lists/reducer'
import logs from './logs/slice'
import mint from './mint/reducer'
import mintV3 from './mint/v3/reducer'
import { routingApi } from './routing/slice'
import swap from './swap/reducer'
import transactions from './transactions/reducer'
import user from './user/reducer'
import wallets from './wallets/reducer'

export default {
application,
user,
connection,
transactions,
wallets,
swap,
mint,
mintV3,
burn,
burnV3,
multicall: multicall.reducer,
lists,
logs,
[routingApi.reducerPath]: routingApi.reducer,
}
2 changes: 1 addition & 1 deletion src/state/swap/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import useENS from '../../hooks/useENS'
import useParsedQueryString from '../../hooks/useParsedQueryString'
import { isAddress } from '../../utils'
import { useCurrencyBalances } from '../connection/hooks'
import { AppState } from '../index'
import { AppState } from '../types'
import { Field, replaceSwapState, selectCurrency, setRecipient, switchCurrencies, typeInput } from './actions'
import { SwapState } from './reducer'

Expand Down
10 changes: 10 additions & 0 deletions src/state/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Reducer } from '@reduxjs/toolkit'

import reducer from './reducer'

/* Utility type to extract state type out of a @reduxjs/toolkit Reducer type */
type GetState<T> = T extends Reducer<infer State> ? State : never

export type AppState = {
[K in keyof typeof reducer]: GetState<typeof reducer[K]>
}
2 changes: 1 addition & 1 deletion src/state/user/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { UserAddedToken } from 'types/tokens'
import { V2_FACTORY_ADDRESSES } from '../../constants/addresses'
import { BASES_TO_TRACK_LIQUIDITY_FOR, PINNED_PAIRS } from '../../constants/routing'
import { useAllTokens } from '../../hooks/Tokens'
import { AppState } from '../index'
import { AppState } from '../types'
import {
addSerializedPair,
addSerializedToken,
Expand Down

0 comments on commit 4ebc467

Please sign in to comment.