Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add and use Accounts API for Account balance calls #4781

Merged

Conversation

Prithpal-Sooriya
Copy link
Contributor

@Prithpal-Sooriya Prithpal-Sooriya commented Oct 10, 2024

Explanation

This integrates the Accounts API (Multi-chain Balances Endpoint) to help alleviate expensive RPC calls made by Token Detection.

The aim is to attempt to use the Accounts API when making balance calls for expensive functionality (e.g. Token Detection)

Code Walkthrough

https://www.loom.com/share/e540cae3967746b0aca343d4c59d0af6?sid=69c2556c-96d3-451e-bd67-7d03f32fff03

References

#4743

https://consensyssoftware.atlassian.net/browse/NOTIFY-1179

Changelog

@metamask/assets-controllers

  • ADDED: MultiChain Accounts Service
    • ADDED: fetchSupportedNetworks() function to dynamically fetch supported networks by the Accounts API
    • ADDED: fetchMultiChainBalances() function to get balances for a given address
  • ADDED: useAccountsAPI to the TokenDetectionController constructor to enable/disable the accounts API feature.
  • ADDED: #addDetectedTokensViaAPI() private method in TokenDetectionController to get detected tokens via the Accounts API.
  • CHANGED: detectTokens() method in TokenDetectionController to try AccountsAPI first before using RPC flow to detect tokens.

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 highlighted breaking changes using the "BREAKING" category above as appropriate
  • I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes

we currently have added the `getBalances` call, we will extend this in the future
@Prithpal-Sooriya Prithpal-Sooriya force-pushed the NOTIFY-1179/add-account-api-to-token-detection-controller branch from 9bd1664 to 6e4171a Compare October 10, 2024 16:17
and add jsdoc comments to balance response type
…troller.

I'll add some tests and might refactor out pieces if it makes it easier for testing
*/
constructor({
interval = DEFAULT_INTERVAL,
disabled = true,
getBalancesInSingleCall,
trackMetaMetricsEvent,
messenger,
useAccountsAPI = true,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Big proponent of feature switching (in case this PR gets too large, or we need to turn this feature off).

Happy to remove otherwise.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is enabled by default

@Prithpal-Sooriya Prithpal-Sooriya force-pushed the NOTIFY-1179/add-account-api-to-token-detection-controller branch from 12a4687 to 9adebed Compare October 11, 2024 18:23
Comment on lines 2399 to 2407
/**
* TODO - discuss if this is correct.
*
* If the Accounts API succeeds, but the tokens cannot be added (unable to create `Token` shape)
* Then should we add no tokens & then finish?
*
* If we want to, we could then do a pass with the RPC flow? But would it be necessary?
* - DEV - we can just add a simple check at the end of the API flow where if no tokens were added, then count it as a failure and perform the RPC flow?
*/
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah lets discuss this scenario.

This is because we are using the TokenCache to create the correct token shape.
The Token shape requires the aggregators and image fields which the API does not provide us.

If the new token from the API does not exist in this cache, then we need to skip it.

Comment on lines 2452 to 2460
/**
* TODO - discuss if this is correct.
*
* If the Accounts API succeeds, but the tokens cannot be added (token already added)
* Then should we add no tokens (if they are all added) & then finish?
*
* If we want to, we could then do a pass with the RPC flow? But would it be necessary?
* - DEV - we can just add a simple check at the end of the API flow where if no tokens were added, then count it as a failure and perform the RPC flow?
*/
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep lets discuss.

This flow happens when we receive tokens from our API, but these tokens are already added into the wallet (as detected tokens, ignored tokens, etc).

So if the token is already added, we will skip it.

Comment on lines 692 to 693
// NOTE: some of this data is also available from the `token` balance,
// however it is missing the `aggregators` and `iconUrl` properties
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These 2 fields that are missing from the Account API are technically optional. We could leave them out.

However I do not know the unintended effects if we were to do this.

currently the supportedNetworks call has places that has unreachable errors, but could still be errors if used directly in the future
@Prithpal-Sooriya Prithpal-Sooriya marked this pull request as ready for review October 14, 2024 13:52
@Prithpal-Sooriya Prithpal-Sooriya requested a review from a team as a code owner October 14, 2024 13:52
@Prithpal-Sooriya Prithpal-Sooriya requested a review from a team October 14, 2024 13:56
@Prithpal-Sooriya
Copy link
Contributor Author

@metamaskbot publish-preview

Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/accounts-controller": "18.2.2-preview-200beb79",
  "@metamask-previews/address-book-controller": "6.0.1-preview-200beb79",
  "@metamask-previews/announcement-controller": "7.0.1-preview-200beb79",
  "@metamask-previews/approval-controller": "7.1.0-preview-200beb79",
  "@metamask-previews/assets-controllers": "38.3.0-preview-200beb79",
  "@metamask-previews/base-controller": "7.0.1-preview-200beb79",
  "@metamask-previews/build-utils": "3.0.1-preview-200beb79",
  "@metamask-previews/chain-controller": "0.1.3-preview-200beb79",
  "@metamask-previews/composable-controller": "9.0.1-preview-200beb79",
  "@metamask-previews/controller-utils": "11.3.0-preview-200beb79",
  "@metamask-previews/ens-controller": "14.0.1-preview-200beb79",
  "@metamask-previews/eth-json-rpc-provider": "4.1.4-preview-200beb79",
  "@metamask-previews/gas-fee-controller": "20.0.1-preview-200beb79",
  "@metamask-previews/json-rpc-engine": "9.0.3-preview-200beb79",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.3-preview-200beb79",
  "@metamask-previews/keyring-controller": "17.2.2-preview-200beb79",
  "@metamask-previews/logging-controller": "6.0.1-preview-200beb79",
  "@metamask-previews/message-manager": "10.1.1-preview-200beb79",
  "@metamask-previews/name-controller": "8.0.1-preview-200beb79",
  "@metamask-previews/network-controller": "21.0.1-preview-200beb79",
  "@metamask-previews/notification-controller": "7.0.0-preview-200beb79",
  "@metamask-previews/notification-services-controller": "0.9.0-preview-200beb79",
  "@metamask-previews/permission-controller": "11.0.2-preview-200beb79",
  "@metamask-previews/permission-log-controller": "3.0.1-preview-200beb79",
  "@metamask-previews/phishing-controller": "12.0.3-preview-200beb79",
  "@metamask-previews/polling-controller": "10.0.1-preview-200beb79",
  "@metamask-previews/preferences-controller": "13.0.3-preview-200beb79",
  "@metamask-previews/profile-sync-controller": "0.9.7-preview-200beb79",
  "@metamask-previews/queued-request-controller": "5.1.0-preview-200beb79",
  "@metamask-previews/rate-limit-controller": "6.0.1-preview-200beb79",
  "@metamask-previews/selected-network-controller": "18.0.1-preview-200beb79",
  "@metamask-previews/signature-controller": "19.1.0-preview-200beb79",
  "@metamask-previews/transaction-controller": "37.2.0-preview-200beb79",
  "@metamask-previews/user-operation-controller": "15.0.1-preview-200beb79"
}

return;
}

