perf(assets-controller): memoize hot-path asset ID and legacy format converters#9555
Merged
Merged
Conversation
…converters Avoid repeated keccak256 checksumming and CAIP parsing on read paths that run far more often than assets state changes.
salimtb
marked this pull request as ready for review
July 20, 2026 13:37
juanmigdr
approved these changes
Jul 20, 2026
juanmigdr
left a comment
Member
There was a problem hiding this comment.
Very impressed with the changes! LGTM 🚀
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Avoid repeated keccak256 checksumming and CAIP parsing on read paths that run far more often than assets state changes.
Explanation
AssetsController's read paths recompute pure, deterministic conversions onevery call, even when the underlying assets state hasn't changed:
normalizeAssetIdrunsparseCaipAssetType+parseCaipChainIdand, for EVMERC-20 tokens,
toChecksumAddress(keccak256) on every call — including thecommon case where the same already-checksummed ID is passed repeatedly across
the pipeline.
formatExchangeRatesForBridge(viagetExchangeRatesForBridge) andformatStateForTransactionPay(viagetStateForTransactionPay) re-runkeccak256 checksumming and CAIP parsing for every priced/held asset. These are
invoked on hot paths — e.g. bridge quote/rate refreshes and every
TransactionController:stateChange— far more often than the assets state thatfeeds them actually changes.
This PR memoizes those conversions so unchanged inputs skip the redundant work:
normalizeAssetIdis wrapped in lodashmemoize, keyed on the asset ID.Repeated calls with the same ID (the norm once IDs are checksummed) return the
cached result without re-parsing or re-checksumming.
formatExchangeRatesForBridgeandformatStateForTransactionPaycachetheir last result. Cache validity is checked by identity (
===) forBaseController state slices — which keep stable references across updates thanks
to immer — and by lodash
isEqualfor maps/arrays that are rebuilt each call(
nativeAssetIdentifiers, andaccountsfor transaction-pay). The computelogic is split into internal
computeExchangeRatesForBridge/computeStateForTransactionPayhelpers, with the exported functions handlingthe cache check.
#getNativeAssetMapnow returns a stable empty-object constant(
EMPTY_NATIVE_ASSET_MAP) when the query cache is empty, so?? {}identitychurn doesn't defeat downstream memoization.
Each memoized module exports a
clear*CacheForTestinghelper (from the internalutilsbarrel) so tests can reset cache state between cases. TheFormatExchangeRatesForBridgeParamsandFormatStateForTransactionPayParamsparameter types are now named and exported for reuse.
No behavior changes for consumers: the functions are pure and the memoized
results are value-equal to the previously recomputed ones. Note that cached
results are returned by reference and should be treated as read-only.
References
N/A
Checklist
Note
Low Risk
Performance-only caching of pure converters with no API or behavior contract changes; main caveat is consumers must not mutate cached return values.
Overview
Adds memoization on read paths that repeatedly run CAIP parsing and EVM address checksumming (
toChecksumAddress/ keccak256) even when underlying assets state is unchanged.normalizeAssetIdis wrapped in lodashmemoizeso repeated calls with the same CAIP-19 ID return a cached checksummed result.formatExchangeRatesForBridgeandformatStateForTransactionPaynow keep a single-entry cache: controller state slices are compared by reference (===), while rebuiltnativeAssetIdentifiers/accountsuse lodashisEqual. Heavy work lives in internalcompute*helpers. Named param typesFormatExchangeRatesForBridgeParamsandFormatStateForTransactionPayParamsare exported; test-onlyclear*CacheForTestinghelpers reset caches between cases.#getNativeAssetMapreturns a module-levelEMPTY_NATIVE_ASSET_MAPinstead of?? {}, so empty native maps keep stable identity and do not invalidate the formatter caches.Outputs are intended to be value-equal to before; cached objects are returned by reference and should be treated as read-only.
Reviewed by Cursor Bugbot for commit 1266836. Bugbot is set up for automated code reviews on this repo. Configure here.