-
-
Notifications
You must be signed in to change notification settings - Fork 276
Expand file tree
/
Copy pathutils.ts
More file actions
223 lines (203 loc) · 6.64 KB
/
utils.ts
File metadata and controls
223 lines (203 loc) · 6.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
import type { KeyringObject } from '@metamask/keyring-controller';
import { KeyringTypes } from '@metamask/keyring-controller';
import type { InternalAccount } from '@metamask/keyring-internal-api';
import type { Infer } from '@metamask/superstruct';
import { is, number, string, type } from '@metamask/superstruct';
import { hexToBytes } from '@metamask/utils';
import { sha256 } from 'ethereum-cryptography/sha256';
import type { V4Options } from 'uuid';
import { v4 as uuid } from 'uuid';
import type { AccountId } from './AccountsController';
/**
* Returns the name of the keyring type.
*
* @param keyringType - The type of the keyring.
* @returns The name of the keyring type.
*/
export function keyringTypeToName(keyringType: string): string {
switch (keyringType) {
case KeyringTypes.simple: {
return 'Account';
}
case KeyringTypes.hd: {
return 'Account';
}
case KeyringTypes.trezor: {
return 'Trezor';
}
case KeyringTypes.oneKey: {
return 'OneKey';
}
case KeyringTypes.ledger: {
return 'Ledger';
}
case KeyringTypes.lattice: {
return 'Lattice';
}
case KeyringTypes.qr: {
return 'QR';
}
case KeyringTypes.money: {
return 'Money Account';
}
case KeyringTypes.snap: {
return 'Snap Account';
}
default: {
throw new Error(`Unknown keyring ${keyringType}`);
}
}
}
/**
* Generates a UUID v4 options from a given Ethereum address.
*
* @param address - The Ethereum address to generate the UUID from.
* @returns The UUID v4 options.
*/
export function getUUIDOptionsFromAddressOfNormalAccount(
address: string,
): V4Options {
const v4options = {
random: sha256(hexToBytes(address)).slice(0, 16),
};
return v4options;
}
/**
* Generates a UUID from a given Ethereum address.
*
* @param address - The Ethereum address to generate the UUID from.
* @returns The generated UUID.
*/
export function getUUIDFromAddressOfNormalAccount(address: string): string {
return uuid(getUUIDOptionsFromAddressOfNormalAccount(address));
}
/**
* Check if a keyring type is considered a "normal" keyring.
*
* @param keyringType - The account's keyring type.
* @returns True if the keyring type is considered a "normal" keyring, false otherwise.
*/
export function isNormalKeyringType(
keyringType: KeyringTypes | string,
): boolean {
// Right now, we only have to "exclude" Snap accounts, but this might need to be
// adapted later on if we have new kind of keyrings!
return keyringType !== (KeyringTypes.snap as string);
}
/**
* Check if a keyring type is a Snap keyring.
*
* @param keyringType - The account's keyring type.
* @returns True if the keyring type is considered a Snap keyring, false otherwise.
*/
export function isSnapKeyringType(keyringType: KeyringTypes | string): boolean {
return keyringType === (KeyringTypes.snap as string);
}
/**
* Check if a keyring type is a simple keyring.
*
* @param keyringType - The account's keyring type.
* @returns True if the keyring type is considered a simple keyring, false otherwise.
*/
export function isSimpleKeyringType(
keyringType: KeyringTypes | string,
): boolean {
return keyringType === (KeyringTypes.simple as string);
}
/**
* Check if a keyring is a HD keyring.
*
* @param keyringType - The account's keyring type.
* @returns True if the keyring is a HD keyring, false otherwise.
*/
export function isHdKeyringType(keyringType: KeyringTypes | string): boolean {
return keyringType === (KeyringTypes.hd as string);
}
/**
* Get the derivation path for the index of an account within a EVM HD keyring.
*
* @param index - The account index.
* @returns The derivation path.
*/
export function getEvmDerivationPathForIndex(index: number): string {
const purpose = '44';
const coinType = '60'; // Ethereum.
return `m/${purpose}'/${coinType}'/0'/0/${index}`;
}
/**
* Get the group index from a keyring object (EVM HD keyring only) and an address.
*
* @param keyring - The keyring object.
* @param address - The address to match.
* @returns The group index for that address, undefined if not able to match the address.
*/
export function getEvmGroupIndexFromAddressIndex(
keyring: KeyringObject,
address: string,
): number | undefined {
// TODO: Remove this function once EVM HD keyrings start using the new unified
// keyring API.
// NOTE: We mostly put that logic in a separate function so we can easily add coverage
// for (supposedly) unreachable code path.
if (!isHdKeyringType(keyring.type)) {
// We cannot extract the group index from non-HD keyrings.
return undefined;
}
// We need to find the account index from its HD keyring. We assume those
// accounts are ordered, thus we can use their index to compute their
// derivation path and group index.
const groupIndex = keyring.accounts.findIndex(
// NOTE: This is ok to use `toLowerCase` here, since we're only dealing
// with EVM addresses.
(accountAddress) => accountAddress.toLowerCase() === address.toLowerCase(),
);
// If for some reason, we cannot find this address, then the caller made a mistake
// and it did not use the proper keyring object. For now, we do not fail and just
// consider this account as "simple account".
if (groupIndex === -1) {
console.warn(`! Unable to get group index for HD account: "${address}"`);
return undefined;
}
return groupIndex;
}
/**
* HD keyring account for Snap accounts that handles non-EVM HD accounts. (e.g the
* Solana Snap).
*
* NOTE: We use `superstruct.type` here `superstruct.object` since it allows
* extra-properties than a Snap might add in its `options`.
*/
export const HdSnapKeyringAccountOptionsStruct = type({
entropySource: string(),
index: number(),
derivationPath: string(),
});
export type HdSnapKeyringAccountOptions = Infer<
typeof HdSnapKeyringAccountOptionsStruct
>;
/**
* HD keyring account for Snap accounts that handles non-EVM HD accounts.
*/
export type HdSnapKeyringAccount = InternalAccount & {
options: InternalAccount['options'] & HdSnapKeyringAccountOptions;
};
/**
* Check if an account is an HD Snap keyring account.
*
* @param account - Snap keyring account.
* @returns True if valid, false otherwise.
*/
export function isHdSnapKeyringAccount(
account: InternalAccount,
): account is HdSnapKeyringAccount {
return is(account.options, HdSnapKeyringAccountOptionsStruct);
}
export function constructAccountIdByAddress(
accountsMap: Record<AccountId, InternalAccount>,
): Record<string, AccountId> {
const accounts = Object.values(accountsMap);
return accounts.reduce<Record<string, AccountId>>((acc, account) => {
acc[account.address] = account.id;
return acc;
}, {});
}