Skip to content

Conversation

@salimtb
Copy link
Contributor

@salimtb salimtb commented Nov 6, 2025

Explanation

What is the current state and why does it need to change?

Currently, the multi-chain accounts API calls in TokenDetectionController and TokenBalancesController are made without authentication. This limits the ability to provide user-specific data and secure API endpoints that require authenticated requests. Additionally, there is no timeout protection for these API calls, which means a hanging request could block token detection indefinitely without falling back to RPC methods.

What is the solution and how does it work?

This PR adds optional JWT token authentication and timeout protection to the accounts API calls:

  1. API Layer Changes (multi-chain-accounts.ts):

    • Added optional jwtToken parameter to fetchMultiChainBalances and fetchMultiChainBalancesV4
    • When a JWT token is provided, it's included in the Authorization: Bearer <token> header
    • The token is optional to maintain backward compatibility
  2. Controller Integration:

    • TokenDetectionController:
      • Fetches JWT token from AuthenticationController:getBearerToken and passes it to fetchMultiChainBalances when detecting tokens via Accounts API
      • Implements 30-second timeout protection (ACCOUNTS_API_TIMEOUT_MS) using Promise.race() to prevent hanging API calls
      • Automatically falls back to RPC detection if API call times out or fails
      • Logs timeout/failure events for debugging
    • TokenBalancesController: Fetches JWT token and passes it through the balance fetcher chain to fetchMultiChainBalancesV4
  3. Balance Fetcher Updates (api-balance-fetcher.ts):

    • Updated AccountsApiBalanceFetcher to accept and pass JWT token through the fetch chain
    • Token flows from updateBalancesfetch#fetchBalances → API calls
  4. Timeout Protection:

    • Added ACCOUNTS_API_TIMEOUT_MS = 30000 constant (30 seconds)
    • Wrapped API calls in #attemptAccountAPIDetection with timeout logic
    • Uses Promise.race() between the API call and a timeout promise
    • On timeout or error, returns { result: 'failed' } to trigger RPC fallback
    • Includes comprehensive error logging with chain IDs and error details

Key Design Decisions

  • Optional Parameter: The JWT token is optional throughout the call chain, ensuring backward compatibility for environments where authentication is not available or required
  • Graceful Degradation: If no token is provided, API calls proceed without authentication, allowing the system to work in both authenticated and unauthenticated scenarios
  • Timeout Protection: 30-second timeout ensures token detection never gets stuck indefinitely, with automatic fallback to reliable RPC methods
  • No Breaking Changes: Existing callers continue to work without modification
  • Resilient Fallback: The timeout mechanism integrates seamlessly with existing error handling, treating timeouts the same as API failures

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
    • Added tests in TokenDetectionController.test.ts to verify JWT token is passed correctly
    • Added test in TokenDetectionController.test.ts to verify 30-second timeout triggers RPC fallback
    • Added tests in TokenBalancesController.test.ts to verify JWT token flows through balance fetcher
    • Added tests in multi-chain-accounts.test.ts to verify Authorization header is set correctly
    • Added tests for both scenarios: with and without JWT token
    • Verified timeout behavior using fake timers (sinon) and the advanceTime helper
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
    • Updated JSDoc comments for fetchMultiChainBalances and fetchMultiChainBalancesV4
    • Updated JSDoc for controller methods that now handle JWT tokens
    • Added inline comments explaining timeout logic and fallback mechanism
  • I've communicated my changes to consumers by updating changelogs for packages I've changed, highlighting breaking changes as necessary
    • Note: No breaking changes - all JWT token parameters are optional and timeout is an internal improvement
  • I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes
    • Note: Not required - no breaking changes introduced

Note

