mainnet-js 2.6.7 → 2.7.1

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 (98) hide show
  1. package/dist/index.html +1 -1
  2. package/dist/{mainnet-2.6.7.js → mainnet-2.7.1.js} +446 -156
  3. package/dist/module/cli.js +0 -4
  4. package/dist/module/cli.js.map +1 -1
  5. package/dist/module/index.d.ts +1 -3
  6. package/dist/module/index.d.ts.map +1 -1
  7. package/dist/module/index.js +1 -3
  8. package/dist/module/index.js.map +1 -1
  9. package/dist/module/network/ElectrumNetworkProvider.d.ts +21 -27
  10. package/dist/module/network/ElectrumNetworkProvider.d.ts.map +1 -1
  11. package/dist/module/network/ElectrumNetworkProvider.js +92 -102
  12. package/dist/module/network/ElectrumNetworkProvider.js.map +1 -1
  13. package/dist/module/network/NetworkProvider.d.ts +9 -22
  14. package/dist/module/network/NetworkProvider.d.ts.map +1 -1
  15. package/dist/module/network/constant.d.ts +0 -21
  16. package/dist/module/network/constant.d.ts.map +1 -1
  17. package/dist/module/network/constant.js +0 -21
  18. package/dist/module/network/constant.js.map +1 -1
  19. package/dist/module/network/default.d.ts +2 -2
  20. package/dist/module/network/default.d.ts.map +1 -1
  21. package/dist/module/network/default.js +15 -45
  22. package/dist/module/network/default.js.map +1 -1
  23. package/dist/module/network/interface.d.ts +2 -8
  24. package/dist/module/network/interface.d.ts.map +1 -1
  25. package/dist/module/network/util.d.ts.map +1 -1
  26. package/dist/module/network/util.js +4 -5
  27. package/dist/module/network/util.js.map +1 -1
  28. package/dist/module/rate/ExchangeRate.js +2 -1
  29. package/dist/module/rate/ExchangeRate.js.map +1 -1
  30. package/dist/module/transaction/Wif.d.ts.map +1 -1
  31. package/dist/module/transaction/Wif.js +1 -1
  32. package/dist/module/transaction/Wif.js.map +1 -1
  33. package/dist/module/wallet/Base.d.ts +282 -88
  34. package/dist/module/wallet/Base.d.ts.map +1 -1
  35. package/dist/module/wallet/Base.js +1058 -215
  36. package/dist/module/wallet/Base.js.map +1 -1
  37. package/dist/module/wallet/Util.d.ts +7 -54
  38. package/dist/module/wallet/Util.d.ts.map +1 -1
  39. package/dist/module/wallet/Util.js +12 -79
  40. package/dist/module/wallet/Util.js.map +1 -1
  41. package/dist/module/wallet/Wif.d.ts +46 -251
  42. package/dist/module/wallet/Wif.d.ts.map +1 -1
  43. package/dist/module/wallet/Wif.js +126 -1026
  44. package/dist/module/wallet/Wif.js.map +1 -1
  45. package/dist/module/wallet/createWallet.d.ts +2 -1
  46. package/dist/module/wallet/createWallet.d.ts.map +1 -1
  47. package/dist/module/wallet/createWallet.js +2 -3
  48. package/dist/module/wallet/createWallet.js.map +1 -1
  49. package/dist/module/wallet/interface.d.ts +2 -4
  50. package/dist/module/wallet/interface.d.ts.map +1 -1
  51. package/dist/tsconfig.tsbuildinfo +1 -1
  52. package/package.json +6 -12
  53. package/src/cli.ts +0 -4
  54. package/src/index.ts +1 -5
  55. package/src/network/ElectrumNetworkProvider.ts +133 -188
  56. package/src/network/NetworkProvider.ts +9 -30
  57. package/src/network/Rpc.test.ts +14 -5
  58. package/src/network/constant.ts +0 -23
  59. package/src/network/default.ts +26 -66
  60. package/src/network/electrum.test.ts +2 -4
  61. package/src/network/interface.ts +2 -9
  62. package/src/network/util.ts +6 -7
  63. package/src/rate/ExchangeRate.test.ts +1 -1
  64. package/src/rate/ExchangeRate.ts +2 -1
  65. package/{polyfill/json.js → src/test/json.test.ts} +7 -1
  66. package/src/transaction/Wif.ts +2 -1
  67. package/src/wallet/Base.ts +1520 -273
  68. package/src/wallet/Cashtokens.test.headless.js +1 -1
  69. package/src/wallet/Cashtokens.test.ts +7 -8
  70. package/src/wallet/Util.ts +20 -102
  71. package/src/wallet/Wif.bip39.test.ts +3 -3
  72. package/src/wallet/Wif.test.ts +31 -25
  73. package/src/wallet/Wif.ts +174 -1493
  74. package/src/wallet/Wif.watchOnly.test.ts +5 -5
  75. package/src/wallet/createWallet.ts +11 -10
  76. package/src/wallet/interface.ts +3 -4
  77. package/webpack.config.cjs +4 -55
  78. package/dist/module/qr/Qr.d.ts +0 -9
  79. package/dist/module/qr/Qr.d.ts.map +0 -1
  80. package/dist/module/qr/Qr.js +0 -22
  81. package/dist/module/qr/Qr.js.map +0 -1
  82. package/dist/module/qr/interface.d.ts +0 -6
  83. package/dist/module/qr/interface.d.ts.map +0 -1
  84. package/dist/module/qr/interface.js +0 -2
  85. package/dist/module/qr/interface.js.map +0 -1
  86. package/dist/module/util/eventsource.d.ts +0 -3
  87. package/dist/module/util/eventsource.d.ts.map +0 -1
  88. package/dist/module/util/eventsource.js +0 -11
  89. package/dist/module/util/eventsource.js.map +0 -1
  90. package/polyfill/README.md +0 -1
  91. package/polyfill/eventsource.js +0 -6
  92. package/polyfill/support/types.js +0 -286
  93. package/polyfill/util.cjs +0 -249
  94. package/src/network/default.test.ts +0 -37
  95. package/src/qr/Qr.test.ts +0 -14
  96. package/src/qr/Qr.ts +0 -24
  97. package/src/qr/interface.ts +0 -5
  98. package/src/util/eventsource.ts +0 -12
@@ -1,94 +1,42 @@
1
1
  //#region Imports
2
- // Stable
3
- import { decodeCashAddress, encodeHdPublicKey, hexToBin, secp256k1, } from "@bitauth/libauth";
4
- // Unstable?
5
- import { binToHex, CashAddressNetworkPrefix, CashAddressType, deriveHdPublicNode, decodePrivateKeyWif, encodeCashAddress, encodePrivateKeyWif, deriveHdPrivateNodeFromSeed, deriveHdPath, generatePrivateKey, } from "@bitauth/libauth";
6
- import { mnemonicToSeedSync, generateMnemonic } from "@scure/bip39";
7
- import { NetworkType, prefixFromNetworkMap, UnitEnum } from "../enum.js";
8
- import { Network, NFTCapability } from "../interface.js";
9
- import { networkPrefixMap } from "../enum.js";
10
- import { BaseWallet } from "./Base.js";
11
- import { FeePaidByEnum, WalletTypeEnum } from "./enum.js";
12
- import { fromUtxoId, OpReturnData, SendRequest, SendResponse, TokenSendRequest, XPubKey, } from "./model.js";
13
- import { buildEncodedTransaction, getSuitableUtxos, getFeeAmount, signUnsignedTransaction, getFeeAmountSimple, } from "../transaction/Wif.js";
14
- import { asSendRequestObject } from "../util/asSendRequestObject.js";
15
- import { balanceFromSatoshi, balanceResponseFromSatoshi, } from "../util/balanceObjectFromSatoshi.js";
16
- import { checkWifNetwork } from "../util/checkWifNetwork.js";
17
- import { deriveCashaddr, deriveTokenaddr, toTokenaddr, } from "../util/deriveCashaddr.js";
18
- import { derivePrefix, derivePublicKeyHash, } from "../util/derivePublicKeyHash.js";
19
- import { checkForEmptySeed } from "../util/checkForEmptySeed.js";
20
- import { sanitizeUnit } from "../util/sanitizeUnit.js";
21
- import { sumTokenAmounts, sumUtxoValue } from "../util/sumUtxoValue.js";
22
- import { sumSendRequestAmounts } from "../util/sumSendRequestAmounts.js";
23
- import { getRelayFeeCache } from "../network/getRelayFeeCache.js";
24
- import { RegTestUtil, RegTestWatchUtil, RegTestWifUtil, TestNetUtil, TestNetWatchUtil, TestNetWifUtil, Util, WatchUtil, WifUtil, } from "./Util.js";
25
- import { getNetworkProvider } from "../network/index.js";
26
- import { generateRandomBytes } from "../util/randomBytes.js";
2
+ import { encodeHdPublicKey, secp256k1 } from "@bitauth/libauth";
3
+ import { binToHex, CashAddressNetworkPrefix, decodePrivateKeyWif, deriveHdPath, deriveHdPrivateNodeFromSeed, deriveHdPublicNode, encodePrivateKeyWif, generatePrivateKey, } from "@bitauth/libauth";
4
+ import { generateMnemonic, mnemonicToSeedSync } from "@scure/bip39";
5
+ import { NetworkType } from "../enum.js";
6
+ import { WalletTypeEnum } from "./enum.js";
7
+ import { XPubKey, } from "./model.js";
8
+ import { signUnsignedTransaction } from "../transaction/Wif.js";
9
+ import { DERIVATION_PATHS } from "../constant.js";
27
10
  import { SignedMessage } from "../message/index.js";
