mainnet-js 0.4.29 → 0.4.33
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/constant.d.ts +1 -0
- package/dist/main/constant.js +17 -1
- package/dist/main/constant.js.map +1 -1
- package/dist/main/util/getAddrsByXpubKey.d.ts +22 -0
- package/dist/main/util/getAddrsByXpubKey.js +79 -0
- package/dist/main/util/getAddrsByXpubKey.js.map +1 -0
- package/dist/main/util/getXPubKey.d.ts +1 -0
- package/dist/main/util/getXPubKey.js +26 -0
- package/dist/main/util/getXPubKey.js.map +1 -0
- package/dist/main/util/index.d.ts +5 -3
- package/dist/main/util/index.js +13 -6
- package/dist/main/util/index.js.map +1 -1
- package/dist/main/wallet/Base.d.ts +3 -1
- package/dist/main/wallet/Base.js +3 -1
- package/dist/main/wallet/Base.js.map +1 -1
- package/dist/main/wallet/Wif.d.ts +31 -10
- package/dist/main/wallet/Wif.js +173 -78
- package/dist/main/wallet/Wif.js.map +1 -1
- package/dist/main/wallet/interface.d.ts +4 -0
- package/dist/main/wallet/model.d.ts +16 -0
- package/dist/main/wallet/model.js +18 -1
- package/dist/main/wallet/model.js.map +1 -1
- package/dist/mainnet-0.4.33.js +2 -0
- package/dist/{mainnet-0.4.29.js.LICENSE.txt → mainnet-0.4.33.js.LICENSE.txt} +0 -0
- package/dist/module/constant.d.ts +1 -0
- package/dist/module/constant.js +16 -0
- package/dist/module/constant.js.map +1 -1
- package/dist/module/util/getAddrsByXpubKey.d.ts +22 -0
- package/dist/module/util/getAddrsByXpubKey.js +71 -0
- package/dist/module/util/getAddrsByXpubKey.js.map +1 -0
- package/dist/module/util/getXPubKey.d.ts +1 -0
- package/dist/module/util/getXPubKey.js +22 -0
- package/dist/module/util/getXPubKey.js.map +1 -0
- package/dist/module/util/index.d.ts +5 -3
- package/dist/module/util/index.js +5 -3
- package/dist/module/util/index.js.map +1 -1
- package/dist/module/wallet/Base.d.ts +3 -1
- package/dist/module/wallet/Base.js +3 -1
- package/dist/module/wallet/Base.js.map +1 -1
- package/dist/module/wallet/Wif.d.ts +31 -10
- package/dist/module/wallet/Wif.js +177 -82
- package/dist/module/wallet/Wif.js.map +1 -1
- package/dist/module/wallet/interface.d.ts +4 -0
- package/dist/module/wallet/model.d.ts +16 -0
- package/dist/module/wallet/model.js +16 -0
- package/dist/module/wallet/model.js.map +1 -1
- package/dist/tsconfig.browser.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/constant.ts +17 -0
- package/src/util/getAddrsByXpubKey.test.ts +115 -0
- package/src/util/getAddrsByXpubKey.ts +98 -0
- package/src/util/getXPubKey.ts +36 -0
- package/src/util/index.ts +10 -3
- package/src/wallet/Base.ts +4 -1
- package/src/wallet/Wif.test.ts +191 -2
- package/src/wallet/Wif.ts +267 -133
- package/src/wallet/createWallet.test.ts +2 -0
- package/src/wallet/interface.ts +4 -0
- package/src/wallet/model.ts +22 -0
- package/dist/mainnet-0.4.29.js +0 -2
package/src/wallet/Wif.ts
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
//#region Imports
|
|
2
2
|
// Stable
|
|
3
|
-
import {
|
|
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,7 +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";
|
|
95
|
-
import {
|
|
103
|
+
import { getXPubKey } from "../util/getXPubKey";
|
|
104
|
+
import { DERIVATION_PATHS, DUST_UTXO_THRESHOLD } from "../constant";
|
|
96
105
|
|
|
97
106
|
//#endregion Imports
|
|
98
107
|
|
|
@@ -105,6 +114,8 @@ const sha256Promise = instantiateSha256();
|
|
|
105
114
|
export class Wallet extends BaseWallet {
|
|
106
115
|
cashaddr?: string;
|
|
107
116
|
derivationPath: string = "m/44'/0'/0'/0/0";
|
|
117
|
+
parentDerivationPath: string = "m/44'/0'/0'";
|
|
118
|
+
parentXPubKey?: string;
|
|
108
119
|
privateKey?: Uint8Array;
|
|
109
120
|
publicKeyCompressed?: Uint8Array;
|
|
110
121
|
privateKeyWif?: string;
|
|
@@ -169,8 +180,8 @@ export class Wallet extends BaseWallet {
|
|
|
169
180
|
*/
|
|
170
181
|
public explorerUrl(txId: string) {
|
|
171
182
|
const explorerUrlMap = {
|
|
172
|
-
mainnet: "https://
|
|
173
|
-
testnet: "https://
|
|
183
|
+
mainnet: "https://blockchair.com/bitcoin-cash/transaction/",
|
|
184
|
+
testnet: "https://www.blockchain.com/bch-testnet/tx/",
|
|
174
185
|
regtest: "",
|
|
175
186
|
};
|
|
176
187
|
|
|
@@ -186,6 +197,10 @@ export class Wallet extends BaseWallet {
|
|
|
186
197
|
network: this.network as any,
|
|
187
198
|
seed: this.mnemonic ? this.getSeed().seed : undefined,
|
|
188
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,
|
|
189
204
|
publicKey: this.publicKey ? binToHex(this.publicKey!) : undefined,
|
|
190
205
|
publicKeyHash: binToHex(this.publicKeyHash!),
|
|
191
206
|
privateKey: this.privateKey ? binToHex(this.privateKey!) : undefined,
|
|
@@ -319,19 +334,25 @@ export class Wallet extends BaseWallet {
|
|
|
319
334
|
}
|
|
320
335
|
|
|
321
336
|
private async _generateMnemonic() {
|
|
322
|
-
const crypto = await instantiateBIP32Crypto();
|
|
323
337
|
this.mnemonic = generateMnemonic();
|
|
324
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();
|
|
325
347
|
let hdNode = deriveHdPrivateNodeFromSeed(crypto, seed);
|
|
326
348
|
if (!hdNode.valid) {
|
|
327
349
|
throw Error("Invalid private key derived from mnemonic seed");
|
|
328
350
|
}
|
|
329
351
|
|
|
330
|
-
let zerothChild = deriveHdPath(
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
) as HdPrivateNodeValid;
|
|
352
|
+
let zerothChild = deriveHdPath(crypto, hdNode, this.derivationPath);
|
|
353
|
+
if (typeof zerothChild === "string") {
|
|
354
|
+
throw Error(zerothChild);
|
|
355
|
+
}
|
|
335
356
|
this.privateKey = zerothChild.privateKey;
|
|
336
357
|
|
|
337
358
|
this.walletType = WalletTypeEnum.Seed;
|
|
@@ -353,6 +374,18 @@ export class Wallet extends BaseWallet {
|
|
|
353
374
|
return super.fromId(walletId);
|
|
354
375
|
};
|
|
355
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
|
+
}
|
|
356
389
|
// Initialize wallet from a mnemonic phrase
|
|
357
390
|
protected async fromSeed(
|
|
358
391
|
mnemonic: string,
|
|
@@ -369,20 +402,73 @@ export class Wallet extends BaseWallet {
|
|
|
369
402
|
}
|
|
370
403
|
if (derivationPath) {
|
|
371
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
|
+
}
|
|
372
411
|
}
|
|
373
412
|
|
|
374
|
-
let zerothChild = deriveHdPath(
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
) as HdPrivateNodeValid;
|
|
413
|
+
let zerothChild = deriveHdPath(crypto, hdNode, this.derivationPath);
|
|
414
|
+
if (typeof zerothChild === "string") {
|
|
415
|
+
throw Error(zerothChild);
|
|
416
|
+
}
|
|
379
417
|
this.privateKey = zerothChild.privateKey;
|
|
380
418
|
|
|
419
|
+
let network = this.isTestnet ? "testnet" : "mainnet";
|
|
420
|
+
this.parentXPubKey = await getXPubKey(
|
|
421
|
+
seed,
|
|
422
|
+
this.parentDerivationPath,
|
|
423
|
+
network
|
|
424
|
+
);
|
|
425
|
+
|
|
381
426
|
this.walletType = WalletTypeEnum.Seed;
|
|
382
427
|
await this.deriveInfo();
|
|
383
428
|
return this;
|
|
384
429
|
}
|
|
385
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
|
+
|
|
386
472
|
// Initialize a watch only wallet from a cash addr
|
|
387
473
|
protected async watchOnly(address: string): Promise<this> {
|
|
388
474
|
this.walletType = WalletTypeEnum.Watch;
|
|
@@ -417,8 +503,7 @@ export class Wallet extends BaseWallet {
|
|
|
417
503
|
const sha256 = await sha256Promise;
|
|
418
504
|
let wifResult = decodePrivateKeyWif(sha256, secret);
|
|
419
505
|
|
|
420
|
-
|
|
421
|
-
if (hasError) {
|
|
506
|
+
if (typeof wifResult === "string") {
|
|
422
507
|
throw Error(wifResult as string);
|
|
423
508
|
}
|
|
424
509
|
let resultData: PrivateKeyI = wifResult as PrivateKeyI;
|
|
@@ -723,6 +808,8 @@ export class Wallet extends BaseWallet {
|
|
|
723
808
|
|
|
724
809
|
/**
|
|
725
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
|
|
726
813
|
*
|
|
727
814
|
* This is a first class function with REST analog, maintainers should strive to keep backward-compatibility
|
|
728
815
|
*
|
|
@@ -735,14 +822,24 @@ export class Wallet extends BaseWallet {
|
|
|
735
822
|
| SendRequestArray[],
|
|
736
823
|
options?: SendRequestOptionsI
|
|
737
824
|
): Promise<SendResponse> {
|
|
738
|
-
let
|
|
739
|
-
|
|
740
|
-
sendRequests,
|
|
825
|
+
let encodedTransaction = await this.encodeTransaction(
|
|
826
|
+
requests,
|
|
741
827
|
undefined,
|
|
742
828
|
options
|
|
743
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
|
+
|
|
744
841
|
let resp = new SendResponse({});
|
|
745
|
-
resp.txId =
|
|
842
|
+
resp.txId = txId;
|
|
746
843
|
const queryBalance =
|
|
747
844
|
!options || options.queryBalance === undefined || options.queryBalance;
|
|
748
845
|
if (queryBalance) {
|
|
@@ -752,6 +849,14 @@ export class Wallet extends BaseWallet {
|
|
|
752
849
|
return resp;
|
|
753
850
|
}
|
|
754
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
|
+
*/
|
|
755
860
|
public async sendMax(
|
|
756
861
|
cashaddr: string,
|
|
757
862
|
options?: SendRequestOptionsI
|
|
@@ -768,7 +873,18 @@ export class Wallet extends BaseWallet {
|
|
|
768
873
|
};
|
|
769
874
|
}
|
|
770
875
|
|
|
771
|
-
|
|
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> {
|
|
772
888
|
let maxSpendableAmount = await this.getMaxAmountToSend({
|
|
773
889
|
outputCount: 1,
|
|
774
890
|
options: options,
|
|
@@ -781,7 +897,129 @@ export class Wallet extends BaseWallet {
|
|
|
781
897
|
value: maxSpendableAmount.sat,
|
|
782
898
|
unit: "sat",
|
|
783
899
|
});
|
|
784
|
-
|
|
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
|
+
);
|
|
785
1023
|
}
|
|
786
1024
|
|
|
787
1025
|
// gets transaction history of this wallet
|
|
@@ -792,11 +1030,16 @@ export class Wallet extends BaseWallet {
|
|
|
792
1030
|
// gets last transaction of this wallet
|
|
793
1031
|
public async getLastTransaction(
|
|
794
1032
|
confirmedOnly: boolean = false
|
|
795
|
-
): Promise<ElectrumRawTransaction> {
|
|
1033
|
+
): Promise<ElectrumRawTransaction | null> {
|
|
796
1034
|
let history: TxI[] = await this.getHistory();
|
|
797
1035
|
if (confirmedOnly) {
|
|
798
1036
|
history = history.filter((val) => val.height > 0);
|
|
799
1037
|
}
|
|
1038
|
+
|
|
1039
|
+
if (!history.length) {
|
|
1040
|
+
return null;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
800
1043
|
const [lastTx] = history.slice(-1);
|
|
801
1044
|
return this.provider!.getRawTransactionObject(lastTx.tx_hash);
|
|
802
1045
|
}
|
|
@@ -917,115 +1160,6 @@ export class Wallet extends BaseWallet {
|
|
|
917
1160
|
this.publicKeyHash = derivePublicKeyHash(this.cashaddr!);
|
|
918
1161
|
return this;
|
|
919
1162
|
}
|
|
920
|
-
|
|
921
|
-
/**
|
|
922
|
-
* _processSendRequests given a list of sendRequests, estimate fees, build the transaction and submit it.
|
|
923
|
-
* This function is an internal wrapper and may change.
|
|
924
|
-
* @param {SendRequest[]} sendRequests SendRequests
|
|
925
|
-
* @param {} discardChange=false
|
|
926
|
-
* @param {SendRequestOptionsI} options Options of the send requests
|
|
927
|
-
*/
|
|
928
|
-
private async _processSendRequests(
|
|
929
|
-
sendRequests: Array<SendRequest | OpReturnData>,
|
|
930
|
-
discardChange = false,
|
|
931
|
-
options?: SendRequestOptionsI
|
|
932
|
-
) {
|
|
933
|
-
if (!this.privateKey) {
|
|
934
|
-
throw new Error(
|
|
935
|
-
`Wallet ${this.name} is missing either a network or private key`
|
|
936
|
-
);
|
|
937
|
-
}
|
|
938
|
-
if (!this.cashaddr) {
|
|
939
|
-
throw Error("attempted to send without a cashaddr");
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
if (options && options.slpAware) {
|
|
943
|
-
this._slpAware = true;
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
if (options && options.slpSemiAware) {
|
|
947
|
-
this._slpSemiAware = true;
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
// get inputs from options or query all inputs
|
|
951
|
-
let utxos: UtxoI[];
|
|
952
|
-
if (options && options.utxoIds) {
|
|
953
|
-
utxos = options.utxoIds.map((utxoId) =>
|
|
954
|
-
UtxoItem.fromId(utxoId).asElectrum()
|
|
955
|
-
);
|
|
956
|
-
} else {
|
|
957
|
-
utxos = await this.getAddressUtxos(this.cashaddr);
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
const bestHeight = await this.provider!.getBlockHeight()!;
|
|
961
|
-
const spendAmount = await sumSendRequestAmounts(sendRequests);
|
|
962
|
-
|
|
963
|
-
if (utxos.length === 0) {
|
|
964
|
-
throw Error("There were no Unspent Outputs");
|
|
965
|
-
}
|
|
966
|
-
if (typeof spendAmount !== "bigint") {
|
|
967
|
-
throw Error("Couldn't get spend amount when building transaction");
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
const relayFeePerByteInSatoshi = await getRelayFeeCache(this.provider!);
|
|
971
|
-
const feeEstimate = await getFeeAmount({
|
|
972
|
-
utxos: utxos,
|
|
973
|
-
sendRequests: sendRequests,
|
|
974
|
-
privateKey: this.privateKey,
|
|
975
|
-
relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
|
|
976
|
-
slpOutputs: [],
|
|
977
|
-
});
|
|
978
|
-
|
|
979
|
-
const fundingUtxos = await getSuitableUtxos(
|
|
980
|
-
utxos,
|
|
981
|
-
BigInt(spendAmount) + BigInt(feeEstimate),
|
|
982
|
-
bestHeight
|
|
983
|
-
);
|
|
984
|
-
if (fundingUtxos.length === 0) {
|
|
985
|
-
throw Error(
|
|
986
|
-
"The available inputs couldn't satisfy the request with fees"
|
|
987
|
-
);
|
|
988
|
-
}
|
|
989
|
-
const fee = await getFeeAmount({
|
|
990
|
-
utxos: fundingUtxos,
|
|
991
|
-
sendRequests: sendRequests,
|
|
992
|
-
privateKey: this.privateKey,
|
|
993
|
-
relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
|
|
994
|
-
slpOutputs: [],
|
|
995
|
-
});
|
|
996
|
-
const encodedTransaction = await buildEncodedTransaction(
|
|
997
|
-
fundingUtxos,
|
|
998
|
-
sendRequests,
|
|
999
|
-
this.privateKey,
|
|
1000
|
-
fee,
|
|
1001
|
-
discardChange
|
|
1002
|
-
);
|
|
1003
|
-
|
|
1004
|
-
const awaitTransactionPropagation =
|
|
1005
|
-
!options ||
|
|
1006
|
-
options.awaitTransactionPropagation === undefined ||
|
|
1007
|
-
options.awaitTransactionPropagation;
|
|
1008
|
-
|
|
1009
|
-
return await this._submitTransaction(
|
|
1010
|
-
encodedTransaction,
|
|
1011
|
-
awaitTransactionPropagation
|
|
1012
|
-
);
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
// Submit a raw transaction
|
|
1016
|
-
private async _submitTransaction(
|
|
1017
|
-
transaction: Uint8Array,
|
|
1018
|
-
awaitPropagation: boolean = true
|
|
1019
|
-
): Promise<string> {
|
|
1020
|
-
if (!this.provider) {
|
|
1021
|
-
throw Error("Wallet network provider was not initialized");
|
|
1022
|
-
}
|
|
1023
|
-
let rawTransaction = binToHex(transaction);
|
|
1024
|
-
return await this.provider.sendRawTransaction(
|
|
1025
|
-
rawTransaction,
|
|
1026
|
-
awaitPropagation
|
|
1027
|
-
);
|
|
1028
|
-
}
|
|
1029
1163
|
//#endregion Private implementation details
|
|
1030
1164
|
|
|
1031
1165
|
//#region Signing
|
package/src/wallet/interface.ts
CHANGED
|
@@ -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;
|
|
@@ -52,6 +55,7 @@ export interface SendRequestOptionsI {
|
|
|
52
55
|
export interface MnemonicI {
|
|
53
56
|
seed: string;
|
|
54
57
|
derivationPath: string;
|
|
58
|
+
parentDerivationPath: string;
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
export interface WalletI {
|
package/src/wallet/model.ts
CHANGED
|
@@ -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
|
+
}
|