@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
package/README.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# @pezkuwi/ui-keyring
|
|
2
|
+
|
|
3
|
+
A wrapper extending the base @pezkuwi/keyring interface for usage in the browser:
|
|
4
|
+
Key management of user accounts including generation and retrieval of keyring pairs from a variety of input combinations.
|
|
5
|
+
|
|
6
|
+
## Usage Examples
|
|
7
|
+
|
|
8
|
+
All module methods are exposed through a single default export.
|
|
9
|
+
|
|
10
|
+
### Regular
|
|
11
|
+
|
|
12
|
+
```js
|
|
13
|
+
import keyring from @pezkuwi/ui-keyring
|
|
14
|
+
|
|
15
|
+
render () {
|
|
16
|
+
// encode publicKey to ss58 address
|
|
17
|
+
const address = keyring.encodeAddress(publicKey);
|
|
18
|
+
|
|
19
|
+
// get keyring pair from ss58 address
|
|
20
|
+
const pair = keyring.getPair(address);
|
|
21
|
+
|
|
22
|
+
// ask questions about that particular keyring pair
|
|
23
|
+
const isLocked = pair.isLocked;
|
|
24
|
+
const meta = pair.meta;
|
|
25
|
+
|
|
26
|
+
// save account from pair
|
|
27
|
+
keyring.saveAccount(pair, password);
|
|
28
|
+
|
|
29
|
+
// save address without unlocking it
|
|
30
|
+
keyring.saveAddress(address, { ...meta });
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Observables
|
|
35
|
+
|
|
36
|
+
Option 1: Declarative subscribe/unsubscribe w/ react-with-observable (recommended 'React' way)
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
import accountObservable from '@pezkuwi/ui-keyring/observable/accounts';
|
|
40
|
+
import { SingleAddress, SubjectInfo } from '@pezkuwi/ui-keyring/observable/types';
|
|
41
|
+
import React from 'react';
|
|
42
|
+
import { Subscribe } from 'react-with-observable';
|
|
43
|
+
import { map } from 'rxjs';
|
|
44
|
+
|
|
45
|
+
class MyReactComponent extends React.PureComponent {
|
|
46
|
+
render () {
|
|
47
|
+
<Subscribe>
|
|
48
|
+
{accountObservable.subject.pipe(
|
|
49
|
+
map((allAccounts: SubjectInfo) =>
|
|
50
|
+
!allAccounts
|
|
51
|
+
? this.renderEmpty()
|
|
52
|
+
: Object.values(allAccounts).map((account: SingleAddress) =>
|
|
53
|
+
// Your component goes here
|
|
54
|
+
console.log(account.json.address)
|
|
55
|
+
)
|
|
56
|
+
))}
|
|
57
|
+
</Subscribe>
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
renderEmpty () {
|
|
61
|
+
return (
|
|
62
|
+
<div> no accounts to display ... </div>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Option 2: Imperative subscribe/unsubscribe
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
import accountObservable from '@pezkuwi/ui-keyring/observable/accounts';
|
|
73
|
+
import { SingleAddress, SubjectInfo } from '@pezkuwi/ui-keyring/observable/types';
|
|
74
|
+
import React from 'react';
|
|
75
|
+
import { Subscription } from 'rxjs';
|
|
76
|
+
|
|
77
|
+
type State = {
|
|
78
|
+
allAccounts?: SubjectInfo,
|
|
79
|
+
subscriptions?: [Subscription]
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
class MyReactComponent extends React.PureComponent<State> {
|
|
83
|
+
componentDidMount () {
|
|
84
|
+
const accountSubscription = accountObservable.subject.subscribe((observedAccounts) => {
|
|
85
|
+
this.setState({
|
|
86
|
+
accounts: observedAccounts
|
|
87
|
+
});
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
this.setState({
|
|
91
|
+
subscriptions: [accountSubscription]
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
componentWillUnmount () {
|
|
96
|
+
const { subscriptions } = this.state;
|
|
97
|
+
|
|
98
|
+
for (s in subscriptions) {
|
|
99
|
+
s.subject.unsubscribe();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
render () {
|
|
104
|
+
const { accounts } = this.state;
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<h1>All Accounts</h1>
|
|
108
|
+
{
|
|
109
|
+
Object.keys(accounts).map((address: SingleAddress) => {
|
|
110
|
+
return <p> {address} </p>;
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## FAQ
|
|
119
|
+
|
|
120
|
+
- Difference between Keyring Accounts and Addresses?
|
|
121
|
+
- From the perspective of the keyring, it saves a particular user's unlocked identities as an account, a la keyring.saveAccount(pair, password). So with these accounts you are able to send and sign transactions.
|
|
122
|
+
- To save addresses without unlocking them (i.e. because a user might want to have easy access to addresses they frequently transact with), use keyring.saveAddress(address, meta)
|
|
123
|
+
- What are 'external' accounts, i.e. when to set the `isExternal` meta key to true?
|
|
124
|
+
- An external account is one where the keys are not managed by keyring, e.g. in Parity Signer or Ledger Nano.
|
|
125
|
+
- SS58 Encode / Decode?
|
|
126
|
+
- SS58 is a simple address format designed for Substrate based chains. You can read about its specification in more detail in the [Substrate Documentation](https://docs.substrate.io/reference/address-formats/).
|
|
127
|
+
|
|
128
|
+
**If you have any unanswered/undocumented questions, please raise an issue [here](https://github.com/pezkuwichain/ui/issues).**
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
## Users
|
|
132
|
+
|
|
133
|
+
Keyring is core to many polkadot/substrate apps.
|
|
134
|
+
|
|
135
|
+
* [pezkuwichain/apps](https://github.com/pezkuwichain/apps)
|
|
136
|
+
* [paritytech/substrate-light-ui](https://github.com/paritytech/substrate-light-ui)
|
package/build/Base.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { KeyringInstance, KeyringPair } from '@pezkuwi/keyring/types';
|
|
2
|
+
import type { HexString } from '@pezkuwi/util/types';
|
|
3
|
+
import type { Prefix } from '@pezkuwi/util-crypto/address/types';
|
|
4
|
+
import type { AddressSubject } from './observable/types.js';
|
|
5
|
+
import type { KeyringOptions, KeyringStore } from './types.js';
|
|
6
|
+
export declare class Base {
|
|
7
|
+
#private;
|
|
8
|
+
protected _store: KeyringStore;
|
|
9
|
+
protected _genesisHash?: HexString | undefined;
|
|
10
|
+
protected _genesisHashAdd: HexString[];
|
|
11
|
+
constructor();
|
|
12
|
+
get accounts(): AddressSubject;
|
|
13
|
+
get addresses(): AddressSubject;
|
|
14
|
+
get contracts(): AddressSubject;
|
|
15
|
+
get isEthereum(): boolean;
|
|
16
|
+
get keyring(): KeyringInstance;
|
|
17
|
+
get genesisHash(): HexString | undefined;
|
|
18
|
+
get genesisHashes(): HexString[];
|
|
19
|
+
decodeAddress: (key: string | Uint8Array, ignoreChecksum?: boolean, ss58Format?: Prefix) => Uint8Array;
|
|
20
|
+
encodeAddress: (key: string | Uint8Array, ss58Format?: Prefix) => string;
|
|
21
|
+
getPair(address: string | Uint8Array): KeyringPair;
|
|
22
|
+
getPairs(): KeyringPair[];
|
|
23
|
+
isAvailable(_address: Uint8Array | string): boolean;
|
|
24
|
+
isPassValid(password: string): boolean;
|
|
25
|
+
setSS58Format(ss58Format?: Prefix): void;
|
|
26
|
+
setDevMode(isDevelopment: boolean): void;
|
|
27
|
+
protected initKeyring(options: KeyringOptions): void;
|
|
28
|
+
protected addAccountPairs(): void;
|
|
29
|
+
protected addTimestamp(pair: KeyringPair): void;
|
|
30
|
+
}
|
package/build/Base.js
ADDED
|
@@ -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,49 @@
|
|
|
1
|
+
import type { KeyringPair, KeyringPair$Json, KeyringPair$Meta } from '@pezkuwi/keyring/types';
|
|
2
|
+
import type { BN } from '@pezkuwi/util';
|
|
3
|
+
import type { EncryptedJson } from '@pezkuwi/util-crypto/json/types';
|
|
4
|
+
import type { KeypairType } from '@pezkuwi/util-crypto/types';
|
|
5
|
+
import type { SingleAddress } from './observable/types.js';
|
|
6
|
+
import type { CreateResult, KeyringAddress, KeyringAddressType, KeyringItemType, KeyringJson$Meta, KeyringOptions, KeyringPairs$Json, KeyringStruct } from './types.js';
|
|
7
|
+
import { KeyringOption } from './options/index.js';
|
|
8
|
+
import { Base } from './Base.js';
|
|
9
|
+
export declare class Keyring extends Base implements KeyringStruct {
|
|
10
|
+
#private;
|
|
11
|
+
readonly keyringOption: KeyringOption;
|
|
12
|
+
addExternal(address: string | Uint8Array, meta?: KeyringPair$Meta): CreateResult;
|
|
13
|
+
addHardware(address: string | Uint8Array, hardwareType: string, meta?: KeyringPair$Meta): CreateResult;
|
|
14
|
+
addMultisig(addresses: (string | Uint8Array)[], threshold: bigint | BN | number, meta?: KeyringPair$Meta): CreateResult;
|
|
15
|
+
addPair(pair: KeyringPair, password: string): CreateResult;
|
|
16
|
+
addUri(suri: string, password?: string, meta?: KeyringPair$Meta, type?: KeypairType): CreateResult;
|
|
17
|
+
backupAccount(pair: KeyringPair, password: string): KeyringPair$Json;
|
|
18
|
+
backupAccounts(addresses: string[], password: string): Promise<KeyringPairs$Json>;
|
|
19
|
+
createFromJson(json: KeyringPair$Json, meta?: KeyringPair$Meta): KeyringPair;
|
|
20
|
+
createFromUri(suri: string, meta?: KeyringPair$Meta, type?: KeypairType): KeyringPair;
|
|
21
|
+
encryptAccount(pair: KeyringPair, password: string): void;
|
|
22
|
+
forgetAccount(address: string): void;
|
|
23
|
+
forgetAddress(address: string): void;
|
|
24
|
+
forgetContract(address: string): void;
|
|
25
|
+
getAccount(address: string | Uint8Array): KeyringAddress | undefined;
|
|
26
|
+
getAccounts(): KeyringAddress[];
|
|
27
|
+
getAddress(_address: string | Uint8Array, type?: KeyringItemType | null): KeyringAddress | undefined;
|
|
28
|
+
getAddresses(): KeyringAddress[];
|
|
29
|
+
getContract(address: string | Uint8Array): KeyringAddress | undefined;
|
|
30
|
+
getContracts(): KeyringAddress[];
|
|
31
|
+
private rewriteKey;
|
|
32
|
+
private loadAccount;
|
|
33
|
+
private loadAddress;
|
|
34
|
+
private loadContract;
|
|
35
|
+
private loadInjected;
|
|
36
|
+
private allowGenesis;
|
|
37
|
+
loadAll(options: KeyringOptions, injected?: {
|
|
38
|
+
address: string;
|
|
39
|
+
meta: KeyringJson$Meta;
|
|
40
|
+
type?: KeypairType;
|
|
41
|
+
}[]): void;
|
|
42
|
+
restoreAccount(json: KeyringPair$Json, password: string): KeyringPair;
|
|
43
|
+
restoreAccounts(json: EncryptedJson, password: string): void;
|
|
44
|
+
saveAccount(pair: KeyringPair, password?: string): KeyringPair$Json;
|
|
45
|
+
saveAccountMeta(pair: KeyringPair, meta: KeyringPair$Meta): void;
|
|
46
|
+
saveAddress(address: string, meta: KeyringPair$Meta, type?: KeyringAddressType): KeyringPair$Json;
|
|
47
|
+
saveContract(address: string, meta: KeyringPair$Meta): KeyringPair$Json;
|
|
48
|
+
saveRecent(address: string): SingleAddress;
|
|
49
|
+
}
|
package/build/Keyring.js
ADDED
|
@@ -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
|
+
}
|