mainnet-js 0.4.28 → 0.4.32

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.
Files changed (61) hide show
  1. package/dist/index.html +1 -1
  2. package/dist/main/constant.d.ts +1 -0
  3. package/dist/main/constant.js +17 -1
  4. package/dist/main/constant.js.map +1 -1
  5. package/dist/main/util/getAddrsByXpubKey.d.ts +22 -0
  6. package/dist/main/util/getAddrsByXpubKey.js +79 -0
  7. package/dist/main/util/getAddrsByXpubKey.js.map +1 -0
  8. package/dist/main/util/getXPubKey.d.ts +1 -0
  9. package/dist/main/util/getXPubKey.js +26 -0
  10. package/dist/main/util/getXPubKey.js.map +1 -0
  11. package/dist/main/util/index.d.ts +5 -3
  12. package/dist/main/util/index.js +13 -6
  13. package/dist/main/util/index.js.map +1 -1
  14. package/dist/main/wallet/Base.d.ts +3 -1
  15. package/dist/main/wallet/Base.js +3 -1
  16. package/dist/main/wallet/Base.js.map +1 -1
  17. package/dist/main/wallet/Wif.d.ts +33 -10
  18. package/dist/main/wallet/Wif.js +188 -77
  19. package/dist/main/wallet/Wif.js.map +1 -1
  20. package/dist/main/wallet/interface.d.ts +5 -0
  21. package/dist/main/wallet/model.d.ts +16 -0
  22. package/dist/main/wallet/model.js +18 -1
  23. package/dist/main/wallet/model.js.map +1 -1
  24. package/dist/{mainnet-0.4.28.js → mainnet-0.4.32.js} +2 -2
  25. package/dist/{mainnet-0.4.28.js.LICENSE.txt → mainnet-0.4.32.js.LICENSE.txt} +0 -0
  26. package/dist/module/constant.d.ts +1 -0
  27. package/dist/module/constant.js +16 -0
  28. package/dist/module/constant.js.map +1 -1
  29. package/dist/module/util/getAddrsByXpubKey.d.ts +22 -0
  30. package/dist/module/util/getAddrsByXpubKey.js +71 -0
  31. package/dist/module/util/getAddrsByXpubKey.js.map +1 -0
  32. package/dist/module/util/getXPubKey.d.ts +1 -0
  33. package/dist/module/util/getXPubKey.js +22 -0
  34. package/dist/module/util/getXPubKey.js.map +1 -0
  35. package/dist/module/util/index.d.ts +5 -3
  36. package/dist/module/util/index.js +5 -3
  37. package/dist/module/util/index.js.map +1 -1
  38. package/dist/module/wallet/Base.d.ts +3 -1
  39. package/dist/module/wallet/Base.js +3 -1
  40. package/dist/module/wallet/Base.js.map +1 -1
  41. package/dist/module/wallet/Wif.d.ts +33 -10
  42. package/dist/module/wallet/Wif.js +191 -80
  43. package/dist/module/wallet/Wif.js.map +1 -1
  44. package/dist/module/wallet/interface.d.ts +5 -0
  45. package/dist/module/wallet/model.d.ts +16 -0
  46. package/dist/module/wallet/model.js +16 -0
  47. package/dist/module/wallet/model.js.map +1 -1
  48. package/dist/tsconfig.browser.tsbuildinfo +1 -1
  49. package/dist/tsconfig.tsbuildinfo +1 -1
  50. package/package.json +1 -1
  51. package/src/constant.ts +17 -0
  52. package/src/util/getAddrsByXpubKey.test.ts +115 -0
  53. package/src/util/getAddrsByXpubKey.ts +98 -0
  54. package/src/util/getXPubKey.ts +36 -0
  55. package/src/util/index.ts +10 -3
  56. package/src/wallet/Base.ts +4 -1
  57. package/src/wallet/Wif.test.ts +215 -2
  58. package/src/wallet/Wif.ts +284 -130
  59. package/src/wallet/createWallet.test.ts +2 -0
  60. package/src/wallet/interface.ts +6 -1
  61. package/src/wallet/model.ts +22 -0