Adds optional JWT authentication to Accounts API calls and 30s timeout + RPC fallback, and replaces single-call token balances with Multicall3; plumbs JWT through controllers with tests and deps updates.

  • Accounts API Authentication
    • fetchMultiChainBalances/fetchMultiChainBalancesV4 accept optional jwtToken; sent as Authorization: Bearer <token>.
    • TokenDetectionController and TokenBalancesController fetch JWT via AuthenticationController:getBearerToken and pass it through fetchers.
  • Resilience & Fallbacks
    • TokenDetectionController: 30s timeout guard for Accounts API; on timeout/error, logs and falls back to RPC detection.
  • Balance Fetching Improvements
    • AssetsContractController.getBalancesInSingleCall now uses multicall.getTokenBalancesForMultipleAddresses (Multicall3) for broad network support; returns non‑zero balances only.
  • Tests
    • Add coverage for JWT header propagation, timeout-to-RPC fallback, and JWT flow through AccountsApiBalanceFetcher.
  • Deps/Config
    • Add @metamask/profile-sync-controller as (dev/peer) dependency for auth token access.
    • Multicall map: add Sonic chain.
  • Changelog
    • Documents JWT auth, timeout fallback, and related changes.

Written by Cursor Bugbot for commit 92708ad. This will update automatically on new commits. Configure here.

@salimtb salimtb changed the title Feat/jwt auth accounts api feat: jwt auth accounts api Nov 7, 2025
@salimtb salimtb changed the title feat: jwt auth accounts api feat: jwt auth accounts api + handle pending requests Nov 7, 2025
@salimtb
Copy link
Contributor Author

salimtb commented Nov 7, 2025

@metamaskbot publish-preview

@github-actions
Copy link
Contributor

github-actions bot commented Nov 7, 2025

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

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "2.0.0-preview-d41a7f35",
  "@metamask-previews/accounts-controller": "34.0.0-preview-d41a7f35",
  "@metamask-previews/address-book-controller": "7.0.0-preview-d41a7f35",
  "@metamask-previews/analytics-controller": "0.0.0-preview-d41a7f35",
  "@metamask-previews/announcement-controller": "8.0.0-preview-d41a7f35",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-d41a7f35",
  "@metamask-previews/approval-controller": "8.0.0-preview-d41a7f35",
  "@metamask-previews/assets-controllers": "87.1.0-preview-d41a7f35",
  "@metamask-previews/base-controller": "9.0.0-preview-d41a7f35",
  "@metamask-previews/bridge-controller": "59.0.0-preview-d41a7f35",
  "@metamask-previews/bridge-status-controller": "59.0.0-preview-d41a7f35",
  "@metamask-previews/build-utils": "3.0.4-preview-d41a7f35",
  "@metamask-previews/chain-agnostic-permission": "1.2.2-preview-d41a7f35",
  "@metamask-previews/composable-controller": "12.0.0-preview-d41a7f35",
  "@metamask-previews/controller-utils": "11.15.0-preview-d41a7f35",
  "@metamask-previews/core-backend": "4.0.0-preview-d41a7f35",
  "@metamask-previews/delegation-controller": "1.0.0-preview-d41a7f35",
  "@metamask-previews/earn-controller": "9.0.0-preview-d41a7f35",
  "@metamask-previews/eip-5792-middleware": "2.0.0-preview-d41a7f35",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-d41a7f35",
  "@metamask-previews/eip1193-permission-middleware": "1.0.2-preview-d41a7f35",
  "@metamask-previews/ens-controller": "18.0.0-preview-d41a7f35",
  "@metamask-previews/error-reporting-service": "3.0.0-preview-d41a7f35",
  "@metamask-previews/eth-block-tracker": "14.0.0-preview-d41a7f35",
  "@metamask-previews/eth-json-rpc-middleware": "21.0.0-preview-d41a7f35",
  "@metamask-previews/eth-json-rpc-provider": "5.0.1-preview-d41a7f35",
  "@metamask-previews/foundryup": "1.0.1-preview-d41a7f35",
  "@metamask-previews/gas-fee-controller": "25.0.0-preview-d41a7f35",
  "@metamask-previews/gator-permissions-controller": "0.4.0-preview-d41a7f35",
  "@metamask-previews/json-rpc-engine": "10.1.1-preview-d41a7f35",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-d41a7f35",
  "@metamask-previews/keyring-controller": "24.0.0-preview-d41a7f35",
  "@metamask-previews/logging-controller": "7.0.0-preview-d41a7f35",
  "@metamask-previews/message-manager": "14.0.0-preview-d41a7f35",
  "@metamask-previews/messenger": "0.3.0-preview-d41a7f35",
  "@metamask-previews/multichain-account-service": "2.1.0-preview-d41a7f35",
  "@metamask-previews/multichain-api-middleware": "1.2.4-preview-d41a7f35",
  "@metamask-previews/multichain-network-controller": "2.0.0-preview-d41a7f35",
  "@metamask-previews/multichain-transactions-controller": "6.0.0-preview-d41a7f35",
  "@metamask-previews/name-controller": "9.0.0-preview-d41a7f35",
  "@metamask-previews/network-controller": "25.0.0-preview-d41a7f35",
  "@metamask-previews/network-enablement-controller": "3.1.0-preview-d41a7f35",
  "@metamask-previews/notification-services-controller": "19.0.0-preview-d41a7f35",
  "@metamask-previews/permission-controller": "12.1.0-preview-d41a7f35",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-d41a7f35",
  "@metamask-previews/phishing-controller": "15.0.0-preview-d41a7f35",
  "@metamask-previews/polling-controller": "15.0.0-preview-d41a7f35",
  "@metamask-previews/preferences-controller": "21.0.0-preview-d41a7f35",
  "@metamask-previews/profile-sync-controller": "26.0.0-preview-d41a7f35",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-d41a7f35",
  "@metamask-previews/remote-feature-flag-controller": "2.0.0-preview-d41a7f35",
  "@metamask-previews/sample-controllers": "3.0.0-preview-d41a7f35",
  "@metamask-previews/seedless-onboarding-controller": "6.1.0-preview-d41a7f35",
  "@metamask-previews/selected-network-controller": "25.0.0-preview-d41a7f35",
  "@metamask-previews/shield-controller": "2.0.0-preview-d41a7f35",
  "@metamask-previews/signature-controller": "36.0.0-preview-d41a7f35",
  "@metamask-previews/subscription-controller": "3.3.0-preview-d41a7f35",
  "@metamask-previews/token-search-discovery-controller": "4.0.0-preview-d41a7f35",
  "@metamask-previews/transaction-controller": "61.1.0-preview-d41a7f35",
  "@metamask-previews/transaction-pay-controller": "3.1.0-preview-d41a7f35",
  "@metamask-previews/user-operation-controller": "40.0.0-preview-d41a7f35"
}