28
- import { amountInSatoshi } from "../util/amountInSatoshi.js";
11
+ import { checkForEmptySeed } from "../util/checkForEmptySeed.js";
12
+ import { checkWifNetwork } from "../util/checkWifNetwork.js";
13
+ import { deriveCashaddr, deriveTokenaddr } from "../util/deriveCashaddr.js";
14
+ import { derivePublicKeyHash } from "../util/derivePublicKeyHash.js";
29
15
  import { getXPubKey } from "../util/getXPubKey.js";
30
- import { DERIVATION_PATHS, DUST_UTXO_THRESHOLD } from "../constant.js";
31
- import { getAddressHistory } from "../history/electrumTransformer.js";
32
- import { BCMR } from "./Bcmr.js";
33
- import { qrAddress } from "../qr/Qr.js";
16
+ import { generateRandomBytes } from "../util/randomBytes.js";
34
17
  import { Config } from "../config.js";
35
- import { checkUtxos } from "../util/checkUtxos.js";
18
+ import { balanceResponseFromSatoshi, } from "../util/balanceObjectFromSatoshi.js";
19
+ import { BaseWallet } from "./Base.js";
36
20
  //#endregion Imports
37
- const placeholderPrivateKey = "0000000000000000000000000000000000000000000000000000000000000001";
38
21
  /**
39
22
  * Class to manage a bitcoin cash wallet.
40
23
  */
