@@ -35,7 +35,6 @@ import {
3535import type { Draft } from 'immer' ;
3636
3737import {
38- deepCloneDraft ,
3938 getUUIDFromAddressOfNormalAccount ,
4039 isNormalKeyringType ,
4140 keyringTypeToName ,
@@ -271,9 +270,22 @@ export class AccountsController extends BaseController<
271270 * @throws An error if the account ID is not found.
272271 */
273272 getAccountExpect ( accountId : string ) : InternalAccount {
273+ const account = this . getAccount ( accountId ) ;
274+ if ( account === undefined ) {
275+ throw new Error ( `Account Id "${ accountId } " not found` ) ;
276+ }
277+ return account ;
278+ }
279+
280+ /**
281+ * Returns the last selected EVM account.
282+ *
283+ * @returns The selected internal account.
284+ */
285+ getSelectedAccount ( ) : InternalAccount {
274286 // Edge case where the extension is setup but the srp is not yet created
275287 // certain ui elements will query the selected address before any accounts are created.
276- if ( ! accountId ) {
288+ if ( this . state . internalAccounts . selectedAccount === '' ) {
277289 return {
278290 id : '' ,
279291 address : '' ,
@@ -290,19 +302,6 @@ export class AccountsController extends BaseController<
290302 } ;
291303 }
292304
293- const account = this . getAccount ( accountId ) ;
294- if ( account === undefined ) {
295- throw new Error ( `Account Id "${ accountId } " not found` ) ;
296- }
297- return account ;
298- }
299-
300- /**
301- * Returns the last selected evm account.
302- *
303- * @returns The selected internal account.
304- */
305- getSelectedAccount ( ) : InternalAccount {
306305 const selectedAccount = this . getAccountExpect (
307306 this . state . internalAccounts . selectedAccount ,
308307 ) ;
@@ -412,12 +411,7 @@ export class AccountsController extends BaseController<
412411 ...account ,
413412 metadata : { ...account . metadata , name : accountName } ,
414413 } ;
415- // FIXME: deep clone of old state to get around Type instantiation is excessively deep and possibly infinite.
416- const newState = deepCloneDraft ( currentState ) ;
417-
418- newState . internalAccounts . accounts [ accountId ] = internalAccount ;
419-
420- return newState ;
414+ currentState . internalAccounts . accounts [ accountId ] = internalAccount ;
421415 } ) ;
422416 }
423417
@@ -474,12 +468,7 @@ export class AccountsController extends BaseController<
474468 } , { } as Record < string , InternalAccount > ) ;
475469
476470 this . update ( ( currentState : Draft < AccountsControllerState > ) => {
477- // FIXME: deep clone of old state to get around Type instantiation is excessively deep and possibly infinite.
478- const newState = deepCloneDraft ( currentState ) ;
479-
480- newState . internalAccounts . accounts = accounts ;
481-
482- return newState ;
471+ currentState . internalAccounts . accounts = accounts ;
483472 } ) ;
484473 }
485474
@@ -491,12 +480,7 @@ export class AccountsController extends BaseController<
491480 loadBackup ( backup : AccountsControllerState ) : void {
492481 if ( backup . internalAccounts ) {
493482 this . update ( ( currentState : Draft < AccountsControllerState > ) => {
494- // FIXME: deep clone of old state to get around Type instantiation is excessively deep and possibly infinite.
495- const newState = deepCloneDraft ( currentState ) ;
496-
497- newState . internalAccounts = backup . internalAccounts ;
498-
499- return newState ;
483+ currentState . internalAccounts = backup . internalAccounts ;
500484 } ) ;
501485 }
502486 }
@@ -717,41 +701,58 @@ export class AccountsController extends BaseController<
717701 }
718702 }
719703
720- if ( deletedAccounts . length > 0 ) {
721- for ( const account of deletedAccounts ) {
722- this . #handleAccountRemoved( account . id ) ;
704+ this . update ( ( currentState : Draft < AccountsControllerState > ) => {
705+ if ( deletedAccounts . length > 0 ) {
706+ for ( const account of deletedAccounts ) {
707+ currentState . internalAccounts . accounts = this . #handleAccountRemoved(
708+ currentState . internalAccounts . accounts ,
709+ account . id ,
710+ ) ;
711+ }
723712 }
724- }
725713
726- if ( addedAccounts . length > 0 ) {
727- for ( const account of addedAccounts ) {
728- this . #handleNewAccountAdded( account ) ;
714+ if ( addedAccounts . length > 0 ) {
715+ for ( const account of addedAccounts ) {
716+ currentState . internalAccounts . accounts =
717+ this . #handleNewAccountAdded(
718+ currentState . internalAccounts . accounts ,
719+ account ,
720+ ) ;
721+ }
729722 }
730- }
731723
732- // handle if the selected account was deleted
733- if ( ! this . getAccount ( this . state . internalAccounts . selectedAccount ) ) {
734- const [ accountToSelect ] = this . listAccounts ( ) . sort (
735- ( accountA , accountB ) => {
736- // sort by lastSelected descending
737- return (
738- ( accountB . metadata . lastSelected ?? 0 ) -
739- ( accountA . metadata . lastSelected ?? 0 )
740- ) ;
741- } ,
724+ // We don't use list accounts because it is not the updated state yet.
725+ const existingAccounts = Object . values (
726+ currentState . internalAccounts . accounts ,
742727 ) ;
743728
744- // if the accountToSelect is undefined, then there are no accounts
745- // it mean the keyring was reinitialized.
746- if ( ! accountToSelect ) {
747- this . update ( ( currentState : Draft < AccountsControllerState > ) => {
729+ // handle if the selected account was deleted
730+ if (
731+ ! currentState . internalAccounts . accounts [
732+ this . state . internalAccounts . selectedAccount
733+ ]
734+ ) {
735+ // if currently selected account is undefined and there are no accounts
736+ // it mean the keyring was reinitialized.
737+ if ( existingAccounts . length === 0 ) {
748738 currentState . internalAccounts . selectedAccount = '' ;
749- } ) ;
750- return ;
751- }
739+ return ;
740+ }
752741
753- this . setSelectedAccount ( accountToSelect . id ) ;
754- }
742+ // at this point, we know that `existingAccounts.length` is > 0, so
743+ // `accountToSelect` won't be `undefined`!
744+ const [ accountToSelect ] = existingAccounts . sort (
745+ ( accountA , accountB ) => {
746+ // sort by lastSelected descending
747+ return (
748+ ( accountB . metadata . lastSelected ?? 0 ) -
749+ ( accountA . metadata . lastSelected ?? 0 )
750+ ) ;
751+ } ,
752+ ) ;
753+ currentState . internalAccounts . selectedAccount = accountToSelect . id ;
754+ }
755+ } ) ;
755756 }
756757 }
757758
@@ -786,10 +787,11 @@ export class AccountsController extends BaseController<
786787 /**
787788 * Returns the list of accounts for a given keyring type.
788789 * @param keyringType - The type of keyring.
790+ * @param accounts - Accounts to filter by keyring type.
789791 * @returns The list of accounts associcated with this keyring type.
790792 */
791- #getAccountsByKeyringType( keyringType : string ) {
792- return this . listAccounts ( ) . filter ( ( internalAccount ) => {
793+ #getAccountsByKeyringType( keyringType : string , accounts ?: InternalAccount [ ] ) {
794+ return ( accounts ?? this . listAccounts ( ) ) . filter ( ( internalAccount ) => {
793795 // We do consider `hd` and `simple` keyrings to be of same type. So we check those 2 types
794796 // to group those accounts together!
795797 if (
@@ -833,11 +835,18 @@ export class AccountsController extends BaseController<
833835 /**
834836 * Returns the next account number for a given keyring type.
835837 * @param keyringType - The type of keyring.
838+ * @param accounts - Existing accounts to check for the next available account number.
836839 * @returns An object containing the account prefix and index to use.
837840 */
838- getNextAvailableAccountName ( keyringType : string = KeyringTypes . hd ) : string {
841+ getNextAvailableAccountName (
842+ keyringType : string = KeyringTypes . hd ,
843+ accounts ?: InternalAccount [ ] ,
844+ ) : string {
839845 const keyringName = keyringTypeToName ( keyringType ) ;
840- const keyringAccounts = this . #getAccountsByKeyringType( keyringType ) ;
846+ const keyringAccounts = this . #getAccountsByKeyringType(
847+ keyringType ,
848+ accounts ,
849+ ) ;
841850 const lastDefaultIndexUsedForKeyringType = keyringAccounts . reduce (
842851 ( maxInternalAccountIndex , internalAccount ) => {
843852 // We **DO NOT USE** `\d+` here to only consider valid "human"
@@ -888,9 +897,14 @@ export class AccountsController extends BaseController<
888897 * Handles the addition of a new account to the controller.
889898 * If the account is not a Snap Keyring account, generates an internal account for it and adds it to the controller.
890899 * If the account is a Snap Keyring account, retrieves the account from the keyring and adds it to the controller.
900+ * @param accountsState - AccountsController accounts state that is to be mutated.
891901 * @param account - The address and keyring type object of the new account.
902+ * @returns The updated AccountsController accounts state.
892903 */
893- #handleNewAccountAdded( account : AddressAndKeyringTypeObject ) {
904+ #handleNewAccountAdded(
905+ accountsState : AccountsControllerState [ 'internalAccounts' ] [ 'accounts' ] ,
906+ account : AddressAndKeyringTypeObject ,
907+ ) : AccountsControllerState [ 'internalAccounts' ] [ 'accounts' ] {
894908 let newAccount : InternalAccount ;
895909 if ( account . type !== KeyringTypes . snap ) {
896910 newAccount = this . #generateInternalAccountForNonSnapAccount(
@@ -909,41 +923,41 @@ export class AccountsController extends BaseController<
909923
910924 // The snap deleted the account before the keyring controller could add it
911925 if ( ! newAccount ) {
912- return ;
926+ return accountsState ;
913927 }
914928 }
915929
916930 // Get next account name available for this given keyring
917931 const accountName = this . getNextAvailableAccountName (
918932 newAccount . metadata . keyring . type ,
933+ Object . values ( accountsState ) ,
919934 ) ;
920935
921- this . update ( ( currentState : Draft < AccountsControllerState > ) => {
922- // FIXME: deep clone of old state to get around Type instantiation is excessively deep and possibly infinite.
923- const newState = deepCloneDraft ( currentState ) ;
924-
925- newState . internalAccounts . accounts [ newAccount . id ] = {
926- ...newAccount ,
927- metadata : {
928- ...newAccount . metadata ,
929- name : accountName ,
930- importTime : Date . now ( ) ,
931- lastSelected : 0 ,
932- } ,
933- } ;
936+ accountsState [ newAccount . id ] = {
937+ ...newAccount ,
938+ metadata : {
939+ ...newAccount . metadata ,
940+ name : accountName ,
941+ importTime : Date . now ( ) ,
942+ lastSelected : 0 ,
943+ } ,
944+ } ;
934945
935- return newState ;
936- } ) ;
946+ return accountsState ;
937947 }
938948
939949 /**
940950 * Handles the removal of an account from the internal accounts list.
951+ * @param accountsState - AccountsController accounts state that is to be mutated.
941952 * @param accountId - The ID of the account to be removed.
953+ * @returns The updated AccountsController state.
942954 */
943- #handleAccountRemoved( accountId : string ) {
944- this . update ( ( currentState : Draft < AccountsControllerState > ) => {
945- delete currentState . internalAccounts . accounts [ accountId ] ;
946- } ) ;
955+ #handleAccountRemoved(
956+ accountsState : AccountsControllerState [ 'internalAccounts' ] [ 'accounts' ] ,
957+ accountId : string ,
958+ ) : AccountsControllerState [ 'internalAccounts' ] [ 'accounts' ] {
959+ delete accountsState [ accountId ] ;
960+ return accountsState ;
947961 }
948962
949963 /**
0 commit comments