@salimtb
Copy link
Contributor Author

salimtb commented Nov 9, 2025

@metamaskbot publish-preview

@salimtb salimtb marked this pull request as ready for review November 9, 2025 23:11
@salimtb salimtb requested review from a team as code owners November 9, 2025 23:11
@salimtb salimtb marked this pull request as draft November 9, 2025 23:24
@socket-security
Copy link

socket-security bot commented Nov 10, 2025

No dependency changes detected. Learn more about Socket for GitHub.

👍 No dependency changes detected in pull request

@salimtb
Copy link
Contributor Author

salimtb commented Nov 10, 2025

@metamaskbot publish-preview

@github-actions
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/account-tree-controller": "3.0.0-preview-12e7e334",
  "@metamask-previews/accounts-controller": "34.0.0-preview-12e7e334",
  "@metamask-previews/address-book-controller": "7.0.0-preview-12e7e334",
  "@metamask-previews/analytics-controller": "0.0.0-preview-12e7e334",
  "@metamask-previews/announcement-controller": "8.0.0-preview-12e7e334",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-12e7e334",
  "@metamask-previews/approval-controller": "8.0.0-preview-12e7e334",
  "@metamask-previews/assets-controllers": "88.0.0-preview-12e7e334",
  "@metamask-previews/base-controller": "9.0.0-preview-12e7e334",
  "@metamask-previews/bridge-controller": "60.0.0-preview-12e7e334",
  "@metamask-previews/bridge-status-controller": "60.0.0-preview-12e7e334",
  "@metamask-previews/build-utils": "3.0.4-preview-12e7e334",
  "@metamask-previews/chain-agnostic-permission": "1.2.2-preview-12e7e334",
  "@metamask-previews/claims-controller": "0.1.0-preview-12e7e334",
  "@metamask-previews/composable-controller": "12.0.0-preview-12e7e334",
  "@metamask-previews/controller-utils": "11.15.0-preview-12e7e334",
  "@metamask-previews/core-backend": "4.0.0-preview-12e7e334",
  "@metamask-previews/delegation-controller": "1.0.0-preview-12e7e334",
  "@metamask-previews/earn-controller": "10.0.0-preview-12e7e334",
  "@metamask-previews/eip-5792-middleware": "2.0.0-preview-12e7e334",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-12e7e334",
  "@metamask-previews/eip1193-permission-middleware": "1.0.2-preview-12e7e334",
  "@metamask-previews/ens-controller": "18.0.0-preview-12e7e334",
  "@metamask-previews/error-reporting-service": "3.0.0-preview-12e7e334",
  "@metamask-previews/eth-block-tracker": "14.0.0-preview-12e7e334",
  "@metamask-previews/eth-json-rpc-middleware": "21.0.0-preview-12e7e334",
  "@metamask-previews/eth-json-rpc-provider": "5.0.1-preview-12e7e334",
  "@metamask-previews/foundryup": "1.0.1-preview-12e7e334",
  "@metamask-previews/gas-fee-controller": "25.0.0-preview-12e7e334",
  "@metamask-previews/gator-permissions-controller": "0.4.0-preview-12e7e334",
  "@metamask-previews/json-rpc-engine": "10.1.1-preview-12e7e334",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-12e7e334",
  "@metamask-previews/keyring-controller": "24.0.0-preview-12e7e334",
  "@metamask-previews/logging-controller": "7.0.0-preview-12e7e334",
  "@metamask-previews/message-manager": "14.0.0-preview-12e7e334",
  "@metamask-previews/messenger": "0.3.0-preview-12e7e334",
  "@metamask-previews/multichain-account-service": "3.0.0-preview-12e7e334",
  "@metamask-previews/multichain-api-middleware": "1.2.4-preview-12e7e334",
  "@metamask-previews/multichain-network-controller": "2.0.0-preview-12e7e334",
  "@metamask-previews/multichain-transactions-controller": "6.0.0-preview-12e7e334",
  "@metamask-previews/name-controller": "9.0.0-preview-12e7e334",
  "@metamask-previews/network-controller": "25.0.0-preview-12e7e334",
  "@metamask-previews/network-enablement-controller": "3.1.0-preview-12e7e334",
  "@metamask-previews/notification-services-controller": "19.0.0-preview-12e7e334",
  "@metamask-previews/permission-controller": "12.1.0-preview-12e7e334",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-12e7e334",
  "@metamask-previews/phishing-controller": "15.0.0-preview-12e7e334",
  "@metamask-previews/polling-controller": "15.0.0-preview-12e7e334",
  "@metamask-previews/preferences-controller": "21.0.0-preview-12e7e334",
  "@metamask-previews/profile-sync-controller": "26.0.0-preview-12e7e334",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-12e7e334",
  "@metamask-previews/remote-feature-flag-controller": "2.0.0-preview-12e7e334",
  "@metamask-previews/sample-controllers": "3.0.0-preview-12e7e334",
  "@metamask-previews/seedless-onboarding-controller": "6.1.0-preview-12e7e334",
  "@metamask-previews/selected-network-controller": "25.0.0-preview-12e7e334",
  "@metamask-previews/shield-controller": "2.0.0-preview-12e7e334",
  "@metamask-previews/signature-controller": "36.0.0-preview-12e7e334",
  "@metamask-previews/subscription-controller": "3.3.0-preview-12e7e334",
  "@metamask-previews/token-search-discovery-controller": "4.0.0-preview-12e7e334",
  "@metamask-previews/transaction-controller": "61.1.0-preview-12e7e334",
  "@metamask-previews/transaction-pay-controller": "4.0.0-preview-12e7e334",
  "@metamask-previews/user-operation-controller": "40.0.0-preview-12e7e334"
}

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.

2 participants