Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(multichain): Use new dynamic config for multichain features #4265

Merged
merged 10 commits into from
Oct 9, 2023
5 changes: 4 additions & 1 deletion src/components/TokenBalance.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import { LocalCurrencyCode } from 'src/localCurrency/consts'
import { navigate } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import { getFeatureGate } from 'src/statsig'
import { getDynamicConfigParams, getFeatureGate } from 'src/statsig'
import { ONE_DAY_IN_MILLIS } from 'src/utils/time'
import { createMockStore, getElementText } from 'test/utils'
import { mockPositions, mockTokenBalances } from 'test/values'
Expand Down Expand Up @@ -48,6 +48,9 @@ const defaultStore = {
}

jest.mocked(getFeatureGate).mockReturnValue(true)
jest.mocked(getDynamicConfigParams).mockReturnValue({
showBalances: [NetworkId['celo-alfajores'], NetworkId['ethereum-sepolia']],
})

describe('FiatExchangeTokenBalance and HomeTokenBalance', () => {
it.each([HomeTokenBalance, FiatExchangeTokenBalance])(
Expand Down
14 changes: 10 additions & 4 deletions src/fiatExchanges/FiatExchangeCurrency.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ import FiatExchangeCurrency from 'src/fiatExchanges/FiatExchangeCurrency'
import { fetchFiatConnectProviders } from 'src/fiatconnect/slice'
import { navigate } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import { getFeatureGate } from 'src/statsig'
import { Network } from 'src/transactions/types'
import { Network, NetworkId } from 'src/transactions/types'
import { createMockStore, getMockStackScreenProps } from 'test/utils'
import { FiatExchangeFlow } from './utils'
import { getDynamicConfigParams } from 'src/statsig'

jest.mock('src/statsig', () => ({
getFeatureGate: jest.fn(() => false),
getDynamicConfigParams: jest.fn(() => {
return {
enableCico: ['celo-alfajores'],
}
}),
}))

const mockScreenProps = (flow: FiatExchangeFlow) =>
Expand Down Expand Up @@ -79,7 +83,9 @@ describe('FiatExchangeCurrency', () => {
})
})
it('ETH Flow', () => {
jest.mocked(getFeatureGate).mockReturnValueOnce(true)
jest.mocked(getDynamicConfigParams).mockReturnValueOnce({
enableCico: [NetworkId['celo-alfajores'], NetworkId['ethereum-sepolia']],
})
const store = createMockStore({})
const tree = render(
<Provider store={store}>
Expand Down
12 changes: 9 additions & 3 deletions src/fiatExchanges/FiatExchangeCurrency.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@ import fontStyles from 'src/styles/fonts'
import variables from 'src/styles/variables'
import { CiCoCurrency, currencyForAnalytics, resolveCurrency } from 'src/utils/currencies'
import { CICOFlow, FiatExchangeFlow } from './utils'
import { getFeatureGate } from 'src/statsig'
import { StatsigFeatureGates } from 'src/statsig/types'
import { getDynamicConfigParams } from 'src/statsig'
import { StatsigDynamicConfigs } from 'src/statsig/types'
import { DynamicConfigs } from 'src/statsig/constants'
import { CiCoCurrencyNetworkMap } from 'src/fiatExchanges/types'
import { fetchFiatConnectProviders } from 'src/fiatconnect/slice'
import { useDispatch } from 'react-redux'
import networkConfig from 'src/web3/networkConfig'
import { Network } from 'src/transactions/types'

type Props = NativeStackScreenProps<StackParamList, Screens.FiatExchangeCurrency>

Expand Down Expand Up @@ -104,7 +107,10 @@ function FiatExchangeCurrency({ route, navigation }: Props) {
const { flow } = route.params

const [selectedCurrency, setSelectedCurrency] = useState<CiCoCurrency>(CiCoCurrency.cUSD)
const showEth = getFeatureGate(StatsigFeatureGates.SHOW_ETH_IN_CICO)
// TODO: Update this to actually respect all possible networkIds correctly
jophish marked this conversation as resolved.
Show resolved Hide resolved
const showEth = getDynamicConfigParams(
DynamicConfigs[StatsigDynamicConfigs.MULTI_CHAIN_FEATURES]
).enableCico.includes(networkConfig.networkToNetworkId[Network.Ethereum])

// Fetch FiatConnect providers silently in the background early in the CICO funnel
useEffect(() => {
Expand Down
5 changes: 5 additions & 0 deletions src/home/WalletHome.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ jest.mock('src/statsig', () => ({
cashInBottomSheetEnabled: true,
})),
getFeatureGate: jest.fn().mockReturnValue(false),
getDynamicConfigParams: jest.fn(() => ({
enableCico: ['celo-alfajores'],
showBalances: ['celo-alfajores'],
showTransfers: ['celo-alfajores'],
})),
}))

jest.mock('src/fiatExchanges/utils', () => ({
Expand Down
5 changes: 5 additions & 0 deletions src/navigator/DrawerNavigator.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import { createMockStore } from 'test/utils'
jest.mock('src/statsig', () => ({
getExperimentParams: jest.fn(),
getFeatureGate: jest.fn().mockReturnValue(false),
getDynamicConfigParams: jest.fn(() => ({
enableCico: ['celo-alfajores'],
showBalances: ['celo-alfajores'],
showTransfers: ['celo-alfajores'],
})),
}))

// TODO avoid rendering WalletHome as we're mostly interested in testing the menu here
Expand Down
12 changes: 9 additions & 3 deletions src/statsig/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
StatsigFeatureGates,
StatsigLayers,
} from 'src/statsig/types'
import networkConfig from 'src/web3/networkConfig'

export const LayerParams = {
// TODO(ACT-659): refactor to imitate defaultExperimentParamValues (more type safe, less boilerplate)
Expand Down Expand Up @@ -33,10 +34,7 @@ export const FeatureGates = {
[StatsigFeatureGates.SHOW_NOTIFICATION_CENTER]: false,
[StatsigFeatureGates.SHOW_CLOUD_ACCOUNT_BACKUP_SETUP]: false,
[StatsigFeatureGates.SHOW_CLOUD_ACCOUNT_BACKUP_RESTORE]: false,
[StatsigFeatureGates.SHOW_MULTI_CHAIN_TRANSFERS]: false,
[StatsigFeatureGates.SHOW_NATIVE_TOKENS]: false,
[StatsigFeatureGates.SHOW_ETH_IN_CICO]: false,
[StatsigFeatureGates.FETCH_MULTI_CHAIN_BALANCES]: false,
[StatsigFeatureGates.USE_VIEM_FOR_SEND]: false,
}

Expand Down Expand Up @@ -91,4 +89,12 @@ export const DynamicConfigs = {
cico: 30,
},
},
[StatsigDynamicConfigs.MULTI_CHAIN_FEATURES]: {
configName: StatsigDynamicConfigs.MULTI_CHAIN_FEATURES,
defaultValues: {
enableCico: [networkConfig.defaultNetworkId],
showBalances: [networkConfig.defaultNetworkId],
showTransfers: [networkConfig.defaultNetworkId],
jophish marked this conversation as resolved.
Show resolved Hide resolved
},
},
}
4 changes: 1 addition & 3 deletions src/statsig/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export enum StatsigLayers {
export enum StatsigDynamicConfigs {
USERNAME_BLOCK_LIST = 'username_block_list',
WALLET_NETWORK_TIMEOUT_SECONDS = 'wallet_network_timeout_seconds',
MULTI_CHAIN_FEATURES = 'multi_chain_features',
}

export enum StatsigFeatureGates {
Expand All @@ -29,10 +30,7 @@ export enum StatsigFeatureGates {
SHOW_NOTIFICATION_CENTER = 'show_notification_center',
SHOW_CLOUD_ACCOUNT_BACKUP_SETUP = 'show_cloud_account_backup_setup',
SHOW_CLOUD_ACCOUNT_BACKUP_RESTORE = 'show_cloud_account_backup_restore',
SHOW_MULTI_CHAIN_TRANSFERS = 'show_multi_chain_transfers',
SHOW_NATIVE_TOKENS = 'show_native_tokens',
SHOW_ETH_IN_CICO = 'show_eth_in_cico',
FETCH_MULTI_CHAIN_BALANCES = 'fetch_multi_chain_balances',
USE_VIEM_FOR_SEND = 'use_viem_for_send',
}

Expand Down
8 changes: 5 additions & 3 deletions src/tokens/Assets.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ import {
jest.mock('src/statsig', () => {
return {
getFeatureGate: jest.fn(),
getDynamicConfigParams: jest.fn().mockReturnValue({
show_native_tokens: false,
}),
getDynamicConfigParams: jest.fn(() => ({
enableCico: ['celo-alfajores'],
showBalances: ['celo-alfajores'],
showTransfers: ['celo-alfajores'],
})),
}
})

Expand Down
10 changes: 8 additions & 2 deletions src/tokens/TokenBalances.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { HomeEvents } from 'src/analytics/Events'
import ValoraAnalytics from 'src/analytics/ValoraAnalytics'
import { navigate } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import { getFeatureGate } from 'src/statsig'
import { getDynamicConfigParams, getFeatureGate } from 'src/statsig'
import TokenBalancesScreen from 'src/tokens/TokenBalances'
import { ONE_DAY_IN_MILLIS } from 'src/utils/time'
import networkConfig from 'src/web3/networkConfig'
Expand All @@ -28,7 +28,7 @@ jest.mock('src/statsig', () => {
return {
getFeatureGate: jest.fn(),
getDynamicConfigParams: jest.fn().mockReturnValue({
show_native_tokens: false,
showBalances: ['celo-alfajores'],
}),
}
})
Expand Down Expand Up @@ -191,6 +191,9 @@ describe('TokenBalancesScreen', () => {
})

it('renders the correct components when there are positions', () => {
jest.mocked(getDynamicConfigParams).mockReturnValue({
showBalances: [NetworkId['celo-alfajores'], NetworkId['ethereum-sepolia']],
})
jest.mocked(getFeatureGate).mockReturnValue(true)
const store = createMockStore(storeWithPositions)

Expand All @@ -211,6 +214,9 @@ describe('TokenBalancesScreen', () => {
})

it('renders the correct information in positions', () => {
jest.mocked(getDynamicConfigParams).mockReturnValue({
showBalances: [NetworkId['celo-alfajores'], NetworkId['ethereum-sepolia']],
})
jophish marked this conversation as resolved.
Show resolved Hide resolved
jest.mocked(getFeatureGate).mockReturnValue(true)
const store = createMockStore(storeWithPositions)

Expand Down
10 changes: 7 additions & 3 deletions src/tokens/saga.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
import { FetchMock } from 'jest-fetch-mock'
import Logger from 'src/utils/Logger'
import { apolloClient } from 'src/apollo'
import { getFeatureGate } from 'src/statsig'
import { getDynamicConfigParams } from 'src/statsig'
import { ApolloQueryResult } from 'apollo-client'
import { NetworkId } from 'src/transactions/types'

Expand Down Expand Up @@ -168,7 +168,9 @@ describe(fetchTokenBalancesSaga, () => {

describe(fetchTokenBalancesForAddress, () => {
it('returns token balances for a single chain', async () => {
jest.mocked(getFeatureGate).mockReturnValueOnce(false)
jest.mocked(getDynamicConfigParams).mockReturnValueOnce({
showBalances: [NetworkId['celo-alfajores']],
})
jest
.mocked(apolloClient.query)
.mockImplementation(async (payload: any): Promise<ApolloQueryResult<unknown>> => {
Expand All @@ -185,7 +187,9 @@ describe(fetchTokenBalancesForAddress, () => {
expect(result).toEqual(expect.arrayContaining(['celo_alfajores balance']))
})
it('returns token balances for multiple chains', async () => {
jest.mocked(getFeatureGate).mockReturnValueOnce(true)
jest.mocked(getDynamicConfigParams).mockReturnValueOnce({
showBalances: [NetworkId['celo-alfajores'], NetworkId['ethereum-sepolia']],
})
jest
.mocked(apolloClient.query)
.mockImplementation(async (payload: any): Promise<ApolloQueryResult<unknown>> => {
Expand Down
7 changes: 2 additions & 5 deletions src/tokens/saga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ import { SentryTransactionHub } from 'src/sentry/SentryTransactionHub'
import { SentryTransaction } from 'src/sentry/SentryTransactions'
import { Actions } from 'src/stableToken/actions'
import { lastKnownTokenBalancesSelector, tokensListWithAddressSelector } from 'src/tokens/selectors'
import { getFeatureGate } from 'src/statsig'
import { StatsigFeatureGates } from 'src/statsig/types'
import {
StoredTokenBalance,
StoredTokenBalances,
Expand All @@ -42,6 +40,7 @@ import networkConfig from 'src/web3/networkConfig'
import { getConnectedUnlockedAccount } from 'src/web3/saga'
import { walletAddressSelector } from 'src/web3/selectors'
import { call, put, select, spawn, take, takeEvery } from 'typed-redux-saga'
import { getSupportedNetworkIdsForTokenBalances } from 'src/tokens/utils'

import * as utf8 from 'utf8'

Expand Down Expand Up @@ -237,9 +236,7 @@ interface UserBalancesResponse {
export async function fetchTokenBalancesForAddress(
address: string
): Promise<FetchedTokenBalance[]> {
const chainsToFetch = getFeatureGate(StatsigFeatureGates.FETCH_MULTI_CHAIN_BALANCES)
? Object.values(networkConfig.networkToNetworkId)
: [networkConfig.defaultNetworkId]
const chainsToFetch = getSupportedNetworkIdsForTokenBalances()
const userBalances = await Promise.all(
chainsToFetch.map((networkId) => {
return apolloClient.query<UserBalancesResponse, { address: string; networkId: string }>({
Expand Down
11 changes: 5 additions & 6 deletions src/tokens/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { CurrencyTokens } from 'src/tokens/selectors'
import { Currency } from 'src/utils/currencies'
import { TokenBalance } from './slice'
import { NetworkId } from 'src/transactions/types'
import { getFeatureGate } from 'src/statsig'
import { StatsigFeatureGates } from 'src/statsig/types'
import networkConfig from 'src/web3/networkConfig'
import { getDynamicConfigParams } from 'src/statsig'
import { StatsigDynamicConfigs } from 'src/statsig/types'
import { DynamicConfigs } from 'src/statsig/constants'

export function getHigherBalanceCurrency(
currencies: Currency[],
Expand Down Expand Up @@ -120,7 +120,6 @@ export function convertTokenToLocalAmount({
}

export function getSupportedNetworkIdsForTokenBalances(): NetworkId[] {
return getFeatureGate(StatsigFeatureGates.FETCH_MULTI_CHAIN_BALANCES)
? Object.values(networkConfig.networkToNetworkId)
: [networkConfig.defaultNetworkId]
return getDynamicConfigParams(DynamicConfigs[StatsigDynamicConfigs.MULTI_CHAIN_FEATURES])
.showBalances
}
5 changes: 5 additions & 0 deletions src/transactions/feed/TransactionFeed.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ import { mockCusdAddress } from 'test/values'

jest.mock('src/statsig', () => ({
getFeatureGate: jest.fn(() => false),
getDynamicConfigParams: jest.fn(() => ({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noticed these in a bunch of test files, are the mocks on these test files required because there's a nested dependency to this dynamic config? And is the getFeatureGate mock now required? I wonder if we should consider creating a shared mock under __mocks__ similar to how navigate / analytics is mocked so we don't need this everywhere

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's correct, re: nested dependency. And yes, the getFeatureGate mock is still required here. And interesting -- I haven't messed around with shared mocks before. How would that work? Would there be a mock for the entirety of src/statsig that would automatically be included in all tests? What if we want to override this mock (which will certainly be the case)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes it would mock src/statsig for all tests. We can still setup test specific mocks as we do normally and it will override this mock. In any case its outside the scope of this PR, but something to consider if we have more of these shared mocks.

enableCico: ['celo-alfajores'],
showBalances: ['celo-alfajores'],
showTransfers: ['celo-alfajores'],
})),
}))

const mockTransaction = (transactionHash: string): TokenTransaction => {
Expand Down
10 changes: 5 additions & 5 deletions src/transactions/feed/queryHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ import { TokenTransaction, NetworkId } from 'src/transactions/types'
import Logger from 'src/utils/Logger'
import config from 'src/web3/networkConfig'
import { walletAddressSelector } from 'src/web3/selectors'
import { getFeatureGate } from 'src/statsig/index'
import { StatsigFeatureGates } from 'src/statsig/types'
import { getDynamicConfigParams } from 'src/statsig/index'
import { StatsigDynamicConfigs } from 'src/statsig/types'
import { DynamicConfigs } from 'src/statsig/constants'

const MIN_NUM_TRANSACTIONS = 10

Expand Down Expand Up @@ -63,9 +64,8 @@ const deduplicateTransactions = (
}

export function getAllowedNetworkIds(): Array<NetworkId> {
return getFeatureGate(StatsigFeatureGates.SHOW_MULTI_CHAIN_TRANSFERS)
? Object.values(config.networkToNetworkId)
: [config.defaultNetworkId]
return getDynamicConfigParams(DynamicConfigs[StatsigDynamicConfigs.MULTI_CHAIN_FEATURES])
.showTransfers
}

export function useFetchTransactions(): QueryHookResult {
Expand Down