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