Commit c9eaf77
authored
fix: Balance empty state incorrectly shown when price conversion unavailable (#38284)
## **Description**
This PR fixes an issue where the balance empty state incorrectly appears
when users have token balances, particularly on chains without price
conversion data.
**The problem:**
PR #37366 introduced a balance empty state feature that shows a "Fund
your wallet" message when accounts have $0 balance. However, the
`selectAccountGroupBalanceForEmptyState` selector only checked
`totalBalanceInUserCurrency` (fiat value) to determine if an account has
a balance.
This approach had multiple issues:
1. When price conversion data is unavailable (custom networks, price API
failures), fiat balance equals $0 even when native tokens are present
2. The expensive `calculateBalanceForAllWallets()` function was called
unnecessarily
3. The logic didn't check for actual token possession, which is what
determines if a user can transact
The issue became visible after PR #37004 turned on BIP-44 by default.
Users with native tokens on custom networks (like Anvil chainId 1338)
would see the empty state despite having a 10 ETH balance.
**The solution:**
The selector now directly checks for token balances (native and
non-native) instead of relying on fiat conversion:
1. **Native token checks:**
- **EVM chains**: Check `accountsByChainId` for non-zero hex balances
(e.g., `0x8ac7230489e80000`)
- **Non-EVM chains**: Check `multichainBalancesState.balances` for
non-zero amounts (Solana, Bitcoin, etc.)
2. **Non-native token checks:**
- **ERC-20 tokens**: Check `tokenBalancesState` for non-zero hex
balances
- **Other tokens**: Future support for SPL tokens, etc.
**Code quality improvements:**
- Uses established utilities from `@metamask/utils` (`isObject`,
`hasProperty`) for robust type checking
- Added helper function `getBalanceOrDefault()` for safe balance
extraction
- Uses `isEmptyHexString()` and `isZeroAmount()` utilities for reliable
zero detection
- Removed unnecessary defensive checks for unrealistic edge cases
- Better performance: no longer calls expensive
`calculateBalanceForAllWallets()`
The empty state now correctly appears only when users have NO tokens
(neither native nor non-native), regardless of price API availability.
## **Changelog**
CHANGELOG entry: Fixed balance empty state incorrectly showing when
price conversion data is unavailable but tokens are present
## **Related issues**
Fixes: Issue exposed by #37004, related to #37366
## **Manual testing steps**
1. Set up a local Anvil node: `anvil --port 8546 --chain-id 1338`
2. Add custom network in MetaMask: RPC URL `http://localhost:8546`,
Chain ID `1338`
3. Send ETH to your account: `cast send <address> --value 10ether
--rpc-url http://localhost:8546 --private-key
0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80`
4. Verify that the balance empty state does NOT appear
5. Create a fresh account with no balance
6. Verify that the balance empty state DOES appear correctly
Alternative test without Anvil:
1. Use browser DevTools to mock price API failures
2. Verify accounts with native tokens don't show empty state
3. Verify accounts with $0 balance AND no native tokens DO show empty
state
## **Screenshots/Recordings**
### **Before**
Empty state incorrectly shown despite 10 ETH balance on custom network
(chainId 1338)
https://github.com/user-attachments/assets/ec78382f-4928-41c8-88fe-c25fc380f69c
### **After**
Empty state correctly hidden when native token balances are present,
regardless of price conversion availability
https://github.com/user-attachments/assets/63997e02-3bf5-4511-a545-ca89b1a9dd6f
Balance empty state displays as intended on completely empty account
groups
https://github.com/user-attachments/assets/0f12fd69-ee78-46f6-8de1-a8ce54cb56a1
## **Pre-merge author checklist**
- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
## **Pre-merge reviewer checklist**
- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Refactors balance-empty-state logic to check actual token balances
(EVM native, non-EVM native, ERC‑20) on mainnets and updates tests
accordingly.
>
> - **Selectors**:
> - Replace fiat-based check in `selectAccountGroupBalanceForEmptyState`
with direct token presence checks:
> - EVM native via `accountsByChainId` (hex non-zero using
`isEmptyHexString`).
> - Non-EVM native via `multichainBalances` (decimal non-zero using
`isZeroAmount`).
> - ERC-20 via `tokenBalances` (hex non-zero).
> - Exclude all testnets using `selectAllMainnetNetworksEnabledMap`;
only mainnet chains considered.
> - Add helpers: `getBalanceOrDefault`,
`selectAccountsByChainIdForBalances`; import `hasProperty`, `isObject`,
`isEmptyHexString`, `isZeroAmount`.
> - **Tests** (`ui/selectors/assets.test.ts`):
> - Update to assert new balance-detection logic, including:
> - Positive/zero balances for EVM and Solana.
> - Ignoring EVM and non-EVM testnets.
> - Decimal-zero handling (e.g., "0.00").
> - ERC-20 tokens without native balance.
> - Mock `selectAssetsBySelectedAccountGroup` to return `{}` by default;
remove reliance on `calculateBalanceForAllWallets` in these tests.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
039fe7e. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->1 parent 5181212 commit c9eaf77
2 files changed
+368
-108
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
51 | 51 | | |
52 | 52 | | |
53 | 53 | | |
54 | | - | |
| 54 | + | |
55 | 55 | | |
56 | 56 | | |
57 | 57 | | |
| |||
1072 | 1072 | | |
1073 | 1073 | | |
1074 | 1074 | | |
1075 | | - | |
1076 | | - | |
1077 | | - | |
1078 | | - | |
1079 | | - | |
1080 | | - | |
1081 | | - | |
1082 | | - | |
1083 | | - | |
1084 | | - | |
1085 | | - | |
1086 | | - | |
1087 | | - | |
1088 | | - | |
1089 | | - | |
1090 | | - | |
1091 | | - | |
1092 | 1075 | | |
1093 | 1076 | | |
1094 | | - | |
1095 | | - | |
1096 | | - | |
1097 | | - | |
1098 | | - | |
| 1077 | + | |
| 1078 | + | |
| 1079 | + | |
| 1080 | + | |
| 1081 | + | |
| 1082 | + | |
| 1083 | + | |
| 1084 | + | |
| 1085 | + | |
| 1086 | + | |
1099 | 1087 | | |
1100 | 1088 | | |
1101 | 1089 | | |
1102 | 1090 | | |
1103 | | - | |
1104 | 1091 | | |
1105 | 1092 | | |
1106 | 1093 | | |
1107 | 1094 | | |
1108 | | - | |
1109 | | - | |
1110 | | - | |
1111 | | - | |
1112 | | - | |
| 1095 | + | |
| 1096 | + | |
| 1097 | + | |
| 1098 | + | |
| 1099 | + | |
| 1100 | + | |
| 1101 | + | |
| 1102 | + | |
| 1103 | + | |
| 1104 | + | |
1113 | 1105 | | |
1114 | 1106 | | |
1115 | 1107 | | |
1116 | 1108 | | |
1117 | | - | |
1118 | 1109 | | |
1119 | 1110 | | |
1120 | 1111 | | |
1121 | 1112 | | |
1122 | | - | |
1123 | | - | |
1124 | | - | |
1125 | | - | |
| 1113 | + | |
| 1114 | + | |
| 1115 | + | |
| 1116 | + | |
| 1117 | + | |
| 1118 | + | |
| 1119 | + | |
| 1120 | + | |
| 1121 | + | |
| 1122 | + | |
1126 | 1123 | | |
1127 | 1124 | | |
1128 | 1125 | | |
| |||
1131 | 1128 | | |
1132 | 1129 | | |
1133 | 1130 | | |
1134 | | - | |
1135 | | - | |
1136 | | - | |
1137 | | - | |
| 1131 | + | |
| 1132 | + | |
| 1133 | + | |
| 1134 | + | |
| 1135 | + | |
| 1136 | + | |
| 1137 | + | |
| 1138 | + | |
| 1139 | + | |
| 1140 | + | |
1138 | 1141 | | |
1139 | 1142 | | |
1140 | 1143 | | |
1141 | 1144 | | |
1142 | 1145 | | |
1143 | 1146 | | |
1144 | | - | |
| 1147 | + | |
1145 | 1148 | | |
1146 | | - | |
1147 | | - | |
1148 | | - | |
1149 | | - | |
| 1149 | + | |
| 1150 | + | |
| 1151 | + | |
| 1152 | + | |
1150 | 1153 | | |
1151 | 1154 | | |
1152 | 1155 | | |
| |||
1155 | 1158 | | |
1156 | 1159 | | |
1157 | 1160 | | |
1158 | | - | |
1159 | | - | |
1160 | | - | |
1161 | | - | |
1162 | 1161 | | |
1163 | | - | |
| 1162 | + | |
| 1163 | + | |
| 1164 | + | |
| 1165 | + | |
| 1166 | + | |
| 1167 | + | |
| 1168 | + | |
| 1169 | + | |
| 1170 | + | |
| 1171 | + | |
| 1172 | + | |
| 1173 | + | |
| 1174 | + | |
| 1175 | + | |
| 1176 | + | |
| 1177 | + | |
| 1178 | + | |
1164 | 1179 | | |
1165 | | - | |
1166 | | - | |
| 1180 | + | |
1167 | 1181 | | |
1168 | | - | |
1169 | | - | |
1170 | | - | |
1171 | | - | |
1172 | | - | |
1173 | | - | |
| 1182 | + | |
| 1183 | + | |
1174 | 1184 | | |
1175 | 1185 | | |
1176 | 1186 | | |
1177 | 1187 | | |
1178 | | - | |
1179 | | - | |
1180 | | - | |
1181 | | - | |
1182 | 1188 | | |
1183 | | - | |
| 1189 | + | |
| 1190 | + | |
| 1191 | + | |
| 1192 | + | |
| 1193 | + | |
| 1194 | + | |
| 1195 | + | |
| 1196 | + | |
| 1197 | + | |
| 1198 | + | |
| 1199 | + | |
| 1200 | + | |
| 1201 | + | |
| 1202 | + | |
| 1203 | + | |
| 1204 | + | |
| 1205 | + | |
1184 | 1206 | | |
1185 | | - | |
1186 | | - | |
| 1207 | + | |
| 1208 | + | |
| 1209 | + | |
1187 | 1210 | | |
1188 | | - | |
1189 | | - | |
1190 | | - | |
1191 | | - | |
1192 | | - | |
1193 | | - | |
1194 | | - | |
1195 | | - | |
1196 | | - | |
1197 | | - | |
1198 | | - | |
| 1211 | + | |
| 1212 | + | |
| 1213 | + | |
| 1214 | + | |
| 1215 | + | |
| 1216 | + | |
| 1217 | + | |
| 1218 | + | |
| 1219 | + | |
| 1220 | + | |
| 1221 | + | |
| 1222 | + | |
| 1223 | + | |
| 1224 | + | |
| 1225 | + | |
| 1226 | + | |
| 1227 | + | |
| 1228 | + | |
| 1229 | + | |
| 1230 | + | |
| 1231 | + | |
| 1232 | + | |
| 1233 | + | |
| 1234 | + | |
| 1235 | + | |
| 1236 | + | |
| 1237 | + | |
| 1238 | + | |
| 1239 | + | |
| 1240 | + | |
| 1241 | + | |
| 1242 | + | |
| 1243 | + | |
| 1244 | + | |
| 1245 | + | |
| 1246 | + | |
| 1247 | + | |
| 1248 | + | |
| 1249 | + | |
| 1250 | + | |
| 1251 | + | |
| 1252 | + | |
| 1253 | + | |
| 1254 | + | |
| 1255 | + | |
| 1256 | + | |
| 1257 | + | |
| 1258 | + | |
| 1259 | + | |
| 1260 | + | |
| 1261 | + | |
| 1262 | + | |
| 1263 | + | |
| 1264 | + | |
| 1265 | + | |
| 1266 | + | |
| 1267 | + | |
| 1268 | + | |
| 1269 | + | |
| 1270 | + | |
| 1271 | + | |
| 1272 | + | |
| 1273 | + | |
| 1274 | + | |
| 1275 | + | |
| 1276 | + | |
| 1277 | + | |
| 1278 | + | |
| 1279 | + | |
| 1280 | + | |
| 1281 | + | |
| 1282 | + | |
| 1283 | + | |
| 1284 | + | |
| 1285 | + | |
| 1286 | + | |
| 1287 | + | |
| 1288 | + | |
| 1289 | + | |
| 1290 | + | |
| 1291 | + | |
| 1292 | + | |
| 1293 | + | |
| 1294 | + | |
| 1295 | + | |
| 1296 | + | |
| 1297 | + | |
| 1298 | + | |
| 1299 | + | |
| 1300 | + | |
| 1301 | + | |
| 1302 | + | |
| 1303 | + | |
| 1304 | + | |
| 1305 | + | |
| 1306 | + | |
| 1307 | + | |
| 1308 | + | |
| 1309 | + | |
| 1310 | + | |
| 1311 | + | |
| 1312 | + | |
| 1313 | + | |
| 1314 | + | |
| 1315 | + | |
1199 | 1316 | | |
1200 | 1317 | | |
1201 | 1318 | | |
| |||
0 commit comments