-
-
Notifications
You must be signed in to change notification settings - Fork 246
feat(backend-platform): Add Websocket and AccountActivity Service #6490
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
base: main
Are you sure you want to change the base?
Conversation
490eef2
to
2855aa0
Compare
@metamaskbot publish-preview |
Preview builds have been published. See these instructions for more information about preview builds. Expand for full list of packages and versions.
|
2855aa0
to
5fcd7ee
Compare
@metamaskbot publish-preview |
3919440
to
dbd83fd
Compare
"@ethersproject/contracts": "^5.7.0", | ||
"@ethersproject/providers": "^5.7.0", | ||
"@metamask/abi-utils": "^2.0.3", | ||
"@metamask/backend-platform": "workspace:^", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i guess we will replace this with a published version ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can already just reference the local version instead of workspace
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I will create another PR for assets-controller changes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey, I noticed that a new package was being added to the monorepo. I haven't dived deeply into the implementation yet but I did leave some comments.
It looks like we are introducing a new package and then trying to use it right away. To make the changes easier to merge and queue up, what are your thoughts on extracting the changes to assets-controllers
to a new PR?
}; | ||
|
||
// Action types for the messaging system | ||
export type AccountActivityServiceSubscribeAccountsAction = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a pattern that we introduced a little while ago that DRYs up the code around "method actions", or actions that merely point to methods.
The way that this works is:
- You define a
MESSENGER_EXPOSED_METHOD
constant that lists each method you'd like to expose through the messenger - You run
yarn generate-method-action-types
to generate a types file - You import
AccountActivityServiceMethodActions
from the generated file into this file, and you add this type to theAccountActivityServiceActions
union. - Instead of registering each action manually in the constructor, you add:
this.#messenger.registerMethodActionHandlers( this, MESSENGER_EXPOSED_METHODS, );
- Now you can remove
AccountActivityServiceSubscribeAccountsAction
,AccountActivityServiceUnsubscribeAccountsAction
, etc.
What do you think of using this pattern? You can see an example in SampleGasPricesService
here:
core/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.ts
Line 25 in c45132e
const MESSENGER_EXPOSED_METHODS = ['fetchGasPrices'] as const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, good tip. I made the change. I wonder why this does not exist for events?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typically events do not correlate with methods on controllers or services, so they need to be listed by hand anyway.
AccountActivityServiceMessenger, | ||
} from './AccountActivityService'; | ||
export { | ||
ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason why these constants are being exported? We don't typically do this in packages. If we need to use an action or event elsewhere we repeat it.
| NetworkControllerStateChangeEvent | ||
| KeyringControllerAccountRemovedEvent; | ||
| KeyringControllerAccountRemovedEvent | ||
| AccountActivityServiceBalanceUpdatedEvent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heads up that updates to AllowedEvents
in a package are breaking changes (because the allowlist needs to be updated on the client side). Can you mention this in the changelog?
TokenBalancesControllerState | ||
>; | ||
|
||
export type TokenBalancesControllerUpdateChainPollingConfigsAction = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like we are removing these actions, should we mention this in the changelog?
* | ||
* @returns The default polling interval in milliseconds | ||
*/ | ||
getDefaultPollingInterval(): number { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like we are adding a new method to the controller, should we mention this in the changelog?
* Processes balance updates and updates the token balance state | ||
* If any balance update has an error, triggers fallback polling for the chain | ||
*/ | ||
readonly #onAccountActivityBalanceUpdate = async ({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anything to mention in the changelog about these changes? Any differences in behavior?
@Kriys94 I realize that your team is named Backend Platform, but I'm wondering whether this is the best package name to use. Is the plan to place all services that talk to internal APIs in this package from now on? Do you plan on moving any code from other controllers to this package in the future? It seems a bit broad in scope and I am concerned about it becoming a sort of catch-all similar to the If having a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some more minor suggestions. I haven't done a full pass of the implementation so I may come back and make some more comments later but this is what I noticed for now.
}, | ||
) { | ||
this.#messenger = options.messenger; | ||
this.#webSocketService = options.webSocketService; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of taking the WebSocketService as an argument, what are your thoughts on having it use it through the messenger?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is more a habit from someone not used to code in MM codebase, I find this more straightforward to inject WebSocketService as the dependency is easier to understand for IDEs (like understanding where functions are implemented). Here I know that the AccountActivityService logic fully depend on WebsocketService from the BackendPlatform package.
But I can switch to messenger if necessary
|
||
readonly #options: Required<WebSocketServiceOptions>; | ||
|
||
#ws!: WebSocket; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why can we assume this is set?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because #ws is set only when we successfully established the connection. In case of error #ws is undefined.
try { | ||
return JSON.parse(data); | ||
} catch { | ||
// Fail fast on parse errors (mobile optimization) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we want to print a message here saying that JSON parsing failed?
// Publish connection state change event | ||
try { | ||
this.#messenger.publish( | ||
'BackendWebSocketService:connectionStateChanged', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we use SERVICE_NAME
here?
'BackendWebSocketService:connectionStateChanged', | |
`${SERVICE_NAME}:connectionStateChanged`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've heard that ${SERVICE_NAME}:connectionStateChanged
is not recommended because it makes the search of the event or action more difficult to find
@metamaskbot publish-preview |
Note: Move Debouncer in the pooling directly |
For new Token we need to call "addToken" from TokenController. First check if this is part of allToken (imported tokens). As a second PR. |
Exclude updates if a token is not part of allTokens or allIgnoredTokens |
Yes, that's the plan even if that will progressive
Not moving existing code, but mainly creating new code to interact with any interface that the backend-platform offers. When that will be the case, we would have to modify the Controllers codebase.
The backend-platform package is really just interfacing our backend-platform APIs with the Controller logic.
WebSocketService is really specific to the WebsocketInterface and behavior that the backend platform offers. Unfortunately, I think that a Websocket package that works with any websocket endpoint is not useful, mainly because the other use-cases (Hyperliquid and Solana for now) connect to their respective websocket via their respective SDKs. As such we could not have a generic Websocket interface that we would inject to Solana to Hyperliquid. |
b222eb5
to
4b89590
Compare
4e5692c
to
80cde4c
Compare
Description
This PR implements the
@metamask/backend-platform
package, providing a unified real-time data layer for MetaMask applications through WebSocket services and event-driven architecture.What Changed
Why These Changes
Current Core packages, i.e. TokenBalancesController or RatesController, lacks real-time data capabilities, forcing all consuming applications to implement polling-based architectures:
Performance Problems: 20-30 second polling intervals create poor user experience for balance updates and transaction status
Resource Waste: Continuous HTTP polling consumes unnecessary bandwidth and server resources
Technical Implementation
Related issues
Integration PRs:
Manual testing steps
Screenshots/Recordings
Service Architecture:
Integration Examples:
Performance Metrics:
Pre-merge checklist
Additional Notes
Documentation Reference
📖 Complete Technical Documentation: https://raw.githubusercontent.com/MetaMask/core/3919440aff6718247715b0add0afa52774d8f6ad/packages/backend-platform/README.md
The README contains comprehensive architecture diagrams, integration guides, and detailed API documentation for implementing real-time data services.
API Interfaces
WebSocketService:
AccountActivityService:
Integration Pattern for Controllers
Files
packages/backend-platform/src/WebSocketService.ts
- Core WebSocket connection managementpackages/backend-platform/src/AccountActivityService.ts
- Real-time account monitoringpackages/backend-platform/src/types.ts
- TypeScript interface definitionspackages/backend-platform/src/index.ts
- Package exports and public APIpackages/backend-platform/package.json
- Package configuration and dependenciespackages/backend-platform/tsconfig.json
- TypeScript compilation settingspackages/assets-controllers/src/TokenBalancesController.ts
- Integration examplepackages/assets-controllers/package.json
- Added backend-platform dependencypackages/assets-controllers/tsconfig.json
- Added workspace referenceThe backend platform package is now implemented with robust WebSocket services and ready for Extension and Mobile integration through standardized controller patterns.