-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathHDKoinos.js
103 lines (92 loc) · 2.72 KB
/
HDKoinos.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/**
* Hierarchical Deterministic Wallet for Koinos
*
* The HDKoinos follows the BIP44 standard as follows
* (see https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki):
*
* path levels
* m / purpose' / coin_type' / account' / change / address_index
*
* purpose: 44
*
* coin_type: 659
* This value was selected by taking the word "koinos", converting it
* to ascii [107 111 105 110 111 115], and adding their values.
*
* account:
* index of the account
*
* change:
* Koinos doesn't use an UTXO model as bitcoin does, so there is no
* change. However, we take the philosophy around change: internal and
* external use.
* - 0 for external use: Accounts. Addresses to receive payments and store
* assets.
* - 1 for internal use: Signers. In Koinos these addresses are not
* meant to be used to store assets (like tokens or NTFs) but to
* define signers for multisig wallets.
*
* address_index:
* - 0 for accounts, or
* - index for signers (when change = 1)
*/
const { Signer } = require("koilib");
const ethers = require("ethers");
const crypto = require("crypto");
const KOINOS_PATH = "m/44'/659'/";
module.exports = class HDKoinos {
constructor(mnemonic) {
this.hdNode = ethers.utils.HDNode.fromMnemonic(mnemonic);
}
static randomMnemonic() {
return ethers.utils.entropyToMnemonic(crypto.randomBytes(16));
}
static parsePath(keyPath) {
if (!keyPath) throw new Error("Account not derived from a seed");
const matchs = keyPath.match(
/^m\/44'\/659'\/([0-9]*)'\/([0-9]*)\/([0-9]*)/
);
if (!matchs) throw new Error(`Invalid keyPath ${keyPath}`);
const [, accountIndex, change, signerIndex] = matchs;
if (change !== "0" && change !== "1")
throw new Error(`Invalid 'change' in keyPath ${keyPath}`);
return {
accountIndex,
...(change === "1" && { signerIndex }),
};
}
deriveKey(account) {
const { name, keyPath, address } = account;
const key = this.hdNode.derivePath(keyPath);
const signer = new Signer({
privateKey: key.privateKey.slice(2),
});
if (address && address !== signer.getAddress()) {
throw new Error(
`Error in "${name}". Expected address: ${address}. Derived: ${signer.getAddress()}`
);
}
return {
public: {
name,
keyPath,
address: signer.getAddress(),
},
private: {
privateKey: signer.getPrivateKey("wif", false),
},
};
}
deriveKeyAccount(accIndex, name) {
return this.deriveKey({
name,
keyPath: `${KOINOS_PATH}${accIndex}'/0/0`,
});
}
deriveKeySigner(accIndex, signerIndex, name) {
return this.deriveKey({
name,
keyPath: `${KOINOS_PATH}${accIndex}'/1/${signerIndex}`,
});
}
};