From e2dcd67db1d2f4d5d34755a5fc0a73d3292f5042 Mon Sep 17 00:00:00 2001 From: Joe Bergeron Date: Mon, 9 Oct 2023 10:39:59 -0400 Subject: [PATCH] feat(multichain): Use new dynamic config for multichain features (#4265) ### Description For ACT-931. Replaces three of our existing feature gates with a single Statsig Dynamic Config (available [here](https://console.statsig.com/4plizaPmWwPL21ASV4QAO0/dynamic_configs/multi_chain_features)). The `show_native_tokens` feature gate is a straggler here in terms of multichain config. This was added as part of a quick and dirty hack to get non-Celo transfers showing up in the home feed a while back, and ought to be removed. I'll remove this in a followup PR. ### Test plan Manual and unit tested. ### Related issues - Fixes ACT-931 ### Backwards compatibility Yes. --- src/components/TokenBalance.test.tsx | 5 ++++- src/fiatExchanges/FiatExchangeCurrency.test.tsx | 14 ++++++++++---- src/fiatExchanges/FiatExchangeCurrency.tsx | 12 +++++++++--- src/home/WalletHome.test.tsx | 4 ++++ src/navigator/DrawerNavigator.test.tsx | 3 +++ src/statsig/constants.ts | 12 +++++++++--- src/statsig/types.ts | 4 +--- src/tokens/Assets.test.tsx | 6 +++--- src/tokens/TokenBalances.test.tsx | 2 +- src/tokens/saga.test.ts | 10 +++++++--- src/tokens/saga.ts | 7 ++----- src/tokens/utils.ts | 13 ++++++------- src/transactions/feed/TransactionFeed.test.tsx | 5 +++++ src/transactions/feed/queryHelper.ts | 10 +++++----- 14 files changed, 69 insertions(+), 38 deletions(-) diff --git a/src/components/TokenBalance.test.tsx b/src/components/TokenBalance.test.tsx index be5073fb163..07f055c106d 100644 --- a/src/components/TokenBalance.test.tsx +++ b/src/components/TokenBalance.test.tsx @@ -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 * as tokenUtils from 'src/tokens/utils' import { NetworkId } from 'src/transactions/types' import { ONE_DAY_IN_MILLIS } from 'src/utils/time' @@ -49,6 +49,9 @@ const defaultStore = { } jest.mocked(getFeatureGate).mockReturnValue(true) +jest.mocked(getDynamicConfigParams).mockReturnValue({ + showBalances: [NetworkId['celo-alfajores']], +}) describe('FiatExchangeTokenBalance and HomeTokenBalance', () => { const showAssetDetailsScreenSpy = jest.spyOn(tokenUtils, 'showAssetDetailsScreen') diff --git a/src/fiatExchanges/FiatExchangeCurrency.test.tsx b/src/fiatExchanges/FiatExchangeCurrency.test.tsx index 9afe3bb96dc..c8ea7749413 100644 --- a/src/fiatExchanges/FiatExchangeCurrency.test.tsx +++ b/src/fiatExchanges/FiatExchangeCurrency.test.tsx @@ -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 { + showCico: ['celo-alfajores'], + } + }), })) const mockScreenProps = (flow: FiatExchangeFlow) => @@ -79,7 +83,9 @@ describe('FiatExchangeCurrency', () => { }) }) it('ETH Flow', () => { - jest.mocked(getFeatureGate).mockReturnValueOnce(true) + jest.mocked(getDynamicConfigParams).mockReturnValueOnce({ + showCico: [NetworkId['celo-alfajores'], NetworkId['ethereum-sepolia']], + }) const store = createMockStore({}) const tree = render( diff --git a/src/fiatExchanges/FiatExchangeCurrency.tsx b/src/fiatExchanges/FiatExchangeCurrency.tsx index 9b0adc394a1..632f2db022e 100644 --- a/src/fiatExchanges/FiatExchangeCurrency.tsx +++ b/src/fiatExchanges/FiatExchangeCurrency.tsx @@ -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 @@ -104,7 +107,10 @@ function FiatExchangeCurrency({ route, navigation }: Props) { const { flow } = route.params const [selectedCurrency, setSelectedCurrency] = useState(CiCoCurrency.cUSD) - const showEth = getFeatureGate(StatsigFeatureGates.SHOW_ETH_IN_CICO) + // TODO: Update this to actually respect all possible networkIds correctly + const showEth = getDynamicConfigParams( + DynamicConfigs[StatsigDynamicConfigs.MULTI_CHAIN_FEATURES] + ).showCico.includes(networkConfig.networkToNetworkId[Network.Ethereum]) // Fetch FiatConnect providers silently in the background early in the CICO funnel useEffect(() => { diff --git a/src/home/WalletHome.test.tsx b/src/home/WalletHome.test.tsx index 21e39b23a76..99d307484fc 100644 --- a/src/home/WalletHome.test.tsx +++ b/src/home/WalletHome.test.tsx @@ -129,6 +129,10 @@ jest.mock('src/statsig', () => ({ cashInBottomSheetEnabled: true, })), getFeatureGate: jest.fn().mockReturnValue(false), + getDynamicConfigParams: jest.fn(() => ({ + showBalances: ['celo-alfajores'], + showTransfers: ['celo-alfajores'], + })), })) jest.mock('src/fiatExchanges/utils', () => ({ diff --git a/src/navigator/DrawerNavigator.test.tsx b/src/navigator/DrawerNavigator.test.tsx index 8dcdceab6e4..a1aae27d9df 100644 --- a/src/navigator/DrawerNavigator.test.tsx +++ b/src/navigator/DrawerNavigator.test.tsx @@ -10,6 +10,9 @@ import { createMockStore } from 'test/utils' jest.mock('src/statsig', () => ({ getExperimentParams: jest.fn(), getFeatureGate: jest.fn().mockReturnValue(false), + getDynamicConfigParams: jest.fn(() => ({ + showBalances: ['celo-alfajores'], + })), })) // TODO avoid rendering WalletHome as we're mostly interested in testing the menu here diff --git a/src/statsig/constants.ts b/src/statsig/constants.ts index d77acf36adc..ccfa6165614 100644 --- a/src/statsig/constants.ts +++ b/src/statsig/constants.ts @@ -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) @@ -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, } @@ -91,4 +89,12 @@ export const DynamicConfigs = { cico: 30, }, }, + [StatsigDynamicConfigs.MULTI_CHAIN_FEATURES]: { + configName: StatsigDynamicConfigs.MULTI_CHAIN_FEATURES, + defaultValues: { + showCico: [networkConfig.defaultNetworkId], + showBalances: [networkConfig.defaultNetworkId], + showTransfers: [networkConfig.defaultNetworkId], + }, + }, } diff --git a/src/statsig/types.ts b/src/statsig/types.ts index a0993d9b7fc..03af742ad85 100644 --- a/src/statsig/types.ts +++ b/src/statsig/types.ts @@ -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 { @@ -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', } diff --git a/src/tokens/Assets.test.tsx b/src/tokens/Assets.test.tsx index 225905a0727..91c0b3d8d35 100644 --- a/src/tokens/Assets.test.tsx +++ b/src/tokens/Assets.test.tsx @@ -21,9 +21,9 @@ import { jest.mock('src/statsig', () => { return { getFeatureGate: jest.fn(), - getDynamicConfigParams: jest.fn().mockReturnValue({ - show_native_tokens: false, - }), + getDynamicConfigParams: jest.fn(() => ({ + showBalances: ['celo-alfajores'], + })), } }) diff --git a/src/tokens/TokenBalances.test.tsx b/src/tokens/TokenBalances.test.tsx index dffccb9b250..9b3c771561d 100644 --- a/src/tokens/TokenBalances.test.tsx +++ b/src/tokens/TokenBalances.test.tsx @@ -28,7 +28,7 @@ jest.mock('src/statsig', () => { return { getFeatureGate: jest.fn(), getDynamicConfigParams: jest.fn().mockReturnValue({ - show_native_tokens: false, + showBalances: ['celo-alfajores'], }), } }) diff --git a/src/tokens/saga.test.ts b/src/tokens/saga.test.ts index 891e958d7be..a5fa81747fb 100644 --- a/src/tokens/saga.test.ts +++ b/src/tokens/saga.test.ts @@ -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' @@ -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> => { @@ -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> => { diff --git a/src/tokens/saga.ts b/src/tokens/saga.ts index 2e8cc9611d9..e409d6c3dee 100644 --- a/src/tokens/saga.ts +++ b/src/tokens/saga.ts @@ -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, @@ -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' @@ -237,9 +236,7 @@ interface UserBalancesResponse { export async function fetchTokenBalancesForAddress( address: string ): Promise { - 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({ diff --git a/src/tokens/utils.ts b/src/tokens/utils.ts index 908a2785198..be28acd46f6 100644 --- a/src/tokens/utils.ts +++ b/src/tokens/utils.ts @@ -1,11 +1,11 @@ import BigNumber from 'bignumber.js' -import { getFeatureGate } from 'src/statsig' -import { StatsigFeatureGates } from 'src/statsig/types' import { CurrencyTokens } from 'src/tokens/selectors' -import { NetworkId } from 'src/transactions/types' import { Currency } from 'src/utils/currencies' -import networkConfig from 'src/web3/networkConfig' import { TokenBalance } from './slice' +import { NetworkId } from 'src/transactions/types' +import { getDynamicConfigParams } from 'src/statsig' +import { StatsigDynamicConfigs } from 'src/statsig/types' +import { DynamicConfigs } from 'src/statsig/constants' export function getHigherBalanceCurrency( currencies: Currency[], @@ -120,9 +120,8 @@ 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 } export function showAssetDetailsScreen() { diff --git a/src/transactions/feed/TransactionFeed.test.tsx b/src/transactions/feed/TransactionFeed.test.tsx index d0c015e0e5d..1b773b478ff 100644 --- a/src/transactions/feed/TransactionFeed.test.tsx +++ b/src/transactions/feed/TransactionFeed.test.tsx @@ -18,6 +18,11 @@ import { mockCusdAddress } from 'test/values' jest.mock('src/statsig', () => ({ getFeatureGate: jest.fn(() => false), + getDynamicConfigParams: jest.fn(() => ({ + showCico: ['celo-alfajores'], + showBalances: ['celo-alfajores'], + showTransfers: ['celo-alfajores'], + })), })) const mockTransaction = (transactionHash: string): TokenTransaction => { diff --git a/src/transactions/feed/queryHelper.ts b/src/transactions/feed/queryHelper.ts index e49fac3f945..64ed4a58746 100644 --- a/src/transactions/feed/queryHelper.ts +++ b/src/transactions/feed/queryHelper.ts @@ -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 @@ -63,9 +64,8 @@ const deduplicateTransactions = ( } export function getAllowedNetworkIds(): Array { - 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 {