@pezkuwi/extension-base 0.62.8 → 0.62.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/background/RequestBytesSign.ts +0 -28
- package/src/background/RequestExtrinsicSign.ts +0 -22
- package/src/background/handlers/Extension.spec.ts +0 -478
- package/src/background/handlers/Extension.ts +0 -690
- package/src/background/handlers/State.ts +0 -664
- package/src/background/handlers/Tabs.ts +0 -289
- package/src/background/handlers/helpers.ts +0 -14
- package/src/background/handlers/index.ts +0 -60
- package/src/background/handlers/subscriptions.ts +0 -32
- package/src/background/index.ts +0 -4
- package/src/background/types.ts +0 -432
- package/src/bundle.ts +0 -4
- package/src/defaults.ts +0 -26
- package/src/index.ts +0 -7
- package/src/packageDetect.ts +0 -14
- package/src/packageInfo.ts +0 -6
- package/src/page/Accounts.ts +0 -33
- package/src/page/Injected.ts +0 -33
- package/src/page/Metadata.ts +0 -22
- package/src/page/PostMessageProvider.ts +0 -182
- package/src/page/Signer.ts +0 -45
- package/src/page/index.ts +0 -89
- package/src/page/types.ts +0 -10
- package/src/stores/Accounts.ts +0 -28
- package/src/stores/Base.ts +0 -93
- package/src/stores/Metadata.ts +0 -17
- package/src/stores/index.ts +0 -5
- package/src/types.ts +0 -12
- package/src/utils/canDerive.ts +0 -8
- package/src/utils/getId.ts +0 -10
- package/src/utils/index.ts +0 -4
- package/src/utils/portUtils.ts +0 -65
- package/tsconfig.build.json +0 -16
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.spec.json +0 -18
- package/tsconfig.spec.tsbuildinfo +0 -1
- /package/{build/background → background}/RequestBytesSign.d.ts +0 -0
- /package/{build/background → background}/RequestBytesSign.js +0 -0
- /package/{build/background → background}/RequestExtrinsicSign.d.ts +0 -0
- /package/{build/background → background}/RequestExtrinsicSign.js +0 -0
- /package/{build/background → background}/handlers/Extension.d.ts +0 -0
- /package/{build/background → background}/handlers/Extension.js +0 -0
- /package/{build/background → background}/handlers/State.d.ts +0 -0
- /package/{build/background → background}/handlers/State.js +0 -0
- /package/{build/background → background}/handlers/Tabs.d.ts +0 -0
- /package/{build/background → background}/handlers/Tabs.js +0 -0
- /package/{build/background → background}/handlers/helpers.d.ts +0 -0
- /package/{build/background → background}/handlers/helpers.js +0 -0
- /package/{build/background → background}/handlers/index.d.ts +0 -0
- /package/{build/background → background}/handlers/index.js +0 -0
- /package/{build/background → background}/handlers/subscriptions.d.ts +0 -0
- /package/{build/background → background}/handlers/subscriptions.js +0 -0
- /package/{build/background → background}/index.d.ts +0 -0
- /package/{build/background → background}/index.js +0 -0
- /package/{build/background → background}/types.d.ts +0 -0
- /package/{build/background → background}/types.js +0 -0
- /package/{build/bundle.d.ts → bundle.d.ts} +0 -0
- /package/{build/bundle.js → bundle.js} +0 -0
- /package/{build/defaults.d.ts → defaults.d.ts} +0 -0
- /package/{build/defaults.js → defaults.js} +0 -0
- /package/{build/index.d.ts → index.d.ts} +0 -0
- /package/{build/index.js → index.js} +0 -0
- /package/{build/packageDetect.d.ts → packageDetect.d.ts} +0 -0
- /package/{build/packageDetect.js → packageDetect.js} +0 -0
- /package/{build/packageInfo.d.ts → packageInfo.d.ts} +0 -0
- /package/{build/packageInfo.js → packageInfo.js} +0 -0
- /package/{build/page → page}/Accounts.d.ts +0 -0
- /package/{build/page → page}/Accounts.js +0 -0
- /package/{build/page → page}/Injected.d.ts +0 -0
- /package/{build/page → page}/Injected.js +0 -0
- /package/{build/page → page}/Metadata.d.ts +0 -0
- /package/{build/page → page}/Metadata.js +0 -0
- /package/{build/page → page}/PostMessageProvider.d.ts +0 -0
- /package/{build/page → page}/PostMessageProvider.js +0 -0
- /package/{build/page → page}/Signer.d.ts +0 -0
- /package/{build/page → page}/Signer.js +0 -0
- /package/{build/page → page}/index.d.ts +0 -0
- /package/{build/page → page}/index.js +0 -0
- /package/{build/page → page}/types.d.ts +0 -0
- /package/{build/page → page}/types.js +0 -0
- /package/{build/stores → stores}/Accounts.d.ts +0 -0
- /package/{build/stores → stores}/Accounts.js +0 -0
- /package/{build/stores → stores}/Base.d.ts +0 -0
- /package/{build/stores → stores}/Base.js +0 -0
- /package/{build/stores → stores}/Metadata.d.ts +0 -0
- /package/{build/stores → stores}/Metadata.js +0 -0
- /package/{build/stores → stores}/index.d.ts +0 -0
- /package/{build/stores → stores}/index.js +0 -0
- /package/{build/types.d.ts → types.d.ts} +0 -0
- /package/{build/types.js → types.js} +0 -0
- /package/{build/utils → utils}/canDerive.d.ts +0 -0
- /package/{build/utils → utils}/canDerive.js +0 -0
- /package/{build/utils → utils}/getId.d.ts +0 -0
- /package/{build/utils → utils}/getId.js +0 -0
- /package/{build/utils → utils}/index.d.ts +0 -0
- /package/{build/utils → utils}/index.js +0 -0
- /package/{build/utils → utils}/portUtils.d.ts +0 -0
- /package/{build/utils → utils}/portUtils.js +0 -0
|
@@ -1,690 +0,0 @@
|
|
|
1
|
-
// Copyright 2019-2025 @pezkuwi/extension-base authors & contributors
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
|
|
4
|
-
/* global chrome */
|
|
5
|
-
|
|
6
|
-
import type { MetadataDef } from '@pezkuwi/extension-inject/types';
|
|
7
|
-
import type { KeyringPair, KeyringPair$Json, KeyringPair$Meta } from '@pezkuwi/keyring/types';
|
|
8
|
-
import type { Registry, SignerPayloadJSON, SignerPayloadRaw } from '@pezkuwi/types/types';
|
|
9
|
-
import type { SubjectInfo } from '@pezkuwi/ui-keyring/observable/types';
|
|
10
|
-
import type { KeypairType } from '@pezkuwi/util-crypto/types';
|
|
11
|
-
import type { AccountJson, AllowedPath, AuthorizeRequest, MessageTypes, MetadataRequest, RequestAccountBatchExport, RequestAccountChangePassword, RequestAccountCreateExternal, RequestAccountCreateHardware, RequestAccountCreateSuri, RequestAccountEdit, RequestAccountExport, RequestAccountForget, RequestAccountShow, RequestAccountTie, RequestAccountValidate, RequestActiveTabsUrlUpdate, RequestAuthorizeApprove, RequestBatchRestore, RequestDeriveCreate, RequestDeriveValidate, RequestJsonRestore, RequestMetadataApprove, RequestMetadataReject, RequestSeedCreate, RequestSeedValidate, RequestSigningApprovePassword, RequestSigningApproveSignature, RequestSigningCancel, RequestSigningIsLocked, RequestTypes, RequestUpdateAuthorizedAccounts, ResponseAccountExport, ResponseAccountsExport, ResponseAuthorizeList, ResponseDeriveValidate, ResponseJsonGetAccountInfo, ResponseSeedCreate, ResponseSeedValidate, ResponseSigningIsLocked, ResponseType, SigningRequest } from '../types.js';
|
|
12
|
-
import type { AuthorizedAccountsDiff } from './State.js';
|
|
13
|
-
import type State from './State.js';
|
|
14
|
-
|
|
15
|
-
import { ALLOWED_PATH, PASSWORD_EXPIRY_MS } from '@pezkuwi/extension-base/defaults';
|
|
16
|
-
import { metadataExpand } from '@pezkuwi/extension-chains';
|
|
17
|
-
import { TypeRegistry } from '@pezkuwi/types';
|
|
18
|
-
import { keyring } from '@pezkuwi/ui-keyring';
|
|
19
|
-
import { accounts as accountsObservable } from '@pezkuwi/ui-keyring/observable/accounts';
|
|
20
|
-
import { assert, isHex } from '@pezkuwi/util';
|
|
21
|
-
import { keyExtractSuri, mnemonicGenerate, mnemonicValidate } from '@pezkuwi/util-crypto';
|
|
22
|
-
|
|
23
|
-
import { withErrorLog } from './helpers.js';
|
|
24
|
-
import { createSubscription, unsubscribe } from './subscriptions.js';
|
|
25
|
-
|
|
26
|
-
type CachedUnlocks = Record<string, number>;
|
|
27
|
-
|
|
28
|
-
const SEED_DEFAULT_LENGTH = 12;
|
|
29
|
-
const SEED_LENGTHS = [12, 15, 18, 21, 24];
|
|
30
|
-
const ETH_DERIVE_DEFAULT = "/m/44'/60'/0'/0/0";
|
|
31
|
-
|
|
32
|
-
function getSuri (seed: string, type?: KeypairType): string {
|
|
33
|
-
return type === 'ethereum'
|
|
34
|
-
? `${seed}${ETH_DERIVE_DEFAULT}`
|
|
35
|
-
: seed;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function isJsonPayload (value: SignerPayloadJSON | SignerPayloadRaw): value is SignerPayloadJSON {
|
|
39
|
-
return (value as SignerPayloadJSON).genesisHash !== undefined;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export default class Extension {
|
|
43
|
-
readonly #cachedUnlocks: CachedUnlocks;
|
|
44
|
-
|
|
45
|
-
readonly #state: State;
|
|
46
|
-
|
|
47
|
-
constructor (state: State) {
|
|
48
|
-
this.#cachedUnlocks = {};
|
|
49
|
-
this.#state = state;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
private transformAccounts (accounts: SubjectInfo): AccountJson[] {
|
|
53
|
-
return Object.values(accounts).map(({ json: { address, meta }, type }): AccountJson => ({
|
|
54
|
-
address,
|
|
55
|
-
isDefaultAuthSelected: this.#state.defaultAuthAccountSelection.includes(address),
|
|
56
|
-
...meta,
|
|
57
|
-
type
|
|
58
|
-
}));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
private accountsCreateExternal ({ address, genesisHash, name }: RequestAccountCreateExternal): boolean {
|
|
62
|
-
keyring.addExternal(address, { genesisHash, name });
|
|
63
|
-
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
private accountsCreateHardware ({ accountIndex, address, addressOffset, genesisHash, hardwareType, name, type }: RequestAccountCreateHardware): boolean {
|
|
68
|
-
keyring.addHardware(address, hardwareType, { accountIndex, addressOffset, genesisHash, name, type });
|
|
69
|
-
|
|
70
|
-
return true;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
private accountsCreateSuri ({ genesisHash, name, password, suri, type }: RequestAccountCreateSuri): boolean {
|
|
74
|
-
keyring.addUri(getSuri(suri, type), password, { genesisHash, name }, type);
|
|
75
|
-
|
|
76
|
-
return true;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
private accountsChangePassword ({ address, newPass, oldPass }: RequestAccountChangePassword): boolean {
|
|
80
|
-
const pair = keyring.getPair(address);
|
|
81
|
-
|
|
82
|
-
assert(pair, 'Unable to find pair');
|
|
83
|
-
|
|
84
|
-
try {
|
|
85
|
-
if (!pair.isLocked) {
|
|
86
|
-
pair.lock();
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
pair.decodePkcs8(oldPass);
|
|
90
|
-
} catch {
|
|
91
|
-
throw new Error('oldPass is invalid');
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
keyring.encryptAccount(pair, newPass);
|
|
95
|
-
|
|
96
|
-
return true;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
private accountsEdit ({ address, name }: RequestAccountEdit): boolean {
|
|
100
|
-
const pair = keyring.getPair(address);
|
|
101
|
-
|
|
102
|
-
assert(pair, 'Unable to find pair');
|
|
103
|
-
|
|
104
|
-
keyring.saveAccountMeta(pair, { ...pair.meta, name });
|
|
105
|
-
|
|
106
|
-
return true;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
private accountsExport ({ address, password }: RequestAccountExport): ResponseAccountExport {
|
|
110
|
-
return { exportedJson: keyring.backupAccount(keyring.getPair(address), password) };
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
private async accountsBatchExport ({ addresses, password }: RequestAccountBatchExport): Promise<ResponseAccountsExport> {
|
|
114
|
-
return {
|
|
115
|
-
exportedJson: await keyring.backupAccounts(addresses, password)
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
private async accountsForget ({ address }: RequestAccountForget): Promise<boolean> {
|
|
120
|
-
const authorizedAccountsDiff: AuthorizedAccountsDiff = [];
|
|
121
|
-
|
|
122
|
-
// cycle through authUrls and prepare the array of diff
|
|
123
|
-
Object.entries(this.#state.authUrls).forEach(([url, urlInfo]) => {
|
|
124
|
-
// Note that urlInfo.authorizedAccounts may be undefined if this website entry
|
|
125
|
-
// was created before the "account authorization per website" functionality was introduced
|
|
126
|
-
if (urlInfo.authorizedAccounts?.includes(address)) {
|
|
127
|
-
authorizedAccountsDiff.push([url, urlInfo.authorizedAccounts.filter((previousAddress) => previousAddress !== address)]);
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
await this.#state.updateAuthorizedAccounts(authorizedAccountsDiff);
|
|
132
|
-
|
|
133
|
-
// cycle through default account selection for auth and remove any occurrence of the account
|
|
134
|
-
const newDefaultAuthAccounts = this.#state.defaultAuthAccountSelection.filter((defaultSelectionAddress) => defaultSelectionAddress !== address);
|
|
135
|
-
|
|
136
|
-
await this.#state.updateDefaultAuthAccounts(newDefaultAuthAccounts);
|
|
137
|
-
|
|
138
|
-
keyring.forgetAccount(address);
|
|
139
|
-
|
|
140
|
-
return true;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
private refreshAccountPasswordCache (pair: KeyringPair): number {
|
|
144
|
-
const { address } = pair;
|
|
145
|
-
|
|
146
|
-
const savedExpiry = this.#cachedUnlocks[address] || 0;
|
|
147
|
-
const remainingTime = savedExpiry - Date.now();
|
|
148
|
-
|
|
149
|
-
if (remainingTime < 0) {
|
|
150
|
-
this.#cachedUnlocks[address] = 0;
|
|
151
|
-
pair.lock();
|
|
152
|
-
|
|
153
|
-
return 0;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return remainingTime;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
private accountsShow ({ address, isShowing }: RequestAccountShow): boolean {
|
|
160
|
-
const pair = keyring.getPair(address);
|
|
161
|
-
|
|
162
|
-
assert(pair, 'Unable to find pair');
|
|
163
|
-
|
|
164
|
-
keyring.saveAccountMeta(pair, { ...pair.meta, isHidden: !isShowing });
|
|
165
|
-
|
|
166
|
-
return true;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
private accountsTie ({ address, genesisHash }: RequestAccountTie): boolean {
|
|
170
|
-
const pair = keyring.getPair(address);
|
|
171
|
-
|
|
172
|
-
assert(pair, 'Unable to find pair');
|
|
173
|
-
|
|
174
|
-
keyring.saveAccountMeta(pair, { ...pair.meta, genesisHash });
|
|
175
|
-
|
|
176
|
-
return true;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
private accountsValidate ({ address, password }: RequestAccountValidate): boolean {
|
|
180
|
-
try {
|
|
181
|
-
keyring.backupAccount(keyring.getPair(address), password);
|
|
182
|
-
|
|
183
|
-
return true;
|
|
184
|
-
} catch {
|
|
185
|
-
return false;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
private accountsSubscribe (id: string, port: chrome.runtime.Port): boolean {
|
|
190
|
-
const cb = createSubscription<'pri(accounts.subscribe)'>(id, port);
|
|
191
|
-
const subscription = accountsObservable.subject.subscribe((accounts: SubjectInfo): void =>
|
|
192
|
-
cb(this.transformAccounts(accounts))
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
port.onDisconnect.addListener((): void => {
|
|
196
|
-
unsubscribe(id);
|
|
197
|
-
subscription.unsubscribe();
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
return true;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
private authorizeApprove ({ authorizedAccounts, id }: RequestAuthorizeApprove): boolean {
|
|
204
|
-
const queued = this.#state.getAuthRequest(id);
|
|
205
|
-
|
|
206
|
-
assert(queued, 'Unable to find request');
|
|
207
|
-
|
|
208
|
-
const { resolve } = queued;
|
|
209
|
-
|
|
210
|
-
resolve({ authorizedAccounts, result: true });
|
|
211
|
-
|
|
212
|
-
return true;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
private async authorizeUpdate ({ authorizedAccounts, url }: RequestUpdateAuthorizedAccounts): Promise<void> {
|
|
216
|
-
return await this.#state.updateAuthorizedAccounts([[url, authorizedAccounts]]);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
private getAuthList (): ResponseAuthorizeList {
|
|
220
|
-
return { list: this.#state.authUrls };
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// FIXME This looks very much like what we have in accounts
|
|
224
|
-
private authorizeSubscribe (id: string, port: chrome.runtime.Port): boolean {
|
|
225
|
-
const cb = createSubscription<'pri(authorize.requests)'>(id, port);
|
|
226
|
-
const subscription = this.#state.authSubject.subscribe((requests: AuthorizeRequest[]): void =>
|
|
227
|
-
cb(requests)
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
port.onDisconnect.addListener((): void => {
|
|
231
|
-
unsubscribe(id);
|
|
232
|
-
subscription.unsubscribe();
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
return true;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
private async metadataApprove ({ id }: RequestMetadataApprove): Promise<boolean> {
|
|
239
|
-
const queued = this.#state.getMetaRequest(id);
|
|
240
|
-
|
|
241
|
-
assert(queued, 'Unable to find request');
|
|
242
|
-
|
|
243
|
-
const { request, resolve } = queued;
|
|
244
|
-
|
|
245
|
-
await this.#state.saveMetadata(request);
|
|
246
|
-
|
|
247
|
-
resolve(true);
|
|
248
|
-
|
|
249
|
-
return true;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
private metadataGet (genesisHash: string | null): MetadataDef | null {
|
|
253
|
-
return this.#state.knownMetadata.find((result) => result.genesisHash === genesisHash) || null;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
private metadataList (): MetadataDef[] {
|
|
257
|
-
return this.#state.knownMetadata;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
private metadataReject ({ id }: RequestMetadataReject): boolean {
|
|
261
|
-
const queued = this.#state.getMetaRequest(id);
|
|
262
|
-
|
|
263
|
-
assert(queued, 'Unable to find request');
|
|
264
|
-
|
|
265
|
-
const { reject } = queued;
|
|
266
|
-
|
|
267
|
-
reject(new Error('Rejected'));
|
|
268
|
-
|
|
269
|
-
return true;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
private metadataSubscribe (id: string, port: chrome.runtime.Port): boolean {
|
|
273
|
-
const cb = createSubscription<'pri(metadata.requests)'>(id, port);
|
|
274
|
-
const subscription = this.#state.metaSubject.subscribe((requests: MetadataRequest[]): void =>
|
|
275
|
-
cb(requests)
|
|
276
|
-
);
|
|
277
|
-
|
|
278
|
-
port.onDisconnect.addListener((): void => {
|
|
279
|
-
unsubscribe(id);
|
|
280
|
-
subscription.unsubscribe();
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
return true;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
private jsonRestore ({ file, password }: RequestJsonRestore): void {
|
|
287
|
-
try {
|
|
288
|
-
keyring.restoreAccount(file, password);
|
|
289
|
-
} catch (error) {
|
|
290
|
-
throw new Error((error as Error).message);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
private batchRestore ({ file, password }: RequestBatchRestore): void {
|
|
295
|
-
try {
|
|
296
|
-
keyring.restoreAccounts(file, password);
|
|
297
|
-
} catch (error) {
|
|
298
|
-
throw new Error((error as Error).message);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
private jsonGetAccountInfo (json: KeyringPair$Json): ResponseJsonGetAccountInfo {
|
|
303
|
-
try {
|
|
304
|
-
const { address, meta: { genesisHash, name }, type } = keyring.createFromJson(json);
|
|
305
|
-
|
|
306
|
-
return {
|
|
307
|
-
address,
|
|
308
|
-
genesisHash,
|
|
309
|
-
name,
|
|
310
|
-
type
|
|
311
|
-
} as ResponseJsonGetAccountInfo;
|
|
312
|
-
} catch (e) {
|
|
313
|
-
console.error(e);
|
|
314
|
-
throw new Error((e as Error).message);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
private seedCreate ({ length = SEED_DEFAULT_LENGTH, seed: _seed, type }: RequestSeedCreate): ResponseSeedCreate {
|
|
319
|
-
const seed = _seed || mnemonicGenerate(length);
|
|
320
|
-
|
|
321
|
-
return {
|
|
322
|
-
address: keyring.createFromUri(getSuri(seed, type), {}, type).address,
|
|
323
|
-
seed
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
private seedValidate ({ suri, type }: RequestSeedValidate): ResponseSeedValidate {
|
|
328
|
-
const { phrase } = keyExtractSuri(suri);
|
|
329
|
-
|
|
330
|
-
if (isHex(phrase)) {
|
|
331
|
-
assert(isHex(phrase, 256), 'Hex seed needs to be 256-bits');
|
|
332
|
-
} else {
|
|
333
|
-
// sadly isHex detects as string, so we need a cast here
|
|
334
|
-
assert(SEED_LENGTHS.includes((phrase).split(' ').length), `Mnemonic needs to contain ${SEED_LENGTHS.join(', ')} words`);
|
|
335
|
-
assert(mnemonicValidate(phrase), 'Not a valid mnemonic seed');
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
return {
|
|
339
|
-
address: keyring.createFromUri(getSuri(suri, type), {}, type).address,
|
|
340
|
-
suri
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
private signingApprovePassword ({ id, password, savePass }: RequestSigningApprovePassword): boolean {
|
|
345
|
-
const queued = this.#state.getSignRequest(id);
|
|
346
|
-
|
|
347
|
-
assert(queued, 'Unable to find request');
|
|
348
|
-
|
|
349
|
-
const { reject, request, resolve } = queued;
|
|
350
|
-
const pair = keyring.getPair(queued.account.address);
|
|
351
|
-
|
|
352
|
-
if (!pair) {
|
|
353
|
-
reject(new Error('Unable to find pair'));
|
|
354
|
-
|
|
355
|
-
return false;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
this.refreshAccountPasswordCache(pair);
|
|
359
|
-
|
|
360
|
-
// if the keyring pair is locked, the password is needed
|
|
361
|
-
if (pair.isLocked && !password) {
|
|
362
|
-
reject(new Error('Password needed to unlock the account'));
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
if (pair.isLocked) {
|
|
366
|
-
pair.decodePkcs8(password);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
// construct a new registry (avoiding pollution), between requests
|
|
370
|
-
let registry: Registry;
|
|
371
|
-
const { payload } = request;
|
|
372
|
-
|
|
373
|
-
if (isJsonPayload(payload)) {
|
|
374
|
-
// Get the metadata for the genesisHash
|
|
375
|
-
const metadata = this.#state.knownMetadata.find(({ genesisHash }) => genesisHash === payload.genesisHash);
|
|
376
|
-
|
|
377
|
-
if (metadata) {
|
|
378
|
-
// we have metadata, expand it and extract the info/registry
|
|
379
|
-
const expanded = metadataExpand(metadata, false);
|
|
380
|
-
|
|
381
|
-
registry = expanded.registry;
|
|
382
|
-
registry.setSignedExtensions(payload.signedExtensions, expanded.definition.userExtensions);
|
|
383
|
-
} else {
|
|
384
|
-
// we have no metadata, create a new registry
|
|
385
|
-
registry = new TypeRegistry();
|
|
386
|
-
registry.setSignedExtensions(payload.signedExtensions);
|
|
387
|
-
}
|
|
388
|
-
} else {
|
|
389
|
-
// for non-payload, just create a registry to use
|
|
390
|
-
registry = new TypeRegistry();
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
const result = request.sign(registry, pair);
|
|
394
|
-
|
|
395
|
-
if (savePass) {
|
|
396
|
-
// unlike queued.account.address the following
|
|
397
|
-
// address is encoded with the default prefix
|
|
398
|
-
// which what is used for password caching mapping
|
|
399
|
-
this.#cachedUnlocks[pair.address] = Date.now() + PASSWORD_EXPIRY_MS;
|
|
400
|
-
} else {
|
|
401
|
-
pair.lock();
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
resolve({
|
|
405
|
-
id,
|
|
406
|
-
...result
|
|
407
|
-
});
|
|
408
|
-
|
|
409
|
-
return true;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
private signingApproveSignature ({ id, signature, signedTransaction }: RequestSigningApproveSignature): boolean {
|
|
413
|
-
const queued = this.#state.getSignRequest(id);
|
|
414
|
-
|
|
415
|
-
assert(queued, 'Unable to find request');
|
|
416
|
-
|
|
417
|
-
const { resolve } = queued;
|
|
418
|
-
|
|
419
|
-
resolve({ id, signature, signedTransaction });
|
|
420
|
-
|
|
421
|
-
return true;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
private signingCancel ({ id }: RequestSigningCancel): boolean {
|
|
425
|
-
const queued = this.#state.getSignRequest(id);
|
|
426
|
-
|
|
427
|
-
assert(queued, 'Unable to find request');
|
|
428
|
-
|
|
429
|
-
const { reject } = queued;
|
|
430
|
-
|
|
431
|
-
reject(new Error('Cancelled'));
|
|
432
|
-
|
|
433
|
-
return true;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
private signingIsLocked ({ id }: RequestSigningIsLocked): ResponseSigningIsLocked {
|
|
437
|
-
const queued = this.#state.getSignRequest(id);
|
|
438
|
-
|
|
439
|
-
assert(queued, 'Unable to find request');
|
|
440
|
-
|
|
441
|
-
const address = queued.request.payload.address;
|
|
442
|
-
const pair = keyring.getPair(address);
|
|
443
|
-
|
|
444
|
-
assert(pair, 'Unable to find pair');
|
|
445
|
-
|
|
446
|
-
const remainingTime = this.refreshAccountPasswordCache(pair);
|
|
447
|
-
|
|
448
|
-
return {
|
|
449
|
-
isLocked: pair.isLocked,
|
|
450
|
-
remainingTime
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// FIXME This looks very much like what we have in authorization
|
|
455
|
-
private signingSubscribe (id: string, port: chrome.runtime.Port): boolean {
|
|
456
|
-
const cb = createSubscription<'pri(signing.requests)'>(id, port);
|
|
457
|
-
const subscription = this.#state.signSubject.subscribe((requests: SigningRequest[]): void =>
|
|
458
|
-
cb(requests)
|
|
459
|
-
);
|
|
460
|
-
|
|
461
|
-
port.onDisconnect.addListener((): void => {
|
|
462
|
-
unsubscribe(id);
|
|
463
|
-
subscription.unsubscribe();
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
return true;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
private windowOpen (path: AllowedPath): boolean {
|
|
470
|
-
const url = `${chrome.runtime.getURL('index.html')}#${path}`;
|
|
471
|
-
|
|
472
|
-
if (!ALLOWED_PATH.includes(path)) {
|
|
473
|
-
console.error('Not allowed to open the url:', url);
|
|
474
|
-
|
|
475
|
-
return false;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
withErrorLog(() => chrome.tabs.create({ url }));
|
|
479
|
-
|
|
480
|
-
return true;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
private derive (parentAddress: string, suri: string, password: string, metadata: KeyringPair$Meta): KeyringPair {
|
|
484
|
-
const parentPair = keyring.getPair(parentAddress);
|
|
485
|
-
|
|
486
|
-
try {
|
|
487
|
-
parentPair.decodePkcs8(password);
|
|
488
|
-
} catch {
|
|
489
|
-
throw new Error('invalid password');
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
try {
|
|
493
|
-
return parentPair.derive(suri, metadata);
|
|
494
|
-
} catch {
|
|
495
|
-
throw new Error(`"${suri}" is not a valid derivation path`);
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
private derivationValidate ({ parentAddress, parentPassword, suri }: RequestDeriveValidate): ResponseDeriveValidate {
|
|
500
|
-
const childPair = this.derive(parentAddress, suri, parentPassword, {});
|
|
501
|
-
|
|
502
|
-
return {
|
|
503
|
-
address: childPair.address,
|
|
504
|
-
suri
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
private derivationCreate ({ genesisHash, name, parentAddress, parentPassword, password, suri }: RequestDeriveCreate): boolean {
|
|
509
|
-
const childPair = this.derive(parentAddress, suri, parentPassword, {
|
|
510
|
-
genesisHash,
|
|
511
|
-
name,
|
|
512
|
-
parentAddress,
|
|
513
|
-
suri
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
keyring.addPair(childPair, password);
|
|
517
|
-
|
|
518
|
-
return true;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
private async removeAuthorization (url: string): Promise<ResponseAuthorizeList> {
|
|
522
|
-
const remAuth = await this.#state.removeAuthorization(url);
|
|
523
|
-
|
|
524
|
-
return { list: remAuth };
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// Reject the authorization request and add the URL to the authorized list with no keys.
|
|
528
|
-
// The site will not prompt for re-authorization on future visits.
|
|
529
|
-
private rejectAuthRequest (id: string): void {
|
|
530
|
-
const queued = this.#state.getAuthRequest(id);
|
|
531
|
-
|
|
532
|
-
assert(queued, 'Unable to find request');
|
|
533
|
-
|
|
534
|
-
const { reject } = queued;
|
|
535
|
-
|
|
536
|
-
reject(new Error('Rejected'));
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
// Cancel the authorization request and do not add the URL to the authorized list.
|
|
540
|
-
// The site will prompt for authorization on future visits.
|
|
541
|
-
private cancelAuthRequest (id: string): void {
|
|
542
|
-
const queued = this.#state.getAuthRequest(id);
|
|
543
|
-
|
|
544
|
-
assert(queued, 'Unable to find request');
|
|
545
|
-
|
|
546
|
-
const { reject } = queued;
|
|
547
|
-
|
|
548
|
-
reject(new Error('Cancelled'));
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
private updateCurrentTabs ({ urls }: RequestActiveTabsUrlUpdate) {
|
|
552
|
-
this.#state.updateCurrentTabsUrl(urls);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
private getConnectedTabsUrl () {
|
|
556
|
-
return this.#state.getConnectedTabsUrl();
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
// Weird thought, the eslint override is not needed in Tabs
|
|
560
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
561
|
-
public async handle<TMessageType extends MessageTypes> (id: string, type: TMessageType, request: RequestTypes[TMessageType], port?: chrome.runtime.Port): Promise<ResponseType<TMessageType>> {
|
|
562
|
-
switch (type) {
|
|
563
|
-
case 'pri(authorize.approve)':
|
|
564
|
-
return this.authorizeApprove(request as RequestAuthorizeApprove);
|
|
565
|
-
|
|
566
|
-
case 'pri(authorize.list)':
|
|
567
|
-
return this.getAuthList();
|
|
568
|
-
|
|
569
|
-
case 'pri(authorize.remove)':
|
|
570
|
-
return this.removeAuthorization(request as string);
|
|
571
|
-
|
|
572
|
-
case 'pri(authorize.reject)':
|
|
573
|
-
return this.rejectAuthRequest(request as string);
|
|
574
|
-
|
|
575
|
-
case 'pri(authorize.cancel)':
|
|
576
|
-
return this.cancelAuthRequest(request as string);
|
|
577
|
-
|
|
578
|
-
case 'pri(authorize.requests)':
|
|
579
|
-
return port && this.authorizeSubscribe(id, port);
|
|
580
|
-
|
|
581
|
-
case 'pri(authorize.update)':
|
|
582
|
-
return this.authorizeUpdate(request as RequestUpdateAuthorizedAccounts);
|
|
583
|
-
|
|
584
|
-
case 'pri(accounts.create.external)':
|
|
585
|
-
return this.accountsCreateExternal(request as RequestAccountCreateExternal);
|
|
586
|
-
|
|
587
|
-
case 'pri(accounts.create.hardware)':
|
|
588
|
-
return this.accountsCreateHardware(request as RequestAccountCreateHardware);
|
|
589
|
-
|
|
590
|
-
case 'pri(accounts.create.suri)':
|
|
591
|
-
return this.accountsCreateSuri(request as RequestAccountCreateSuri);
|
|
592
|
-
|
|
593
|
-
case 'pri(accounts.changePassword)':
|
|
594
|
-
return this.accountsChangePassword(request as RequestAccountChangePassword);
|
|
595
|
-
|
|
596
|
-
case 'pri(accounts.edit)':
|
|
597
|
-
return this.accountsEdit(request as RequestAccountEdit);
|
|
598
|
-
|
|
599
|
-
case 'pri(accounts.export)':
|
|
600
|
-
return this.accountsExport(request as RequestAccountExport);
|
|
601
|
-
|
|
602
|
-
case 'pri(accounts.batchExport)':
|
|
603
|
-
return this.accountsBatchExport(request as RequestAccountBatchExport);
|
|
604
|
-
|
|
605
|
-
case 'pri(accounts.forget)':
|
|
606
|
-
return this.accountsForget(request as RequestAccountForget);
|
|
607
|
-
|
|
608
|
-
case 'pri(accounts.show)':
|
|
609
|
-
return this.accountsShow(request as RequestAccountShow);
|
|
610
|
-
|
|
611
|
-
case 'pri(accounts.subscribe)':
|
|
612
|
-
return port && this.accountsSubscribe(id, port);
|
|
613
|
-
|
|
614
|
-
case 'pri(accounts.tie)':
|
|
615
|
-
return this.accountsTie(request as RequestAccountTie);
|
|
616
|
-
|
|
617
|
-
case 'pri(accounts.validate)':
|
|
618
|
-
return this.accountsValidate(request as RequestAccountValidate);
|
|
619
|
-
|
|
620
|
-
case 'pri(metadata.approve)':
|
|
621
|
-
return await this.metadataApprove(request as RequestMetadataApprove);
|
|
622
|
-
|
|
623
|
-
case 'pri(metadata.get)':
|
|
624
|
-
return this.metadataGet(request as string);
|
|
625
|
-
|
|
626
|
-
case 'pri(metadata.list)':
|
|
627
|
-
return this.metadataList();
|
|
628
|
-
|
|
629
|
-
case 'pri(metadata.reject)':
|
|
630
|
-
return this.metadataReject(request as RequestMetadataReject);
|
|
631
|
-
|
|
632
|
-
case 'pri(metadata.requests)':
|
|
633
|
-
return port && this.metadataSubscribe(id, port);
|
|
634
|
-
|
|
635
|
-
case 'pri(activeTabsUrl.update)':
|
|
636
|
-
return this.updateCurrentTabs(request as RequestActiveTabsUrlUpdate);
|
|
637
|
-
|
|
638
|
-
case 'pri(connectedTabsUrl.get)':
|
|
639
|
-
return this.getConnectedTabsUrl();
|
|
640
|
-
|
|
641
|
-
case 'pri(derivation.create)':
|
|
642
|
-
return this.derivationCreate(request as RequestDeriveCreate);
|
|
643
|
-
|
|
644
|
-
case 'pri(derivation.validate)':
|
|
645
|
-
return this.derivationValidate(request as RequestDeriveValidate);
|
|
646
|
-
|
|
647
|
-
case 'pri(json.restore)':
|
|
648
|
-
return this.jsonRestore(request as RequestJsonRestore);
|
|
649
|
-
|
|
650
|
-
case 'pri(json.batchRestore)':
|
|
651
|
-
return this.batchRestore(request as RequestBatchRestore);
|
|
652
|
-
|
|
653
|
-
case 'pri(json.account.info)':
|
|
654
|
-
return this.jsonGetAccountInfo(request as KeyringPair$Json);
|
|
655
|
-
|
|
656
|
-
case 'pri(ping)':
|
|
657
|
-
return Promise.resolve(true);
|
|
658
|
-
|
|
659
|
-
case 'pri(seed.create)':
|
|
660
|
-
return this.seedCreate(request as RequestSeedCreate);
|
|
661
|
-
|
|
662
|
-
case 'pri(seed.validate)':
|
|
663
|
-
return this.seedValidate(request as RequestSeedValidate);
|
|
664
|
-
|
|
665
|
-
case 'pri(settings.notification)':
|
|
666
|
-
return this.#state.setNotification(request as string);
|
|
667
|
-
|
|
668
|
-
case 'pri(signing.approve.password)':
|
|
669
|
-
return this.signingApprovePassword(request as RequestSigningApprovePassword);
|
|
670
|
-
|
|
671
|
-
case 'pri(signing.approve.signature)':
|
|
672
|
-
return this.signingApproveSignature(request as RequestSigningApproveSignature);
|
|
673
|
-
|
|
674
|
-
case 'pri(signing.cancel)':
|
|
675
|
-
return this.signingCancel(request as RequestSigningCancel);
|
|
676
|
-
|
|
677
|
-
case 'pri(signing.isLocked)':
|
|
678
|
-
return this.signingIsLocked(request as RequestSigningIsLocked);
|
|
679
|
-
|
|
680
|
-
case 'pri(signing.requests)':
|
|
681
|
-
return port && this.signingSubscribe(id, port);
|
|
682
|
-
|
|
683
|
-
case 'pri(window.open)':
|
|
684
|
-
return this.windowOpen(request as AllowedPath);
|
|
685
|
-
|
|
686
|
-
default:
|
|
687
|
-
throw new Error(`Unable to handle message of type ${type}`);
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
}
|