Line data Source code
1 : import 'dart:typed_data';
2 : import 'package:bip32/bip32.dart' as bip32;
3 : import 'package:walletkit_dart/src/crypto/utxo/utils/pubkey_to_address.dart';
4 : import 'package:bs58check/bs58check.dart' as bs58check;
5 : import 'package:walletkit_dart/src/utils/var_uint.dart';
6 : import 'package:walletkit_dart/walletkit_dart.dart';
7 :
8 : typedef BipNode = bip32.BIP32;
9 :
10 2 : String deriveExtendedPubKey({
11 : required Uint8List seed,
12 : required HDWalletPath walletPurpose,
13 : UTXONetworkType? type,
14 : }) {
15 : ///
16 : /// Walletkit Compatibility
17 : ///
18 2 : if (type == LitecoinNetwork) {
19 2 : final depth1MasterNode = deriveMasterNodeFromSeed(
20 : seed: seed,
21 : networkType: type,
22 : walletPath: bitcoinBip44HDPath, // TODO: Check if still valid
23 : );
24 :
25 2 : final parentFingerprint = depth1MasterNode.parentFingerprint;
26 :
27 2 : final masterNode = deriveMasterNodeFromSeed(
28 : seed: seed,
29 : networkType: type,
30 : walletPath: walletPurpose,
31 : );
32 4 : return masterNode.neutered().toBase58wkCompatibility(parentFingerprint, 1);
33 : }
34 :
35 1 : final masterNode = deriveMasterNodeFromSeed(
36 : seed: seed,
37 : networkType: type,
38 : walletPath: walletPurpose,
39 : );
40 2 : return masterNode.neutered().toBase58();
41 : }
42 :
43 10 : BipNode deriveMasterNodeFromSeed({
44 : required Uint8List seed,
45 : required HDWalletPath walletPath,
46 : UTXONetworkType? networkType,
47 : }) {
48 21 : final bipNetworkType = networkType?.networkBIP.getForWalletType(walletPath.purpose);
49 :
50 10 : final parentNode = BipNode.fromSeed(seed, bipNetworkType);
51 10 : final derivationPath = switch (walletPath.basePath) {
52 12 : "m/44'/2'" => walletPath.account0Path,
53 20 : _ => walletPath.purpose.string,
54 : };
55 10 : final node = parentNode.derivePath(derivationPath); // TODO: Use base Path with Account
56 :
57 : return node;
58 : }
59 :
60 6 : BipNode deriveMasterNodeFromExtendedKeyWithCheck({
61 : required String ePubKey,
62 : required UTXONetworkType networkType,
63 : required HDWalletPurpose purpose,
64 : }) {
65 6 : final (node, version) = deriveMasterNodeFromExtendedKey(
66 : ePubKey,
67 : networkType: networkType,
68 : purpose: purpose,
69 : );
70 :
71 48 : if (version != node.network.bip32.private && version != node.network.bip32.public) {
72 0 : throw ArgumentError(
73 0 : "Version mismatch. Extracted Version: $version. Expected: ${node.network.bip32.private} or ${node.network.bip32.public}",
74 : );
75 : }
76 :
77 : return node;
78 : }
79 :
80 7 : (BipNode node, int version) deriveMasterNodeFromExtendedKey(
81 : String ePubKey, {
82 : UTXONetworkType? networkType,
83 : HDWalletPurpose? purpose,
84 : }) {
85 7 : final buffer = bs58check.decode(ePubKey);
86 :
87 14 : if (buffer.length != 78) {
88 0 : throw UnsupportedError("invalid ePubKey");
89 : }
90 :
91 14 : final version = buffer.bytes.getUint32(0);
92 :
93 7 : final node = BipNode.fromBase58(
94 : ePubKey,
95 : switch ((networkType, purpose)) {
96 13 : (UTXONetworkType network, HDWalletPurpose purpose) =>
97 12 : network.networkBIP.getForWalletType(purpose),
98 1 : _ => bip32.NetworkType(
99 : wif: 0x80,
100 1 : bip32: bip32.Bip32Type(
101 : private: 0x0488ADE4,
102 : public: 0x0488B21E,
103 : ),
104 : ),
105 : },
106 : );
107 :
108 : return (node, version);
109 : }
110 :
111 9 : NodeWithAddress deriveChildNode({
112 : required BipNode masterNode,
113 : required int chainIndex,
114 : required int index,
115 : required UTXONetworkType networkType,
116 : required Iterable<AddressType> addressTypes,
117 : required HDWalletPurpose? walletPurpose,
118 : }) {
119 9 : if (index < 0) {
120 0 : throw UnsupportedError("index must not be negative");
121 : }
122 18 : if (chainIndex != EXTERNAL_CHAIN_INDEX && chainIndex != INTERNAL_CHAIN_INDEX) {
123 0 : throw UnsupportedError("unexpected chainIndex");
124 : }
125 :
126 9 : final childDerivationPath = "$chainIndex/$index";
127 :
128 9 : final node = masterNode.derivePath(childDerivationPath);
129 :
130 9 : final publicKey = node.publicKey;
131 :
132 9 : final addressMap = {
133 9 : for (final addressType in addressTypes)
134 18 : addressType: pubKeyToAddress(publicKey, addressType, networkType),
135 : };
136 :
137 18 : final mainAddress = addressMap[addressTypes.first]!;
138 :
139 9 : return NodeWithAddress.fromChainIndex(
140 : node: node,
141 : address: mainAddress,
142 : chainIndex: chainIndex,
143 : derivationPath: childDerivationPath,
144 : addresses: addressMap,
145 : walletPurpose: walletPurpose,
146 : );
147 : }
148 :
149 1 : bip32.BIP32 deriveChildNodeFromPath({
150 : required Uint8List seed,
151 : required String childDerivationPath,
152 : required HDWalletPath walletPath,
153 : UTXONetworkType? networkType,
154 : }) {
155 1 : final masterNode = deriveMasterNodeFromSeed(
156 : seed: seed,
157 : networkType: networkType,
158 : walletPath: walletPath,
159 : );
160 :
161 1 : final node = masterNode.derivePath(childDerivationPath);
162 :
163 : return node;
164 : }
165 :
166 : extension on BipNode {
167 2 : String toBase58wkCompatibility(int parentFingerprint, int depth) {
168 8 : final version = (!isNeutered()) ? network.bip32.private : network.bip32.public;
169 2 : Uint8List buffer = new Uint8List(78);
170 4 : ByteData bytes = buffer.buffer.asByteData();
171 2 : bytes.setUint32(0, version);
172 2 : bytes.setUint8(4, depth);
173 2 : bytes.setUint32(5, parentFingerprint);
174 4 : bytes.setUint32(9, index);
175 4 : buffer.setRange(13, 45, chainCode);
176 2 : if (!isNeutered()) {
177 0 : bytes.setUint8(45, 0);
178 0 : buffer.setRange(46, 78, privateKey!);
179 : } else {
180 4 : buffer.setRange(45, 78, publicKey);
181 : }
182 :
183 2 : return bs58check.encode(buffer);
184 : }
185 : }
186 :
187 2 : BipNode deriveNode(Uint8List seed, String path) {
188 2 : final node = bip32.BIP32.fromSeed(seed);
189 2 : return node.derivePath(path);
190 : }
|