@pezkuwi/ui-keyring 3.16.6
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/README.md +136 -0
- package/build/Base.d.ts +30 -0
- package/build/Base.js +109 -0
- package/build/Keyring.d.ts +49 -0
- package/build/Keyring.js +304 -0
- package/build/LICENSE +201 -0
- package/build/README.md +136 -0
- package/build/bundle-pezkuwi-ui-keyring.js +2751 -0
- package/build/bundle.d.ts +4 -0
- package/build/bundle.js +4 -0
- package/build/cjs/Base.d.ts +30 -0
- package/build/cjs/Base.js +113 -0
- package/build/cjs/Keyring.d.ts +49 -0
- package/build/cjs/Keyring.js +308 -0
- package/build/cjs/bundle.d.ts +4 -0
- package/build/cjs/bundle.js +8 -0
- package/build/cjs/defaults.d.ts +6 -0
- package/build/cjs/defaults.js +28 -0
- package/build/cjs/index.d.ts +4 -0
- package/build/cjs/index.js +7 -0
- package/build/cjs/observable/accounts.d.ts +1 -0
- package/build/cjs/observable/accounts.js +6 -0
- package/build/cjs/observable/addresses.d.ts +1 -0
- package/build/cjs/observable/addresses.js +6 -0
- package/build/cjs/observable/contracts.d.ts +1 -0
- package/build/cjs/observable/contracts.js +6 -0
- package/build/cjs/observable/env.d.ts +6 -0
- package/build/cjs/observable/env.js +12 -0
- package/build/cjs/observable/genericSubject.d.ts +2 -0
- package/build/cjs/observable/genericSubject.js +47 -0
- package/build/cjs/observable/index.d.ts +8 -0
- package/build/cjs/observable/index.js +16 -0
- package/build/cjs/observable/types.d.ts +15 -0
- package/build/cjs/observable/types.js +2 -0
- package/build/cjs/options/index.d.ts +15 -0
- package/build/cjs/options/index.js +116 -0
- package/build/cjs/options/item.d.ts +2 -0
- package/build/cjs/options/item.js +16 -0
- package/build/cjs/options/types.d.ts +15 -0
- package/build/cjs/options/types.js +2 -0
- package/build/cjs/package.json +3 -0
- package/build/cjs/packageDetect.d.ts +1 -0
- package/build/cjs/packageDetect.js +6 -0
- package/build/cjs/packageInfo.d.ts +6 -0
- package/build/cjs/packageInfo.js +4 -0
- package/build/cjs/stores/Browser.d.ts +7 -0
- package/build/cjs/stores/Browser.js +24 -0
- package/build/cjs/stores/File.d.ts +12 -0
- package/build/cjs/stores/File.js +72 -0
- package/build/cjs/stores/index.d.ts +2 -0
- package/build/cjs/stores/index.js +7 -0
- package/build/cjs/types.d.ts +78 -0
- package/build/cjs/types.js +2 -0
- package/build/defaults.d.ts +6 -0
- package/build/defaults.js +22 -0
- package/build/index.d.ts +4 -0
- package/build/index.js +4 -0
- package/build/observable/accounts.d.ts +1 -0
- package/build/observable/accounts.js +3 -0
- package/build/observable/addresses.d.ts +1 -0
- package/build/observable/addresses.js +3 -0
- package/build/observable/contracts.d.ts +1 -0
- package/build/observable/contracts.js +3 -0
- package/build/observable/env.d.ts +6 -0
- package/build/observable/env.js +9 -0
- package/build/observable/genericSubject.d.ts +2 -0
- package/build/observable/genericSubject.js +44 -0
- package/build/observable/index.d.ts +8 -0
- package/build/observable/index.js +13 -0
- package/build/observable/types.d.ts +15 -0
- package/build/observable/types.js +1 -0
- package/build/options/index.d.ts +15 -0
- package/build/options/index.js +112 -0
- package/build/options/item.d.ts +2 -0
- package/build/options/item.js +13 -0
- package/build/options/types.d.ts +15 -0
- package/build/options/types.js +1 -0
- package/build/package.json +355 -0
- package/build/packageDetect.d.ts +1 -0
- package/build/packageDetect.js +4 -0
- package/build/packageInfo.d.ts +6 -0
- package/build/packageInfo.js +1 -0
- package/build/stores/Browser.d.ts +7 -0
- package/build/stores/Browser.js +19 -0
- package/build/stores/File.d.ts +12 -0
- package/build/stores/File.js +67 -0
- package/build/stores/index.d.ts +2 -0
- package/build/stores/index.js +2 -0
- package/build/types.d.ts +78 -0
- package/build/types.js +1 -0
- package/build-tsc/Base.d.ts +30 -0
- package/build-tsc/Keyring.d.ts +49 -0
- package/build-tsc/bundle.d.ts +4 -0
- package/build-tsc/defaults.d.ts +6 -0
- package/build-tsc/index.d.ts +4 -0
- package/build-tsc/observable/accounts.d.ts +1 -0
- package/build-tsc/observable/addresses.d.ts +1 -0
- package/build-tsc/observable/contracts.d.ts +1 -0
- package/build-tsc/observable/env.d.ts +6 -0
- package/build-tsc/observable/genericSubject.d.ts +2 -0
- package/build-tsc/observable/index.d.ts +8 -0
- package/build-tsc/observable/types.d.ts +15 -0
- package/build-tsc/options/index.d.ts +15 -0
- package/build-tsc/options/item.d.ts +2 -0
- package/build-tsc/options/types.d.ts +15 -0
- package/build-tsc/packageDetect.d.ts +1 -0
- package/build-tsc/packageInfo.d.ts +6 -0
- package/build-tsc/stores/Browser.d.ts +7 -0
- package/build-tsc/stores/File.d.ts +12 -0
- package/build-tsc/stores/index.d.ts +2 -0
- package/build-tsc/types.d.ts +78 -0
- package/build-tsc-cjs/Base.js +113 -0
- package/build-tsc-cjs/Keyring.js +308 -0
- package/build-tsc-cjs/bundle.js +8 -0
- package/build-tsc-cjs/defaults.js +28 -0
- package/build-tsc-cjs/index.js +7 -0
- package/build-tsc-cjs/observable/accounts.js +6 -0
- package/build-tsc-cjs/observable/addresses.js +6 -0
- package/build-tsc-cjs/observable/contracts.js +6 -0
- package/build-tsc-cjs/observable/env.js +12 -0
- package/build-tsc-cjs/observable/genericSubject.js +47 -0
- package/build-tsc-cjs/observable/index.js +16 -0
- package/build-tsc-cjs/observable/types.js +2 -0
- package/build-tsc-cjs/options/index.js +116 -0
- package/build-tsc-cjs/options/item.js +16 -0
- package/build-tsc-cjs/options/types.js +2 -0
- package/build-tsc-cjs/packageDetect.js +6 -0
- package/build-tsc-cjs/packageInfo.js +4 -0
- package/build-tsc-cjs/stores/Browser.js +24 -0
- package/build-tsc-cjs/stores/File.js +72 -0
- package/build-tsc-cjs/stores/index.js +7 -0
- package/build-tsc-cjs/types.js +2 -0
- package/build-tsc-esm/Base.js +109 -0
- package/build-tsc-esm/Keyring.js +304 -0
- package/build-tsc-esm/bundle.js +4 -0
- package/build-tsc-esm/defaults.js +22 -0
- package/build-tsc-esm/index.js +4 -0
- package/build-tsc-esm/observable/accounts.js +3 -0
- package/build-tsc-esm/observable/addresses.js +3 -0
- package/build-tsc-esm/observable/contracts.js +3 -0
- package/build-tsc-esm/observable/env.js +9 -0
- package/build-tsc-esm/observable/genericSubject.js +44 -0
- package/build-tsc-esm/observable/index.js +13 -0
- package/build-tsc-esm/observable/types.js +1 -0
- package/build-tsc-esm/options/index.js +112 -0
- package/build-tsc-esm/options/item.js +13 -0
- package/build-tsc-esm/options/types.js +1 -0
- package/build-tsc-esm/packageDetect.js +4 -0
- package/build-tsc-esm/packageInfo.js +1 -0
- package/build-tsc-esm/stores/Browser.js +19 -0
- package/build-tsc-esm/stores/File.js +67 -0
- package/build-tsc-esm/stores/index.js +2 -0
- package/build-tsc-esm/types.js +1 -0
- package/package.json +41 -0
- package/src/Base.ts +156 -0
- package/src/Keyring.ts +420 -0
- package/src/bundle.ts +10 -0
- package/src/defaults.ts +34 -0
- package/src/index.ts +10 -0
- package/src/json.d.ts +11 -0
- package/src/observable/accounts.ts +7 -0
- package/src/observable/addresses.ts +7 -0
- package/src/observable/contracts.ts +7 -0
- package/src/observable/env.ts +15 -0
- package/src/observable/genericSubject.ts +66 -0
- package/src/observable/index.ts +28 -0
- package/src/observable/types.ts +21 -0
- package/src/options/index.ts +150 -0
- package/src/options/item.ts +22 -0
- package/src/options/types.ts +23 -0
- package/src/packageDetect.ts +12 -0
- package/src/packageInfo.ts +6 -0
- package/src/stores/Browser.ts +28 -0
- package/src/stores/File.ts +94 -0
- package/src/stores/index.ts +5 -0
- package/src/types.ts +91 -0
- package/tsconfig.build.json +14 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.spec.json +16 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FileStore = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const mkdirp_1 = require("mkdirp");
|
|
6
|
+
const node_fs_1 = tslib_1.__importDefault(require("node:fs"));
|
|
7
|
+
const node_path_1 = tslib_1.__importDefault(require("node:path"));
|
|
8
|
+
class FileStore {
|
|
9
|
+
#path;
|
|
10
|
+
constructor(path) {
|
|
11
|
+
if (!node_fs_1.default.existsSync(path)) {
|
|
12
|
+
(0, mkdirp_1.mkdirpSync)(path);
|
|
13
|
+
}
|
|
14
|
+
this.#path = path;
|
|
15
|
+
}
|
|
16
|
+
validateKey(key) {
|
|
17
|
+
// Make sure the key has a .json extension
|
|
18
|
+
if (!key.endsWith('.json')) {
|
|
19
|
+
console.error('Non-JSON file requested: ', key);
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
// Remove '.json'
|
|
23
|
+
const keyWithoutExtension = key.slice(0, -5);
|
|
24
|
+
// Only allow alphanumeric characters, hyphens, and underscores in the base filename
|
|
25
|
+
const safeKeyRegex = /^[a-zA-Z0-9_-]+$/;
|
|
26
|
+
if (!safeKeyRegex.test(keyWithoutExtension)) {
|
|
27
|
+
console.error('Invalid key format detected: ', key);
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
all(fn) {
|
|
33
|
+
node_fs_1.default
|
|
34
|
+
.readdirSync(this.#path)
|
|
35
|
+
.filter((key) => !['.', '..', '.DS_Store'].includes(key))
|
|
36
|
+
.forEach((key) => {
|
|
37
|
+
const value = this._readKey(key);
|
|
38
|
+
value?.address && fn(key, value);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
get(key, fn) {
|
|
42
|
+
const value = this._readKey(key);
|
|
43
|
+
if (!value?.address) {
|
|
44
|
+
throw new Error(`Invalid JSON found for ${key}`);
|
|
45
|
+
}
|
|
46
|
+
fn(value);
|
|
47
|
+
}
|
|
48
|
+
remove(key, fn) {
|
|
49
|
+
node_fs_1.default.unlinkSync(this._getPath(key));
|
|
50
|
+
fn && fn();
|
|
51
|
+
}
|
|
52
|
+
set(key, value, fn) {
|
|
53
|
+
node_fs_1.default.writeFileSync(this._getPath(key), Buffer.from(JSON.stringify(value), 'utf-8'));
|
|
54
|
+
fn && fn();
|
|
55
|
+
}
|
|
56
|
+
_getPath(key) {
|
|
57
|
+
if (!this.validateKey(key)) {
|
|
58
|
+
throw new Error('Invalid key format');
|
|
59
|
+
}
|
|
60
|
+
return node_path_1.default.join(this.#path, key);
|
|
61
|
+
}
|
|
62
|
+
_readKey(key) {
|
|
63
|
+
try {
|
|
64
|
+
return JSON.parse(node_fs_1.default.readFileSync(this._getPath(key)).toString('utf-8'));
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
console.error(error);
|
|
68
|
+
}
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
exports.FileStore = FileStore;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FileStore = exports.BrowserStore = void 0;
|
|
4
|
+
var Browser_js_1 = require("./Browser.js");
|
|
5
|
+
Object.defineProperty(exports, "BrowserStore", { enumerable: true, get: function () { return Browser_js_1.BrowserStore; } });
|
|
6
|
+
var File_js_1 = require("./File.js");
|
|
7
|
+
Object.defineProperty(exports, "FileStore", { enumerable: true, get: function () { return File_js_1.FileStore; } });
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { createTestKeyring } from '@pezkuwi/keyring';
|
|
2
|
+
import { isBoolean, isNumber, isString } from '@pezkuwi/util';
|
|
3
|
+
import { accounts } from './observable/accounts.js';
|
|
4
|
+
import { addresses } from './observable/addresses.js';
|
|
5
|
+
import { contracts } from './observable/contracts.js';
|
|
6
|
+
import { env } from './observable/env.js';
|
|
7
|
+
import { BrowserStore } from './stores/Browser.js'; // direct import (skip index with all)
|
|
8
|
+
export class Base {
|
|
9
|
+
#accounts;
|
|
10
|
+
#addresses;
|
|
11
|
+
#contracts;
|
|
12
|
+
#isEthereum;
|
|
13
|
+
#keyring;
|
|
14
|
+
_store;
|
|
15
|
+
_genesisHash;
|
|
16
|
+
_genesisHashAdd = [];
|
|
17
|
+
constructor() {
|
|
18
|
+
this.#accounts = accounts;
|
|
19
|
+
this.#addresses = addresses;
|
|
20
|
+
this.#contracts = contracts;
|
|
21
|
+
this.#isEthereum = false;
|
|
22
|
+
this._store = new BrowserStore();
|
|
23
|
+
}
|
|
24
|
+
get accounts() {
|
|
25
|
+
return this.#accounts;
|
|
26
|
+
}
|
|
27
|
+
get addresses() {
|
|
28
|
+
return this.#addresses;
|
|
29
|
+
}
|
|
30
|
+
get contracts() {
|
|
31
|
+
return this.#contracts;
|
|
32
|
+
}
|
|
33
|
+
get isEthereum() {
|
|
34
|
+
return this.#isEthereum;
|
|
35
|
+
}
|
|
36
|
+
get keyring() {
|
|
37
|
+
if (this.#keyring) {
|
|
38
|
+
return this.#keyring;
|
|
39
|
+
}
|
|
40
|
+
throw new Error('Keyring should be initialised via \'loadAll\' before use');
|
|
41
|
+
}
|
|
42
|
+
get genesisHash() {
|
|
43
|
+
return this._genesisHash;
|
|
44
|
+
}
|
|
45
|
+
get genesisHashes() {
|
|
46
|
+
return this._genesisHash
|
|
47
|
+
? [this._genesisHash, ...this._genesisHashAdd]
|
|
48
|
+
: [...this._genesisHashAdd];
|
|
49
|
+
}
|
|
50
|
+
decodeAddress = (key, ignoreChecksum, ss58Format) => {
|
|
51
|
+
return this.keyring.decodeAddress(key, ignoreChecksum, ss58Format);
|
|
52
|
+
};
|
|
53
|
+
encodeAddress = (key, ss58Format) => {
|
|
54
|
+
return this.keyring.encodeAddress(key, ss58Format);
|
|
55
|
+
};
|
|
56
|
+
getPair(address) {
|
|
57
|
+
return this.keyring.getPair(address);
|
|
58
|
+
}
|
|
59
|
+
getPairs() {
|
|
60
|
+
return this.keyring.getPairs().filter((pair) => env.isDevelopment() || pair.meta.isTesting !== true);
|
|
61
|
+
}
|
|
62
|
+
isAvailable(_address) {
|
|
63
|
+
const accountsValue = this.accounts.subject.getValue();
|
|
64
|
+
const addressesValue = this.addresses.subject.getValue();
|
|
65
|
+
const contractsValue = this.contracts.subject.getValue();
|
|
66
|
+
const address = isString(_address)
|
|
67
|
+
? _address
|
|
68
|
+
: this.encodeAddress(_address);
|
|
69
|
+
return !accountsValue[address] && !addressesValue[address] && !contractsValue[address];
|
|
70
|
+
}
|
|
71
|
+
isPassValid(password) {
|
|
72
|
+
return password.length > 0;
|
|
73
|
+
}
|
|
74
|
+
setSS58Format(ss58Format) {
|
|
75
|
+
if (this.#keyring && isNumber(ss58Format)) {
|
|
76
|
+
this.#keyring.setSS58Format(ss58Format);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
setDevMode(isDevelopment) {
|
|
80
|
+
env.set(isDevelopment);
|
|
81
|
+
}
|
|
82
|
+
initKeyring(options) {
|
|
83
|
+
const keyring = createTestKeyring(options, true);
|
|
84
|
+
if (isBoolean(options.isDevelopment)) {
|
|
85
|
+
this.setDevMode(options.isDevelopment);
|
|
86
|
+
}
|
|
87
|
+
// set Ethereum state
|
|
88
|
+
this.#isEthereum = keyring.type === 'ethereum';
|
|
89
|
+
this.#keyring = keyring;
|
|
90
|
+
this._genesisHash = options.genesisHash && (isString(options.genesisHash)
|
|
91
|
+
? options.genesisHash.toString()
|
|
92
|
+
: options.genesisHash.toHex());
|
|
93
|
+
this._genesisHashAdd = options.genesisHashAdd || [];
|
|
94
|
+
this._store = options.store || this._store;
|
|
95
|
+
this.addAccountPairs();
|
|
96
|
+
}
|
|
97
|
+
addAccountPairs() {
|
|
98
|
+
this.keyring
|
|
99
|
+
.getPairs()
|
|
100
|
+
.forEach(({ address, meta }) => {
|
|
101
|
+
this.accounts.add(this._store, address, { address, meta });
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
addTimestamp(pair) {
|
|
105
|
+
if (!pair.meta.whenCreated) {
|
|
106
|
+
pair.setMeta({ whenCreated: Date.now() });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { createPair } from '@pezkuwi/keyring';
|
|
2
|
+
import { chains } from '@pezkuwi/ui-settings';
|
|
3
|
+
import { bnToBn, hexToU8a, isFunction, isHex, isString, objectSpread, stringify, stringToU8a, u8aSorted, u8aToString } from '@pezkuwi/util';
|
|
4
|
+
import { base64Decode, createKeyMulti, jsonDecrypt, jsonEncrypt } from '@pezkuwi/util-crypto';
|
|
5
|
+
import { env } from './observable/env.js';
|
|
6
|
+
import { KeyringOption } from './options/index.js';
|
|
7
|
+
import { Base } from './Base.js';
|
|
8
|
+
import { accountKey, accountRegex, addressKey, addressRegex, contractKey, contractRegex } from './defaults.js';
|
|
9
|
+
const RECENT_EXPIRY = 24 * 60 * 60;
|
|
10
|
+
export class Keyring extends Base {
|
|
11
|
+
keyringOption = new KeyringOption();
|
|
12
|
+
#stores = {
|
|
13
|
+
account: () => this.accounts,
|
|
14
|
+
address: () => this.addresses,
|
|
15
|
+
contract: () => this.contracts
|
|
16
|
+
};
|
|
17
|
+
addExternal(address, meta = {}) {
|
|
18
|
+
const pair = this.keyring.addFromAddress(address, objectSpread({}, meta, { isExternal: true }), null, meta?.type);
|
|
19
|
+
return {
|
|
20
|
+
json: this.saveAccount(pair),
|
|
21
|
+
pair
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
addHardware(address, hardwareType, meta = {}) {
|
|
25
|
+
return this.addExternal(address, objectSpread({}, meta, { hardwareType, isHardware: true }));
|
|
26
|
+
}
|
|
27
|
+
addMultisig(addresses, threshold, meta = {}) {
|
|
28
|
+
let address = createKeyMulti(addresses, threshold);
|
|
29
|
+
// For Ethereum chains, the first 20 bytes of the hash indicates the actual address
|
|
30
|
+
// Testcases via creation and on-chain events:
|
|
31
|
+
// - input: 0x7a1671a0224c8927b08f978027d586ab6868de0d31bb5bc956b625ced2ab18c4
|
|
32
|
+
// - output: 0x7a1671a0224c8927b08f978027d586ab6868de0d
|
|
33
|
+
if (this.isEthereum) {
|
|
34
|
+
address = address.slice(0, 20);
|
|
35
|
+
}
|
|
36
|
+
// we could use `sortAddresses`, but rather use internal encode/decode so we are 100%
|
|
37
|
+
const who = u8aSorted(addresses.map((who) => this.decodeAddress(who))).map((who) => this.encodeAddress(who));
|
|
38
|
+
return this.addExternal(address, objectSpread({}, meta, { isMultisig: true, threshold: bnToBn(threshold).toNumber(), who }));
|
|
39
|
+
}
|
|
40
|
+
addPair(pair, password) {
|
|
41
|
+
this.keyring.addPair(pair);
|
|
42
|
+
return {
|
|
43
|
+
json: this.saveAccount(pair, password),
|
|
44
|
+
pair
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
addUri(suri, password, meta = {}, type) {
|
|
48
|
+
const pair = this.keyring.addFromUri(suri, meta, type);
|
|
49
|
+
return {
|
|
50
|
+
json: this.saveAccount(pair, password),
|
|
51
|
+
pair
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
backupAccount(pair, password) {
|
|
55
|
+
if (!pair.isLocked) {
|
|
56
|
+
pair.lock();
|
|
57
|
+
}
|
|
58
|
+
pair.decodePkcs8(password);
|
|
59
|
+
return pair.toJson(password);
|
|
60
|
+
}
|
|
61
|
+
async backupAccounts(addresses, password) {
|
|
62
|
+
const accountPromises = addresses.map((address) => {
|
|
63
|
+
return new Promise((resolve) => {
|
|
64
|
+
this._store.get(accountKey(address), resolve);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
const accounts = await Promise.all(accountPromises);
|
|
68
|
+
return objectSpread({}, jsonEncrypt(stringToU8a(JSON.stringify(accounts)), ['batch-pkcs8'], password), {
|
|
69
|
+
accounts: accounts.map((account) => ({
|
|
70
|
+
address: account.address,
|
|
71
|
+
meta: account.meta
|
|
72
|
+
}))
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
createFromJson(json, meta = {}) {
|
|
76
|
+
return this.keyring.createFromJson(objectSpread({}, json, {
|
|
77
|
+
meta: objectSpread({}, json.meta, meta)
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
createFromUri(suri, meta = {}, type) {
|
|
81
|
+
return this.keyring.createFromUri(suri, meta, type);
|
|
82
|
+
}
|
|
83
|
+
encryptAccount(pair, password) {
|
|
84
|
+
const json = pair.toJson(password);
|
|
85
|
+
json.meta.whenEdited = Date.now();
|
|
86
|
+
this.keyring.addFromJson(json);
|
|
87
|
+
this.accounts.add(this._store, pair.address, json, pair.type);
|
|
88
|
+
}
|
|
89
|
+
forgetAccount(address) {
|
|
90
|
+
this.keyring.removePair(address);
|
|
91
|
+
this.accounts.remove(this._store, address);
|
|
92
|
+
}
|
|
93
|
+
forgetAddress(address) {
|
|
94
|
+
this.addresses.remove(this._store, address);
|
|
95
|
+
}
|
|
96
|
+
forgetContract(address) {
|
|
97
|
+
this.contracts.remove(this._store, address);
|
|
98
|
+
}
|
|
99
|
+
getAccount(address) {
|
|
100
|
+
return this.getAddress(address, 'account');
|
|
101
|
+
}
|
|
102
|
+
getAccounts() {
|
|
103
|
+
const available = this.accounts.subject.getValue();
|
|
104
|
+
return Object
|
|
105
|
+
.keys(available)
|
|
106
|
+
.map((address) => this.getAddress(address, 'account'))
|
|
107
|
+
.filter((account) => !!account &&
|
|
108
|
+
(env.isDevelopment() ||
|
|
109
|
+
account.meta.isTesting !== true));
|
|
110
|
+
}
|
|
111
|
+
getAddress(_address, type = null) {
|
|
112
|
+
const address = isString(_address)
|
|
113
|
+
? _address
|
|
114
|
+
: this.encodeAddress(_address);
|
|
115
|
+
const publicKey = this.decodeAddress(address);
|
|
116
|
+
const stores = type
|
|
117
|
+
? [this.#stores[type]]
|
|
118
|
+
: Object.values(this.#stores);
|
|
119
|
+
const info = stores.reduce((lastInfo, store) => (store().subject.getValue()[address] || lastInfo), undefined);
|
|
120
|
+
return info && {
|
|
121
|
+
address,
|
|
122
|
+
meta: info.json.meta,
|
|
123
|
+
publicKey
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
getAddresses() {
|
|
127
|
+
const available = this.addresses.subject.getValue();
|
|
128
|
+
return Object
|
|
129
|
+
.keys(available)
|
|
130
|
+
.map((address) => this.getAddress(address))
|
|
131
|
+
.filter((account) => !!account);
|
|
132
|
+
}
|
|
133
|
+
getContract(address) {
|
|
134
|
+
return this.getAddress(address, 'contract');
|
|
135
|
+
}
|
|
136
|
+
getContracts() {
|
|
137
|
+
const available = this.contracts.subject.getValue();
|
|
138
|
+
return Object
|
|
139
|
+
.entries(available)
|
|
140
|
+
.filter(([, { json: { meta: { contract } } }]) => !!contract && contract.genesisHash === this.genesisHash)
|
|
141
|
+
.map(([address]) => this.getContract(address))
|
|
142
|
+
.filter((account) => !!account);
|
|
143
|
+
}
|
|
144
|
+
rewriteKey(json, key, hexAddr, creator) {
|
|
145
|
+
if (hexAddr.startsWith('0x')) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
this._store.remove(key);
|
|
149
|
+
this._store.set(creator(hexAddr), json);
|
|
150
|
+
}
|
|
151
|
+
loadAccount(json, key) {
|
|
152
|
+
if (!json.meta.isTesting && json.encoded) {
|
|
153
|
+
const pair = this.keyring.addFromJson(json, true);
|
|
154
|
+
this.accounts.add(this._store, pair.address, json, pair.type);
|
|
155
|
+
}
|
|
156
|
+
const [, hexAddr] = key.split(':');
|
|
157
|
+
this.rewriteKey(json, key, hexAddr.trim(), accountKey);
|
|
158
|
+
}
|
|
159
|
+
loadAddress(json, key) {
|
|
160
|
+
const { isRecent, whenCreated = 0 } = json.meta;
|
|
161
|
+
if (isRecent && (Date.now() - whenCreated) > RECENT_EXPIRY) {
|
|
162
|
+
this._store.remove(key);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
// We assume anything hex that is not 32bytes (64 + 2 bytes hex) is an Ethereum-like address
|
|
166
|
+
// (this caters for both H160 addresses as well as full or compressed publicKeys) - in the case
|
|
167
|
+
// of both ecdsa and ethereum, we keep it as-is
|
|
168
|
+
const address = isHex(json.address) && json.address.length !== 66
|
|
169
|
+
? json.address
|
|
170
|
+
: this.encodeAddress(isHex(json.address)
|
|
171
|
+
? hexToU8a(json.address)
|
|
172
|
+
// FIXME Just for the transition period (ignoreChecksum)
|
|
173
|
+
: this.decodeAddress(json.address, true));
|
|
174
|
+
const [, hexAddr] = key.split(':');
|
|
175
|
+
this.addresses.add(this._store, address, json);
|
|
176
|
+
this.rewriteKey(json, key, hexAddr, addressKey);
|
|
177
|
+
}
|
|
178
|
+
loadContract(json, key) {
|
|
179
|
+
const address = this.encodeAddress(this.decodeAddress(json.address));
|
|
180
|
+
const [, hexAddr] = key.split(':');
|
|
181
|
+
// move genesisHash to top-level (TODO Remove from contracts section?)
|
|
182
|
+
json.meta.genesisHash = json.meta.genesisHash || (json.meta.contract?.genesisHash);
|
|
183
|
+
this.contracts.add(this._store, address, json);
|
|
184
|
+
this.rewriteKey(json, key, hexAddr, contractKey);
|
|
185
|
+
}
|
|
186
|
+
loadInjected(address, meta, type) {
|
|
187
|
+
const json = {
|
|
188
|
+
address,
|
|
189
|
+
meta: objectSpread({}, meta, { isInjected: true })
|
|
190
|
+
};
|
|
191
|
+
const pair = this.keyring.addFromAddress(address, json.meta, null, type);
|
|
192
|
+
this.accounts.add(this._store, pair.address, json, pair.type);
|
|
193
|
+
}
|
|
194
|
+
allowGenesis(json) {
|
|
195
|
+
if (json?.meta && this.genesisHash) {
|
|
196
|
+
const hashes = Object.values(chains).find((hashes) => hashes.includes(this.genesisHash || '')) || [this.genesisHash];
|
|
197
|
+
if (json.meta.genesisHash) {
|
|
198
|
+
return hashes.includes(json.meta.genesisHash) || this.genesisHashes.includes(json.meta.genesisHash);
|
|
199
|
+
}
|
|
200
|
+
else if (json.meta.contract) {
|
|
201
|
+
return hashes.includes(json.meta.contract.genesisHash);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
loadAll(options, injected = []) {
|
|
207
|
+
super.initKeyring(options);
|
|
208
|
+
this._store.all((key, json) => {
|
|
209
|
+
if (!isFunction(options.filter) || options.filter(json)) {
|
|
210
|
+
try {
|
|
211
|
+
if (this.allowGenesis(json)) {
|
|
212
|
+
if (accountRegex.test(key)) {
|
|
213
|
+
this.loadAccount(json, key);
|
|
214
|
+
}
|
|
215
|
+
else if (addressRegex.test(key)) {
|
|
216
|
+
this.loadAddress(json, key);
|
|
217
|
+
}
|
|
218
|
+
else if (contractRegex.test(key)) {
|
|
219
|
+
this.loadContract(json, key);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
console.warn(`Keyring: Unable to load ${key}:${stringify(json)}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
injected.forEach((account) => {
|
|
229
|
+
if (this.allowGenesis(account)) {
|
|
230
|
+
try {
|
|
231
|
+
this.loadInjected(account.address, account.meta, account.type);
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
console.warn(`Keyring: Unable to inject ${stringify(account)}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
this.keyringOption.init(this);
|
|
239
|
+
}
|
|
240
|
+
restoreAccount(json, password) {
|
|
241
|
+
const cryptoType = Array.isArray(json.encoding.content) ? json.encoding.content[1] : 'ed25519';
|
|
242
|
+
const encType = Array.isArray(json.encoding.type) ? json.encoding.type : [json.encoding.type];
|
|
243
|
+
const pair = createPair({ toSS58: this.encodeAddress, type: cryptoType }, { publicKey: this.decodeAddress(json.address, true) }, json.meta, isHex(json.encoded) ? hexToU8a(json.encoded) : base64Decode(json.encoded), encType);
|
|
244
|
+
// unlock, save account and then lock (locking cleans secretKey, so needs to be last)
|
|
245
|
+
pair.decodePkcs8(password);
|
|
246
|
+
this.addPair(pair, password);
|
|
247
|
+
pair.lock();
|
|
248
|
+
return pair;
|
|
249
|
+
}
|
|
250
|
+
restoreAccounts(json, password) {
|
|
251
|
+
const accounts = JSON.parse(u8aToString(jsonDecrypt(json, password)));
|
|
252
|
+
accounts.forEach((account) => {
|
|
253
|
+
this.loadAccount(account, accountKey(account.address));
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
saveAccount(pair, password) {
|
|
257
|
+
this.addTimestamp(pair);
|
|
258
|
+
const json = pair.toJson(password);
|
|
259
|
+
this.keyring.addFromJson(json);
|
|
260
|
+
this.accounts.add(this._store, pair.address, json, pair.type);
|
|
261
|
+
return json;
|
|
262
|
+
}
|
|
263
|
+
saveAccountMeta(pair, meta) {
|
|
264
|
+
const address = pair.address;
|
|
265
|
+
this._store.get(accountKey(address), (json) => {
|
|
266
|
+
pair.setMeta(meta);
|
|
267
|
+
json.meta = pair.meta;
|
|
268
|
+
this.accounts.add(this._store, address, json, pair.type);
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
saveAddress(address, meta, type = 'address') {
|
|
272
|
+
const available = this.addresses.subject.getValue();
|
|
273
|
+
const json = available[address]?.json || {
|
|
274
|
+
address,
|
|
275
|
+
meta: {
|
|
276
|
+
isRecent: undefined,
|
|
277
|
+
whenCreated: Date.now()
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
Object.keys(meta).forEach((key) => {
|
|
281
|
+
json.meta[key] = meta[key];
|
|
282
|
+
});
|
|
283
|
+
delete json.meta.isRecent;
|
|
284
|
+
this.#stores[type]().add(this._store, address, json);
|
|
285
|
+
return json;
|
|
286
|
+
}
|
|
287
|
+
saveContract(address, meta) {
|
|
288
|
+
return this.saveAddress(address, meta, 'contract');
|
|
289
|
+
}
|
|
290
|
+
saveRecent(address) {
|
|
291
|
+
const available = this.addresses.subject.getValue();
|
|
292
|
+
if (!available[address]) {
|
|
293
|
+
this.addresses.add(this._store, address, {
|
|
294
|
+
address,
|
|
295
|
+
meta: {
|
|
296
|
+
genesisHash: this.genesisHash,
|
|
297
|
+
isRecent: true,
|
|
298
|
+
whenCreated: Date.now()
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
return this.addresses.subject.getValue()[address];
|
|
303
|
+
}
|
|
304
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { decodeAddress } from '@pezkuwi/keyring';
|
|
2
|
+
import { u8aToHex } from '@pezkuwi/util';
|
|
3
|
+
const ACCOUNT_PREFIX = 'account:';
|
|
4
|
+
const ADDRESS_PREFIX = 'address:';
|
|
5
|
+
const CONTRACT_PREFIX = 'contract:';
|
|
6
|
+
function toHex(address) {
|
|
7
|
+
return u8aToHex(
|
|
8
|
+
// When saving pre-checksum changes, ensure that we can decode
|
|
9
|
+
decodeAddress(address, true));
|
|
10
|
+
}
|
|
11
|
+
export function accountKey(address) {
|
|
12
|
+
return `${ACCOUNT_PREFIX}${toHex(address)}`;
|
|
13
|
+
}
|
|
14
|
+
export function addressKey(address) {
|
|
15
|
+
return `${ADDRESS_PREFIX}${toHex(address)}`;
|
|
16
|
+
}
|
|
17
|
+
export function contractKey(address) {
|
|
18
|
+
return `${CONTRACT_PREFIX}${toHex(address)}`;
|
|
19
|
+
}
|
|
20
|
+
export const accountRegex = new RegExp(`^${ACCOUNT_PREFIX}0x[0-9a-f]*`, '');
|
|
21
|
+
export const addressRegex = new RegExp(`^${ADDRESS_PREFIX}0x[0-9a-f]*`, '');
|
|
22
|
+
export const contractRegex = new RegExp(`^${CONTRACT_PREFIX}0x[0-9a-f]*`, '');
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { BehaviorSubject } from 'rxjs';
|
|
2
|
+
import { objectCopy, objectSpread } from '@pezkuwi/util';
|
|
3
|
+
import { createOptionItem } from '../options/item.js';
|
|
4
|
+
import { env } from './env.js';
|
|
5
|
+
function callNext(current, subject, withTest) {
|
|
6
|
+
const isDevMode = env.isDevelopment();
|
|
7
|
+
const filtered = {};
|
|
8
|
+
Object.keys(current).forEach((key) => {
|
|
9
|
+
const { json: { meta: { isTesting = false } = {} } = {} } = current[key];
|
|
10
|
+
if (!withTest || isDevMode || isTesting !== true) {
|
|
11
|
+
filtered[key] = current[key];
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
subject.next(filtered);
|
|
15
|
+
}
|
|
16
|
+
export function genericSubject(keyCreator, withTest = false) {
|
|
17
|
+
let current = {};
|
|
18
|
+
const subject = new BehaviorSubject({});
|
|
19
|
+
const next = () => callNext(current, subject, withTest);
|
|
20
|
+
env.subject.subscribe(next);
|
|
21
|
+
return {
|
|
22
|
+
add: (store, address, json, type) => {
|
|
23
|
+
current = objectCopy(current);
|
|
24
|
+
current[address] = {
|
|
25
|
+
json: objectSpread({}, json, { address }),
|
|
26
|
+
option: createOptionItem(address, json.meta.name),
|
|
27
|
+
type
|
|
28
|
+
};
|
|
29
|
+
// we do not store dev or injected accounts (external/transient)
|
|
30
|
+
if (!json.meta.isInjected && (!json.meta.isTesting || env.isDevelopment())) {
|
|
31
|
+
store.set(keyCreator(address), json);
|
|
32
|
+
}
|
|
33
|
+
next();
|
|
34
|
+
return current[address];
|
|
35
|
+
},
|
|
36
|
+
remove: (store, address) => {
|
|
37
|
+
current = objectCopy(current);
|
|
38
|
+
delete current[address];
|
|
39
|
+
store.remove(keyCreator(address));
|
|
40
|
+
next();
|
|
41
|
+
},
|
|
42
|
+
subject
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { combineLatest, map } from 'rxjs';
|
|
2
|
+
import { accounts } from './accounts.js';
|
|
3
|
+
import { addresses } from './addresses.js';
|
|
4
|
+
import { contracts } from './contracts.js';
|
|
5
|
+
export const obervableAll = /*#__PURE__*/ combineLatest([
|
|
6
|
+
accounts.subject,
|
|
7
|
+
addresses.subject,
|
|
8
|
+
contracts.subject
|
|
9
|
+
]).pipe(map(([accounts, addresses, contracts]) => ({
|
|
10
|
+
accounts,
|
|
11
|
+
addresses,
|
|
12
|
+
contracts
|
|
13
|
+
})));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|