-
-
Notifications
You must be signed in to change notification settings - Fork 205
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 MultichainNetworkController
to handle both EVM and non-EVM network and account switching
#5215
base: main
Are you sure you want to change the base?
Conversation
… to update nonEvmNetwork state variable to false; added unit tests for setActiveNetwork and both new setters
This looks amazing! Lets align today with the review made on the PR #5209 |
…script documentation, and solve most of the review comments
…Mask/core into feat/handle-multichain-network-and-account-switching
…Mask/core into feat/handle-multichain-network-and-account-switching
New dependencies detected. Learn more about Socket for GitHub ↗︎
|
…ichain-network-and-account-switching
MultichainNetworkController
to handle both EVM and non-EVM network and account switching
{ "path": "../network-controller" }, | ||
{ "path": "../keyring-controller" } | ||
], | ||
"include": ["../../types", "./src"] |
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 include tests folder?
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 do this in other projects that have a tests/
directory, so yes, I think so:
"include": ["../../types", "./src"] | |
"include": ["../../types", "./src", "./tests"] |
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.
Hello! I left some comments. I'm still trying to understand the changes introduced here. I know more changes are coming, but I am still curious why some things are being introduced here without being explicitly used, when they could be introduced later. Other than that I had some improvements we could make to the types, and some naming adjustments.
>; | ||
|
||
export type MultichainNetworkSetActiveNetworkEvent = { | ||
type: `${typeof controllerName}:setActiveNetwork`; |
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.
Hmm, I would expect an action to be called setActiveNetwork
, but not an event. Besides this, it looks like we already have an action called MultichainNetworkController:setActiveNetwork
, so this could be confusing.
Perhaps we want to call this networkDidChange
to mimic NetworkController?
*/ | ||
blockExplorers: { | ||
urls: string[]; | ||
defaultIndex: 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.
This could create an API networkConfiguration.blockExplorers.urls
. Is that what we want? Usually I would expect a property that ends in a plural to be an array or a collection of some kind. But here it seems we are using blockExplorers
as just a grouping.
There are only two properties here, what are our thoughts on using networkConfiguration.blockExplorerUrls
and networkConfiguration.defaultBlockExplorerUrlIndex
?
Besides this, this API diverges from NetworkController so it adds overhead when translating between the two.
If we really want this, then perhaps this property should be called defaultUrlIndex
to reflect that it refers to the urls
property?
*/ | ||
rpcEndpoints: { | ||
urls: string[]; | ||
defaultIndex: 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.
Same commentary as for blockExplorers
.
If we really want this then perhaps this should be called defaultUrlIndex
?
* The network configurations by chain ID. | ||
*/ | ||
multichainNetworkConfigurationsByChainId: Record< | ||
string, |
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 CaipChainId
for the keys here?
string, | |
CaipChainId, |
/** | ||
* Whether the non-EVM network is selected by the wallet. | ||
*/ | ||
nonEvmSelected: boolean; |
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.
Since this is a boolean, what do you think about prefixing this with is
? I think we can also tweak this a bit. And what do you think about inverting it?
nonEvmSelected: boolean; | |
isEvmNetworkSelected: boolean; |
export const getDefaultMultichainNetworkControllerState = | ||
(): MultichainNetworkControllerState => ({ | ||
multichainNetworkConfigurationsByChainId: {}, | ||
selectedMultichainNetworkChainId: SolScope.Mainnet, |
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 should probably make this a proper guideline, but something that we should really try to do when writing controllers is ensure that state is coherent at all times. That is, if there is a property in state that connects to another property, that connection should be valid at all times.
In this case, selectedMultichainNetworkChainId
is really a connection to an entry in multichainNetworkConfigurationsByChainId
. That is, a consumer should always be able to fetch the network configuration for a chain ID by saying state.multichainNetworkConfigurationsByChainId[state.selectedMultichainNetworkChainId]
.
As it is, if the consumer were to create a MultichainNetworkController without passing any initial state, then the state of the resulting controller would not be coherent, because although state.selectedMultichainNetworkChainId
would be present, state.multichainNetworkConfigurationsByChainId[state.selectedMultichainNetworkChainId]
would be undefined.
In the NetworkController we solved this by prepopulating networkConfigurationsByChainId
with known networks, one of which is Mainnet. This way we can default selectedNetworkClientId
to Mainnet and then the consumer can call controller.getNetworkConfigurationByNetworkClientId(state.selectedNetworkClientId)
and then get the resulting network configuration.
Can we prepopulate multichainNetworkConfigurationsByChainId
with Solana and Bitcoin? Or, alternatively, maybe we cannot default selectedMultichainNetworkChainId
to anything since we won't know what the wallet will have?
MultichainNetworkMetadata, | ||
} from './MultichainNetworkController'; | ||
|
||
export const btcNativeAsset = `${BtcScope.Mainnet}/slip44:0`; |
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.
What are your thoughts on using SCREAMING_SNAKE_CASE
for these constants?
export const btcNativeAsset = `${BtcScope.Mainnet}/slip44:0`; | |
export const BTC_NATIVE_ASSET = `${BtcScope.Mainnet}/slip44:0`; |
} from './MultichainNetworkController'; | ||
|
||
export const btcNativeAsset = `${BtcScope.Mainnet}/slip44:0`; | ||
export const solNativeAsset = `${SolScope.Mainnet}/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`; |
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.
export const solNativeAsset = `${SolScope.Mainnet}/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`; | |
export const SOL_NATIVE_ASSET = `${SolScope.Mainnet}/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`; |
export const btcNativeAsset = `${BtcScope.Mainnet}/slip44:0`; | ||
export const solNativeAsset = `${SolScope.Mainnet}/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`; | ||
|
||
export const multichainNetworkConfigurations: Record< |
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.
What are your thoughts on using SCREAMING_SNAKE_CASE
and also using the word "available" (to communicate that these are things that you could initialize the state with, but you don't have to use all of them).
export const multichainNetworkConfigurations: Record< | |
export const AVAILABLE_MULTICHAIN_NETWORK_CONFIGURATIONS: Record< |
// Update selected account to non evm account | ||
const lastSelectedNonEvmAccount = | ||
this.getSelectedMultichainAccount(nonEvmChainId); | ||
// @ts-expect-error - This should never be undefined, otherwise it's a bug that should be handled |
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 throw a specific error, so that if it is a bug, we have some more information?
@Cal-L Can you run |
Explanation
This PR updates both the MultichainNetworkController and AccountsController to handle network switching as well as account switching. The logic handles the following logic:
References
Fixes https://github.com/MetaMask/accounts-planning/issues/804
Changelog
@metamask/accounts-controller
MultichainNetworkSetActiveNetworkEvent
to allowed events. This is used to subscribe to thesetActiveNetwork
event from theMultichainNetworkController
and is responsible for updating selected account based on network changes (both EVM and non-EVM).@metamask/multichain-network-controller
NetworkControllerGetStateAction
|NetworkControllerSetActiveNetworkAction
. TheMultichainNetworkController
acts as a proxy for theNetworkController
and will update it based on EVM network changes.AccountsControllerSelectedAccountChangeEvent
to allowed events. This is used to subscribe to theselectedAccountChange
event from theAccountsController
and is responsible for updating active network based on account changes (both EVM and non-EVM).Checklist