@@ -8,7 +8,7 @@ import type { RestrictedControllerMessenger } from '@metamask/base-controller';
88import { BaseController } from '@metamask/base-controller' ;
99import * as encryptorUtils from '@metamask/browser-passworder' ;
1010import HDKeyring from '@metamask/eth-hd-keyring' ;
11- import { normalize } from '@metamask/eth-sig-util' ;
11+ import { normalize as ethNormalize } from '@metamask/eth-sig-util' ;
1212import SimpleKeyring from '@metamask/eth-simple-keyring' ;
1313import type {
1414 EthBaseTransaction ,
@@ -34,6 +34,7 @@ import {
3434 bytesToHex ,
3535 hasProperty ,
3636 isObject ,
37+ isStrictHexString ,
3738 isValidHexAddress ,
3839 isValidJson ,
3940 remove0x ,
@@ -503,12 +504,44 @@ async function displayForKeyring(
503504
504505 return {
505506 type : keyring . type ,
506- // Cast to `Hex []` here is safe here because `accounts` has no nullish
507- // values, and `normalize` returns `Hex ` unless given a nullish value
508- accounts : accounts . map ( normalize ) as Hex [ ] ,
507+ // Cast to `string []` here is safe here because `accounts` has no nullish
508+ // values, and `normalize` returns `string ` unless given a nullish value
509+ accounts : accounts . map ( normalize ) as string [ ] ,
509510 } ;
510511}
511512
513+ /**
514+ * Check if address is an ethereum address
515+ *
516+ * @param address - An address.
517+ * @returns Returns true if the address is an ethereum one, false otherwise.
518+ */
519+ function isEthAddress ( address : string ) : boolean {
520+ // We first check if it's a matching `Hex` string, so that is narrows down
521+ // `address` as an `Hex` type, allowing us to use `isValidHexAddress`
522+ return (
523+ // NOTE: This function only checks for lowercased strings
524+ isStrictHexString ( address . toLowerCase ( ) ) &&
525+ // This checks for lowercased addresses and checksum addresses too
526+ isValidHexAddress ( address as Hex )
527+ ) ;
528+ }
529+
530+ /**
531+ * Normalize ethereum or non-EVM address.
532+ *
533+ * @param address - Ethereum or non-EVM address.
534+ * @returns The normalized address.
535+ */
536+ function normalize ( address : string ) : string | undefined {
537+ // Since the `KeyringController` is only dealing with address, we have
538+ // no other way to get the associated account type with this address. So we
539+ // are down to check the actual address format for now
540+ // TODO: Find a better way to not have those runtime checks based on the
541+ // address value!
542+ return isEthAddress ( address ) ? ethNormalize ( address ) : address ;
543+ }
544+
512545/**
513546 * Controller responsible for establishing and managing user identity.
514547 *
@@ -644,6 +677,10 @@ export class KeyringController extends BaseController<
644677 keyring : EthKeyring < Json > ,
645678 accountCount ?: number ,
646679 ) : Promise < Hex > {
680+ // READ THIS CAREFULLY:
681+ // We still uses `Hex` here, since we are not using this method when creating
682+ // and account using a "Snap Keyring". This function assume the `keyring` is
683+ // ethereum compatible, but "Snap Keyring" might not be.
647684 return this . #persistOrRollback( async ( ) => {
648685 const oldAccounts = await this . #getAccountsFromKeyrings( ) ;
649686
@@ -828,15 +865,15 @@ export class KeyringController extends BaseController<
828865 account : string ,
829866 opts ?: Record < string , unknown > ,
830867 ) : Promise < string > {
831- const normalizedAddress = normalize ( account ) as Hex ;
868+ const address = ethNormalize ( account ) as Hex ;
832869 const keyring = ( await this . getKeyringForAccount (
833870 account ,
834871 ) ) as EthKeyring < Json > ;
835872 if ( ! keyring . getEncryptionPublicKey ) {
836873 throw new Error ( KeyringControllerError . UnsupportedGetEncryptionPublicKey ) ;
837874 }
838875
839- return await keyring . getEncryptionPublicKey ( normalizedAddress , opts ) ;
876+ return await keyring . getEncryptionPublicKey ( address , opts ) ;
840877 }
841878
842879 /**
@@ -851,7 +888,7 @@ export class KeyringController extends BaseController<
851888 from : string ;
852889 data : Eip1024EncryptedData ;
853890 } ) : Promise < string > {
854- const address = normalize ( messageParams . from ) as Hex ;
891+ const address = ethNormalize ( messageParams . from ) as Hex ;
855892 const keyring = ( await this . getKeyringForAccount (
856893 address ,
857894 ) ) as EthKeyring < Json > ;
@@ -873,9 +910,7 @@ export class KeyringController extends BaseController<
873910 * @returns Promise resolving to keyring of the `account` if one exists.
874911 */
875912 async getKeyringForAccount ( account : string ) : Promise < unknown > {
876- // Cast to `Hex` here is safe here because `address` is not nullish.
877- // `normalizeToHex` returns `Hex` unless given a nullish value.
878- const hexed = normalize ( account ) as Hex ;
913+ const address = normalize ( account ) ;
879914
880915 const candidates = await Promise . all (
881916 this . #keyrings. map ( async ( keyring ) => {
@@ -885,7 +920,7 @@ export class KeyringController extends BaseController<
885920
886921 const winners = candidates . filter ( ( candidate ) => {
887922 const accounts = candidate [ 1 ] . map ( normalize ) ;
888- return accounts . includes ( hexed ) ;
923+ return accounts . includes ( address ) ;
889924 } ) ;
890925
891926 if ( winners . length && winners [ 0 ] ?. length ) {
@@ -894,9 +929,7 @@ export class KeyringController extends BaseController<
894929
895930 // Adding more info to the error
896931 let errorInfo = '' ;
897- if ( ! isValidHexAddress ( hexed ) ) {
898- errorInfo = 'The address passed in is invalid/empty' ;
899- } else if ( ! candidates . length ) {
932+ if ( ! candidates . length ) {
900933 errorInfo = 'There are no keyrings' ;
901934 } else if ( ! winners . length ) {
902935 errorInfo = 'There are keyrings, but none match the address' ;
@@ -999,7 +1032,7 @@ export class KeyringController extends BaseController<
9991032 * @fires KeyringController:accountRemoved
10001033 * @returns Promise resolving when the account is removed.
10011034 */
1002- async removeAccount ( address : Hex ) : Promise < void > {
1035+ async removeAccount ( address : string ) : Promise < void > {
10031036 await this . #persistOrRollback( async ( ) => {
10041037 const keyring = ( await this . getKeyringForAccount (
10051038 address ,
@@ -1013,7 +1046,10 @@ export class KeyringController extends BaseController<
10131046 // The `removeAccount` method of snaps keyring is async. We have to update
10141047 // the interface of the other keyrings to be async as well.
10151048 // eslint-disable-next-line @typescript-eslint/await-thenable
1016- await keyring . removeAccount ( address ) ;
1049+ // FIXME: We do cast to `Hex` to makes the type checker happy here, and
1050+ // because `Keyring<State>.removeAccount` requires address to be `Hex`. Those
1051+ // type would need to be updated for a full non-EVM support.
1052+ await keyring . removeAccount ( address as Hex ) ;
10171053
10181054 const accounts = await keyring . getAccounts ( ) ;
10191055 // Check if this was the last/only account
@@ -1057,7 +1093,7 @@ export class KeyringController extends BaseController<
10571093 throw new Error ( "Can't sign an empty message" ) ;
10581094 }
10591095
1060- const address = normalize ( messageParams . from ) as Hex ;
1096+ const address = ethNormalize ( messageParams . from ) as Hex ;
10611097 const keyring = ( await this . getKeyringForAccount (
10621098 address ,
10631099 ) ) as EthKeyring < Json > ;
@@ -1075,7 +1111,7 @@ export class KeyringController extends BaseController<
10751111 * @returns Promise resolving to a signed message string.
10761112 */
10771113 async signPersonalMessage ( messageParams : PersonalMessageParams ) {
1078- const address = normalize ( messageParams . from ) as Hex ;
1114+ const address = ethNormalize ( messageParams . from ) as Hex ;
10791115 const keyring = ( await this . getKeyringForAccount (
10801116 address ,
10811117 ) ) as EthKeyring < Json > ;
@@ -1113,7 +1149,7 @@ export class KeyringController extends BaseController<
11131149
11141150 // Cast to `Hex` here is safe here because `messageParams.from` is not nullish.
11151151 // `normalize` returns `Hex` unless given a nullish value.
1116- const address = normalize ( messageParams . from ) as Hex ;
1152+ const address = ethNormalize ( messageParams . from ) as Hex ;
11171153 const keyring = ( await this . getKeyringForAccount (
11181154 address ,
11191155 ) ) as EthKeyring < Json > ;
@@ -1147,7 +1183,7 @@ export class KeyringController extends BaseController<
11471183 from : string ,
11481184 opts ?: Record < string , unknown > ,
11491185 ) : Promise < TxData > {
1150- const address = normalize ( from ) as Hex ;
1186+ const address = ethNormalize ( from ) as Hex ;
11511187 const keyring = ( await this . getKeyringForAccount (
11521188 address ,
11531189 ) ) as EthKeyring < Json > ;
@@ -1171,7 +1207,7 @@ export class KeyringController extends BaseController<
11711207 transactions : EthBaseTransaction [ ] ,
11721208 executionContext : KeyringExecutionContext ,
11731209 ) : Promise < EthBaseUserOperation > {
1174- const address = normalize ( from ) as Hex ;
1210+ const address = ethNormalize ( from ) as Hex ;
11751211 const keyring = ( await this . getKeyringForAccount (
11761212 address ,
11771213 ) ) as EthKeyring < Json > ;
@@ -1201,7 +1237,7 @@ export class KeyringController extends BaseController<
12011237 userOp : EthUserOperation ,
12021238 executionContext : KeyringExecutionContext ,
12031239 ) : Promise < EthUserOperationPatch > {
1204- const address = normalize ( from ) as Hex ;
1240+ const address = ethNormalize ( from ) as Hex ;
12051241 const keyring = ( await this . getKeyringForAccount (
12061242 address ,
12071243 ) ) as EthKeyring < Json > ;
@@ -1226,7 +1262,7 @@ export class KeyringController extends BaseController<
12261262 userOp : EthUserOperation ,
12271263 executionContext : KeyringExecutionContext ,
12281264 ) : Promise < string > {
1229- const address = normalize ( from ) as Hex ;
1265+ const address = ethNormalize ( from ) as Hex ;
12301266 const keyring = ( await this . getKeyringForAccount (
12311267 address ,
12321268 ) ) as EthKeyring < Json > ;
@@ -1979,7 +2015,7 @@ export class KeyringController extends BaseController<
19792015 *
19802016 * @returns A promise resolving to an array of accounts.
19812017 */
1982- async #getAccountsFromKeyrings( ) : Promise < Hex [ ] > {
2018+ async #getAccountsFromKeyrings( ) : Promise < string [ ] > {
19832019 const keyrings = this . #keyrings;
19842020
19852021 const keyringArrays = await Promise . all (
@@ -1989,9 +2025,9 @@ export class KeyringController extends BaseController<
19892025 return res . concat ( arr ) ;
19902026 } , [ ] ) ;
19912027
1992- // Cast to `Hex []` here is safe here because `addresses` has no nullish
1993- // values, and `normalize` returns `Hex ` unless given a nullish value
1994- return addresses . map ( normalize ) as Hex [ ] ;
2028+ // Cast to `string []` here is safe here because `addresses` has no nullish
2029+ // values, and `normalize` returns `string ` unless given a nullish value
2030+ return addresses . map ( normalize ) as string [ ] ;
19952031 }
19962032
19972033 /**
0 commit comments