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.
- package/dist/index.html +1 -1
- package/dist/main/wallet/Wif.d.ts +27 -10
- package/dist/main/wallet/Wif.js +99 -73
- package/dist/main/wallet/Wif.js.map +1 -1
- package/dist/mainnet-0.4.32.js +2 -0
- package/dist/{mainnet-0.4.31.js.LICENSE.txt → mainnet-0.4.32.js.LICENSE.txt} +0 -0
- package/dist/module/wallet/Wif.d.ts +27 -10
- package/dist/module/wallet/Wif.js +99 -73
- package/dist/module/wallet/Wif.js.map +1 -1
- package/dist/tsconfig.browser.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/wallet/Wif.test.ts +18 -0
- package/src/wallet/Wif.ts +165 -116
- package/dist/mainnet-0.4.31.js +0 -2
package/src/wallet/Wif.test.ts
CHANGED
|
@@ -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
|
|
824
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|