Skip to content

Commit d148dd7

Browse files
authored
fix: batch NFT ownership checks via Multicall3 (#8281)
## Explanation - Replace individual `AssetsContractController` messenger calls (`getERC721OwnerOf`, `getERC1155BalanceOf`) with a new `getNftOwnershipForMultipleNfts` utility that batches ERC-721 `ownerOf` and ERC-1155 `balanceOf` calls through Multicall3's `aggregate3`, falling back to individual RPC calls on unsupported chains or when multicall fails. - Remove `checkAndUpdateSingleNftOwnershipStatus` in favor of `checkAndUpdateAllNftsOwnershipStatus`, which now batches all NFTs in a single pass. - Add an optional `standard` parameter to `isNftOwner` so callers that already know the token standard skip redundant subcalls. - `checkAndUpdateAllNftsOwnershipStatus` now removes NFTs confirmed as unowned from state rather than retaining them with `isCurrentlyOwned: false`. The "Previously Owned" NFT feature this flag powered is no longer supported. **Breaking Changes** - **`checkAndUpdateSingleNftOwnershipStatus` removed** — use `checkAndUpdateAllNftsOwnershipStatus` instead. - **`AllowedActions` narrowed** — `AssetsContractController:getERC721OwnerOf` and `AssetsContractController:getERC1155BalanceOf` are no longer required by `NftController`'s messenger. Consumers constructing the messenger must remove these from their allowed actions list. <!-- Thanks for your contribution! Take a moment to answer these questions so that reviewers have the information they need to properly understand your changes: * What is the current state of things and why does it need to change? * What is the solution your changes offer and how does it work? * Are there any changes whose purpose might not obvious to those unfamiliar with the domain? * If your primary goal was to update one package but you found you had to update another one along the way, why did you do so? * If you had to upgrade a dependency, why did you do so? --> ## References Ticket: https://consensyssoftware.atlassian.net/browse/ASSETS-2959 PR on Mobile: MetaMask/metamask-mobile#28655 PR on Extension: <!-- Are there any issues that this pull request is tied to? Are there other links that reviewers should consult to understand these changes better? Are there client or consumer pull requests to adopt any breaking changes? For example: * Fixes #12345 * Related to #67890 --> ## 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](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md) - [ ] I've introduced [breaking changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md) in this PR and have prepared draft pull requests for clients and consumer packages to resolve them <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Medium risk due to breaking API surface (`AllowedActions` narrowed, `checkAndUpdateSingleNftOwnershipStatus` removed) and changed state semantics (unowned NFTs are deleted), plus new multicall batching logic that could affect ownership determination across networks. > > **Overview** > **NFT ownership checks are refactored to batch on-chain calls via Multicall3.** `NftController.isNftOwner` and `checkAndUpdateAllNftsOwnershipStatus` now use the new `getNftOwnershipForMultipleNfts` helper to aggregate ERC-721 `ownerOf` / ERC-1155 `balanceOf` calls (with fallback to individual calls when multicall/chain support is unavailable), and callers can pass an optional `standard` to skip unnecessary subcalls. > > **Breaking behavior changes:** `AssetsContractController:getERC721OwnerOf` and `AssetsContractController:getERC1155BalanceOf` are removed from `NftController` `AllowedActions`, `checkAndUpdateSingleNftOwnershipStatus` is removed, and ownership refresh now **removes NFTs confirmed as unowned** from state instead of setting `isCurrentlyOwned: false`. Tests and changelog are updated accordingly, and `multicall` gains comprehensive coverage for the new NFT ownership batching utility. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit f782f90. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 93b8bc4 commit d148dd7

5 files changed

Lines changed: 894 additions & 443 deletions

File tree

packages/assets-controllers/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Changed
1111

12+
- **BREAKING:** `NftController` no longer uses the `AssetsContractController:getERC721OwnerOf` and `AssetsContractController:getERC1155BalanceOf` messenger actions for ownership checks; these have been removed from `AllowedActions` ([#8281](https://github.com/MetaMask/core/pull/8281))
13+
- Consumers that construct the `NftController` messenger and register handlers for these two actions must remove them from their allowed actions list.
14+
- **BREAKING:** Removed the `checkAndUpdateSingleNftOwnershipStatus` method from `NftController` ([#8281](https://github.com/MetaMask/core/pull/8281))
15+
- Use `checkAndUpdateAllNftsOwnershipStatus` instead, which now batches all ownership checks via Multicall3 in a single RPC request.
16+
- `checkAndUpdateAllNftsOwnershipStatus` now removes NFTs confirmed as unowned from state instead of setting `isCurrentlyOwned: false` ([#8281](https://github.com/MetaMask/core/pull/8281))
17+
- The `isCurrentlyOwned: false` flag was originally used to power a "Previously Owned" NFTs section in MetaMask, which is no longer supported. NFTs that are confirmed as no longer owned are now removed from state immediately rather than being retained with a stale flag.
18+
- `NftController` NFT ownership checks (`isNftOwner`, `checkAndUpdateAllNftsOwnershipStatus`) now use Multicall3 to batch ERC-721 `ownerOf` and ERC-1155 `balanceOf` calls into fewer RPC requests, falling back to individual calls on unsupported chains ([#8281](https://github.com/MetaMask/core/pull/8281))
1219
- Bump `@metamask/accounts-controller` from `^37.1.1` to `^37.2.0` ([#8363](https://github.com/MetaMask/core/pull/8363))
1320
- Bump `@metamask/keyring-controller` from `^25.1.1` to `^25.2.0` ([#8363](https://github.com/MetaMask/core/pull/8363))
1421
- Bump `@metamask/messenger` from `^1.0.0` to `^1.1.1` ([#8364](https://github.com/MetaMask/core/pull/8364), [#8373](https://github.com/MetaMask/core/pull/8373))

0 commit comments

Comments
 (0)