package/src/wallet/Wif.ts CHANGED
@@ -1,11 +1,18 @@
1
1
  //#region Imports
2
2
  // Stable
3
- import { instantiateSecp256k1, instantiateSha256 } from "@bitauth/libauth";
3
+ import {
4
+ deriveHdPublicNodeIdentifier,
5
+ encodeHdPublicKey,
6
+ HdKeyNetwork,
7
+ instantiateSecp256k1,
8
+ instantiateSha256,
9
+ } from "@bitauth/libauth";
4
10
 
5
11
  // Unstable?
6
12
  import {
7
13
  binToHex,
8
14
  CashAddressNetworkPrefix,
15
+ deriveHdPublicNode,
9
16
  decodePrivateKeyWif,
10
17
  encodePrivateKeyWif,
11
18
  deriveHdPrivateNodeFromSeed,
@@ -40,6 +47,7 @@ import {
40
47
  SendResponse,
41
48
  UtxoItem,
42
49
  UtxoResponse,
50
+ XPubKey,
43
51
  } from "./model";
44
52
 
45
53
  import {
@@ -92,6 +100,8 @@ import { generateRandomBytes } from "../util/randomBytes";
92
100
  import { SignedMessageI, SignedMessage } from "../message";
93
101
  import ElectrumNetworkProvider from "../network/ElectrumNetworkProvider";
94
102
  import { amountInSatoshi } from "../util/amountInSatoshi";
103
+ import { getXPubKey } from "../util/getXPubKey";
104
+ import { DERIVATION_PATHS, DUST_UTXO_THRESHOLD } from "../constant";
95
105
 
96
106
  //#endregion Imports
97
107
 
@@ -104,6 +114,8 @@ const sha256Promise = instantiateSha256();
104
114
  export class Wallet extends BaseWallet {
105
115
  cashaddr?: string;
106
116
  derivationPath: string = "m/44'/0'/0'/0/0";
117
+ parentDerivationPath: string = "m/44'/0'/0'";
118
+ parentXPubKey?: string;
107
119
  privateKey?: Uint8Array;
108
120
  publicKeyCompressed?: Uint8Array;
109
121
  privateKeyWif?: string;
@@ -111,7 +123,8 @@ export class Wallet extends BaseWallet {
111
123
  publicKeyHash?: Uint8Array;
112
124
  networkPrefix: CashAddressNetworkPrefix;
113
125
  _slp?: Slp;
114
- _slpAware: boolean = false;
126
+ _slpAware: boolean = false; // a flag which activates utxo checking against an external slp indexer
127
+ _slpSemiAware: boolean = false; // a flag which requires an utxo to have more than 546 sats to be spendable and counted in the balance
115
128
  _util?: Util;
116
129
  static signedMessage: SignedMessageI = new SignedMessage();
117
130
 
@@ -150,6 +163,11 @@ export class Wallet extends BaseWallet {
150
163
  return this;
151
164
  }
152
165
 
166
+ public slpSemiAware(value: boolean = true): Wallet {
167
+ this._slpSemiAware = value;
168
+ return this;
169
+ }
170
+
153
171
  public getNetworkProvider(network: Network = Network.MAINNET) {
154
172
  return getNetworkProvider(network);
155
173
  }
@@ -162,8 +180,8 @@ export class Wallet extends BaseWallet {
162
180
  */
163
181
  public explorerUrl(txId: string) {
164
182
  const explorerUrlMap = {
165
- mainnet: "https://explorer.bitcoin.com/bch/tx/",
166
- testnet: "https://explorer.bitcoin.com/tbch/tx/",
183
+ mainnet: "https://blockchair.com/bitcoin-cash/transaction/",
184
+ testnet: "https://www.blockchain.com/bch-testnet/tx/",
167
185
  regtest: "",
168
186
  };
169
187
 
@@ -179,6 +197,10 @@ export class Wallet extends BaseWallet {
179
197
  network: this.network as any,
180
198
  seed: this.mnemonic ? this.getSeed().seed : undefined,
181
199
  derivationPath: this.mnemonic ? this.getSeed().derivationPath : undefined,
200
+ parentDerivationPath: this.mnemonic
201
+ ? this.getSeed().parentDerivationPath
202
+ : undefined,
203
+ parentXPubKey: this.parentXPubKey ? this.parentXPubKey : undefined,
182
204
  publicKey: this.publicKey ? binToHex(this.publicKey!) : undefined,
183
205
  publicKeyHash: binToHex(this.publicKeyHash!),
184
206
  privateKey: this.privateKey ? binToHex(this.privateKey!) : undefined,
@@ -312,19 +334,25 @@ export class Wallet extends BaseWallet {
312
334
  }
313
335
 
314
336
  private async _generateMnemonic() {
315
- const crypto = await instantiateBIP32Crypto();
316
337
  this.mnemonic = generateMnemonic();
317
338
  let seed = mnemonicToSeedSync(this.mnemonic!);
339
+ let network = this.isTestnet ? "testnet" : "mainnet";
340
+ this.parentXPubKey = await getXPubKey(
341
+ seed,
342
+ this.parentDerivationPath,
343
+ network
344
+ );
345
+
346
+ const crypto = await instantiateBIP32Crypto();
318
347
  let hdNode = deriveHdPrivateNodeFromSeed(crypto, seed);
319
348
  if (!hdNode.valid) {
320
349
  throw Error("Invalid private key derived from mnemonic seed");
321
350
  }
322
351
 
323
- let zerothChild = deriveHdPath(
324
- crypto,
325
- hdNode,
326
- this.derivationPath
327
- ) as HdPrivateNodeValid;
352
+ let zerothChild = deriveHdPath(crypto, hdNode, this.derivationPath);
353
+ if (typeof zerothChild === "string") {
354
+ throw Error(zerothChild);
355
+ }
328
356
  this.privateKey = zerothChild.privateKey;
329
357
 
330
358
  this.walletType = WalletTypeEnum.Seed;
@@ -346,6 +374,18 @@ export class Wallet extends BaseWallet {
346
374
  return super.fromId(walletId);
347
375
  };
348
376
 
377
+ public async getXPubKeys(paths?) {
378
+ if (this.mnemonic) {
379
+ if (paths) {
380
+ let xPubKeys = await this.deriveHdPaths(paths);
381
+ return [xPubKeys];
382
+ } else {
383
+ return await this.deriveHdPaths(DERIVATION_PATHS);
384
+ }
385
+ } else {
386
+ throw Error("xpubkeys can only be derived from seed type wallets.");
387
+ }
388
+ }
349
389
  // Initialize wallet from a mnemonic phrase
350
390
  protected async fromSeed(
351
391
  mnemonic: string,
@@ -362,20 +402,73 @@ export class Wallet extends BaseWallet {
362
402
  }
363
403
  if (derivationPath) {
364
404
  this.derivationPath = derivationPath;
405
+
406
+ // If the derivation path is for the first account child, set the parent derivation path
407
+ let path = derivationPath.split("/");
408
+ if (path.slice(-2).join("/") == "0/0") {
409
+ this.parentDerivationPath = path.slice(0, -2).join("/");
410
+ }
365
411
  }
366
412
 
367
- let zerothChild = deriveHdPath(
368
- crypto,
369
- hdNode,
370
- this.derivationPath
371
- ) as HdPrivateNodeValid;
413
+ let zerothChild = deriveHdPath(crypto, hdNode, this.derivationPath);
414
+ if (typeof zerothChild === "string") {
415
+ throw Error(zerothChild);
416
+ }
372
417
  this.privateKey = zerothChild.privateKey;
373
418
 
419
+ let network = this.isTestnet ? "testnet" : "mainnet";
420
+ this.parentXPubKey = await getXPubKey(
421
+ seed,
422
+ this.parentDerivationPath,
423
+ network
424
+ );
425
+
374
426
  this.walletType = WalletTypeEnum.Seed;
375
427
  await this.deriveInfo();
376
428
  return this;
377
429
  }
378
430
 
431
+ // Get common xpub paths from zerothChild privateKey
432
+ public async deriveHdPaths(hdPaths: string[]): Promise<any[]> {
433
+ const crypto = await instantiateBIP32Crypto();
434
+ let seed = mnemonicToSeedSync(this.mnemonic!);
435
+ let hdNode = deriveHdPrivateNodeFromSeed(crypto, seed);
436
+ if (!hdNode.valid) {
437
+ throw Error("Invalid private key derived from mnemonic seed");
438
+ }
439
+
440
+ let result: any[] = [];
441
+
442
+ for (const path of hdPaths) {
443
+ if (path === "m") {
444
+ throw Error(
445
+ "Storing or sharing of parent public key may lead to loss of funds. Storing or sharing *root* parent public keys is strongly discouraged, although all parent keys have risk. See: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#implications"
446
+ );
447
+ }
448
+ let childNode = deriveHdPath(crypto, hdNode, path);
449
+ if (typeof childNode === "string") {
450
+ throw Error(childNode);
451
+ }
452
+ let node = deriveHdPublicNode(crypto, childNode);
453
+ if (typeof node === "string") {
454
+ throw Error(node);
455
+ }
456
+ let xPubKey = encodeHdPublicKey(crypto, {
457
+ network: this.network as HdKeyNetwork,
458
+ node: node,
459
+ });
460
+ let key = new XPubKey({
461
+ path: path,
462
+ xPubKey: xPubKey,
463
+ });
464
+
465
+ result.push(await key.ready());
466
+ }
467
+ return await Promise.all(result).then((result) => {
468
+ return result;
469
+ });
470
+ }
471
+
379
472
  // Initialize a watch only wallet from a cash addr
380
473
  protected async watchOnly(address: string): Promise<this> {
381
474
  this.walletType = WalletTypeEnum.Watch;
@@ -410,8 +503,7 @@ export class Wallet extends BaseWallet {
410
503
  const sha256 = await sha256Promise;
411
504
  let wifResult = decodePrivateKeyWif(sha256, secret);
412
505
 
413
- const hasError = typeof wifResult === "string";
414
- if (hasError) {
506
+ if (typeof wifResult === "string") {
415
507
  throw Error(wifResult as string);
416
508
  }
417
509
  let resultData: PrivateKeyI = wifResult as PrivateKeyI;
@@ -498,6 +590,11 @@ export class Wallet extends BaseWallet {
498
590
  (slpOutpoint) => `${bchutxo.txid}:${bchutxo.vout}` === slpOutpoint
499
591
  ) === -1
500
592
  );
593
+ } else if (this._slpSemiAware) {
594
+ const bchUtxos: UtxoI[] = await this.provider!.getUtxos(address);
595
+ return bchUtxos.filter(
596
+ (bchutxo) => bchutxo.satoshis > DUST_UTXO_THRESHOLD
597
+ );
501
598
  } else {
502
599
  return await this.provider!.getUtxos(address);
503
600
  }
@@ -550,7 +647,7 @@ export class Wallet extends BaseWallet {
550
647
  // Gets balance from fulcrum
551
648
  public async getBalanceFromProvider(): Promise<number> {
552
649
  // TODO not sure why getting the balance from a provider doesn't work
553
- if (this._slpAware) {
650
+ if (this._slpAware || this._slpSemiAware) {
554
651
  return await this.getBalanceFromUtxos();
555
652
  } else {
556
653
  return await this.provider!.getBalance(this.cashaddr!);
@@ -663,6 +760,10 @@ export class Wallet extends BaseWallet {
663
760
  this._slpAware = true;
664
761
  }
665
762
 
763
+ if (params.options && params.options.slpSemiAware) {
764
+ this._slpSemiAware = true;
765
+ }
766
+
666
767
  // get inputs
667
768
  let utxos: UtxoI[];
668
769
  if (params.options && params.options.utxoIds) {
@@ -707,6 +808,8 @@ export class Wallet extends BaseWallet {
707
808
 
708
809
  /**
709
810
  * send Send some amount to an address
811
+ * this function processes the send requests, encodes the transaction, sends it to the network
812
+ * @returns (depending on the options parameter) the transaction id, new address balance and a link to the transaction on the blockchain explorer
710
813
  *
711
814
  * This is a first class function with REST analog, maintainers should strive to keep backward-compatibility
712
815
  *
@@ -719,14 +822,24 @@ export class Wallet extends BaseWallet {
719
822
  | SendRequestArray[],
720
823
  options?: SendRequestOptionsI
721
824
  ): Promise<SendResponse> {
722
- let sendRequests = asSendRequestObject(requests);
723
- let result = await this._processSendRequests(
724
- sendRequests,
825
+ let encodedTransaction = await this.encodeTransaction(
826
+ requests,
725
827
  undefined,
726
828
  options
727
829
  );
830
+
831
+ const awaitTransactionPropagation =
832
+ !options ||
833
+ options.awaitTransactionPropagation === undefined ||
834
+ options.awaitTransactionPropagation;
835
+
836
+ const txId = await this.submitTransaction(
837
+ encodedTransaction,
838
+ awaitTransactionPropagation
839
+ );
840
+
728
841
  let resp = new SendResponse({});
729
- resp.txId = result;
842
+ resp.txId = txId;
730
843
  const queryBalance =
731
844
  !options || options.queryBalance === undefined || options.queryBalance;
732
845
  if (queryBalance) {
@@ -736,6 +849,14 @@ export class Wallet extends BaseWallet {
736
849
  return resp;
737
850
  }
738
851
 
852
+ /**
853
+ * sendMax Send all available funds to a destination cash address
854
+ *
855
+ * @param {string} cashaddr destination cash address
856
+ * @param {SendRequestOptionsI} options Options of the send requests
857
+ *
858
+ * @returns (depending on the options parameter) the transaction id, new address balance and a link to the transaction on the blockchain explorer
859
+ */
739
860
  public async sendMax(
740
861
  cashaddr: string,
741
862
  options?: SendRequestOptionsI
@@ -752,7 +873,18 @@ export class Wallet extends BaseWallet {
752
873
  };
753
874
  }
754
875
 
755
- private async sendMaxRaw(cashaddr: string, options?: SendRequestOptionsI) {
876
+ /**
877
+ * sendMaxRaw (internal) Send all available funds to a destination cash address
878
+ *
879
+ * @param {string} cashaddr destination cash address
880
+ * @param {SendRequestOptionsI} options Options of the send requests
881
+ *
882
+ * @returns the transaction id sent to the network
883
+ */
884
+ private async sendMaxRaw(
885
+ cashaddr: string,
886
+ options?: SendRequestOptionsI
887
+ ): Promise<string> {
756
888
  let maxSpendableAmount = await this.getMaxAmountToSend({
757
889
  outputCount: 1,
758
890
  options: options,
@@ -765,7 +897,129 @@ export class Wallet extends BaseWallet {
765
897
  value: maxSpendableAmount.sat,
766
898
  unit: "sat",
767
899
  });
768
- return await this._processSendRequests([sendRequest], true, options);
900
+
901
+ const encodedTransaction = await this.encodeTransaction(
902
+ [sendRequest],
903
+ true,
904
+ options
905
+ );
906
+ const awaitTransactionPropagation =
907
+ !options ||
908
+ options.awaitTransactionPropagation === undefined ||
909
+ options.awaitTransactionPropagation;
910
+
911
+ const txId = await this.submitTransaction(
912
+ encodedTransaction,
913
+ awaitTransactionPropagation
914
+ );
915
+
916
+ return txId;
917
+ }
918
+
919
+ /**
920
+ * encodeTransaction given a list of sendRequests, options and estimate fees.
921
+ * @param {SendRequest[]} sendRequests SendRequests
922
+ * @param {boolean} discardChange=false
923
+ * @param {SendRequestOptionsI} options Options of the send requests
924
+ */
925
+ public async encodeTransaction(
926
+ requests:
927
+ | SendRequest
928
+ | OpReturnData
929
+ | Array<SendRequest | OpReturnData>
930
+ | SendRequestArray[],
931
+ discardChange: boolean = false,
932
+ options?: SendRequestOptionsI
933
+ ) {
934
+ let sendRequests = asSendRequestObject(requests);
935
+
936
+ if (!this.privateKey) {
937
+ throw new Error(
938
+ `Wallet ${this.name} is missing either a network or private key`
939
+ );
940
+ }
941
+ if (!this.cashaddr) {
942
+ throw Error("attempted to send without a cashaddr");
943
+ }
944
+
945
+ if (options && options.slpAware) {
946
+ this._slpAware = true;
947
+ }
948
+
949
+ if (options && options.slpSemiAware) {
950
+ this._slpSemiAware = true;
951
+ }
952
+
953
+ // get inputs from options or query all inputs
954
+ let utxos: UtxoI[];
955
+ if (options && options.utxoIds) {
956
+ utxos = options.utxoIds.map((utxoId) =>
957
+ UtxoItem.fromId(utxoId).asElectrum()
958
+ );
959
+ } else {
960
+ utxos = await this.getAddressUtxos(this.cashaddr);
961
+ }
962
+
963
+ const bestHeight = await this.provider!.getBlockHeight()!;
964
+ const spendAmount = await sumSendRequestAmounts(sendRequests);
965
+
966
+ if (utxos.length === 0) {
967
+ throw Error("There were no Unspent Outputs");
968
+ }
969
+ if (typeof spendAmount !== "bigint") {
970
+ throw Error("Couldn't get spend amount when building transaction");
971
+ }
972
+
973
+ const relayFeePerByteInSatoshi = await getRelayFeeCache(this.provider!);
974
+ const feeEstimate = await getFeeAmount({
975
+ utxos: utxos,
976
+ sendRequests: sendRequests,
977
+ privateKey: this.privateKey,
978
+ relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
979
+ slpOutputs: [],
980
+ });
981
+
982
+ const fundingUtxos = await getSuitableUtxos(
983
+ utxos,
984
+ BigInt(spendAmount) + BigInt(feeEstimate),
985
+ bestHeight
986
+ );
987
+ if (fundingUtxos.length === 0) {
988
+ throw Error(
989
+ "The available inputs couldn't satisfy the request with fees"
990
+ );
991
+ }
992
+ const fee = await getFeeAmount({
993
+ utxos: fundingUtxos,
994
+ sendRequests: sendRequests,
995
+ privateKey: this.privateKey,
996
+ relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
997
+ slpOutputs: [],
998
+ });
999
+ const encodedTransaction = await buildEncodedTransaction(
1000
+ fundingUtxos,
1001
+ sendRequests,
1002
+ this.privateKey,
1003
+ fee,
1004
+ discardChange
1005
+ );
1006
+
1007
+ return encodedTransaction;
1008
+ }
1009
+
1010
+ // Submit a raw transaction
1011
+ public async submitTransaction(
1012
+ transaction: Uint8Array,
1013
+ awaitPropagation: boolean = true
1014
+ ): Promise<string> {
1015
+ if (!this.provider) {
1016
+ throw Error("Wallet network provider was not initialized");
1017
+ }
1018
+ let rawTransaction = binToHex(transaction);
1019
+ return await this.provider.sendRawTransaction(
1020
+ rawTransaction,
1021
+ awaitPropagation
1022
+ );
769
1023
  }
770
1024
 
771
1025
  // gets transaction history of this wallet
@@ -776,11 +1030,16 @@ export class Wallet extends BaseWallet {
776
1030
  // gets last transaction of this wallet
777
1031
  public async getLastTransaction(
778
1032
  confirmedOnly: boolean = false
779
- ): Promise<ElectrumRawTransaction> {
1033
+ ): Promise<ElectrumRawTransaction | null> {
780
1034
  let history: TxI[] = await this.getHistory();
781
1035
  if (confirmedOnly) {
782
1036
  history = history.filter((val) => val.height > 0);
783
1037
  }
1038
+
1039
+ if (!history.length) {
1040
+ return null;
1041
+ }
1042
+
784
1043
  const [lastTx] = history.slice(-1);
785
1044
  return this.provider!.getRawTransactionObject(lastTx.tx_hash);
786
1045
  }
@@ -901,111 +1160,6 @@ export class Wallet extends BaseWallet {
901
1160
  this.publicKeyHash = derivePublicKeyHash(this.cashaddr!);
902
1161
  return this;
903
1162
  }
904
-
905
- /**
906
- * _processSendRequests given a list of sendRequests, estimate fees, build the transaction and submit it.
907
- * This function is an internal wrapper and may change.
908
- * @param {SendRequest[]} sendRequests SendRequests
909
- * @param {} discardChange=false
910
- * @param {SendRequestOptionsI} options Options of the send requests
911
- */
912
- private async _processSendRequests(
913
- sendRequests: Array<SendRequest | OpReturnData>,
914
- discardChange = false,
915
- options?: SendRequestOptionsI
916
- ) {
917
- if (!this.privateKey) {
918
- throw new Error(
919
- `Wallet ${this.name} is missing either a network or private key`
920
- );
921
- }
922
- if (!this.cashaddr) {
923
- throw Error("attempted to send without a cashaddr");
924
- }
925
-
926
- if (options && options.slpAware) {
927
- this._slpAware = true;
928
- }
929
-
930
- // get inputs from options or query all inputs
931
- let utxos: UtxoI[];
932
- if (options && options.utxoIds) {
933
- utxos = options.utxoIds.map((utxoId) =>
934
- UtxoItem.fromId(utxoId).asElectrum()
935
- );
936
- } else {
937
- utxos = await this.getAddressUtxos(this.cashaddr);
938
- }
939
-
940
- const bestHeight = await this.provider!.getBlockHeight()!;
941
- const spendAmount = await sumSendRequestAmounts(sendRequests);
942
-
943
- if (utxos.length === 0) {
944
- throw Error("There were no Unspent Outputs");
945
- }
946
- if (typeof spendAmount !== "bigint") {
947
- throw Error("Couldn't get spend amount when building transaction");
948
- }
949
-
950
- const relayFeePerByteInSatoshi = await getRelayFeeCache(this.provider!);
951
- const feeEstimate = await getFeeAmount({
952
- utxos: utxos,
953
- sendRequests: sendRequests,
954
- privateKey: this.privateKey,
955
- relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
956
- slpOutputs: [],
957
- });
958
-
959
- const fundingUtxos = await getSuitableUtxos(
960
- utxos,
961
- BigInt(spendAmount) + BigInt(feeEstimate),
962
- bestHeight
963
- );
964
- if (fundingUtxos.length === 0) {
965
- throw Error(
966
- "The available inputs couldn't satisfy the request with fees"
967
- );
968
- }
969
- const fee = await getFeeAmount({
970
- utxos: fundingUtxos,
971
- sendRequests: sendRequests,
972
- privateKey: this.privateKey,
973
- relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
974
- slpOutputs: [],
975
- });
976
- const encodedTransaction = await buildEncodedTransaction(
977
- fundingUtxos,
978
- sendRequests,
979
- this.privateKey,
980
- fee,
981
- discardChange
982
- );
983
-
984
- const awaitTransactionPropagation =
985
- !options ||
986
- options.awaitTransactionPropagation === undefined ||
987
- options.awaitTransactionPropagation;
988
-
989
- return await this._submitTransaction(
990
- encodedTransaction,
991
- awaitTransactionPropagation
992
- );
993
- }
994
-
995
- // Submit a raw transaction
996
- private async _submitTransaction(
997
- transaction: Uint8Array,
998
- awaitPropagation: boolean = true
999
- ): Promise<string> {
1000
- if (!this.provider) {
1001
- throw Error("Wallet network provider was not initialized");
1002
- }
1003
- let rawTransaction = binToHex(transaction);
1004
- return await this.provider.sendRawTransaction(
1005
- rawTransaction,
1006
- awaitPropagation
1007
- );
1008
- }
1009
1163
  //#endregion Private implementation details
1010
1164
 
1011
1165
  //#region Signing
@@ -66,6 +66,8 @@ describe(`Named Wallets`, () => {
66
66
  isTestnet: true,
67
67
  name: "",
68
68
  network: "testnet",
69
+ parentDerivationPath: undefined,
70
+ parentXPubKey: undefined,
69
71
  privateKey: undefined,
70
72
  privateKeyWif: undefined,
71
73
  publicKey: undefined,
@@ -23,6 +23,7 @@ export interface WalletResponseI {
23
23
  privkey?: string;
24
24
  seed?: string;
25
25
  derivationPath?: string;
26
+ parentDerivationPath?: string;
26
27
  }
27
28
 
28
29
  export interface WalletInfoI {
@@ -32,6 +33,8 @@ export interface WalletInfoI {
32
33
  network: NetworkEnum;
33
34
  seed?: string;
34
35
  derivationPath?: string;
36
+ parentDerivationPath?: string;
37
+ parentXPubKey?: string;
35
38
  publicKey?: string;
36
39
  publicKeyHash?: string;
37
40
  privateKey?: string;
@@ -43,7 +46,8 @@ export interface WalletInfoI {
43
46
  export interface SendRequestOptionsI {
44
47
  utxoIds?: string[];
45
48
  changeAddress?: string;
46
- slpAware?: boolean;
49
+ slpAware?: boolean; // a flag which activates utxo checking against an external slp indexer
50
+ slpSemiAware?: boolean; // a flag which requires an utxo to have more than 546 sats to be spendable and counted in the balance
47
51
  queryBalance?: boolean;
48
52
  awaitTransactionPropagation?: boolean;
49
53
  }
@@ -51,6 +55,7 @@ export interface SendRequestOptionsI {
51
55
  export interface MnemonicI {
52
56
  seed: string;
53
57
  derivationPath: string;
58
+ parentDerivationPath: string;
54
59
  }
55
60
 
56
61
  export interface WalletI {
@@ -159,3 +159,25 @@ export class SendResponse {
159
159
  this.explorerUrl = explorerUrl;
160
160
  }
161
161
  }
162
+
163
+ export class XPubKey {
164
+ path: string;
165
+ xPubKey: string;
166
+
167
+ constructor({ path, xPubKey }: { path: string; xPubKey: string }) {
168
+ this.path = path;
169
+ this.xPubKey = xPubKey;
170
+ }
171
+
172
+ public async ready() {
173
+ await this.xPubKey;
174
+ return this.asObject();
175
+ }
176
+
177
+ public asObject() {
178
+ return {
179
+ path: this.path,
180
+ xPubKey: this.xPubKey,
181
+ };
182
+ }
183
+ }