mainnet-js 0.4.31 → 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.
@@ -500,6 +500,24 @@ describe(`Watch only Wallets`, () => {
500
500
  expect(aliceBalance.sat).toBeGreaterThan(2000);
501
501
  }
502
502
  });
503
+
504
+ test("Should get last transaction", async () => {
505
+ const aliceWif = `wif:regtest:${process.env.PRIVATE_WIF!}`;
506
+ const aliceWallet = await RegTestWallet.fromId(aliceWif);
507
+ const bobWallet = await RegTestWallet.newRandom();
508
+
509
+ expect(await bobWallet.getLastTransaction()).toBeNull();
510
+
511
+ await aliceWallet.send([
512
+ {
513
+ cashaddr: bobWallet.cashaddr!,
514
+ value: 2000,
515
+ unit: "satoshis",
516
+ },
517
+ ]);
518
+
519
+ expect(await bobWallet.getLastTransaction()).not.toBeNull();
520
+ });
503
521
  });
504
522
  describe(`Wallet subscriptions`, () => {
505
523
  test("Should wait for transaction", async () => {
package/src/wallet/Wif.ts CHANGED
@@ -808,6 +808,8 @@ export class Wallet extends BaseWallet {
808
808
 
809
809
  /**
810
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
811
813
  *
812
814
  * This is a first class function with REST analog, maintainers should strive to keep backward-compatibility
813
815
  *
@@ -820,14 +822,24 @@ export class Wallet extends BaseWallet {
820
822
  | SendRequestArray[],
821
823
  options?: SendRequestOptionsI
822
824
  ): Promise<SendResponse> {
823
- let sendRequests = asSendRequestObject(requests);
824
- let result = await this._processSendRequests(
825
- sendRequests,
825
+ let encodedTransaction = await this.encodeTransaction(
826
+ requests,
826
827
  undefined,
827
828
  options
828
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
+
829
841
  let resp = new SendResponse({});
830
- resp.txId = result;
842
+ resp.txId = txId;
831
843
  const queryBalance =
832
844
  !options || options.queryBalance === undefined || options.queryBalance;
833
845
  if (queryBalance) {
@@ -837,6 +849,14 @@ export class Wallet extends BaseWallet {
837
849
  return resp;
838
850
  }
839
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
+ */
840
860
  public async sendMax(
841
861
  cashaddr: string,
842
862
  options?: SendRequestOptionsI
@@ -853,7 +873,18 @@ export class Wallet extends BaseWallet {
853
873
  };
854
874
  }
855
875
 
856
- 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> {
857
888
  let maxSpendableAmount = await this.getMaxAmountToSend({
858
889
  outputCount: 1,
859
890
  options: options,
@@ -866,7 +897,129 @@ export class Wallet extends BaseWallet {
866
897
  value: maxSpendableAmount.sat,
867
898
  unit: "sat",
868
899
  });
869
- 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
+ );
870
1023
  }
871
1024
 
872
1025
  // gets transaction history of this wallet
@@ -877,11 +1030,16 @@ export class Wallet extends BaseWallet {
877
1030
  // gets last transaction of this wallet
878
1031
  public async getLastTransaction(
879
1032
  confirmedOnly: boolean = false
880
- ): Promise<ElectrumRawTransaction> {
1033
+ ): Promise<ElectrumRawTransaction | null> {
881
1034
  let history: TxI[] = await this.getHistory();
882
1035
  if (confirmedOnly) {
883
1036
  history = history.filter((val) => val.height > 0);
884
1037
  }
1038
+
1039
+ if (!history.length) {
1040
+ return null;
1041
+ }
1042
+
885
1043
  const [lastTx] = history.slice(-1);
886
1044
  return this.provider!.getRawTransactionObject(lastTx.tx_hash);
887
1045
  }
@@ -1002,115 +1160,6 @@ export class Wallet extends BaseWallet {
1002
1160
  this.publicKeyHash = derivePublicKeyHash(this.cashaddr!);
1003
1161
  return this;
1004
1162
  }
1005
-
1006
- /**
1007
- * _processSendRequests given a list of sendRequests, estimate fees, build the transaction and submit it.
1008
- * This function is an internal wrapper and may change.
1009
- * @param {SendRequest[]} sendRequests SendRequests
1010
- * @param {} discardChange=false
1011
- * @param {SendRequestOptionsI} options Options of the send requests
1012
- */
1013
- private async _processSendRequests(
1014
- sendRequests: Array<SendRequest | OpReturnData>,
1015
- discardChange = false,
1016
- options?: SendRequestOptionsI
1017
- ) {
1018
- if (!this.privateKey) {
1019
- throw new Error(
1020
- `Wallet ${this.name} is missing either a network or private key`
1021
- );
1022
- }
1023
- if (!this.cashaddr) {
1024
- throw Error("attempted to send without a cashaddr");
1025
- }
1026
-
1027
- if (options && options.slpAware) {
1028
- this._slpAware = true;
1029
- }
1030
-
1031
- if (options && options.slpSemiAware) {
1032
- this._slpSemiAware = true;
1033
- }
1034
-
1035
- // get inputs from options or query all inputs
1036
- let utxos: UtxoI[];
1037
- if (options && options.utxoIds) {
1038
- utxos = options.utxoIds.map((utxoId) =>
1039
- UtxoItem.fromId(utxoId).asElectrum()
1040
- );
1041
- } else {
1042
- utxos = await this.getAddressUtxos(this.cashaddr);
1043
- }
1044
-
1045
- const bestHeight = await this.provider!.getBlockHeight()!;
1046
- const spendAmount = await sumSendRequestAmounts(sendRequests);
1047
-
1048
- if (utxos.length === 0) {
1049
- throw Error("There were no Unspent Outputs");
1050
- }
1051
- if (typeof spendAmount !== "bigint") {
1052
- throw Error("Couldn't get spend amount when building transaction");
1053
- }
1054
-
1055
- const relayFeePerByteInSatoshi = await getRelayFeeCache(this.provider!);
1056
- const feeEstimate = await getFeeAmount({
1057
- utxos: utxos,
1058
- sendRequests: sendRequests,
1059
- privateKey: this.privateKey,
1060
- relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
1061
- slpOutputs: [],
1062
- });
1063
-
1064
- const fundingUtxos = await getSuitableUtxos(
1065
- utxos,
1066
- BigInt(spendAmount) + BigInt(feeEstimate),
1067
- bestHeight
1068
- );
1069
- if (fundingUtxos.length === 0) {
1070
- throw Error(
1071
- "The available inputs couldn't satisfy the request with fees"
1072
- );
1073
- }
1074
- const fee = await getFeeAmount({
1075
- utxos: fundingUtxos,
1076
- sendRequests: sendRequests,
1077
- privateKey: this.privateKey,
1078
- relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
1079
- slpOutputs: [],
1080
- });
1081
- const encodedTransaction = await buildEncodedTransaction(
1082
- fundingUtxos,
1083
- sendRequests,
1084
- this.privateKey,
1085
- fee,
1086
- discardChange
1087
- );
1088
-
1089
- const awaitTransactionPropagation =
1090
- !options ||
1091
- options.awaitTransactionPropagation === undefined ||
1092
- options.awaitTransactionPropagation;
1093
-
1094
- return await this._submitTransaction(
1095
- encodedTransaction,
1096
- awaitTransactionPropagation
1097
- );
1098
- }
1099
-
1100
- // Submit a raw transaction
1101
- private async _submitTransaction(
1102
- transaction: Uint8Array,
1103
- awaitPropagation: boolean = true
1104
- ): Promise<string> {
1105
- if (!this.provider) {
1106
- throw Error("Wallet network provider was not initialized");
1107
- }
1108
- let rawTransaction = binToHex(transaction);
1109
- return await this.provider.sendRawTransaction(
1110
- rawTransaction,
1111
- awaitPropagation
1112
- );
1113
- }
1114
1163
  //#endregion Private implementation details
1115
1164
 
1116
1165
  //#region Signing