diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 1657db0d3..58c1be582 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -843,6 +843,9 @@
"message": "Hide $1",
"description": "$1 is the symbol for a token (e.g. 'DAI')"
},
+ "hideZeroBalanceTokens": {
+ "message": "Hide Tokens Without Balance"
+ },
"history": {
"message": "History"
},
diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js
index 539978bca..240aaaa18 100644
--- a/app/scripts/controllers/preferences.js
+++ b/app/scripts/controllers/preferences.js
@@ -61,6 +61,7 @@ export default class PreferencesController {
autoLockTimeLimit: undefined,
showFiatInTestnets: false,
useNativeCurrencyAsPrimaryCurrency: true,
+ hideZeroBalanceTokens: false,
},
completedOnboarding: false,
// ENS decentralized website resolution
diff --git a/ui/app/components/app/token-list/token-list.js b/ui/app/components/app/token-list/token-list.js
index 298dda081..3fcc71d38 100644
--- a/ui/app/components/app/token-list/token-list.js
+++ b/ui/app/components/app/token-list/token-list.js
@@ -6,17 +6,31 @@ import { useSelector } from 'react-redux';
import TokenCell from '../token-cell';
import { useI18nContext } from '../../../hooks/useI18nContext';
import { useTokenTracker } from '../../../hooks/useTokenTracker';
-import { getAssetImages } from '../../../selectors';
-import { getTokens } from '../../../ducks/metamask/metamask';
+import {
+ getAssetImages,
+ getShouldHideZeroBalanceTokens,
+} from '../../../selectors';
+import {
+ getTokens,
+ getTokensWithBalance,
+} from '../../../ducks/metamask/metamask';
export default function TokenList({ onTokenClick }) {
const t = useI18nContext();
const assetImages = useSelector(getAssetImages);
+ const shouldHideZeroBalanceTokens = useSelector(
+ getShouldHideZeroBalanceTokens,
+ );
// use `isEqual` comparison function because the token array is serialized
// from the background so it has a new reference with each background update,
// even if the tokens haven't changed
const tokens = useSelector(getTokens, isEqual);
- const { loading, tokensWithBalances } = useTokenTracker(tokens, true);
+ const tokensWithBalance = useSelector(getTokensWithBalance, isEqual);
+
+ const { loading, tokensWithBalances } = useTokenTracker(
+ shouldHideZeroBalanceTokens ? tokensWithBalance : tokens,
+ true,
+ );
if (loading) {
return (
diff --git a/ui/app/ducks/metamask/metamask.js b/ui/app/ducks/metamask/metamask.js
index 31c808877..63591e5ce 100644
--- a/ui/app/ducks/metamask/metamask.js
+++ b/ui/app/ducks/metamask/metamask.js
@@ -386,3 +386,6 @@ export const getUnconnectedAccountAlertShown = (state) =>
state.metamask.unconnectedAccountAlertShownOrigins;
export const getTokens = (state) => state.metamask.tokens;
+
+export const getTokensWithBalance = (state) =>
+ state.metamask.tokens.filter((token) => Number(token.balance) > 0);
diff --git a/ui/app/pages/settings/settings-tab/settings-tab.component.js b/ui/app/pages/settings/settings-tab/settings-tab.component.js
index 74e9c8563..71aae33a6 100644
--- a/ui/app/pages/settings/settings-tab/settings-tab.component.js
+++ b/ui/app/pages/settings/settings-tab/settings-tab.component.js
@@ -41,6 +41,8 @@ export default class SettingsTab extends PureComponent {
nativeCurrency: PropTypes.string,
useNativeCurrencyAsPrimaryCurrency: PropTypes.bool,
setUseNativeCurrencyAsPrimaryCurrencyPreference: PropTypes.func,
+ hideZeroBalanceTokens: PropTypes.bool,
+ setHideZeroBalanceTokens: PropTypes.func,
};
renderCurrentConversion() {
@@ -101,12 +103,35 @@ export default class SettingsTab extends PureComponent {
);
}
+ renderHideZeroBalanceTokensOptIn() {
+ const { t } = this.context;
+ const { hideZeroBalanceTokens, setHideZeroBalanceTokens } = this.props;
+
+ return (
+
+
+ {t('hideZeroBalanceTokens')}
+
+
+
+ setHideZeroBalanceTokens(!value)}
+ offLabel={t('off')}
+ onLabel={t('on')}
+ />
+
+
+
+ );
+ }
+
renderBlockieOptIn() {
const { t } = this.context;
const { useBlockie, setUseBlockie } = this.props;
return (
-
+
{this.context.t('blockiesIdenticon')}
@@ -192,6 +217,7 @@ export default class SettingsTab extends PureComponent {
{this.renderUsePrimaryCurrencyOptions()}
{this.renderCurrentLocale()}
{this.renderBlockieOptIn()}
+ {this.renderHideZeroBalanceTokensOptIn()}
);
}
diff --git a/ui/app/pages/settings/settings-tab/settings-tab.container.js b/ui/app/pages/settings/settings-tab/settings-tab.container.js
index ce8de1363..2e964e551 100644
--- a/ui/app/pages/settings/settings-tab/settings-tab.container.js
+++ b/ui/app/pages/settings/settings-tab/settings-tab.container.js
@@ -4,6 +4,7 @@ import {
setUseBlockie,
updateCurrentLocale,
setUseNativeCurrencyAsPrimaryCurrencyPreference,
+ setHideZeroBalanceTokens,
setParticipateInMetaMetrics,
} from '../../../store/actions';
import { getPreferences } from '../../../selectors';
@@ -21,7 +22,10 @@ const mapStateToProps = (state) => {
useBlockie,
currentLocale,
} = metamask;
- const { useNativeCurrencyAsPrimaryCurrency } = getPreferences(state);
+ const {
+ useNativeCurrencyAsPrimaryCurrency,
+ hideZeroBalanceTokens,
+ } = getPreferences(state);
return {
warning,
@@ -31,6 +35,7 @@ const mapStateToProps = (state) => {
nativeCurrency,
useBlockie,
useNativeCurrencyAsPrimaryCurrency,
+ hideZeroBalanceTokens,
};
};
@@ -44,6 +49,8 @@ const mapDispatchToProps = (dispatch) => {
},
setParticipateInMetaMetrics: (val) =>
dispatch(setParticipateInMetaMetrics(val)),
+ setHideZeroBalanceTokens: (value) =>
+ dispatch(setHideZeroBalanceTokens(value)),
};
};
diff --git a/ui/app/pages/settings/settings-tab/tests/settings-tab.test.js b/ui/app/pages/settings/settings-tab/tests/settings-tab.test.js
index da3630a24..969044da7 100644
--- a/ui/app/pages/settings/settings-tab/tests/settings-tab.test.js
+++ b/ui/app/pages/settings/settings-tab/tests/settings-tab.test.js
@@ -13,6 +13,7 @@ describe('Settings Tab', function () {
setUseBlockie: sinon.spy(),
updateCurrentLocale: sinon.spy(),
setUseNativeCurrencyAsPrimaryCurrencyPreference: sinon.spy(),
+ setHideZeroBalanceTokens: sinon.spy(),
warning: '',
currentLocale: 'en',
useBlockie: false,
@@ -51,9 +52,16 @@ describe('Settings Tab', function () {
});
it('toggles blockies', function () {
- const toggleBlockies = wrapper.find({ type: 'checkbox' });
+ const toggleBlockies = wrapper.find('#blockie-optin input');
toggleBlockies.simulate('click');
assert(props.setUseBlockie.calledOnce);
});
+
+ it('toggles hiding zero balance', function () {
+ const toggleBlockies = wrapper.find('#toggle-zero-balance input');
+
+ toggleBlockies.simulate('click');
+ assert(props.setHideZeroBalanceTokens.calledOnce);
+ });
});
diff --git a/ui/app/selectors/selectors.js b/ui/app/selectors/selectors.js
index af6e942ff..a2bbbc5a8 100644
--- a/ui/app/selectors/selectors.js
+++ b/ui/app/selectors/selectors.js
@@ -320,6 +320,11 @@ export function getShouldShowFiat(state) {
return Boolean(isMainNet || showFiatInTestnets);
}
+export function getShouldHideZeroBalanceTokens(state) {
+ const { hideZeroBalanceTokens } = getPreferences(state);
+ return hideZeroBalanceTokens;
+}
+
export function getAdvancedInlineGasShown(state) {
return Boolean(state.metamask.featureFlags.advancedInlineGas);
}
diff --git a/ui/app/store/actions.js b/ui/app/store/actions.js
index d0dae9c76..56fcd3473 100644
--- a/ui/app/store/actions.js
+++ b/ui/app/store/actions.js
@@ -2046,6 +2046,10 @@ export function setUseNativeCurrencyAsPrimaryCurrencyPreference(value) {
return setPreference('useNativeCurrencyAsPrimaryCurrency', value);
}
+export function setHideZeroBalanceTokens(value) {
+ return setPreference('hideZeroBalanceTokens', value);
+}
+
export function setShowFiatConversionOnTestnetsPreference(value) {
return setPreference('showFiatInTestnets', value);
}