41
24
  export class Wallet extends BaseWallet {
42
- static { this.networkPrefix = CashAddressNetworkPrefix.mainnet; }
43
25
  static { this.signedMessage = new SignedMessage(); }
44
26
  //#region Accessors
45
- // interface to util functions. see Util.ts
46
- get util() {
47
- if (!this._util) {
48
- this._util = new Util(this);
27
+ // Get mnemonic and derivation path for wallet
28
+ getSeed() {
29
+ if (!this.mnemonic) {
30
+ throw Error("Wallet mnemonic seed phrase not set");
49
31
  }
50
- return this._util;
51
- }
52
- // interface to util util. see Util.Util
53
- static get util() {
54
- return Util;
55
- }
56
- slpSemiAware(value = true) {
57
- this._slpSemiAware = value;
58
- return this;
59
- }
60
- getNetworkProvider(network = Network.MAINNET) {
61
- return getNetworkProvider(network);
62
- }
63
- /**
64
- * getTokenDepositAddress - get a cashtoken aware wallet deposit address
65
- *
66
- * @returns The cashtoken aware deposit address as a string
67
- */
68
- getTokenDepositAddress() {
69
- return this.tokenaddr;
70
- }
71
- /**
72
- * getDepositQr - get an address qrcode, encoded for display on the web
73
- *
74
- * @returns The qrcode for the token aware address
75
- */
76
- getTokenDepositQr() {
77
- return qrAddress(this.getTokenDepositAddress());
78
- }
79
- /**
80
- * explorerUrl Web url to a transaction on a block explorer
81
- *
82
- * @param txId transaction Id
83
- * @returns Url string
84
- */
85
- explorerUrl(txId) {
86
- const explorerUrlMap = {
87
- mainnet: "https://blockchair.com/bitcoin-cash/transaction/",
88
- testnet: "https://www.blockchain.com/bch-testnet/tx/",
89
- regtest: "",
32
+ if (!this.derivationPath) {
33
+ throw Error("Wallet derivation path not set");
34
+ }
35
+ return {
36
+ seed: this.mnemonic,
37
+ derivationPath: this.derivationPath,
38
+ parentDerivationPath: this.parentDerivationPath,
90
39
  };
91
- return explorerUrlMap[this.network] + txId;
92
40
  }
93
41
  // Return wallet info
94
42
  getInfo() {
@@ -132,34 +80,61 @@ export class Wallet extends BaseWallet {
132
80
  throw Error("The compressed public key for this wallet is not known, perhaps the wallet was created to watch the *hash* of a public key? i.e. a cashaddress.");
133
81
  }
134
82
  }
135
- // returns the public key hash for an address
136
- getPublicKeyHash(hex = false) {
137
- if (this.publicKeyHash) {
138
- return hex ? binToHex(this.publicKeyHash) : this.publicKeyHash;
139
- }
140
- else {
141
- throw Error("The public key hash for this wallet is not known. If this wallet was created from the constructor directly, calling the deriveInfo() function may help. ");
142
- }
143
- }
144
83
  //#endregion
145
84
  //#region Constructors and Statics
146
85
  constructor(name = "", network = NetworkType.Mainnet, walletType = WalletTypeEnum.Seed) {
147
- super(name, network, walletType);
86
+ super(network);
148
87
  this.derivationPath = Config.DefaultParentDerivationPath + "/0/0";
149
88
  this.parentDerivationPath = Config.DefaultParentDerivationPath;
150
- this._slpSemiAware = false; // a flag which requires an utxo to have more than 546 sats to be spendable and counted in the balance
151
89
  this.fromId = async (walletId) => {
152
- const [walletType, networkGiven, arg1] = walletId.split(":");
153
- if (this.network != networkGiven) {
90
+ const [walletType, networkGiven, arg1, arg2] = walletId.split(":");
91
+ if (this.network !== networkGiven) {
154
92
  throw Error(`Network prefix ${networkGiven} to a ${this.network} wallet`);
155
93
  }
156
94
  // "wif:regtest:cNfsPtqN2bMRS7vH5qd8tR8GMvgXyL5BjnGAKgZ8DYEiCrCCQcP6"
157
- if (walletType === "wif") {
158
- return this.fromWIF(arg1);
95
+ switch (walletType) {
96
+ case "wif":
97
+ return this.fromWIF(arg1);
98
+ case "watch":
99
+ if (arg2) {
100
+ // watch:testnet:bchtest:qq1234567
101
+ return this.watchOnly(`${arg1}:${arg2}`);
102
+ }
103
+ // watch:testnet:qq1234567
104
+ return this.watchOnly(`${arg1}`);
105
+ case "named":
106
+ if (arg2) {
107
+ // named:testnet:wallet_1:my_database
108
+ return this.named(arg1, arg2);
109
+ }
110
+ else {
111
+ // named:testnet:wallet_1
112
+ return this.named(arg1);
113
+ }
114
+ case "seed":
115
+ if (arg2) {
116
+ // seed:testnet:table later ... stove kitten pluck:m/44'/0'/0'/0/0
117
+ return this.fromSeed(arg1, arg2);
118
+ }
119
+ // seed:testnet:table later ... stove kitten pluck
120
+ return this.fromSeed(arg1);
121
+ default:
122
+ throw Error(`Unknown wallet type '${walletType}'`);
159
123
  }
160
- return super.fromId(walletId);
161
124
  };
162
- this.networkPrefix = prefixFromNetworkMap[this.network];
125
+ this.name = name;
126
+ this.walletType = walletType;
127
+ }
128
+ //#region Statics
129
+ /**
130
+ * fromId - create a wallet from encoded walletId string
131
+ *
132
+ * @param walletId walletId options to steer the creation process
133
+ *
134
+ * @returns wallet instantiated accordingly to the walletId rules
135
+ */
136
+ static async fromId(walletId) {
137
+ return new this().fromId(walletId);
163
138
  }
164
139
  /**
165
140
  * fromWIF - create a wallet using the private key supplied in `Wallet Import Format`
@@ -172,36 +147,33 @@ export class Wallet extends BaseWallet {
172
147
  return new this().fromWIF(wif);
173
148
  }
174
149
  /**
175
- * fromCashaddr - create a watch-only wallet in the network derived from the address
150
+ * fromSeed - create a wallet using the seed phrase and derivation path
176
151
  *
177
- * such kind of wallet does not have a private key and is unable to spend any funds
178
- * however it still allows to use many utility functions such as getting and watching balance, etc.
152
+ * unless specified the derivation path m/44'/245'/0'/0/0 will be userd
153
+ * this derivation path is standard for Electron Cash SLP and other SLP enabled wallets
179
154
  *
180
- * @param address cashaddress of a wallet
155
+ * @param seed BIP39 12 word seed phrase
156
+ * @param derivationPath BIP44 HD wallet derivation path to get a single the private key from hierarchy
181
157
  *
182
158
  * @returns instantiated wallet
183
159
  */
184
- static async fromCashaddr(address) {
185
- const prefix = derivePrefix(address);
186
- const networkType = networkPrefixMap[prefix];
187
- return new this("", networkType, WalletTypeEnum.Watch).watchOnly(address);
160
+ static async fromSeed(seed, derivationPath) {
161
+ return new this().fromSeed(seed, derivationPath);
188
162
  }
189
163
  /**
190
- * fromTokenaddr - create a watch-only wallet in the network derived from the address
164
+ * newRandom - create a random wallet
191
165
  *
192
- * such kind of wallet does not have a private key and is unable to spend any funds
193
- * however it still allows to use many utility functions such as getting and watching balance, etc.
166
+ * if `name` parameter is specified, the wallet will also be persisted to DB
194
167
  *
195
- * @param address token aware cashaddress of a wallet
168
+ * @param name user friendly wallet alias
169
+ * @param dbName name under which the wallet will be stored in the database
196
170
  *
197
171
  * @returns instantiated wallet
198
172
  */
199
- static async fromTokenaddr(address) {
200
- const prefix = derivePrefix(address);
201
- const networkType = networkPrefixMap[prefix];
202
- return new this("", networkType, WalletTypeEnum.Watch).watchOnly(address);
173
+ static async newRandom(name = "", dbName) {
174
+ return new this().newRandom(name, dbName);
203
175
  }
204
- //#endregion Constructors and Statics
176
+ //#endregion Constructors
205
177
  //#region Protected implementations
206
178
  async generate() {
207
179
  if (this.walletType === WalletTypeEnum.Wif) {
@@ -330,46 +302,6 @@ export class Wallet extends BaseWallet {
330
302
  return result;
331
303
  });
332
304
  }
333
- // Initialize a watch only wallet from a cash addr
334
- async watchOnly(address) {
335
- this.walletType = WalletTypeEnum.Watch;
336
- const addressComponents = address.split(":");
337
- let addressPrefix, addressBase;
338
- if (addressComponents.length === 1) {
339
- addressBase = addressComponents.shift();
340
- addressPrefix = derivePrefix(addressBase);
341
- }
342
- else {
343
- addressPrefix = addressComponents.shift();
344
- addressBase = addressComponents.shift();
345
- if (addressPrefix in networkPrefixMap) {
346
- if (networkPrefixMap[addressPrefix] != this.network) {
347
- throw Error(`a ${addressPrefix} address cannot be watched from a ${this.network} Walconst`);
348
- }
349
- }
350
- }
351
- const prefixedAddress = `${addressPrefix}:${addressBase}`;
352
- // check if a token aware address was provided
353
- const addressData = decodeCashAddress(prefixedAddress);
354
- if (typeof addressData === "string")
355
- throw addressData;
356
- this.publicKeyHash = addressData.payload;
357
- let nonTokenAwareType = addressData.type;
358
- if (nonTokenAwareType == CashAddressType.p2pkhWithTokens)
359
- nonTokenAwareType = CashAddressType.p2pkh;
360
- if (nonTokenAwareType == CashAddressType.p2shWithTokens)
361
- nonTokenAwareType = CashAddressType.p2sh;
362
- if (nonTokenAwareType == CashAddressType.p2pkh)
363
- this.publicKeyHash = addressData.payload;
364
- this.cashaddr = encodeCashAddress({
365
- prefix: addressData.prefix,
366
- type: nonTokenAwareType,
367
- payload: addressData.payload,
368
- }).address;
369
- this.address = this.cashaddr;
370
- this.tokenaddr = deriveTokenaddr(addressData.payload, this.networkPrefix);
371
- return this;
372
- }
373
305
  // Initialize wallet from Wallet Import Format
374
306
  async fromWIF(secret) {
375
307
  checkWifNetwork(secret, this.network);
@@ -384,271 +316,72 @@ export class Wallet extends BaseWallet {
384
316
  await this.deriveInfo();
385
317
  return this;
386
318
  }
319
+ /**
320
+ * newRandom (internal) if the wallet is named, get or create it; otherwise create a random
321
+ * unnamed wallet
322
+ * @param {string} name name of the wallet
323
+ * @param {string} dbName database name the wallet is stored in
324
+ */
387
325
  async newRandom(name, dbName) {
388
326
  dbName = dbName ? dbName : this.networkPrefix;
389
- return super.newRandom(name, dbName);
390
- }
391
- async named(name, dbName, forceNew = false) {
392
- dbName = dbName ? dbName : this.networkPrefix;
393
- return super.named(name, dbName, forceNew);
394
- }
395
- async replaceNamed(name, walletId, dbName) {
396
- dbName = dbName ? dbName : this.networkPrefix;
397
- return super.replaceNamed(name, walletId, dbName);
398
- }
399
- async namedExists(name, dbName) {
400
- dbName = dbName ? dbName : this.networkPrefix;
401
- return super.namedExists(name, dbName);
327
+ if (name.length > 0) {
328
+ return this.named(name, dbName);
329
+ }
330
+ else {
331
+ return this.generate();
332
+ }
402
333
  }
403
334
  //#endregion Protected Implementations
404
335
  //#region Serialization
405
336
  // Returns the serialized wallet as a string
406
337
  // If storing in a database, set asNamed to false to store secrets
407
338
  // In all other cases, the a named wallet is deserialized from the database
408
- // by the name key
339
+ // by the name key
409
340
  toString() {
410
- const result = super.toString();
411
- if (result)
412
- return result;
413
- if (this.walletType === WalletTypeEnum.Wif) {
414
- return `${this.walletType}:${this.network}:${this.privateKeyWif}`;
415
- }
416
- throw Error("toString unsupported wallet type");
417
- }
418
- //
419
- toDbString() {
420
- const result = super.toDbString();
421
- if (result)
422
- return result;
423
- if (this.walletType === WalletTypeEnum.Wif) {
424
- return `${this.walletType}:${this.network}:${this.privateKeyWif}`;
341
+ if (this.name) {
342
+ return `named:${this.network}:${this.name}`;
425
343
  }
426
- throw Error("toDbString unsupported wallet type");
427
- }
428
- //#endregion Serialization
429
- //#region Funds
430
- //
431
- async getAddressUtxos(address) {
432
- if (!address) {
433
- address = this.cashaddr;
344
+ else if (this.walletType == WalletTypeEnum.Seed) {
345
+ return `${this.walletType}:${this.network}:${this.mnemonic}:${this.derivationPath}`;
434
346
  }
435
- if (this._slpSemiAware) {
436
- const bchUtxos = await this.provider.getUtxos(address);
437
- return bchUtxos.filter((bchutxo) => bchutxo.satoshis > DUST_UTXO_THRESHOLD);
347
+ else if (this.walletType === WalletTypeEnum.Wif) {
348
+ return `${this.walletType}:${this.network}:${this.privateKeyWif}`;
438
349
  }
439
- else {
440
- return await this.provider.getUtxos(address);
350
+ else if (this.walletType == WalletTypeEnum.Watch) {
351
+ return super.toString();
441
352
  }
353
+ throw Error("toString unsupported wallet type");
442
354
  }
443
355
  /**
444
- * utxos Get unspent outputs for the wallet
356
+ * toDbString - store the serialized version of the wallet in the database, not just the name
445
357
  *
358
+ * @throws {Error} if called on BaseWallet
446
359
  */
447
- async getUtxos() {
448
- if (!this.cashaddr) {
449
- throw Error("Attempted to get utxos without an address");
450
- }
451
- return await this.getAddressUtxos(this.cashaddr);
452
- }
453
- // gets wallet balance in sats, bch and currency
454
- async getBalance(rawUnit, priceCache = true) {
455
- if (rawUnit) {
456
- const unit = sanitizeUnit(rawUnit);
457
- return await balanceFromSatoshi(await this.getBalanceFromProvider(), unit, priceCache);
458
- }
459
- else {
460
- return await balanceResponseFromSatoshi(await this.getBalanceFromProvider(), priceCache);
461
- }
462
- }
463
- // Gets balance by summing value in all utxos in stats
464
- async getBalanceFromUtxos() {
465
- const utxos = (await this.getAddressUtxos(this.cashaddr)).filter((val) => val.token === undefined);
466
- return sumUtxoValue(utxos);
467
- }
468
- // Gets balance from fulcrum
469
- async getBalanceFromProvider() {
470
- // Fulcrum reports balance of all utxos, including tokens, which is undesirable
471
- // // TODO not sure why getting the balance from a provider doesn't work
472
- // if (this._slpAware || this._slpSemiAware) {
473
- // return await this.getBalanceFromUtxos();
474
- // } else {
475
- // return await this.provider!.getBalance(this.cashaddr!);
476
- // }
477
- // FIXME
478
- return this.getBalanceFromUtxos();
479
- }
480
- // watching for any transaction hash of this wallet
481
- watchAddress(callback) {
482
- return this.provider.watchAddress(this.getDepositAddress(), callback);
483
- }
484
- // watching for any transaction of this wallet
485
- watchAddressTransactions(callback) {
486
- return this.provider.watchAddressTransactions(this.getDepositAddress(), callback);
487
- }
488
- // watching for cashtoken transaction of this wallet
489
- watchAddressTokenTransactions(callback) {
490
- return this.provider.watchAddressTokenTransactions(this.getDepositAddress(), callback);
491
- }
492
- // sets up a callback to be called upon wallet's balance change
493
- // can be cancelled by calling the function returned from this one
494
- watchBalance(callback) {
495
- return this.provider.watchAddressStatus(this.getDepositAddress(), async (_status) => {
496
- const balance = (await this.getBalance());
497
- callback(balance);
498
- });
499
- }
500
- // sets up a callback to be called upon wallet's BCH or USD balance change
501
- // if BCH balance does not change, the callback will be triggered every
502
- // @param `usdPriceRefreshInterval` milliseconds by polling for new BCH USD price
503
- // Since we want to be most sensitive to usd value change, we do not use the cached exchange rates
504
- // can be cancelled by calling the function returned from this one
505
- watchBalanceUsd(callback, usdPriceRefreshInterval = 30000) {
506
- let usdPrice = -1;
507
- const _callback = async () => {
508
- const balance = (await this.getBalance(undefined, false));
509
- if (usdPrice !== balance.usd) {
510
- usdPrice = balance.usd;
511
- callback(balance);
512
- }
513
- };
514
- const watchCancel = this.provider.watchAddressStatus(this.getDepositAddress(), _callback);
515
- const interval = setInterval(_callback, usdPriceRefreshInterval);
516
- return async () => {
517
- await watchCancel();
518
- clearInterval(interval);
519
- };
520
- }
521
- // waits for address balance to be greater than or equal to the target value
522
- // this call halts the execution
523
- async waitForBalance(value, rawUnit = UnitEnum.BCH) {
524
- return new Promise(async (resolve) => {
525
- const watchCancel = this.watchBalance(async (balance) => {
526
- const satoshiBalance = await amountInSatoshi(value, rawUnit);
527
- if (balance.sat >= satoshiBalance) {
528
- await watchCancel();
529
- resolve(balance);
530
- }
531
- });
532
- });
533
- }
534
- // sets up a callback to be called upon wallet's token balance change
535
- // can be cancelled by calling the function returned from this one
536
- watchTokenBalance(tokenId, callback) {
537
- let previous = undefined;
538
- return this.provider.watchAddressStatus(this.getDepositAddress(), async (_status) => {
539
- const balance = await this.getTokenBalance(tokenId);
540
- if (previous != balance) {
541
- callback(balance);
542
- }
543
- previous = balance;
544
- });
545
- }
546
- // waits for address token balance to be greater than or equal to the target amount
547
- // this call halts the execution
548
- async waitForTokenBalance(tokenId, amount) {
549
- return new Promise(async (resolve) => {
550
- const watchCancel = this.watchTokenBalance(tokenId, async (balance) => {
551
- if (balance >= amount) {
552
- await watchCancel();
553
- resolve(balance);
554
- }
555
- });
556
- });
557
- }
558
- async getTokenInfo(tokenId) {
559
- return BCMR.getTokenInfo(tokenId);
560
- }
561
- async _getMaxAmountToSend(params = {
562
- outputCount: 1,
563
- options: {},
564
- }) {
565
- if (!this.privateKey && params.options?.buildUnsigned !== true) {
566
- throw Error("Couldn't get network or private key for wallet.");
567
- }
568
- if (!this.cashaddr) {
569
- throw Error("attempted to send without a cashaddr");
570
- }
571
- if (params.options && params.options.slpSemiAware) {
572
- this._slpSemiAware = true;
573
- }
574
- let feePaidBy;
575
- if (params.options && params.options.feePaidBy) {
576
- feePaidBy = params.options.feePaidBy;
577
- }
578
- else {
579
- feePaidBy = FeePaidByEnum.change;
580
- }
581
- // get inputs
582
- let utxos;
583
- if (params.options && params.options.utxoIds) {
584
- utxos = await checkUtxos(params.options.utxoIds.map((utxoId) => typeof utxoId === "string" ? fromUtxoId(utxoId) : utxoId), this);
360
+ toDbString() {
361
+ if (this.walletType == WalletTypeEnum.Seed) {
362
+ return `${this.walletType}:${this.network}:${this.mnemonic}:${this.derivationPath}`;
585
363
  }
586
- else {
587
- utxos = (await this.getAddressUtxos(this.cashaddr)).filter((utxo) => !utxo.token);
364
+ else if (this.walletType === WalletTypeEnum.Wif) {
365
+ return `${this.walletType}:${this.network}:${this.privateKeyWif}`;
588
366
  }
589
- // Get current height to assure recently mined coins are not spent.
590
- const bestHeight = await this.provider.getBlockHeight();
591
- // simulate outputs using the sender's address
592
- const sendRequest = new SendRequest({
593
- cashaddr: this.cashaddr,
594
- value: 100,
595
- unit: "sat",
596
- });
597
- const sendRequests = Array(params.outputCount)
598
- .fill(0)
599
- .map(() => sendRequest);
600
- const fundingUtxos = await getSuitableUtxos(utxos, undefined, bestHeight, feePaidBy, sendRequests);
601
- const relayFeePerByteInSatoshi = await getRelayFeeCache(this.provider);
602
- const fee = await getFeeAmountSimple({
603
- utxos: fundingUtxos,
604
- sendRequests: sendRequests,
605
- privateKey: this.privateKey ?? hexToBin(placeholderPrivateKey),
606
- sourceAddress: this.cashaddr,
607
- relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
608
- feePaidBy: feePaidBy,
609
- });
610
- const spendableAmount = sumUtxoValue(fundingUtxos);
611
- let result = spendableAmount - fee;
612
- if (result < 0) {
613
- result = 0;
367
+ else if (this.walletType == WalletTypeEnum.Watch) {
368
+ return super.toDbString();
614
369
  }
615
- return { value: result, utxos: fundingUtxos };
370
+ throw Error("toDbString unsupported wallet type");
616
371
  }
372
+ //#endregion Serialization
373
+ //#region Funds
617
374
  async getMaxAmountToSend(params = {
618
375
  outputCount: 1,
619
376
  options: {},
620
377
  }) {
621
- const { value: result } = await this._getMaxAmountToSend(params);
378
+ const { value: result } = await this._getMaxAmountToSend({
379
+ options: params.options,
380
+ outputCount: params.outputCount,
381
+ privateKey: this.privateKey,
382
+ });
622
383
  return await balanceResponseFromSatoshi(result);
623
384
  }
624
- /**
625
- * send Send some amount to an address
626
- * this function processes the send requests, encodes the transaction, sends it to the network
627
- * @returns (depending on the options parameter) the transaction id, new address balance and a link to the transaction on the blockchain explorer
628
- *
629
- * This is a first class function with REST analog, maintainers should strive to keep backward-compatibility
630
- *
631
- */
632
- async send(requests, options) {
633
- const { encodedTransaction, tokenIds, sourceOutputs } = await this.encodeTransaction(requests, undefined, options);
634
- const resp = new SendResponse({});
635
- resp.tokenIds = tokenIds;
636
- if (options?.buildUnsigned !== true) {
637
- const txId = await this.submitTransaction(encodedTransaction, options?.awaitTransactionPropagation === undefined ||
638
- options?.awaitTransactionPropagation === true);
639
- resp.txId = txId;
640
- resp.explorerUrl = this.explorerUrl(resp.txId);
641
- if (options?.queryBalance === undefined ||
642
- options?.queryBalance === true) {
643
- resp.balance = (await this.getBalance());
644
- }
645
- }
646
- else {
647
- resp.unsignedTransaction = binToHex(encodedTransaction);
648
- resp.sourceOutputs = sourceOutputs;
649
- }
650
- return resp;
651
- }
652
385
  /**
653
386
  * sendMax Send all available funds to a destination cash address
654
387
  *
@@ -658,48 +391,7 @@ export class Wallet extends BaseWallet {
658
391
  * @returns (depending on the options parameter) the transaction id, new address balance and a link to the transaction on the blockchain explorer
659
392
  */
660
393
  async sendMax(cashaddr, options) {
661
- return await this.sendMaxRaw(cashaddr, options);
662
- }
663
- /**
664
- * sendMaxRaw (internal) Send all available funds to a destination cash address
665
- *
666
- * @param {string} cashaddr destination cash address
667
- * @param {SendRequestOptionsI} options Options of the send requests
668
- *
669
- * @returns the transaction id sent to the network
670
- */
671
- async sendMaxRaw(cashaddr, options) {
672
- const { value: maxSpendableAmount, utxos } = await this._getMaxAmountToSend({
673
- outputCount: 1,
674
- options: options,
675
- });
676
- if (!options) {
677
- options = {};
678
- }
679
- options.utxoIds = utxos;
680
- const sendRequest = new SendRequest({
681
- cashaddr: cashaddr,
682
- value: maxSpendableAmount,
683
- unit: "sat",
684
- });
685
- const { encodedTransaction, tokenIds, sourceOutputs } = await this.encodeTransaction([sendRequest], true, options);
686
- const resp = new SendResponse({});
687
- resp.tokenIds = tokenIds;
688
- if (options?.buildUnsigned !== true) {
689
- const txId = await this.submitTransaction(encodedTransaction, options?.awaitTransactionPropagation === undefined ||
690
- options?.awaitTransactionPropagation === true);
691
- resp.txId = txId;
692
- resp.explorerUrl = this.explorerUrl(resp.txId);
693
- if (options?.queryBalance === undefined ||
694
- options?.queryBalance === true) {
695
- resp.balance = (await this.getBalance());
696
- }
697
- }
698
- else {
699
- resp.unsignedTransaction = binToHex(encodedTransaction);
700
- resp.sourceOutputs = sourceOutputs;
701
- }
702
- return resp;
394
+ return this.sendMaxRaw(cashaddr, options, this.privateKey);
703
395
  }
704
396
  /**
705
397
  * encodeTransaction Encode and sign a transaction given a list of sendRequests, options and estimate fees.
@@ -707,152 +399,8 @@ export class Wallet extends BaseWallet {
707
399
  * @param {boolean} discardChange=false
708
400
  * @param {SendRequestOptionsI} options Options of the send requests
709
401
  */
710
- async encodeTransaction(requests, discardChange = false, options) {
711
- let sendRequests = asSendRequestObject(requests);
712
- if (!this.privateKey && options?.buildUnsigned !== true) {
713
- throw new Error(`Wallet ${this.name} is missing either a network or private key`);
714
- }
715
- if (!this.cashaddr) {
716
- throw Error("attempted to send without a cashaddr");
717
- }
718
- if (options && options.slpSemiAware) {
719
- this._slpSemiAware = true;
720
- }
721
- let feePaidBy;
722
- if (options && options.feePaidBy) {
723
- feePaidBy = options.feePaidBy;
724
- }
725
- else {
726
- feePaidBy = FeePaidByEnum.change;
727
- }
728
- let changeAddress;
729
- if (options && options.changeAddress) {
730
- changeAddress = options.changeAddress;
731
- }
732
- else {
733
- changeAddress = this.cashaddr;
734
- }
735
- let checkTokenQuantities = true;
736
- if (options && options.checkTokenQuantities === false) {
737
- checkTokenQuantities = false;
738
- }
739
- // get inputs from options or query all inputs
740
- let utxos;
741
- if (options && options.utxoIds) {
742
- utxos = await checkUtxos(options.utxoIds.map((utxoId) => typeof utxoId === "string" ? fromUtxoId(utxoId) : utxoId), this);
743
- }
744
- else {
745
- utxos = await this.getAddressUtxos(this.cashaddr);
746
- }
747
- // filter out token utxos if there are no token requests
748
- if (checkTokenQuantities &&
749
- !sendRequests.some((val) => val instanceof TokenSendRequest)) {
750
- utxos = utxos.filter((val) => !val.token);
751
- }
752
- const addTokenChangeOutputs = (inputs, outputs) => {
753
- // Allow for implicit token burn if the total amount sent is less than user had
754
- // allow for token genesis, creating more tokens than we had before (0)
755
- if (!checkTokenQuantities) {
756
- return;
757
- }
758
- const allTokenInputs = inputs.filter((val) => val.token);
759
- const allTokenOutputs = outputs.filter((val) => val instanceof TokenSendRequest);
760
- const tokenIds = allTokenOutputs
761
- .map((val) => val.tokenId)
762
- .filter((val, idx, arr) => arr.indexOf(val) === idx);
763
- for (let tokenId of tokenIds) {
764
- const tokenInputs = allTokenInputs.filter((val) => val.token?.tokenId === tokenId);
765
- const inputAmountSum = tokenInputs.reduce((prev, cur) => prev + cur.token.amount, 0n);
766
- const tokenOutputs = allTokenOutputs.filter((val) => val.tokenId === tokenId);
767
- const outputAmountSum = tokenOutputs.reduce((prev, cur) => prev + cur.amount, 0n);
768
- const diff = inputAmountSum - outputAmountSum;
769
- if (diff < 0) {
770
- throw new Error("Not enough token amount to send");
771
- }
772
- if (diff >= 0) {
773
- let available = 0n;
774
- let change = 0n;
775
- const ensureUtxos = [];
776
- for (const token of tokenInputs.filter((val) => val.token?.amount)) {
777
- ensureUtxos.push(token);
778
- available += token.token?.amount;
779
- if (available >= outputAmountSum) {
780
- change = available - outputAmountSum;
781
- //break;
782
- }
783
- }
784
- if (ensureUtxos.length) {
785
- if (!options) {
786
- options = {};
787
- }
788
- options.ensureUtxos = [
789
- ...(options.ensureUtxos ?? []),
790
- ...ensureUtxos,
791
- ].filter((val, index, array) => array.findIndex((other) => other.txid === val.txid && other.vout === val.vout) === index);
792
- }
793
- if (change > 0) {
794
- outputs.push(new TokenSendRequest({
795
- cashaddr: toTokenaddr(changeAddress) || this.tokenaddr,
796
- amount: change,
797
- tokenId: tokenId,
798
- commitment: tokenOutputs[0].commitment,
799
- capability: tokenOutputs[0].capability,
800
- value: tokenOutputs[0].value,
801
- }));
802
- }
803
- }
804
- }
805
- };
806
- addTokenChangeOutputs(utxos, sendRequests);
807
- const bestHeight = await this.provider.getBlockHeight();
808
- const spendAmount = await sumSendRequestAmounts(sendRequests);
809
- if (utxos.length === 0) {
810
- throw Error("There were no Unspent Outputs");
811
- }
812
- if (typeof spendAmount !== "bigint") {
813
- throw Error("Couldn't get spend amount when building transaction");
814
- }
815
- const relayFeePerByteInSatoshi = await getRelayFeeCache(this.provider);
816
- const feeEstimate = await getFeeAmountSimple({
817
- utxos: utxos,
818
- sendRequests: sendRequests,
819
- privateKey: this.privateKey ?? hexToBin(placeholderPrivateKey),
820
- sourceAddress: this.cashaddr,
821
- relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
822
- feePaidBy: feePaidBy,
823
- });
824
- const fundingUtxos = await getSuitableUtxos(utxos, BigInt(spendAmount) + BigInt(Math.ceil(feeEstimate)), bestHeight, feePaidBy, sendRequests, options?.ensureUtxos || [], options?.tokenOperation);
825
- if (fundingUtxos.length === 0) {
826
- throw Error("The available inputs couldn't satisfy the request with fees");
827
- }
828
- const fee = await getFeeAmount({
829
- utxos: fundingUtxos,
830
- sendRequests: sendRequests,
831
- privateKey: this.privateKey ?? hexToBin(placeholderPrivateKey),
832
- sourceAddress: this.cashaddr,
833
- relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
834
- feePaidBy: feePaidBy,
835
- });
836
- const { encodedTransaction, sourceOutputs } = await buildEncodedTransaction({
837
- inputs: fundingUtxos,
838
- outputs: sendRequests,
839
- signingKey: this.privateKey ?? hexToBin(placeholderPrivateKey),
840
- sourceAddress: this.cashaddr,
841
- fee,
842
- discardChange,
843
- feePaidBy,
844
- changeAddress,
845
- buildUnsigned: options?.buildUnsigned === true,
846
- });
847
- const tokenIds = [
848
- ...fundingUtxos
849
- .filter((val) => val.token?.tokenId)
850
- .map((val) => val.token.tokenId),
851
- ...sendRequests
852
- .filter((val) => val instanceof TokenSendRequest)
853
- .map((val) => val.tokenId),
854
- ].filter((value, index, array) => array.indexOf(value) === index);
855
- return { encodedTransaction, tokenIds, sourceOutputs };
402
+ async encodeTransaction(requests, discardChange = false, options, privateKey) {
403
+ return super.encodeTransaction(requests, discardChange, options, this.privateKey);
856
404
  }
857
405
  async signUnsignedTransaction(transaction, sourceOutputs) {
858
406
  if (!this.privateKey) {
@@ -860,124 +408,6 @@ export class Wallet extends BaseWallet {
860
408
  }
861
409
  return signUnsignedTransaction(transaction, sourceOutputs, this.privateKey);
862
410
  }
863
- // Submit a raw transaction
864
- async submitTransaction(transaction, awaitPropagation = true) {
865
- if (!this.provider) {
866
- throw Error("Wallet network provider was not initialized");
867
- }
868
- let rawTransaction = binToHex(transaction);
869
- return await this.provider.sendRawTransaction(rawTransaction, awaitPropagation);
870
- }
871
- // gets transaction history of this wallet
872
- async getRawHistory(fromHeight = 0, toHeight = -1) {
873
- return await this.provider.getHistory(this.cashaddr, fromHeight, toHeight);
874
- }
875
- /**
876
- * getHistory gets transaction history of this wallet with most data decoded and ready to present to user
877
- * @note balance calculations are valid only if querying to the blockchain tip (`toHeight` === -1, `count` === -1)
878
- * @note this method is heavy on network calls, if invoked in browser use of cache is advised, @see `Config.UseLocalStorageCache`
879
- * @note this method tries to recreate the history tab view of Electron Cash wallet, however, it may not be 100% accurate if the tnransaction value changes are the same in the same block (ordering)
880
- *
881
- * @param unit optional, BCH or currency unit to present balance and balance changes. If unit is currency like USD or EUR, balances will be subject to possible rounding errors. Default 0
882
- * @param fromHeight optional, if set, history will be limited. Default 0
883
- * @param toHeight optional, if set, history will be limited. Default -1, meaning that all history items will be returned, including mempool
884
- * @param start optional, if set, the result set will be paginated with offset `start`
885
- * @param count optional, if set, the result set will be paginated with `count`. Default -1, meaning that all history items will be returned
886
- *
887
- * @returns an array of transaction history items, with input values and addresses encoded in cashaddress format. @see `TransactionHistoryItem` type
888
- */
889
- async getHistory({ unit = "sat", fromHeight = 0, toHeight = -1, start = 0, count = -1, }) {
890
- return getAddressHistory({
891
- address: this.cashaddr,
892
- provider: this.provider,
893
- unit,
894
- fromHeight,
895
- toHeight,
896
- start,
897
- count,
898
- });
899
- }
900
- // gets last transaction of this wallet
901
- async getLastTransaction(confirmedOnly = false) {
902
- let history = await this.getRawHistory();
903
- if (confirmedOnly) {
904
- history = history.filter((val) => val.height > 0);
905
- }
906
- if (!history.length) {
907
- return null;
908
- }
909
- const [lastTx] = history.slice(-1);
910
- return this.provider.getRawTransactionObject(lastTx.tx_hash);
911
- }
912
- // waits for next transaction, program execution is halted
913
- async waitForTransaction(options = {
914
- getTransactionInfo: true,
915
- getBalance: false,
916
- txHash: undefined,
917
- }) {
918
- if (options.getTransactionInfo === undefined) {
919
- options.getTransactionInfo = true;
920
- }
921
- return new Promise(async (resolve) => {
922
- let txHashSeen = false;
923
- const makeResponse = async (txHash) => {
924
- const response = {};
925
- const promises = [undefined, undefined];
926
- if (options.getBalance === true) {
927
- promises[0] = this.getBalance();
928
- }
929
- if (options.getTransactionInfo === true) {
930
- if (!txHash) {
931
- promises[1] = this.getLastTransaction();
932
- }
933
- else {
934
- promises[1] = this.provider.getRawTransactionObject(txHash);
935
- }
936
- }
937
- const result = await Promise.all(promises);
938
- response.balance = result[0];
939
- response.transactionInfo = result[1];
940
- return response;
941
- };
942
- // waiting for a specific transaction to propagate
943
- if (options.txHash) {
944
- const waitForTransactionCallback = async (data) => {
945
- if (data && data[0] === options.txHash) {
946
- txHashSeen = true;
947
- this.provider.unsubscribeFromTransaction(options.txHash, waitForTransactionCallback);
948
- resolve(makeResponse(options.txHash));
949
- }
950
- };
951
- this.provider.subscribeToTransaction(options.txHash, waitForTransactionCallback);
952
- return;
953
- }
954
- // waiting for any address transaction
955
- const watchCancel = this.provider.watchAddressStatus(this.getDepositAddress(), async (_status) => {
956
- watchCancel();
957
- resolve(makeResponse());
958
- });
959
- });
960
- }
961
- /**
962
- * watchBlocks Watch network blocks
963
- *
964
- * @param callback callback with a block header object
965
- * @param skipCurrentHeight if set, the notification about current block will not arrive
966
- *
967
- * @returns a function which will cancel watching upon evaluation
968
- */
969
- watchBlocks(callback, skipCurrentHeight = true) {
970
- return this.provider.watchBlocks(callback, skipCurrentHeight);
971
- }
972
- /**
973
- * waitForBlock Wait for a network block
974
- *
975
- * @param height if specified waits for this exact blockchain height, otherwise resolves with the next block
976
- *
977
- */
978
- async waitForBlock(height) {
979
- return this.provider.waitForBlock(height);
980
- }
981
411
  //#endregion Funds
982
412
  //#region Private implementation details
983
413
  async deriveInfo() {
@@ -996,7 +426,6 @@ export class Wallet extends BaseWallet {
996
426
  checkWifNetwork(this.privateKeyWif, this.network);
997
427
  this.cashaddr = deriveCashaddr(this.privateKey, this.networkPrefix);
998
428
  this.tokenaddr = deriveTokenaddr(this.privateKey, this.networkPrefix);
999
- this.address = this.cashaddr;
1000
429
  this.publicKeyHash = derivePublicKeyHash(this.cashaddr);
1001
430
  return this;
1002
431
  }
@@ -1006,273 +435,6 @@ export class Wallet extends BaseWallet {
1006
435
  async sign(message) {
1007
436
  return await Wallet.signedMessage.sign(message, this.privateKey);
1008
437
  }
1009
- // Convenience wrapper to verify interface
1010
- async verify(message, sig, publicKey) {
1011
- return await Wallet.signedMessage.verify(message, sig, this.cashaddr, publicKey);
1012
- }
1013
- //#endregion Signing
1014
- //#region Cashtokens
1015
- /**
1016
- * Create new cashtoken, both funglible and/or non-fungible (NFT)
1017
- * Refer to spec https://github.com/bitjson/cashtokens
1018
- * @param {number} genesisRequest.amount amount of *fungible* tokens to create
1019
- * @param {NFTCapability?} genesisRequest.capability capability of new NFT
1020
- * @param {string?} genesisRequest.commitment NFT commitment message
1021
- * @param {string?} genesisRequest.cashaddr cash address to send the created token UTXO to; if undefined will default to your address
1022
- * @param {number?} genesisRequest.value satoshi value to send alongside with tokens; if undefined will default to 1000 satoshi
1023
- * @param {SendRequestType | SendRequestType[]} sendRequests single or an array of extra send requests (OP_RETURN, value transfer, etc.) to include in genesis transaction
1024
- * @param {SendRequestOptionsI} options Options of the send requests
1025
- */
1026
- async tokenGenesis(genesisRequest, sendRequests = [], options) {
1027
- if (!Array.isArray(sendRequests)) {
1028
- sendRequests = [sendRequests];
1029
- }
1030
- let utxos;
1031
- if (options && options.utxoIds) {
1032
- utxos = await checkUtxos(options.utxoIds.map((utxoId) => typeof utxoId === "string" ? fromUtxoId(utxoId) : utxoId), this);
1033
- }
1034
- else {
1035
- utxos = await this.getAddressUtxos(this.cashaddr);
1036
- }
1037
- const genesisInputs = utxos.filter((val) => val.vout === 0 && !val.token);
1038
- if (genesisInputs.length === 0) {
1039
- throw new Error("No suitable inputs with vout=0 available for new token genesis");
1040
- }
1041
- const genesisSendRequest = new TokenSendRequest({
1042
- cashaddr: genesisRequest.cashaddr || this.tokenaddr,
1043
- amount: genesisRequest.amount,
1044
- value: genesisRequest.value || 1000,
1045
- capability: genesisRequest.capability,
1046
- commitment: genesisRequest.commitment,
1047
- tokenId: genesisInputs[0].txid,
1048
- });
1049
- return this.send([genesisSendRequest, ...sendRequests], {
1050
- ...options,
1051
- utxoIds: utxos,
1052
- ensureUtxos: [genesisInputs[0]],
1053
- checkTokenQuantities: false,
1054
- queryBalance: false,
1055
- tokenOperation: "genesis",
1056
- });
1057
- }
1058
- /**
1059
- * Mint new NFT cashtokens using an existing minting token
1060
- * Refer to spec https://github.com/bitjson/cashtokens
1061
- * @param {string} tokenId tokenId of an NFT to mint
1062
- * @param {TokenMintRequest | TokenMintRequest[]} mintRequests mint requests with new token properties and recipients
1063
- * @param {NFTCapability?} mintRequest.capability capability of new NFT
1064
- * @param {string?} mintRequest.commitment NFT commitment message
1065
- * @param {string?} mintRequest.cashaddr cash address to send the created token UTXO to; if undefined will default to your address
1066
- * @param {number?} mintRequest.value satoshi value to send alongside with tokens; if undefined will default to 1000 satoshi
1067
- * @param {boolean?} deductTokenAmount if minting token contains fungible amount, deduct from it by amount of minted tokens
1068
- * @param {SendRequestOptionsI} options Options of the send requests
1069
- */
1070
- async tokenMint(tokenId, mintRequests, deductTokenAmount = false, options) {
1071
- if (tokenId?.length !== 64) {
1072
- throw Error(`Invalid tokenId supplied: ${tokenId}`);
1073
- }
1074
- if (!Array.isArray(mintRequests)) {
1075
- mintRequests = [mintRequests];
1076
- }
1077
- const utxos = await this.getAddressUtxos(this.cashaddr);
1078
- const nftUtxos = utxos.filter((val) => val.token?.tokenId === tokenId &&
1079
- val.token?.capability === NFTCapability.minting);
1080
- if (!nftUtxos.length) {
1081
- throw new Error("You do not have any token UTXOs with minting capability for specified tokenId");
1082
- }
1083
- const newAmount = deductTokenAmount && nftUtxos[0].token.amount > 0
1084
- ? nftUtxos[0].token.amount - BigInt(mintRequests.length)
1085
- : nftUtxos[0].token.amount;
1086
- const safeNewAmount = newAmount < 0n ? 0n : newAmount;
1087
- const mintingInput = new TokenSendRequest({
1088
- cashaddr: this.tokenaddr,
1089
- tokenId: tokenId,
1090
- capability: nftUtxos[0].token.capability,
1091
- commitment: nftUtxos[0].token.commitment,
1092
- amount: safeNewAmount,
1093
- value: nftUtxos[0].satoshis,
1094
- });
1095
- return this.send([
1096
- mintingInput,
1097
- ...mintRequests.map((val) => new TokenSendRequest({
1098
- cashaddr: val.cashaddr || this.tokenaddr,
1099
- amount: 0,
1100
- tokenId: tokenId,
1101
- value: val.value,
1102
- capability: val.capability,
1103
- commitment: val.commitment,
1104
- })),
1105
- ], {
1106
- ...options,
1107
- ensureUtxos: [nftUtxos[0]],
1108
- checkTokenQuantities: false,
1109
- queryBalance: false,
1110
- tokenOperation: "mint",
1111
- });
1112
- }
1113
- /**
1114
- * Perform an explicit token burning by spending a token utxo to an OP_RETURN
1115
- *
1116
- * Behaves differently for fungible and non-fungible tokens:
1117
- * * NFTs are always "destroyed"
1118
- * * FTs' amount is reduced by the amount specified, if 0 FT amount is left and no NFT present, the token is "destroyed"
1119
- *
1120
- * Refer to spec https://github.com/bitjson/cashtokens
1121
- * @param {string} burnRequest.tokenId tokenId of a token to burn
1122
- * @param {NFTCapability} burnRequest.capability capability of the NFT token to select, optional
1123
- * @param {string?} burnRequest.commitment commitment of the NFT token to select, optional
1124
- * @param {number?} burnRequest.amount amount of fungible tokens to burn, optional
1125
- * @param {string?} burnRequest.cashaddr address to return token and satoshi change to
1126
- * @param {string?} message optional message to include in OP_RETURN
1127
- * @param {SendRequestOptionsI} options Options of the send requests
1128
- */
1129
- async tokenBurn(burnRequest, message, options) {
1130
- if (burnRequest.tokenId?.length !== 64) {
1131
- throw Error(`Invalid tokenId supplied: ${burnRequest.tokenId}`);
1132
- }
1133
- const utxos = await this.getAddressUtxos(this.cashaddr);
1134
- const tokenUtxos = utxos.filter((val) => val.token?.tokenId === burnRequest.tokenId &&
1135
- val.token?.capability === burnRequest.capability &&
1136
- val.token?.commitment === burnRequest.commitment);
1137
- if (!tokenUtxos.length) {
1138
- throw new Error("You do not have suitable token UTXOs to perform burn");
1139
- }
1140
- const totalFungibleAmount = tokenUtxos.reduce((prev, cur) => prev + (cur.token?.amount || 0n), 0n);
1141
- let fungibleBurnAmount = burnRequest.amount && burnRequest.amount > 0 ? burnRequest.amount : 0n;
1142
- fungibleBurnAmount = BigInt(fungibleBurnAmount);
1143
- const hasNFT = burnRequest.capability || burnRequest.commitment;
1144
- let utxoIds = [];
1145
- let changeSendRequests;
1146
- if (hasNFT) {
1147
- // does not have FT tokens, let us destroy the token completely
1148
- if (totalFungibleAmount === 0n) {
1149
- changeSendRequests = [];
1150
- utxoIds.push(tokenUtxos[0]);
1151
- }
1152
- else {
1153
- // add utxos to spend from
1154
- let available = 0n;
1155
- for (const token of tokenUtxos.filter((val) => val.token?.amount)) {
1156
- utxoIds.push(token);
1157
- available += token.token?.amount;
1158
- if (available >= fungibleBurnAmount) {
1159
- break;
1160
- }
1161
- }
1162
- // if there are FT, reduce their amount
1163
- const newAmount = totalFungibleAmount - fungibleBurnAmount;
1164
- const safeNewAmount = newAmount < 0n ? 0n : newAmount;
1165
- changeSendRequests = [
1166
- new TokenSendRequest({
1167
- cashaddr: burnRequest.cashaddr || this.tokenaddr,
1168
- tokenId: burnRequest.tokenId,
1169
- capability: burnRequest.capability,
1170
- commitment: burnRequest.commitment,
1171
- amount: safeNewAmount,
1172
- value: tokenUtxos[0].satoshis,
1173
- }),
1174
- ];
1175
- }
1176
- }
1177
- else {
1178
- // if we are burning last fungible tokens, let us destroy the token completely
1179
- if (totalFungibleAmount === fungibleBurnAmount) {
1180
- changeSendRequests = [];
1181
- utxoIds.push(...tokenUtxos);
1182
- }
1183
- else {
1184
- // add utxos to spend from
1185
- let available = 0n;
1186
- for (const token of tokenUtxos.filter((val) => val.token?.amount)) {
1187
- utxoIds.push(token);
1188
- available += token.token?.amount;
1189
- if (available >= fungibleBurnAmount) {
1190
- break;
1191
- }
1192
- }
1193
- // reduce the FT amount
1194
- const newAmount = available - fungibleBurnAmount;
1195
- const safeNewAmount = newAmount < 0n ? 0n : newAmount;
1196
- changeSendRequests = [
1197
- new TokenSendRequest({
1198
- cashaddr: burnRequest.cashaddr || this.tokenaddr,
1199
- tokenId: burnRequest.tokenId,
1200
- amount: safeNewAmount,
1201
- value: tokenUtxos.reduce((a, c) => a + c.satoshis, 0),
1202
- }),
1203
- ];
1204
- }
1205
- }
1206
- const opReturn = OpReturnData.fromString(message || "");
1207
- return this.send([opReturn, ...changeSendRequests], {
1208
- ...options,
1209
- checkTokenQuantities: false,
1210
- queryBalance: false,
1211
- ensureUtxos: utxoIds.length > 0 ? utxoIds : undefined,
1212
- tokenOperation: "burn",
1213
- });
1214
- }
1215
- /**
1216
- * getTokenUtxos Get unspent token outputs for the wallet
1217
- * will return utxos only for the specified token if `tokenId` provided
1218
- * @param {string?} tokenId tokenId (category) to filter utxos by, if not set will return utxos from all tokens
1219
- * @returns {UtxoI[]} token utxos
1220
- */
1221
- async getTokenUtxos(tokenId) {
1222
- const utxos = await this.getAddressUtxos(this.address);
1223
- return utxos.filter((val) => tokenId ? val.token?.tokenId === tokenId : val.token);
1224
- }
1225
- /**
1226
- * getTokenBalance Gets fungible token balance
1227
- * for NFT token balance see @ref getNftTokenBalance
1228
- * @param {string} tokenId tokenId to get balance for
1229
- * @returns {bigint} fungible token balance
1230
- */
1231
- async getTokenBalance(tokenId) {
1232
- const utxos = (await this.getTokenUtxos(tokenId)).filter((val) => val.token?.amount);
1233
- return sumTokenAmounts(utxos, tokenId);
1234
- }
1235
- /**
1236
- * getNftTokenBalance Gets non-fungible token (NFT) balance for a particular tokenId
1237
- * disregards fungible token balances
1238
- * for fungible token balance see @ref getTokenBalance
1239
- * @param {string} tokenId tokenId to get balance for
1240
- * @returns {number} non-fungible token balance
1241
- */
1242
- async getNftTokenBalance(tokenId) {
1243
- const utxos = (await this.getTokenUtxos(tokenId)).filter((val) => val.token?.commitment !== undefined);
1244
- return utxos.length;
1245
- }
1246
- /**
1247
- * getAllTokenBalances Gets all fungible token balances in this wallet
1248
- * @returns {Object} a map [tokenId => balance] for all tokens in this wallet
1249
- */
1250
- async getAllTokenBalances() {
1251
- const result = {};
1252
- const utxos = (await this.getTokenUtxos()).filter((val) => val.token?.amount);
1253
- for (const utxo of utxos) {
1254
- if (!result[utxo.token.tokenId]) {
1255
- result[utxo.token.tokenId] = 0n;
1256
- }
1257
- result[utxo.token.tokenId] += utxo.token.amount;
1258
- }
1259
- return result;
1260
- }
1261
- /**
1262
- * getAllNftTokenBalances Gets all non-fungible token (NFT) balances in this wallet
1263
- * @returns {Object} a map [tokenId => balance] for all NFTs in this wallet
1264
- */
1265
- async getAllNftTokenBalances() {
1266
- const result = {};
1267
- const utxos = (await this.getTokenUtxos()).filter((val) => val.token?.commitment !== undefined);
1268
- for (const utxo of utxos) {
1269
- if (!result[utxo.token.tokenId]) {
1270
- result[utxo.token.tokenId] = 0;
1271
- }
1272
- result[utxo.token.tokenId] += 1;
1273
- }
1274
- return result;
1275
- }
1276
438
  }
1277
439
  /**
1278
440
  * Class to manage a testnet wallet.
@@ -1283,10 +445,6 @@ export class TestNetWallet extends Wallet {
1283
445
  constructor(name = "") {
1284
446
  super(name, NetworkType.Testnet);
1285
447
  }
1286
- // interface to static util functions. see Util.ts
1287
- static get util() {
1288
- return TestNetUtil;
1289
- }
1290
448
  }
1291
449
  /**
1292
450
  * Class to manage a regtest wallet.
@@ -1296,10 +454,6 @@ export class RegTestWallet extends Wallet {
1296
454
  constructor(name = "") {
1297
455
  super(name, NetworkType.Regtest);
1298
456
  }
1299
- // interface to static util functions. see Util.ts
1300
- static get util() {
1301
- return RegTestUtil;
1302
- }
1303
457
  }
1304
458
  /**
1305
459
  * Class to manage a bitcoin cash wif wallet.
@@ -1310,10 +464,6 @@ export class WifWallet extends Wallet {
1310
464
  constructor(name = "") {
1311
465
  super(name, NetworkType.Mainnet, WalletTypeEnum.Wif);
1312
466
  }
1313
- // interface to static util functions. see Util.ts
1314
- static get util() {
1315
- return WifUtil;
1316
- }
1317
467
  }
1318
468
  /**
1319
469
  * Class to manage a testnet wif wallet.
@@ -1324,10 +474,6 @@ export class TestNetWifWallet extends Wallet {
1324
474
  constructor(name = "") {
1325
475
  super(name, NetworkType.Testnet, WalletTypeEnum.Wif);
1326
476
  }
1327
- // interface to static util functions. see Util.ts
1328
- static get util() {
1329
- return TestNetWifUtil;
1330
- }
1331
477
  }
1332
478
  /**
1333
479
  * Class to manage a regtest wif wallet.
@@ -1338,51 +484,5 @@ export class RegTestWifWallet extends Wallet {
1338
484
  constructor(name = "") {
1339
485
  super(name, NetworkType.Regtest, WalletTypeEnum.Wif);
1340
486
  }
1341
- // interface to static util functions. see Util.ts
1342
- static get util() {
1343
- return RegTestWifUtil;
1344
- }
1345
- }
1346
- /**
1347
- * Class to manage a bitcoin cash watch wallet.
1348
- */
1349
- export class WatchWallet extends Wallet {
1350
- static { this.networkPrefix = CashAddressNetworkPrefix.mainnet; }
1351
- static { this.walletType = WalletTypeEnum.Watch; }
1352
- constructor(name = "") {
1353
- super(name, NetworkType.Mainnet, WalletTypeEnum.Watch);
1354
- }
1355
- // interface to static util functions. see Util.ts
1356
- static get util() {
1357
- return WatchUtil;
1358
- }
1359
- }
1360
- /**
1361
- * Class to manage a testnet watch wallet.
1362
- */
1363
- export class TestNetWatchWallet extends Wallet {
1364
- static { this.networkPrefix = CashAddressNetworkPrefix.testnet; }
1365
- static { this.walletType = WalletTypeEnum.Watch; }
1366
- constructor(name = "") {
1367
- super(name, NetworkType.Testnet, WalletTypeEnum.Watch);
1368
- }
1369
- // interface to static util functions. see Util.ts
1370
- static get util() {
1371
- return TestNetWatchUtil;
1372
- }
1373
- }
1374
- /**
1375
- * Class to manage a regtest watch wallet.
1376
- */
1377
- export class RegTestWatchWallet extends Wallet {
1378
- static { this.networkPrefix = CashAddressNetworkPrefix.regtest; }
1379
- static { this.walletType = WalletTypeEnum.Watch; }
1380
- constructor(name = "") {
1381
- super(name, NetworkType.Regtest, WalletTypeEnum.Watch);
1382
- }
1383
- // interface to static util functions. see Util.ts
1384
- static get util() {
1385
- return RegTestWatchUtil;
1386
- }
1387
487
  }
1388
488
  //# sourceMappingURL=Wif.js.map