Skip to content

feat(assets-controller): use AccountsAPI v6 balances#9344

Open
Kriys94 wants to merge 2 commits into
mainfrom
feature/AssetsBalanceV6
Open

feat(assets-controller): use AccountsAPI v6 balances#9344
Kriys94 wants to merge 2 commits into
mainfrom
feature/AssetsBalanceV6

Conversation

@Kriys94

@Kriys94 Kriys94 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Explanation

The AccountsApiDataSource currently fetches account balances from the Accounts API v5 multi-account endpoint. This PR adds support for the v6 endpoint and lets it run at parity with v5, gated behind the assetsAccountsApiV6 remote feature flag (default off → v5).

What changed:

  • Flag-gated endpoint selection. When the assetsAccountsApiV6 remote flag is true, balances are fetched via fetchV6MultiAccountBalances; otherwise the legacy v5 endpoint is used. The flag is read from RemoteFeatureFlagController on every fetch (not cached), so v6 can be rolled out — and instantly rolled back to v5 — remotely without a client release.
  • Parity with v5. Only category: 'token' rows from the v6 response are consumed (DeFi positions are ignored), so token-balance behavior is identical to v5. Unprocessed networks and fetch timeouts still surface as per-chain errors for RPC fallback, exactly as before.
  • Custom assets. Custom ERC-20 tokens are forwarded to v6 as includeAssetIds, scoped to chains in the current request. Native (slip44) and non-EVM/off-chain custom assets are filtered out.
  • Wiring. AccountsApiDataSource now takes the shared AssetsController messenger and calls RemoteFeatureFlagController:getState; AssetsController passes the messenger through and extends AllowedActions accordingly. Adds @metamask/remote-feature-flag-controller as a dependency.
  • Test override. An optional useBalanceV6 getter on AccountsApiDataSourceConfig overrides the remote-flag-driven selection (mainly for tests); when omitted, the remote flag is used.

Because the default is v5 and v6 produces the same token balances, there is no user-facing behavior change until the flag is enabled.

References

  • Consumer integration (extension): wires RemoteFeatureFlagController:getState into the Assets controller messenger and bumps @metamask/core-backend / @metamask/assets-controller.

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Medium Risk
Changes which backend balances API runs and how responses are normalized; incorrect flag wiring or v6 parsing could affect displayed token balances, though rollout is flag-gated and defaults to v5.

Overview
Accounts API balance fetches can use the v6 multi-account endpoint when the remote flag assetsAccountsApiV6 is true; otherwise behavior stays on v5. The choice is evaluated on each fetch (not cached) so clients can roll out or roll back without redeploying, with an optional useBalanceV6 getter on AccountsApiDataSourceConfig to override the flag (mainly for tests).

AccountsApiDataSource now requires the shared AssetsController messenger and calls RemoteFeatureFlagController:getState; AssetsController passes that messenger through and extends its allowed actions accordingly. The package adds @metamask/remote-feature-flag-controller as a dependency.

On the v6 path, balances are loaded via fetchV6MultiAccountBalances, mapped from the per-account response shape, and only category: 'token' rows are applied (DeFi positions are skipped to match v5). Custom assets are forwarded as includeAssetIds for ERC-20 tokens on chains in the current request (native and off-chain assets filtered out). Unprocessed networks and timeouts still surface as chain errors for RPC fallback, same as v5.

Reviewed by Cursor Bugbot for commit 766168b. Bugbot is set up for automated code reviews on this repo. Configure here.


Note

Medium Risk
Switches which backend API supplies token balances and how v6 payloads are mapped; wrong flag wiring or parsing could affect displayed balances, though the flag defaults off and v6 is intended to match v5 token behavior.

Overview
AccountsApiDataSource can fetch balances from the Accounts API v6 multi-account endpoint when remote flag assetsAccountsApiV6 is enabled ({ value: true } from RemoteFeatureFlagController); otherwise it keeps using v5. The flag is read on each fetch (not cached) so rollout and rollback do not require a client release.

The data source now requires the shared AssetsController messenger and delegates RemoteFeatureFlagController:getState; AssetsController passes the messenger through and allows that action. The package adds @metamask/remote-feature-flag-controller (README dependency graph updated).

On the v6 path, responses are normalized from the per-account shape via #processV6Balances, applying only category: 'token' rows (DeFi skipped for v5 parity). Unprocessed networks and fetch failures still mark chains as errors for RPC fallback. v6 calls use the same account-ID arguments as v5 in this diff (tests assert custom assets are not forwarded as includeAssetIds on v6).

Reviewed by Cursor Bugbot for commit bb22b96. Bugbot is set up for automated code reviews on this repo. Configure here.

@Kriys94 Kriys94 force-pushed the feature/AssetsBalanceV6 branch 5 times, most recently from 3d14604 to 766168b Compare July 9, 2026 09:46
@Kriys94 Kriys94 marked this pull request as ready for review July 9, 2026 09:46
@Kriys94 Kriys94 requested review from a team as code owners July 9, 2026 09:46
@Kriys94 Kriys94 temporarily deployed to default-branch July 9, 2026 09:46 — with GitHub Actions Inactive
@Kriys94 Kriys94 force-pushed the feature/AssetsBalanceV6 branch 3 times, most recently from c908168 to dd41aed Compare July 9, 2026 12:19
@Kriys94 Kriys94 force-pushed the feature/AssetsBalanceV6 branch from dd41aed to 3804144 Compare July 9, 2026 12:34
options.tokenDetectionEnabled ?? ((): boolean => true);
this.#messenger = options.messenger;
this.#useBalanceV6 =
options.useBalanceV6 ?? ((): boolean => this.#isBalanceV6Enabled());

@bergarces bergarces Jul 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why have both a parameter (that is not being used) and a default? It's adding code surface that makes it more difficult to find the source and needs to be tested.

If we pass it down as a method there are certain advantages:

  • It can be tested in isolation
  • It stops requiring passing down the messenger.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agree!

amount: item.balance,
};
}
}

@bergarces bergarces Jul 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not blocking for this PR, but I'm noticing that most of the tests for the logic-heavy processV6Balances and processV5Balances are just happy paths with a single account and a single balance.

Maybe it would make sense in a follow-up ticket to move this logic outside and test it against some responses with more edge cases (for v6 only, not point doing it for v5 if we are going to drop it soon). Ideally, we would want to take the opportunity to see if there is a suitable parser in core that we can use to generate a schema for the api response and validate it beforehand to protect ourselves from runtime errors (this could even be done at core-backend level).

That would leave this class just responsible of dealing with a well-known and safe piece of data.

But again, this is more of an afterthought to reduce the cognitive load when trying to work with this data source middleware.

bergarces
bergarces previously approved these changes Jul 9, 2026
…assignment'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants