@@ -17,9 +17,12 @@ import {
1717 isEvmAccountType ,
1818 KeyringAccountEntropyTypeOption ,
1919} from '@metamask/keyring-api' ;
20- import type { KeyringObject } from '@metamask/keyring-controller' ;
20+ import type {
21+ KeyringControllerAccountAddedEvent ,
22+ KeyringControllerAccountRemovedEvent ,
23+ KeyringObject ,
24+ } from '@metamask/keyring-controller' ;
2125import {
22- type KeyringControllerState ,
2326 type KeyringControllerGetKeyringsByTypeAction ,
2427 type KeyringControllerStateChangeEvent ,
2528 type KeyringControllerGetStateAction ,
@@ -36,7 +39,7 @@ import type {
3639import type { SnapId } from '@metamask/snaps-sdk' ;
3740import { type CaipChainId , isCaipChainId } from '@metamask/utils' ;
3841import type { WritableDraft } from 'immer/dist/internal.js' ;
39- import { cloneDeep } from 'lodash' ;
42+ import { add , cloneDeep } from 'lodash' ;
4043
4144import type { MultichainNetworkControllerNetworkDidChangeEvent } from './types' ;
4245import type { AccountsControllerStrictState } from './typing' ;
@@ -201,6 +204,8 @@ export type AccountsControllerAccountAssetListUpdatedEvent = {
201204export type AllowedEvents =
202205 | SnapStateChange
203206 | KeyringControllerStateChangeEvent
207+ | KeyringControllerAccountRemovedEvent
208+ | KeyringControllerAccountAddedEvent
204209 | SnapKeyringAccountAssetListUpdatedEvent
205210 | SnapKeyringAccountBalancesUpdatedEvent
206211 | SnapKeyringAccountTransactionsUpdatedEvent
@@ -747,168 +752,59 @@ export class AccountsController extends BaseController<
747752 this . messenger . publish ( event , ...payload ) ;
748753 }
749754
750- /**
751- * Handles changes in the keyring state, specifically when new accounts are added or removed.
752- *
753- * @param keyringState - The new state of the keyring controller.
754- * @param keyringState.isUnlocked - True if the keyrings are unlocked, false otherwise.
755- * @param keyringState.keyrings - List of all keyrings.
756- */
757- #handleOnKeyringStateChange( {
758- isUnlocked,
759- keyrings,
760- } : KeyringControllerState ) : void {
761- // TODO: Change when accountAdded event is added to the keyring controller.
762-
763- // We check for keyrings length to be greater than 0 because the extension client may try execute
764- // submit password twice and clear the keyring state.
765- // https://github.com/MetaMask/KeyringController/blob/2d73a4deed8d013913f6ef0c9f5c0bb7c614f7d3/src/KeyringController.ts#L910
766- if ( ! isUnlocked || keyrings . length === 0 ) {
767- return ;
768- }
769-
770- // State patches.
771- const generatePatch = ( ) => {
772- return {
773- previous : { } as Record < string , InternalAccount > ,
774- added : [ ] as {
775- address : string ;
776- keyring : KeyringObject ;
777- } [ ] ,
778- updated : [ ] as InternalAccount [ ] ,
779- removed : [ ] as InternalAccount [ ] ,
780- } ;
781- } ;
782- const patches = {
783- snap : generatePatch ( ) ,
784- normal : generatePatch ( ) ,
785- } ;
755+ #handleOnKeyringAccountAdded( address : string , keyring : KeyringObject ) {
756+ let account : InternalAccount | undefined ;
786757
787- // Gets the patch object based on the keyring type (since Snap accounts and other accounts
788- // are handled differently).
789- const patchOf = ( type : string ) => {
790- if ( isSnapKeyringType ( type ) ) {
791- return patches . snap ;
792- }
793- return patches . normal ;
794- } ;
795-
796- // Create a map (with lower-cased addresses) of all existing accounts.
797- for ( const account of this . listMultichainAccounts ( ) ) {
798- const address = account . address . toLowerCase ( ) ;
799- const patch = patchOf ( account . metadata . keyring . type ) ;
800-
801- patch . previous [ address ] = account ;
802- }
758+ this . #update( ( state ) => {
759+ const { internalAccounts } = state ;
803760
804- // Go over all keyring changes and create patches out of it.
805- const addresses = new Set < string > ( ) ;
806- for ( const keyring of keyrings ) {
807- const patch = patchOf ( keyring . type ) ;
761+ account = this . #getInternalAccountFromAddressAndType( address , keyring ) ;
762+ if ( account ) {
763+ // Re-compute the list of accounts everytime, so we can make sure new names
764+ // are also considered.
765+ const accounts = Object . values (
766+ internalAccounts . accounts ,
767+ ) as InternalAccount [ ] ;
808768
809- for ( const accountAddress of keyring . accounts ) {
810- // Lower-case address to use it in the `previous` map.
811- const address = accountAddress . toLowerCase ( ) ;
812- const account = patch . previous [ address ] ;
769+ // Get next account name available for this given keyring.
770+ const name = this . getNextAvailableAccountName (
771+ account . metadata . keyring . type ,
772+ accounts ,
773+ ) ;
813774
814- if ( account ) {
815- // If the account exists before, this might be an update.
816- patch . updated . push ( account ) ;
817- } else {
818- // Otherwise, that's a new account.
819- patch . added . push ( {
820- address,
821- keyring,
822- } ) ;
823- }
775+ // If it's the first account, we need to select it.
776+ const lastSelected =
777+ accounts . length === 0 ? this . #getLastSelectedIndex( ) : 0 ;
824778
825- // Keep track of those address to check for removed accounts later.
826- addresses . add ( address ) ;
779+ internalAccounts . accounts [ account . id ] = {
780+ ...account ,
781+ metadata : {
782+ ...account . metadata ,
783+ name,
784+ importTime : Date . now ( ) ,
785+ lastSelected,
786+ } ,
787+ } ;
827788 }
828- }
789+ } ) ;
829790
830- // We might have accounts associated with removed keyrings, so we iterate
831- // over all previous known accounts and check against the keyring addresses.
832- for ( const patch of [ patches . snap , patches . normal ] ) {
833- for ( const [ address , account ] of Object . entries ( patch . previous ) ) {
834- // If a previous address is not part of the new addesses, then it got removed.
835- if ( ! addresses . has ( address ) ) {
836- patch . removed . push ( account ) ;
837- }
838- }
791+ if ( account ) {
792+ this . messenger . publish ( 'AccountsController:accountAdded' , account ) ;
839793 }
794+ }
840795
841- // Diff that we will use to publish events afterward.
842- const diff = {
843- removed : [ ] as string [ ] ,
844- added : [ ] as InternalAccount [ ] ,
845- } ;
796+ #handleOnKeyringAccountRemoved( address : string ) {
797+ const account = this . listMultichainAccounts ( ) . find (
798+ ( { address : accountAddress } ) => accountAddress === address ,
799+ ) ;
846800
847- this . #update (
848- ( state ) => {
801+ if ( account ) {
802+ this . #update ( ( state ) => {
849803 const { internalAccounts } = state ;
850804
851- for ( const patch of [ patches . snap , patches . normal ] ) {
852- for ( const account of patch . removed ) {
853- delete internalAccounts . accounts [ account . id ] ;
854-
855- diff . removed . push ( account . id ) ;
856- }
857-
858- for ( const added of patch . added ) {
859- const account = this . #getInternalAccountFromAddressAndType(
860- added . address ,
861- added . keyring ,
862- ) ;
863-
864- if ( account ) {
865- // Re-compute the list of accounts everytime, so we can make sure new names
866- // are also considered.
867- const accounts = Object . values (
868- internalAccounts . accounts ,
869- ) as InternalAccount [ ] ;
870-
871- // Get next account name available for this given keyring.
872- const name = this . getNextAvailableAccountName (
873- account . metadata . keyring . type ,
874- accounts ,
875- ) ;
876-
877- // If it's the first account, we need to select it.
878- const lastSelected =
879- accounts . length === 0 ? this . #getLastSelectedIndex( ) : 0 ;
880-
881- internalAccounts . accounts [ account . id ] = {
882- ...account ,
883- metadata : {
884- ...account . metadata ,
885- name,
886- importTime : Date . now ( ) ,
887- lastSelected,
888- } ,
889- } ;
890-
891- diff . added . push ( internalAccounts . accounts [ account . id ] ) ;
892- }
893- }
894- }
895- } ,
896- // Will get executed after the update, but before re-selecting an account in case
897- // the current one is not valid anymore.
898- ( ) => {
899- // Now publish events
900- for ( const id of diff . removed ) {
901- this . messenger . publish ( 'AccountsController:accountRemoved' , id ) ;
902- }
903-
904- for ( const account of diff . added ) {
905- this . messenger . publish ( 'AccountsController:accountAdded' , account ) ;
906- }
907- } ,
908- ) ;
909-
910- // NOTE: Since we also track "updated" accounts with our patches, we could fire a new event
911- // like `accountUpdated` (we would still need to check if anything really changed on the account).
805+ delete internalAccounts . accounts [ account . id ] ;
806+ } ) ;
807+ }
912808 }
913809
914810 /**
@@ -1214,8 +1110,13 @@ export class AccountsController extends BaseController<
12141110 this . #handleOnSnapStateChange( snapStateState ) ,
12151111 ) ;
12161112
1217- this . messenger . subscribe ( 'KeyringController:stateChange' , ( keyringState ) =>
1218- this . #handleOnKeyringStateChange( keyringState ) ,
1113+ this . messenger . subscribe ( 'KeyringController:accountRemoved' , ( address ) =>
1114+ this . #handleOnKeyringAccountRemoved( address ) ,
1115+ ) ;
1116+
1117+ this . messenger . subscribe (
1118+ 'KeyringController:accountAdded' ,
1119+ ( address , keyring ) => this . #handleOnKeyringAccountAdded( address , keyring ) ,
12191120 ) ;
12201121
12211122 this . messenger . subscribe (
0 commit comments