// We need specific data from tokenList to correctly create a token
Copy link
Contributor

Choose a reason for hiding this comment

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

Are the fields we get from the token list the icon url and aggregators?

We'll eventually stop storing icon urls in state since its always calculable as "https://static.cx.metamask.io/api/v1/tokenIcons/${chainId}/${address}.png".

Aggregators are shown in the UI but we could just query them from the big token list rather than duplicating on each imported token. So I think we keep this for now but could remove the dependency later.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep 100% agree. How expensive would it be for the accounts API to serve this additional information?

Copy link
Contributor Author

@Prithpal-Sooriya Prithpal-Sooriya Oct 17, 2024

Choose a reason for hiding this comment

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

Aha saw a relevant comment you've made before, great callout.
https://github.com/MetaMask/mobile-planning/issues/1935#issuecomment-2384061477

}

const result = await fetchSupportedNetworks().catch(() => null);
this.supportedNetworksCache = result;
Copy link
Contributor

Choose a reason for hiding this comment

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

I like the cache and dynamic lookup of support. Since its in memory rather than state, it'll re run when extension is reloaded or browser close/open. But not each time you open the popup. Which seems like a reasonable interval.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep only on browser close/open. We can think of more sophisticating caches if need be... later though 😄

@@ -0,0 +1,52 @@
import { handleFetch } from '@metamask/controller-utils';
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice move with the separate client. Also good to attempt OpenAPI generation but we can live without it for now.

Copy link
Contributor

@bergeron bergeron left a comment

Choose a reason for hiding this comment

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

This looks great to me, I couldn't find any bugs. I tested chains with and without account API support, hiding tokens, and manually adding them.

@Prithpal-Sooriya Prithpal-Sooriya merged commit 1251b86 into main Oct 18, 2024
116 checks passed
@Prithpal-Sooriya Prithpal-Sooriya deleted the NOTIFY-1179/add-account-api-to-token-detection-controller branch October 18, 2024 10:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants