mainnet-js 2.6.7 → 2.7.0

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.0.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
package/src/wallet/Wif.ts CHANGED
@@ -1,201 +1,88 @@
1
1
  //#region Imports
2
- // Stable
3
- import {
4
- decodeCashAddress,
5
- encodeHdPublicKey,
6
- HdKeyNetwork,
7
- hexToBin,
8
- secp256k1,
9
- } from "@bitauth/libauth";
2
+ import { encodeHdPublicKey, HdKeyNetwork, secp256k1 } from "@bitauth/libauth";
10
3
 
11
- // Unstable?
12
4
  import {
13
5
  binToHex,
14
6
  CashAddressNetworkPrefix,
15
- CashAddressType,
16
- deriveHdPublicNode,
17
7
  decodePrivateKeyWif,
18
- encodeCashAddress,
19
- encodePrivateKeyWif,
20
- deriveHdPrivateNodeFromSeed,
21
8
  deriveHdPath,
9
+ deriveHdPrivateNodeFromSeed,
10
+ deriveHdPublicNode,
11
+ encodePrivateKeyWif,
22
12
  generatePrivateKey,
23
13
  } from "@bitauth/libauth";
24
14
 
25
- import { mnemonicToSeedSync, generateMnemonic } from "@scure/bip39";
26
- import { NetworkType, prefixFromNetworkMap, UnitEnum } from "../enum.js";
15
+ import { generateMnemonic, mnemonicToSeedSync } from "@scure/bip39";
16
+ import { NetworkType } from "../enum.js";
27
17
 
28
- import { Network, HexHeaderI, TxI, NFTCapability } from "../interface.js";
18
+ import { PrivateKeyI } from "../interface.js";
29
19
 
30
- import { networkPrefixMap } from "../enum.js";
31
- import { PrivateKeyI, UtxoI } from "../interface.js";
32
-
33
- import { BaseWallet } from "./Base.js";
34
- import { FeePaidByEnum, WalletTypeEnum } from "./enum.js";
35
- import {
36
- CancelWatchFn,
37
- SendRequestOptionsI,
38
- WaitForTransactionOptions,
39
- WaitForTransactionResponse,
40
- WalletInfoI,
41
- } from "./interface.js";
20
+ import { WalletTypeEnum } from "./enum.js";
21
+ import { MnemonicI, SendRequestOptionsI, WalletInfoI } from "./interface.js";
42
22
 
43
23
  import {
44
- fromUtxoId,
45
24
  OpReturnData,
46
25
  SendRequest,
47
26
  SendRequestArray,
48
- SendRequestType,
49
27
  SendResponse,
50
28
  SourceOutput,
51
- TokenBurnRequest,
52
- TokenGenesisRequest,
53
- TokenMintRequest,
54
29
  TokenSendRequest,
55
30
  XPubKey,
56
31
  } from "./model.js";
57
32
 
58
- import {
59
- buildEncodedTransaction,
60
- getSuitableUtxos,
61
- getFeeAmount,
62
- signUnsignedTransaction,
63
- getFeeAmountSimple,
64
- } from "../transaction/Wif.js";
33
+ import { signUnsignedTransaction } from "../transaction/Wif.js";
65
34
 
66
- import { asSendRequestObject } from "../util/asSendRequestObject.js";
67
- import {
68
- balanceFromSatoshi,
69
- balanceResponseFromSatoshi,
70
- BalanceResponse,
71
- } from "../util/balanceObjectFromSatoshi.js";
72
- import { checkWifNetwork } from "../util/checkWifNetwork.js";
73
- import {
74
- deriveCashaddr,
75
- deriveTokenaddr,
76
- toTokenaddr,
77
- } from "../util/deriveCashaddr.js";
78
- import {
79
- derivePrefix,
80
- derivePublicKeyHash,
81
- } from "../util/derivePublicKeyHash.js";
82
- import { checkForEmptySeed } from "../util/checkForEmptySeed.js";
83
- import { sanitizeUnit } from "../util/sanitizeUnit.js";
84
- import { sumTokenAmounts, sumUtxoValue } from "../util/sumUtxoValue.js";
85
- import { sumSendRequestAmounts } from "../util/sumSendRequestAmounts.js";
86
- import { ElectrumRawTransaction } from "../network/interface.js";
87
- import { getRelayFeeCache } from "../network/getRelayFeeCache.js";
88
- import {
89
- RegTestUtil,
90
- RegTestWatchUtil,
91
- RegTestWifUtil,
92
- TestNetUtil,
93
- TestNetWatchUtil,
94
- TestNetWifUtil,
95
- Util,
96
- WatchUtil,
97
- WifUtil,
98
- } from "./Util.js";
99
- import { getNetworkProvider } from "../network/index.js";
100
- import { generateRandomBytes } from "../util/randomBytes.js";
101
- import { SignedMessageI, SignedMessage } from "../message/index.js";
35
+ import { DERIVATION_PATHS } from "../constant.js";
36
+ import { SignedMessage, SignedMessageI } from "../message/index.js";
102
37
  import ElectrumNetworkProvider from "../network/ElectrumNetworkProvider.js";
103
- import { amountInSatoshi } from "../util/amountInSatoshi.js";
38
+ import { checkForEmptySeed } from "../util/checkForEmptySeed.js";
39
+ import { checkWifNetwork } from "../util/checkWifNetwork.js";
40
+ import { deriveCashaddr, deriveTokenaddr } from "../util/deriveCashaddr.js";
41
+ import { derivePublicKeyHash } from "../util/derivePublicKeyHash.js";
104
42
  import { getXPubKey } from "../util/getXPubKey.js";
105
- import { DERIVATION_PATHS, DUST_UTXO_THRESHOLD } from "../constant.js";
43
+ import { generateRandomBytes } from "../util/randomBytes.js";
106
44
 
107
- import { getAddressHistory } from "../history/electrumTransformer.js";
108
- import { IdentitySnapshot } from "./bcmr-v2.schema.js";
109
- import { BCMR } from "./Bcmr.js";
110
- import { qrAddress } from "../qr/Qr.js";
111
- import { ImageI } from "../qr/interface.js";
112
45
  import { Config } from "../config.js";
113
- import { checkUtxos } from "../util/checkUtxos.js";
114
- import { TransactionHistoryItem } from "../history/interface.js";
115
-
46
+ import {
47
+ BalanceResponse,
48
+ balanceResponseFromSatoshi,
49
+ } from "../util/balanceObjectFromSatoshi.js";
50
+ import { BaseWallet } from "./Base.js";
116
51
  //#endregion Imports
117
52
 
118
- const placeholderPrivateKey =
119
- "0000000000000000000000000000000000000000000000000000000000000001";
120
-
121
53
  /**
122
54
  * Class to manage a bitcoin cash wallet.
123
55
  */
124
56
  export class Wallet extends BaseWallet {
125
- static networkPrefix = CashAddressNetworkPrefix.mainnet;
126
-
127
- provider?: ElectrumNetworkProvider;
128
- cashaddr?: string;
129
- tokenaddr?: string;
57
+ declare provider: ElectrumNetworkProvider;
58
+ declare cashaddr: string;
59
+ declare tokenaddr: string;
130
60
  derivationPath: string = Config.DefaultParentDerivationPath + "/0/0";
131
61
  parentDerivationPath: string = Config.DefaultParentDerivationPath;
132
- parentXPubKey?: string;
133
- privateKey?: Uint8Array;
134
- publicKeyCompressed?: Uint8Array;
135
- privateKeyWif?: string;
136
- publicKey?: Uint8Array;
137
- publicKeyHash?: Uint8Array;
138
- networkPrefix: CashAddressNetworkPrefix;
139
- _slpSemiAware: boolean = false; // a flag which requires an utxo to have more than 546 sats to be spendable and counted in the balance
140
- _util?: Util;
62
+ mnemonic!: string;
63
+ parentXPubKey!: string;
64
+ privateKey!: Uint8Array;
65
+ publicKeyCompressed!: Uint8Array;
66
+ privateKeyWif!: string;
67
+ publicKey!: Uint8Array;
68
+ declare publicKeyHash: Uint8Array;
69
+ declare name: string;
141
70
  static signedMessage: SignedMessageI = new SignedMessage();
142
71
 
143
72
  //#region Accessors
144
- // interface to util functions. see Util.ts
145
- public get util() {
146
- if (!this._util) {
147
- this._util = new Util(this);
73
+ // Get mnemonic and derivation path for wallet
74
+ public getSeed(): MnemonicI {
75
+ if (!this.mnemonic) {
76
+ throw Error("Wallet mnemonic seed phrase not set");
148
77
  }
149
-
150
- return this._util;
151
- }
152
-
153
- // interface to util util. see Util.Util
154
- public static get util() {
155
- return Util;
156
- }
157
-
158
- public slpSemiAware(value: boolean = true): Wallet {
159
- this._slpSemiAware = value;
160
- return this;
161
- }
162
-
163
- public getNetworkProvider(network: Network = Network.MAINNET) {
164
- return getNetworkProvider(network);
165
- }
166
-
167
- /**
168
- * getTokenDepositAddress - get a cashtoken aware wallet deposit address
169
- *
170
- * @returns The cashtoken aware deposit address as a string
171
- */
172
- public getTokenDepositAddress(): string {
173
- return this.tokenaddr!;
174
- }
175
-
176
- /**
177
- * getDepositQr - get an address qrcode, encoded for display on the web
178
- *
179
- * @returns The qrcode for the token aware address
180
- */
181
- public getTokenDepositQr(): ImageI {
182
- return qrAddress(this.getTokenDepositAddress());
183
- }
184
-
185
- /**
186
- * explorerUrl Web url to a transaction on a block explorer
187
- *
188
- * @param txId transaction Id
189
- * @returns Url string
190
- */
191
- public explorerUrl(txId: string) {
192
- const explorerUrlMap = {
193
- mainnet: "https://blockchair.com/bitcoin-cash/transaction/",
194
- testnet: "https://www.blockchain.com/bch-testnet/tx/",
195
- regtest: "",
78
+ if (!this.derivationPath) {
79
+ throw Error("Wallet derivation path not set");
80
+ }
81
+ return {
82
+ seed: this.mnemonic,
83
+ derivationPath: this.derivationPath,
84
+ parentDerivationPath: this.parentDerivationPath,
196
85
  };
197
-
198
- return explorerUrlMap[this.network] + txId;
199
86
  }
200
87
 
201
88
  // Return wallet info
@@ -212,9 +99,9 @@ export class Wallet extends BaseWallet {
212
99
  ? this.getSeed().parentDerivationPath
213
100
  : undefined,
214
101
  parentXPubKey: this.parentXPubKey ? this.parentXPubKey : undefined,
215
- publicKey: this.publicKey ? binToHex(this.publicKey!) : undefined,
216
- publicKeyHash: binToHex(this.publicKeyHash!),
217
- privateKey: this.privateKey ? binToHex(this.privateKey!) : undefined,
102
+ publicKey: this.publicKey ? binToHex(this.publicKey) : undefined,
103
+ publicKeyHash: binToHex(this.publicKeyHash),
104
+ privateKey: this.privateKey ? binToHex(this.privateKey) : undefined,
218
105
  privateKeyWif: this.privateKeyWif,
219
106
  walletId: this.toString(),
220
107
  walletDbEntry: this.toDbString(),
@@ -224,7 +111,7 @@ export class Wallet extends BaseWallet {
224
111
  // returns the public key hash for an address
225
112
  public getPublicKey(hex = false): string | Uint8Array {
226
113
  if (this.publicKey) {
227
- return hex ? binToHex(this.publicKey!) : this.publicKey;
114
+ return hex ? binToHex(this.publicKey) : this.publicKey;
228
115
  } else {
229
116
  throw Error(
230
117
  "The 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."
@@ -236,7 +123,7 @@ export class Wallet extends BaseWallet {
236
123
  public getPublicKeyCompressed(hex = false): string | Uint8Array {
237
124
  if (this.publicKeyCompressed) {
238
125
  return hex
239
- ? binToHex(this.publicKeyCompressed!)
126
+ ? binToHex(this.publicKeyCompressed)
240
127
  : this.publicKeyCompressed;
241
128
  } else {
242
129
  throw Error(
@@ -244,18 +131,6 @@ export class Wallet extends BaseWallet {
244
131
  );
245
132
  }
246
133
  }
247
-
248
- // returns the public key hash for an address
249
- public getPublicKeyHash(hex = false): string | Uint8Array {
250
- if (this.publicKeyHash) {
251
- return hex ? binToHex(this.publicKeyHash!) : this.publicKeyHash;
252
- } else {
253
- throw Error(
254
- "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. "
255
- );
256
- }
257
- }
258
-
259
134
  //#endregion
260
135
 
261
136
  //#region Constructors and Statics
@@ -264,8 +139,24 @@ export class Wallet extends BaseWallet {
264
139
  network = NetworkType.Mainnet,
265
140
  walletType = WalletTypeEnum.Seed
266
141
  ) {
267
- super(name, network, walletType);
268
- this.networkPrefix = prefixFromNetworkMap[this.network];
142
+ super(network);
143
+ this.name = name;
144
+ this.walletType = walletType;
145
+ }
146
+
147
+ //#region Statics
148
+ /**
149
+ * fromId - create a wallet from encoded walletId string
150
+ *
151
+ * @param walletId walletId options to steer the creation process
152
+ *
153
+ * @returns wallet instantiated accordingly to the walletId rules
154
+ */
155
+ public static async fromId<T extends typeof Wallet>(
156
+ this: T,
157
+ walletId: string
158
+ ): Promise<InstanceType<T>> {
159
+ return new this().fromId(walletId) as InstanceType<T>;
269
160
  }
270
161
 
271
162
  /**
@@ -283,47 +174,42 @@ export class Wallet extends BaseWallet {
283
174
  }
284
175
 
285
176
  /**
286
- * fromCashaddr - create a watch-only wallet in the network derived from the address
177
+ * fromSeed - create a wallet using the seed phrase and derivation path
287
178
  *
288
- * such kind of wallet does not have a private key and is unable to spend any funds
289
- * however it still allows to use many utility functions such as getting and watching balance, etc.
179
+ * unless specified the derivation path m/44'/245'/0'/0/0 will be userd
180
+ * this derivation path is standard for Electron Cash SLP and other SLP enabled wallets
290
181
  *
291
- * @param address cashaddress of a wallet
182
+ * @param seed BIP39 12 word seed phrase
183
+ * @param derivationPath BIP44 HD wallet derivation path to get a single the private key from hierarchy
292
184
  *
293
185
  * @returns instantiated wallet
294
186
  */
295
- public static async fromCashaddr<T extends typeof Wallet>(
187
+ public static async fromSeed<T extends typeof Wallet>(
296
188
  this: T,
297
- address: string
189
+ seed: string,
190
+ derivationPath?: string
298
191
  ): Promise<InstanceType<T>> {
299
- const prefix = derivePrefix(address);
300
- const networkType = networkPrefixMap[prefix] as NetworkType;
301
- return new this("", networkType, WalletTypeEnum.Watch).watchOnly(
302
- address
303
- ) as InstanceType<T>;
192
+ return new this().fromSeed(seed, derivationPath) as InstanceType<T>;
304
193
  }
305
194
 
306
195
  /**
307
- * fromTokenaddr - create a watch-only wallet in the network derived from the address
196
+ * newRandom - create a random wallet
308
197
  *
309
- * such kind of wallet does not have a private key and is unable to spend any funds
310
- * however it still allows to use many utility functions such as getting and watching balance, etc.
198
+ * if `name` parameter is specified, the wallet will also be persisted to DB
311
199
  *
312
- * @param address token aware cashaddress of a wallet
200
+ * @param name user friendly wallet alias
201
+ * @param dbName name under which the wallet will be stored in the database
313
202
  *
314
203
  * @returns instantiated wallet
315
204
  */
316
- public static async fromTokenaddr<T extends typeof Wallet>(
205
+ public static async newRandom<T extends typeof Wallet>(
317
206
  this: T,
318
- address: string
207
+ name: string = "",
208
+ dbName?: string
319
209
  ): Promise<InstanceType<T>> {
320
- const prefix = derivePrefix(address);
321
- const networkType = networkPrefixMap[prefix] as NetworkType;
322
- return new this("", networkType, WalletTypeEnum.Watch).watchOnly(
323
- address
324
- ) as InstanceType<T>;
210
+ return new this().newRandom(name, dbName) as InstanceType<T>;
325
211
  }
326
- //#endregion Constructors and Statics
212
+ //#endregion Constructors
327
213
 
328
214
  //#region Protected implementations
329
215
  protected async generate(): Promise<this> {
@@ -343,7 +229,9 @@ export class Wallet extends BaseWallet {
343
229
 
344
230
  private async _generateWif() {
345
231
  if (!this.privateKey) {
346
- this.privateKey = generatePrivateKey(() => generateRandomBytes(32));
232
+ this.privateKey = generatePrivateKey(
233
+ () => generateRandomBytes(32) as Uint8Array
234
+ );
347
235
  }
348
236
  return this.deriveInfo();
349
237
  }
@@ -352,7 +240,7 @@ export class Wallet extends BaseWallet {
352
240
  this.mnemonic = generateMnemonic(Config.getWordlist());
353
241
  if (this.mnemonic.length == 0)
354
242
  throw Error("refusing to create wallet from empty mnemonic");
355
- const seed = mnemonicToSeedSync(this.mnemonic!);
243
+ const seed = mnemonicToSeedSync(this.mnemonic);
356
244
  checkForEmptySeed(seed);
357
245
  const network = this.isTestnet ? "testnet" : "mainnet";
358
246
  this.parentXPubKey = getXPubKey(seed, this.parentDerivationPath, network);
@@ -373,18 +261,46 @@ export class Wallet extends BaseWallet {
373
261
  }
374
262
 
375
263
  protected fromId = async (walletId: string): Promise<this> => {
376
- const [walletType, networkGiven, arg1]: string[] = walletId.split(":");
264
+ const [walletType, networkGiven, arg1, arg2]: string[] =
265
+ walletId.split(":");
377
266
 
378
- if (this.network != networkGiven) {
267
+ if (this.network !== networkGiven) {
379
268
  throw Error(`Network prefix ${networkGiven} to a ${this.network} wallet`);
380
269
  }
381
270
 
382
271
  // "wif:regtest:cNfsPtqN2bMRS7vH5qd8tR8GMvgXyL5BjnGAKgZ8DYEiCrCCQcP6"
383
- if (walletType === "wif") {
384
- return this.fromWIF(arg1);
385
- }
272
+ switch (walletType) {
273
+ case "wif":
274
+ return this.fromWIF(arg1);
275
+
276
+ case "watch":
277
+ if (arg2) {
278
+ // watch:testnet:bchtest:qq1234567
279
+ return this.watchOnly(`${arg1}:${arg2}`);
280
+ }
281
+ // watch:testnet:qq1234567
282
+ return this.watchOnly(`${arg1}`);
283
+
284
+ case "named":
285
+ if (arg2) {
286
+ // named:testnet:wallet_1:my_database
287
+ return this.named(arg1, arg2);
288
+ } else {
289
+ // named:testnet:wallet_1
290
+ return this.named(arg1);
291
+ }
292
+
293
+ case "seed":
294
+ if (arg2) {
295
+ // seed:testnet:table later ... stove kitten pluck:m/44'/0'/0'/0/0
296
+ return this.fromSeed(arg1, arg2);
297
+ }
298
+ // seed:testnet:table later ... stove kitten pluck
299
+ return this.fromSeed(arg1);
386
300
 
387
- return super.fromId(walletId);
301
+ default:
302
+ throw Error(`Unknown wallet type '${walletType}'`);
303
+ }
388
304
  };
389
305
 
390
306
  public async getXPubKeys(paths?) {
@@ -446,7 +362,7 @@ export class Wallet extends BaseWallet {
446
362
  public async deriveHdPaths(hdPaths: string[]): Promise<any[]> {
447
363
  if (!this.mnemonic)
448
364
  throw Error("refusing to create wallet from empty mnemonic");
449
- const seed = mnemonicToSeedSync(this.mnemonic!);
365
+ const seed = mnemonicToSeedSync(this.mnemonic);
450
366
  checkForEmptySeed(seed);
451
367
  const hdNode = deriveHdPrivateNodeFromSeed(seed, {
452
368
  assumeValidity: true, // TODO: we should switch to libauth's BIP39 implementation and set this to false
@@ -490,53 +406,6 @@ export class Wallet extends BaseWallet {
490
406
  });
491
407
  }
492
408
 
493
- // Initialize a watch only wallet from a cash addr
494
- protected async watchOnly(address: string): Promise<this> {
495
- this.walletType = WalletTypeEnum.Watch;
496
- const addressComponents = address.split(":");
497
- let addressPrefix, addressBase;
498
- if (addressComponents.length === 1) {
499
- addressBase = addressComponents.shift() as string;
500
- addressPrefix = derivePrefix(addressBase);
501
- } else {
502
- addressPrefix = addressComponents.shift() as string;
503
- addressBase = addressComponents.shift() as string;
504
- if (addressPrefix in networkPrefixMap) {
505
- if (networkPrefixMap[addressPrefix] != this.network) {
506
- throw Error(
507
- `a ${addressPrefix} address cannot be watched from a ${this.network} Walconst`
508
- );
509
- }
510
- }
511
- }
512
-
513
- const prefixedAddress = `${addressPrefix}:${addressBase}`;
514
-
515
- // check if a token aware address was provided
516
- const addressData = decodeCashAddress(prefixedAddress);
517
- if (typeof addressData === "string") throw addressData;
518
-
519
- this.publicKeyHash = addressData.payload;
520
-
521
- let nonTokenAwareType = addressData.type;
522
- if (nonTokenAwareType == CashAddressType.p2pkhWithTokens)
523
- nonTokenAwareType = CashAddressType.p2pkh;
524
- if (nonTokenAwareType == CashAddressType.p2shWithTokens)
525
- nonTokenAwareType = CashAddressType.p2sh;
526
- if (nonTokenAwareType == CashAddressType.p2pkh)
527
- this.publicKeyHash = addressData.payload;
528
-
529
- this.cashaddr = encodeCashAddress({
530
- prefix: addressData.prefix as CashAddressNetworkPrefix,
531
- type: nonTokenAwareType,
532
- payload: addressData.payload,
533
- }).address;
534
- this.address = this.cashaddr;
535
- this.tokenaddr = deriveTokenaddr(addressData.payload, this.networkPrefix);
536
-
537
- return this;
538
- }
539
-
540
409
  // Initialize wallet from Wallet Import Format
541
410
  protected async fromWIF(secret: string): Promise<this> {
542
411
  checkWifNetwork(secret, this.network);
@@ -554,32 +423,19 @@ export class Wallet extends BaseWallet {
554
423
  return this;
555
424
  }
556
425
 
426
+ /**
427
+ * newRandom (internal) if the wallet is named, get or create it; otherwise create a random
428
+ * unnamed wallet
429
+ * @param {string} name name of the wallet
430
+ * @param {string} dbName database name the wallet is stored in
431
+ */
557
432
  protected async newRandom(name: string, dbName?: string): Promise<this> {
558
433
  dbName = dbName ? dbName : this.networkPrefix;
559
- return super.newRandom(name, dbName);
560
- }
561
-
562
- protected async named(
563
- name: string,
564
- dbName?: string,
565
- forceNew: boolean = false
566
- ): Promise<this> {
567
- dbName = dbName ? dbName : this.networkPrefix;
568
- return super.named(name, dbName, forceNew);
569
- }
570
-
571
- protected async replaceNamed(
572
- name: string,
573
- walletId: string,
574
- dbName?: string
575
- ): Promise<this> {
576
- dbName = dbName ? dbName : this.networkPrefix;
577
- return super.replaceNamed(name, walletId, dbName);
578
- }
579
-
580
- protected async namedExists(name: string, dbName?: string): Promise<boolean> {
581
- dbName = dbName ? dbName : this.networkPrefix;
582
- return super.namedExists(name, dbName);
434
+ if (name.length > 0) {
435
+ return this.named(name, dbName);
436
+ } else {
437
+ return this.generate();
438
+ }
583
439
  }
584
440
  //#endregion Protected Implementations
585
441
 
@@ -587,25 +443,33 @@ export class Wallet extends BaseWallet {
587
443
  // Returns the serialized wallet as a string
588
444
  // If storing in a database, set asNamed to false to store secrets
589
445
  // In all other cases, the a named wallet is deserialized from the database
590
- // by the name key
446
+ // by the name key
591
447
  public toString() {
592
- const result = super.toString();
593
- if (result) return result;
594
-
595
- if (this.walletType === WalletTypeEnum.Wif) {
448
+ if (this.name) {
449
+ return `named:${this.network}:${this.name}`;
450
+ } else if (this.walletType == WalletTypeEnum.Seed) {
451
+ return `${this.walletType}:${this.network}:${this.mnemonic}:${this.derivationPath}`;
452
+ } else if (this.walletType === WalletTypeEnum.Wif) {
596
453
  return `${this.walletType}:${this.network}:${this.privateKeyWif}`;
454
+ } else if (this.walletType == WalletTypeEnum.Watch) {
455
+ return super.toString();
597
456
  }
598
457
 
599
458
  throw Error("toString unsupported wallet type");
600
459
  }
601
460
 
602
- //
461
+ /**
462
+ * toDbString - store the serialized version of the wallet in the database, not just the name
463
+ *
464
+ * @throws {Error} if called on BaseWallet
465
+ */
603
466
  public toDbString() {
604
- const result = super.toDbString();
605
- if (result) return result;
606
-
607
- if (this.walletType === WalletTypeEnum.Wif) {
467
+ if (this.walletType == WalletTypeEnum.Seed) {
468
+ return `${this.walletType}:${this.network}:${this.mnemonic}:${this.derivationPath}`;
469
+ } else if (this.walletType === WalletTypeEnum.Wif) {
608
470
  return `${this.walletType}:${this.network}:${this.privateKeyWif}`;
471
+ } else if (this.walletType == WalletTypeEnum.Watch) {
472
+ return super.toDbString();
609
473
  }
610
474
 
611
475
  throw Error("toDbString unsupported wallet type");
@@ -613,293 +477,6 @@ export class Wallet extends BaseWallet {
613
477
  //#endregion Serialization
614
478
 
615
479
  //#region Funds
616
- //
617
- public async getAddressUtxos(address?: string): Promise<UtxoI[]> {
618
- if (!address) {
619
- address = this.cashaddr!;
620
- }
621
-
622
- if (this._slpSemiAware) {
623
- const bchUtxos: UtxoI[] = await this.provider!.getUtxos(address);
624
- return bchUtxos.filter(
625
- (bchutxo) => bchutxo.satoshis > DUST_UTXO_THRESHOLD
626
- );
627
- } else {
628
- return await this.provider!.getUtxos(address);
629
- }
630
- }
631
-
632
- /**
633
- * utxos Get unspent outputs for the wallet
634
- *
635
- */
636
- public async getUtxos() {
637
- if (!this.cashaddr) {
638
- throw Error("Attempted to get utxos without an address");
639
- }
640
- return await this.getAddressUtxos(this.cashaddr);
641
- }
642
-
643
- // gets wallet balance in sats, bch and currency
644
- public async getBalance(
645
- rawUnit?: string,
646
- priceCache = true
647
- ): Promise<BalanceResponse | number> {
648
- if (rawUnit) {
649
- const unit = sanitizeUnit(rawUnit);
650
- return await balanceFromSatoshi(
651
- await this.getBalanceFromProvider(),
652
- unit,
653
- priceCache
654
- );
655
- } else {
656
- return await balanceResponseFromSatoshi(
657
- await this.getBalanceFromProvider(),
658
- priceCache
659
- );
660
- }
661
- }
662
-
663
- // Gets balance by summing value in all utxos in stats
664
- public async getBalanceFromUtxos(): Promise<number> {
665
- const utxos = (await this.getAddressUtxos(this.cashaddr!)).filter(
666
- (val) => val.token === undefined
667
- );
668
- return sumUtxoValue(utxos);
669
- }
670
-
671
- // Gets balance from fulcrum
672
- public async getBalanceFromProvider(): Promise<number> {
673
- // Fulcrum reports balance of all utxos, including tokens, which is undesirable
674
- // // TODO not sure why getting the balance from a provider doesn't work
675
- // if (this._slpAware || this._slpSemiAware) {
676
- // return await this.getBalanceFromUtxos();
677
- // } else {
678
- // return await this.provider!.getBalance(this.cashaddr!);
679
- // }
680
-
681
- // FIXME
682
- return this.getBalanceFromUtxos();
683
- }
684
-
685
- // watching for any transaction hash of this wallet
686
- public watchAddress(callback: (txHash: string) => void): CancelWatchFn {
687
- return (this.provider! as ElectrumNetworkProvider).watchAddress(
688
- this.getDepositAddress(),
689
- callback
690
- );
691
- }
692
-
693
- // watching for any transaction of this wallet
694
- public watchAddressTransactions(
695
- callback: (tx: ElectrumRawTransaction) => void
696
- ): CancelWatchFn {
697
- return (this.provider! as ElectrumNetworkProvider).watchAddressTransactions(
698
- this.getDepositAddress(),
699
- callback
700
- );
701
- }
702
-
703
- // watching for cashtoken transaction of this wallet
704
- public watchAddressTokenTransactions(
705
- callback: (tx: ElectrumRawTransaction) => void
706
- ): CancelWatchFn {
707
- return (
708
- this.provider! as ElectrumNetworkProvider
709
- ).watchAddressTokenTransactions(this.getDepositAddress(), callback);
710
- }
711
-
712
- // sets up a callback to be called upon wallet's balance change
713
- // can be cancelled by calling the function returned from this one
714
- public watchBalance(
715
- callback: (balance: BalanceResponse) => void
716
- ): CancelWatchFn {
717
- return (this.provider! as ElectrumNetworkProvider).watchAddressStatus(
718
- this.getDepositAddress(),
719
- async (_status: string) => {
720
- const balance = (await this.getBalance()) as BalanceResponse;
721
- callback(balance);
722
- }
723
- );
724
- }
725
-
726
- // sets up a callback to be called upon wallet's BCH or USD balance change
727
- // if BCH balance does not change, the callback will be triggered every
728
- // @param `usdPriceRefreshInterval` milliseconds by polling for new BCH USD price
729
- // Since we want to be most sensitive to usd value change, we do not use the cached exchange rates
730
- // can be cancelled by calling the function returned from this one
731
- public watchBalanceUsd(
732
- callback: (balance: BalanceResponse) => void,
733
- usdPriceRefreshInterval = 30000
734
- ): CancelWatchFn {
735
- let usdPrice = -1;
736
-
737
- const _callback = async () => {
738
- const balance = (await this.getBalance(
739
- undefined,
740
- false
741
- )) as BalanceResponse;
742
- if (usdPrice !== balance.usd!) {
743
- usdPrice = balance.usd!;
744
- callback(balance);
745
- }
746
- };
747
-
748
- const watchCancel = (
749
- this.provider! as ElectrumNetworkProvider
750
- ).watchAddressStatus(this.getDepositAddress(), _callback);
751
- const interval = setInterval(_callback, usdPriceRefreshInterval);
752
-
753
- return async () => {
754
- await watchCancel();
755
- clearInterval(interval);
756
- };
757
- }
758
-
759
- // waits for address balance to be greater than or equal to the target value
760
- // this call halts the execution
761
- public async waitForBalance(
762
- value: number,
763
- rawUnit: UnitEnum = UnitEnum.BCH
764
- ): Promise<BalanceResponse> {
765
- return new Promise(async (resolve) => {
766
- const watchCancel = this.watchBalance(
767
- async (balance: BalanceResponse) => {
768
- const satoshiBalance = await amountInSatoshi(value, rawUnit);
769
- if (balance.sat! >= satoshiBalance) {
770
- await watchCancel();
771
- resolve(balance);
772
- }
773
- }
774
- );
775
- });
776
- }
777
-
778
- // sets up a callback to be called upon wallet's token balance change
779
- // can be cancelled by calling the function returned from this one
780
- public watchTokenBalance(
781
- tokenId: string,
782
- callback: (balance: bigint) => void
783
- ): CancelWatchFn {
784
- let previous: bigint | undefined = undefined;
785
- return (this.provider! as ElectrumNetworkProvider).watchAddressStatus(
786
- this.getDepositAddress(),
787
- async (_status: string) => {
788
- const balance = await this.getTokenBalance(tokenId);
789
- if (previous != balance) {
790
- callback(balance);
791
- }
792
- previous = balance;
793
- }
794
- );
795
- }
796
-
797
- // waits for address token balance to be greater than or equal to the target amount
798
- // this call halts the execution
799
- public async waitForTokenBalance(
800
- tokenId: string,
801
- amount: bigint
802
- ): Promise<bigint> {
803
- return new Promise(async (resolve) => {
804
- const watchCancel = this.watchTokenBalance(
805
- tokenId,
806
- async (balance: bigint) => {
807
- if (balance >= amount) {
808
- await watchCancel();
809
- resolve(balance);
810
- }
811
- }
812
- );
813
- });
814
- }
815
-
816
- public async getTokenInfo(
817
- tokenId: string
818
- ): Promise<IdentitySnapshot | undefined> {
819
- return BCMR.getTokenInfo(tokenId);
820
- }
821
-
822
- private async _getMaxAmountToSend(
823
- params: {
824
- outputCount?: number;
825
- options?: SendRequestOptionsI;
826
- } = {
827
- outputCount: 1,
828
- options: {},
829
- }
830
- ): Promise<{ value: number; utxos: UtxoI[] }> {
831
- if (!this.privateKey && params.options?.buildUnsigned !== true) {
832
- throw Error("Couldn't get network or private key for wallet.");
833
- }
834
- if (!this.cashaddr) {
835
- throw Error("attempted to send without a cashaddr");
836
- }
837
-
838
- if (params.options && params.options.slpSemiAware) {
839
- this._slpSemiAware = true;
840
- }
841
-
842
- let feePaidBy;
843
- if (params.options && params.options.feePaidBy) {
844
- feePaidBy = params.options.feePaidBy;
845
- } else {
846
- feePaidBy = FeePaidByEnum.change;
847
- }
848
-
849
- // get inputs
850
- let utxos: UtxoI[];
851
- if (params.options && params.options.utxoIds) {
852
- utxos = await checkUtxos(
853
- params.options.utxoIds.map((utxoId: UtxoI | string) =>
854
- typeof utxoId === "string" ? fromUtxoId(utxoId) : utxoId
855
- ),
856
- this
857
- );
858
- } else {
859
- utxos = (await this.getAddressUtxos(this.cashaddr)).filter(
860
- (utxo) => !utxo.token
861
- );
862
- }
863
-
864
- // Get current height to assure recently mined coins are not spent.
865
- const bestHeight = await this.provider!.getBlockHeight();
866
-
867
- // simulate outputs using the sender's address
868
- const sendRequest = new SendRequest({
869
- cashaddr: this.cashaddr,
870
- value: 100,
871
- unit: "sat",
872
- });
873
- const sendRequests = Array(params.outputCount)
874
- .fill(0)
875
- .map(() => sendRequest);
876
-
877
- const fundingUtxos = await getSuitableUtxos(
878
- utxos,
879
- undefined,
880
- bestHeight,
881
- feePaidBy,
882
- sendRequests
883
- );
884
- const relayFeePerByteInSatoshi = await getRelayFeeCache(this.provider!);
885
- const fee = await getFeeAmountSimple({
886
- utxos: fundingUtxos,
887
- sendRequests: sendRequests,
888
- privateKey: this.privateKey ?? hexToBin(placeholderPrivateKey),
889
- sourceAddress: this.cashaddr!,
890
- relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
891
- feePaidBy: feePaidBy,
892
- });
893
- const spendableAmount = sumUtxoValue(fundingUtxos);
894
-
895
- let result = spendableAmount - fee;
896
- if (result < 0) {
897
- result = 0;
898
- }
899
-
900
- return { value: result, utxos: fundingUtxos };
901
- }
902
-
903
480
  public async getMaxAmountToSend(
904
481
  params: {
905
482
  outputCount?: number;
@@ -909,58 +486,15 @@ export class Wallet extends BaseWallet {
909
486
  options: {},
910
487
  }
911
488
  ): Promise<BalanceResponse> {
912
- const { value: result } = await this._getMaxAmountToSend(params);
489
+ const { value: result } = await this._getMaxAmountToSend({
490
+ options: params.options,
491
+ outputCount: params.outputCount,
492
+ privateKey: this.privateKey,
493
+ });
913
494
 
914
495
  return await balanceResponseFromSatoshi(result);
915
496
  }
916
497
 
917
- /**
918
- * send Send some amount to an address
919
- * this function processes the send requests, encodes the transaction, sends it to the network
920
- * @returns (depending on the options parameter) the transaction id, new address balance and a link to the transaction on the blockchain explorer
921
- *
922
- * This is a first class function with REST analog, maintainers should strive to keep backward-compatibility
923
- *
924
- */
925
- public async send(
926
- requests:
927
- | SendRequest
928
- | TokenSendRequest
929
- | OpReturnData
930
- | Array<SendRequest | TokenSendRequest | OpReturnData>
931
- | SendRequestArray[],
932
- options?: SendRequestOptionsI
933
- ): Promise<SendResponse> {
934
- const { encodedTransaction, tokenIds, sourceOutputs } =
935
- await this.encodeTransaction(requests, undefined, options);
936
-
937
- const resp = new SendResponse({});
938
- resp.tokenIds = tokenIds;
939
-
940
- if (options?.buildUnsigned !== true) {
941
- const txId = await this.submitTransaction(
942
- encodedTransaction,
943
- options?.awaitTransactionPropagation === undefined ||
944
- options?.awaitTransactionPropagation === true
945
- );
946
-
947
- resp.txId = txId;
948
- resp.explorerUrl = this.explorerUrl(resp.txId);
949
-
950
- if (
951
- options?.queryBalance === undefined ||
952
- options?.queryBalance === true
953
- ) {
954
- resp.balance = (await this.getBalance()) as BalanceResponse;
955
- }
956
- } else {
957
- resp.unsignedTransaction = binToHex(encodedTransaction);
958
- resp.sourceOutputs = sourceOutputs;
959
- }
960
-
961
- return resp;
962
- }
963
-
964
498
  /**
965
499
  * sendMax Send all available funds to a destination cash address
966
500
  *
@@ -973,68 +507,7 @@ export class Wallet extends BaseWallet {
973
507
  cashaddr: string,
974
508
  options?: SendRequestOptionsI
975
509
  ): Promise<SendResponse> {
976
- return await this.sendMaxRaw(cashaddr, options);
977
- }
978
-
979
- /**
980
- * sendMaxRaw (internal) Send all available funds to a destination cash address
981
- *
982
- * @param {string} cashaddr destination cash address
983
- * @param {SendRequestOptionsI} options Options of the send requests
984
- *
985
- * @returns the transaction id sent to the network
986
- */
987
- private async sendMaxRaw(
988
- cashaddr: string,
989
- options?: SendRequestOptionsI
990
- ): Promise<SendResponse> {
991
- const { value: maxSpendableAmount, utxos } = await this._getMaxAmountToSend(
992
- {
993
- outputCount: 1,
994
- options: options,
995
- }
996
- );
997
-
998
- if (!options) {
999
- options = {};
1000
- }
1001
-
1002
- options.utxoIds = utxos;
1003
-
1004
- const sendRequest = new SendRequest({
1005
- cashaddr: cashaddr,
1006
- value: maxSpendableAmount,
1007
- unit: "sat",
1008
- });
1009
-
1010
- const { encodedTransaction, tokenIds, sourceOutputs } =
1011
- await this.encodeTransaction([sendRequest], true, options);
1012
-
1013
- const resp = new SendResponse({});
1014
- resp.tokenIds = tokenIds;
1015
-
1016
- if (options?.buildUnsigned !== true) {
1017
- const txId = await this.submitTransaction(
1018
- encodedTransaction,
1019
- options?.awaitTransactionPropagation === undefined ||
1020
- options?.awaitTransactionPropagation === true
1021
- );
1022
-
1023
- resp.txId = txId;
1024
- resp.explorerUrl = this.explorerUrl(resp.txId);
1025
-
1026
- if (
1027
- options?.queryBalance === undefined ||
1028
- options?.queryBalance === true
1029
- ) {
1030
- resp.balance = (await this.getBalance()) as BalanceResponse;
1031
- }
1032
- } else {
1033
- resp.unsignedTransaction = binToHex(encodedTransaction);
1034
- resp.sourceOutputs = sourceOutputs;
1035
- }
1036
-
1037
- return resp;
510
+ return this.sendMaxRaw(cashaddr, options, this.privateKey);
1038
511
  }
1039
512
 
1040
513
  /**
@@ -1051,208 +524,15 @@ export class Wallet extends BaseWallet {
1051
524
  | Array<SendRequest | TokenSendRequest | OpReturnData>
1052
525
  | SendRequestArray[],
1053
526
  discardChange: boolean = false,
1054
- options?: SendRequestOptionsI
527
+ options?: SendRequestOptionsI,
528
+ privateKey?: Uint8Array
1055
529
  ) {
1056
- let sendRequests = asSendRequestObject(requests);
1057
-
1058
- if (!this.privateKey && options?.buildUnsigned !== true) {
1059
- throw new Error(
1060
- `Wallet ${this.name} is missing either a network or private key`
1061
- );
1062
- }
1063
- if (!this.cashaddr) {
1064
- throw Error("attempted to send without a cashaddr");
1065
- }
1066
-
1067
- if (options && options.slpSemiAware) {
1068
- this._slpSemiAware = true;
1069
- }
1070
-
1071
- let feePaidBy;
1072
- if (options && options.feePaidBy) {
1073
- feePaidBy = options.feePaidBy;
1074
- } else {
1075
- feePaidBy = FeePaidByEnum.change;
1076
- }
1077
-
1078
- let changeAddress;
1079
- if (options && options.changeAddress) {
1080
- changeAddress = options.changeAddress;
1081
- } else {
1082
- changeAddress = this.cashaddr!;
1083
- }
1084
-
1085
- let checkTokenQuantities: boolean = true;
1086
- if (options && options.checkTokenQuantities === false) {
1087
- checkTokenQuantities = false;
1088
- }
1089
-
1090
- // get inputs from options or query all inputs
1091
- let utxos: UtxoI[];
1092
- if (options && options.utxoIds) {
1093
- utxos = await checkUtxos(
1094
- options.utxoIds.map((utxoId: UtxoI | string) =>
1095
- typeof utxoId === "string" ? fromUtxoId(utxoId) : utxoId
1096
- ),
1097
- this
1098
- );
1099
- } else {
1100
- utxos = await this.getAddressUtxos(this.cashaddr);
1101
- }
1102
-
1103
- // filter out token utxos if there are no token requests
1104
- if (
1105
- checkTokenQuantities &&
1106
- !sendRequests.some((val) => val instanceof TokenSendRequest)
1107
- ) {
1108
- utxos = utxos.filter((val) => !val.token);
1109
- }
1110
-
1111
- const addTokenChangeOutputs = (
1112
- inputs: UtxoI[],
1113
- outputs: SendRequestType[]
1114
- ) => {
1115
- // Allow for implicit token burn if the total amount sent is less than user had
1116
- // allow for token genesis, creating more tokens than we had before (0)
1117
- if (!checkTokenQuantities) {
1118
- return;
1119
- }
1120
- const allTokenInputs = inputs.filter((val) => val.token);
1121
- const allTokenOutputs = outputs.filter(
1122
- (val) => val instanceof TokenSendRequest
1123
- ) as TokenSendRequest[];
1124
- const tokenIds = allTokenOutputs
1125
- .map((val) => val.tokenId)
1126
- .filter((val, idx, arr) => arr.indexOf(val) === idx);
1127
- for (let tokenId of tokenIds) {
1128
- const tokenInputs = allTokenInputs.filter(
1129
- (val) => val.token?.tokenId === tokenId
1130
- );
1131
- const inputAmountSum = tokenInputs.reduce(
1132
- (prev, cur) => prev + cur.token!.amount,
1133
- 0n
1134
- );
1135
- const tokenOutputs = allTokenOutputs.filter(
1136
- (val) => val.tokenId === tokenId
1137
- );
1138
- const outputAmountSum = tokenOutputs.reduce(
1139
- (prev, cur) => prev + cur.amount,
1140
- 0n
1141
- );
1142
-
1143
- const diff = inputAmountSum - outputAmountSum;
1144
- if (diff < 0) {
1145
- throw new Error("Not enough token amount to send");
1146
- }
1147
- if (diff >= 0) {
1148
- let available = 0n;
1149
- let change = 0n;
1150
- const ensureUtxos: UtxoI[] = [];
1151
- for (const token of tokenInputs.filter((val) => val.token?.amount)) {
1152
- ensureUtxos.push(token);
1153
- available += token.token?.amount!;
1154
- if (available >= outputAmountSum) {
1155
- change = available - outputAmountSum;
1156
- //break;
1157
- }
1158
- }
1159
- if (ensureUtxos.length) {
1160
- if (!options) {
1161
- options = {};
1162
- }
1163
- options!.ensureUtxos = [
1164
- ...(options.ensureUtxos ?? []),
1165
- ...ensureUtxos,
1166
- ].filter(
1167
- (val, index, array) =>
1168
- array.findIndex(
1169
- (other) => other.txid === val.txid && other.vout === val.vout
1170
- ) === index
1171
- );
1172
- }
1173
- if (change > 0) {
1174
- outputs.push(
1175
- new TokenSendRequest({
1176
- cashaddr: toTokenaddr(changeAddress) || this.tokenaddr!,
1177
- amount: change,
1178
- tokenId: tokenId,
1179
- commitment: tokenOutputs[0].commitment,
1180
- capability: tokenOutputs[0].capability,
1181
- value: tokenOutputs[0].value,
1182
- })
1183
- );
1184
- }
1185
- }
1186
- }
1187
- };
1188
- addTokenChangeOutputs(utxos, sendRequests);
1189
-
1190
- const bestHeight = await this.provider!.getBlockHeight()!;
1191
- const spendAmount = await sumSendRequestAmounts(sendRequests);
1192
-
1193
- if (utxos.length === 0) {
1194
- throw Error("There were no Unspent Outputs");
1195
- }
1196
- if (typeof spendAmount !== "bigint") {
1197
- throw Error("Couldn't get spend amount when building transaction");
1198
- }
1199
-
1200
- const relayFeePerByteInSatoshi = await getRelayFeeCache(this.provider!);
1201
- const feeEstimate = await getFeeAmountSimple({
1202
- utxos: utxos,
1203
- sendRequests: sendRequests,
1204
- privateKey: this.privateKey ?? hexToBin(placeholderPrivateKey),
1205
- sourceAddress: this.cashaddr!,
1206
- relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
1207
- feePaidBy: feePaidBy,
1208
- });
1209
-
1210
- const fundingUtxos = await getSuitableUtxos(
1211
- utxos,
1212
- BigInt(spendAmount) + BigInt(Math.ceil(feeEstimate)),
1213
- bestHeight,
1214
- feePaidBy,
1215
- sendRequests,
1216
- options?.ensureUtxos || [],
1217
- options?.tokenOperation
530
+ return super.encodeTransaction(
531
+ requests,
532
+ discardChange,
533
+ options,
534
+ this.privateKey
1218
535
  );
1219
- if (fundingUtxos.length === 0) {
1220
- throw Error(
1221
- "The available inputs couldn't satisfy the request with fees"
1222
- );
1223
- }
1224
- const fee = await getFeeAmount({
1225
- utxos: fundingUtxos,
1226
- sendRequests: sendRequests,
1227
- privateKey: this.privateKey ?? hexToBin(placeholderPrivateKey),
1228
- sourceAddress: this.cashaddr!,
1229
- relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
1230
- feePaidBy: feePaidBy,
1231
- });
1232
- const { encodedTransaction, sourceOutputs } = await buildEncodedTransaction(
1233
- {
1234
- inputs: fundingUtxos,
1235
- outputs: sendRequests,
1236
- signingKey: this.privateKey ?? hexToBin(placeholderPrivateKey),
1237
- sourceAddress: this.cashaddr!,
1238
- fee,
1239
- discardChange,
1240
- feePaidBy,
1241
- changeAddress,
1242
- buildUnsigned: options?.buildUnsigned === true,
1243
- }
1244
- );
1245
-
1246
- const tokenIds = [
1247
- ...fundingUtxos
1248
- .filter((val) => val.token?.tokenId)
1249
- .map((val) => val.token!.tokenId),
1250
- ...sendRequests
1251
- .filter((val) => val instanceof TokenSendRequest)
1252
- .map((val) => (val as TokenSendRequest).tokenId),
1253
- ].filter((value, index, array) => array.indexOf(value) === index);
1254
-
1255
- return { encodedTransaction, tokenIds, sourceOutputs };
1256
536
  }
1257
537
 
1258
538
  public async signUnsignedTransaction(
@@ -1263,202 +543,19 @@ export class Wallet extends BaseWallet {
1263
543
  throw Error("Can not sign a transaction with watch-only wallet.");
1264
544
  }
1265
545
 
1266
- return signUnsignedTransaction(
1267
- transaction,
1268
- sourceOutputs,
1269
- this.privateKey!
1270
- );
1271
- }
1272
-
1273
- // Submit a raw transaction
1274
- public async submitTransaction(
1275
- transaction: Uint8Array,
1276
- awaitPropagation: boolean = true
1277
- ): Promise<string> {
1278
- if (!this.provider) {
1279
- throw Error("Wallet network provider was not initialized");
1280
- }
1281
- let rawTransaction = binToHex(transaction);
1282
- return await this.provider.sendRawTransaction(
1283
- rawTransaction,
1284
- awaitPropagation
1285
- );
1286
- }
1287
-
1288
- // gets transaction history of this wallet
1289
- public async getRawHistory(
1290
- fromHeight: number = 0,
1291
- toHeight: number = -1
1292
- ): Promise<TxI[]> {
1293
- return await this.provider!.getHistory(
1294
- this.cashaddr!,
1295
- fromHeight,
1296
- toHeight
1297
- );
1298
- }
1299
-
1300
- /**
1301
- * getHistory gets transaction history of this wallet with most data decoded and ready to present to user
1302
- * @note balance calculations are valid only if querying to the blockchain tip (`toHeight` === -1, `count` === -1)
1303
- * @note this method is heavy on network calls, if invoked in browser use of cache is advised, @see `Config.UseLocalStorageCache`
1304
- * @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)
1305
- *
1306
- * @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
1307
- * @param fromHeight optional, if set, history will be limited. Default 0
1308
- * @param toHeight optional, if set, history will be limited. Default -1, meaning that all history items will be returned, including mempool
1309
- * @param start optional, if set, the result set will be paginated with offset `start`
1310
- * @param count optional, if set, the result set will be paginated with `count`. Default -1, meaning that all history items will be returned
1311
- *
1312
- * @returns an array of transaction history items, with input values and addresses encoded in cashaddress format. @see `TransactionHistoryItem` type
1313
- */
1314
- public async getHistory({
1315
- unit = "sat",
1316
- fromHeight = 0,
1317
- toHeight = -1,
1318
- start = 0,
1319
- count = -1,
1320
- }: {
1321
- unit?: UnitEnum;
1322
- fromHeight?: number;
1323
- toHeight?: number;
1324
- start?: number;
1325
- count?: number;
1326
- }): Promise<TransactionHistoryItem[]> {
1327
- return getAddressHistory({
1328
- address: this.cashaddr!,
1329
- provider: this.provider!,
1330
- unit,
1331
- fromHeight,
1332
- toHeight,
1333
- start,
1334
- count,
1335
- });
1336
- }
1337
-
1338
- // gets last transaction of this wallet
1339
- public async getLastTransaction(
1340
- confirmedOnly: boolean = false
1341
- ): Promise<ElectrumRawTransaction | null> {
1342
- let history: TxI[] = await this.getRawHistory();
1343
- if (confirmedOnly) {
1344
- history = history.filter((val) => val.height > 0);
1345
- }
1346
-
1347
- if (!history.length) {
1348
- return null;
1349
- }
1350
-
1351
- const [lastTx] = history.slice(-1);
1352
- return this.provider!.getRawTransactionObject(lastTx.tx_hash);
1353
- }
1354
-
1355
- // waits for next transaction, program execution is halted
1356
- public async waitForTransaction(
1357
- options: WaitForTransactionOptions = {
1358
- getTransactionInfo: true,
1359
- getBalance: false,
1360
- txHash: undefined,
1361
- }
1362
- ): Promise<WaitForTransactionResponse> {
1363
- if (options.getTransactionInfo === undefined) {
1364
- options.getTransactionInfo = true;
1365
- }
1366
-
1367
- return new Promise(async (resolve) => {
1368
- let txHashSeen = false;
1369
-
1370
- const makeResponse = async (txHash?: string) => {
1371
- const response = <WaitForTransactionResponse>{};
1372
- const promises: any[] = [undefined, undefined];
1373
-
1374
- if (options.getBalance === true) {
1375
- promises[0] = this.getBalance();
1376
- }
1377
-
1378
- if (options.getTransactionInfo === true) {
1379
- if (!txHash) {
1380
- promises[1] = this.getLastTransaction();
1381
- } else {
1382
- promises[1] = this.provider!.getRawTransactionObject(txHash);
1383
- }
1384
- }
1385
-
1386
- const result = await Promise.all(promises);
1387
- response.balance = result[0];
1388
- response.transactionInfo = result[1];
1389
-
1390
- return response;
1391
- };
1392
-
1393
- // waiting for a specific transaction to propagate
1394
- if (options.txHash) {
1395
- const waitForTransactionCallback = async (data) => {
1396
- if (data && data[0] === options.txHash!) {
1397
- txHashSeen = true;
1398
- this.provider!.unsubscribeFromTransaction(
1399
- options.txHash!,
1400
- waitForTransactionCallback
1401
- );
1402
-
1403
- resolve(makeResponse(options.txHash!));
1404
- }
1405
- };
1406
-
1407
- this.provider!.subscribeToTransaction(
1408
- options.txHash,
1409
- waitForTransactionCallback
1410
- );
1411
- return;
1412
- }
1413
-
1414
- // waiting for any address transaction
1415
- const watchCancel = (
1416
- this.provider! as ElectrumNetworkProvider
1417
- ).watchAddressStatus(this.getDepositAddress(), async (_status) => {
1418
- watchCancel();
1419
- resolve(makeResponse());
1420
- });
1421
- });
1422
- }
1423
-
1424
- /**
1425
- * watchBlocks Watch network blocks
1426
- *
1427
- * @param callback callback with a block header object
1428
- * @param skipCurrentHeight if set, the notification about current block will not arrive
1429
- *
1430
- * @returns a function which will cancel watching upon evaluation
1431
- */
1432
- public watchBlocks(
1433
- callback: (header: HexHeaderI) => void,
1434
- skipCurrentHeight: boolean = true
1435
- ): CancelWatchFn {
1436
- return (this.provider! as ElectrumNetworkProvider).watchBlocks(
1437
- callback,
1438
- skipCurrentHeight
1439
- );
1440
- }
1441
-
1442
- /**
1443
- * waitForBlock Wait for a network block
1444
- *
1445
- * @param height if specified waits for this exact blockchain height, otherwise resolves with the next block
1446
- *
1447
- */
1448
- public async waitForBlock(height?: number): Promise<HexHeaderI> {
1449
- return (this.provider! as ElectrumNetworkProvider).waitForBlock(height);
546
+ return signUnsignedTransaction(transaction, sourceOutputs, this.privateKey);
1450
547
  }
1451
548
  //#endregion Funds
1452
549
 
1453
550
  //#region Private implementation details
1454
551
  private async deriveInfo() {
1455
- const publicKey = secp256k1.derivePublicKeyUncompressed(this.privateKey!);
552
+ const publicKey = secp256k1.derivePublicKeyUncompressed(this.privateKey);
1456
553
  if (typeof publicKey === "string") {
1457
554
  throw new Error(publicKey);
1458
555
  }
1459
556
  this.publicKey = publicKey;
1460
557
  const publicKeyCompressed = secp256k1.derivePublicKeyCompressed(
1461
- this.privateKey!
558
+ this.privateKey
1462
559
  );
1463
560
  if (typeof publicKeyCompressed === "string") {
1464
561
  throw new Error(publicKeyCompressed);
@@ -1466,13 +563,12 @@ export class Wallet extends BaseWallet {
1466
563
  this.publicKeyCompressed = publicKeyCompressed;
1467
564
  const networkType =
1468
565
  this.network === NetworkType.Regtest ? NetworkType.Testnet : this.network;
1469
- this.privateKeyWif = encodePrivateKeyWif(this.privateKey!, networkType);
566
+ this.privateKeyWif = encodePrivateKeyWif(this.privateKey, networkType);
1470
567
  checkWifNetwork(this.privateKeyWif, this.network);
1471
568
 
1472
- this.cashaddr = deriveCashaddr(this.privateKey!, this.networkPrefix);
1473
- this.tokenaddr = deriveTokenaddr(this.privateKey!, this.networkPrefix);
1474
- this.address = this.cashaddr;
1475
- this.publicKeyHash = derivePublicKeyHash(this.cashaddr!);
569
+ this.cashaddr = deriveCashaddr(this.privateKey, this.networkPrefix);
570
+ this.tokenaddr = deriveTokenaddr(this.privateKey, this.networkPrefix);
571
+ this.publicKeyHash = derivePublicKeyHash(this.cashaddr);
1476
572
  return this;
1477
573
  }
1478
574
  //#endregion Private implementation details
@@ -1480,350 +576,8 @@ export class Wallet extends BaseWallet {
1480
576
  //#region Signing
1481
577
  // Convenience wrapper to sign interface
1482
578
  public async sign(message: string) {
1483
- return await Wallet.signedMessage.sign(message, this.privateKey!);
1484
- }
1485
-
1486
- // Convenience wrapper to verify interface
1487
- public async verify(message: string, sig: string, publicKey?: Uint8Array) {
1488
- return await Wallet.signedMessage.verify(
1489
- message,
1490
- sig,
1491
- this.cashaddr!,
1492
- publicKey
1493
- );
1494
- }
1495
- //#endregion Signing
1496
-
1497
- //#region Cashtokens
1498
- /**
1499
- * Create new cashtoken, both funglible and/or non-fungible (NFT)
1500
- * Refer to spec https://github.com/bitjson/cashtokens
1501
- * @param {number} genesisRequest.amount amount of *fungible* tokens to create
1502
- * @param {NFTCapability?} genesisRequest.capability capability of new NFT
1503
- * @param {string?} genesisRequest.commitment NFT commitment message
1504
- * @param {string?} genesisRequest.cashaddr cash address to send the created token UTXO to; if undefined will default to your address
1505
- * @param {number?} genesisRequest.value satoshi value to send alongside with tokens; if undefined will default to 1000 satoshi
1506
- * @param {SendRequestType | SendRequestType[]} sendRequests single or an array of extra send requests (OP_RETURN, value transfer, etc.) to include in genesis transaction
1507
- * @param {SendRequestOptionsI} options Options of the send requests
1508
- */
1509
- public async tokenGenesis(
1510
- genesisRequest: TokenGenesisRequest,
1511
- sendRequests: SendRequestType | SendRequestType[] = [],
1512
- options?: SendRequestOptionsI
1513
- ): Promise<SendResponse> {
1514
- if (!Array.isArray(sendRequests)) {
1515
- sendRequests = [sendRequests];
1516
- }
1517
-
1518
- let utxos: UtxoI[];
1519
- if (options && options.utxoIds) {
1520
- utxos = await checkUtxos(
1521
- options.utxoIds.map((utxoId: UtxoI | string) =>
1522
- typeof utxoId === "string" ? fromUtxoId(utxoId) : utxoId
1523
- ),
1524
- this
1525
- );
1526
- } else {
1527
- utxos = await this.getAddressUtxos(this.cashaddr);
1528
- }
1529
-
1530
- const genesisInputs = utxos.filter((val) => val.vout === 0 && !val.token);
1531
- if (genesisInputs.length === 0) {
1532
- throw new Error(
1533
- "No suitable inputs with vout=0 available for new token genesis"
1534
- );
1535
- }
1536
-
1537
- const genesisSendRequest = new TokenSendRequest({
1538
- cashaddr: genesisRequest.cashaddr || this.tokenaddr!,
1539
- amount: genesisRequest.amount,
1540
- value: genesisRequest.value || 1000,
1541
- capability: genesisRequest.capability,
1542
- commitment: genesisRequest.commitment,
1543
- tokenId: genesisInputs[0].txid,
1544
- });
1545
-
1546
- return this.send([genesisSendRequest, ...(sendRequests as any)], {
1547
- ...options,
1548
- utxoIds: utxos,
1549
- ensureUtxos: [genesisInputs[0]],
1550
- checkTokenQuantities: false,
1551
- queryBalance: false,
1552
- tokenOperation: "genesis",
1553
- });
1554
- }
1555
-
1556
- /**
1557
- * Mint new NFT cashtokens using an existing minting token
1558
- * Refer to spec https://github.com/bitjson/cashtokens
1559
- * @param {string} tokenId tokenId of an NFT to mint
1560
- * @param {TokenMintRequest | TokenMintRequest[]} mintRequests mint requests with new token properties and recipients
1561
- * @param {NFTCapability?} mintRequest.capability capability of new NFT
1562
- * @param {string?} mintRequest.commitment NFT commitment message
1563
- * @param {string?} mintRequest.cashaddr cash address to send the created token UTXO to; if undefined will default to your address
1564
- * @param {number?} mintRequest.value satoshi value to send alongside with tokens; if undefined will default to 1000 satoshi
1565
- * @param {boolean?} deductTokenAmount if minting token contains fungible amount, deduct from it by amount of minted tokens
1566
- * @param {SendRequestOptionsI} options Options of the send requests
1567
- */
1568
- public async tokenMint(
1569
- tokenId: string,
1570
- mintRequests: TokenMintRequest | Array<TokenMintRequest>,
1571
- deductTokenAmount: boolean = false,
1572
- options?: SendRequestOptionsI
1573
- ): Promise<SendResponse> {
1574
- if (tokenId?.length !== 64) {
1575
- throw Error(`Invalid tokenId supplied: ${tokenId}`);
1576
- }
1577
-
1578
- if (!Array.isArray(mintRequests)) {
1579
- mintRequests = [mintRequests];
1580
- }
1581
-
1582
- const utxos = await this.getAddressUtxos(this.cashaddr!);
1583
- const nftUtxos = utxos.filter(
1584
- (val) =>
1585
- val.token?.tokenId === tokenId &&
1586
- val.token?.capability === NFTCapability.minting
1587
- );
1588
- if (!nftUtxos.length) {
1589
- throw new Error(
1590
- "You do not have any token UTXOs with minting capability for specified tokenId"
1591
- );
1592
- }
1593
- const newAmount =
1594
- deductTokenAmount && nftUtxos[0].token!.amount > 0
1595
- ? nftUtxos[0].token!.amount - BigInt(mintRequests.length)
1596
- : nftUtxos[0].token!.amount;
1597
- const safeNewAmount = newAmount < 0n ? 0n : newAmount;
1598
- const mintingInput = new TokenSendRequest({
1599
- cashaddr: this.tokenaddr!,
1600
- tokenId: tokenId,
1601
- capability: nftUtxos[0].token!.capability,
1602
- commitment: nftUtxos[0].token!.commitment,
1603
- amount: safeNewAmount,
1604
- value: nftUtxos[0].satoshis,
1605
- });
1606
- return this.send(
1607
- [
1608
- mintingInput,
1609
- ...mintRequests.map(
1610
- (val) =>
1611
- new TokenSendRequest({
1612
- cashaddr: val.cashaddr || this.tokenaddr!,
1613
- amount: 0,
1614
- tokenId: tokenId,
1615
- value: val.value,
1616
- capability: val.capability,
1617
- commitment: val.commitment,
1618
- })
1619
- ),
1620
- ],
1621
- {
1622
- ...options,
1623
- ensureUtxos: [nftUtxos[0]],
1624
- checkTokenQuantities: false,
1625
- queryBalance: false,
1626
- tokenOperation: "mint",
1627
- }
1628
- );
1629
- }
1630
-
1631
- /**
1632
- * Perform an explicit token burning by spending a token utxo to an OP_RETURN
1633
- *
1634
- * Behaves differently for fungible and non-fungible tokens:
1635
- * * NFTs are always "destroyed"
1636
- * * FTs' amount is reduced by the amount specified, if 0 FT amount is left and no NFT present, the token is "destroyed"
1637
- *
1638
- * Refer to spec https://github.com/bitjson/cashtokens
1639
- * @param {string} burnRequest.tokenId tokenId of a token to burn
1640
- * @param {NFTCapability} burnRequest.capability capability of the NFT token to select, optional
1641
- * @param {string?} burnRequest.commitment commitment of the NFT token to select, optional
1642
- * @param {number?} burnRequest.amount amount of fungible tokens to burn, optional
1643
- * @param {string?} burnRequest.cashaddr address to return token and satoshi change to
1644
- * @param {string?} message optional message to include in OP_RETURN
1645
- * @param {SendRequestOptionsI} options Options of the send requests
1646
- */
1647
- public async tokenBurn(
1648
- burnRequest: TokenBurnRequest,
1649
- message?: string,
1650
- options?: SendRequestOptionsI
1651
- ): Promise<SendResponse> {
1652
- if (burnRequest.tokenId?.length !== 64) {
1653
- throw Error(`Invalid tokenId supplied: ${burnRequest.tokenId}`);
1654
- }
1655
-
1656
- const utxos = await this.getAddressUtxos(this.cashaddr!);
1657
- const tokenUtxos = utxos.filter(
1658
- (val) =>
1659
- val.token?.tokenId === burnRequest.tokenId &&
1660
- val.token?.capability === burnRequest.capability &&
1661
- val.token?.commitment === burnRequest.commitment
1662
- );
1663
-
1664
- if (!tokenUtxos.length) {
1665
- throw new Error("You do not have suitable token UTXOs to perform burn");
1666
- }
1667
-
1668
- const totalFungibleAmount = tokenUtxos.reduce(
1669
- (prev, cur) => prev + (cur.token?.amount || 0n),
1670
- 0n
1671
- );
1672
- let fungibleBurnAmount =
1673
- burnRequest.amount && burnRequest.amount > 0 ? burnRequest.amount! : 0n;
1674
- fungibleBurnAmount = BigInt(fungibleBurnAmount);
1675
- const hasNFT = burnRequest.capability || burnRequest.commitment;
1676
-
1677
- let utxoIds: UtxoI[] = [];
1678
- let changeSendRequests: TokenSendRequest[];
1679
- if (hasNFT) {
1680
- // does not have FT tokens, let us destroy the token completely
1681
- if (totalFungibleAmount === 0n) {
1682
- changeSendRequests = [];
1683
- utxoIds.push(tokenUtxos[0]);
1684
- } else {
1685
- // add utxos to spend from
1686
- let available = 0n;
1687
- for (const token of tokenUtxos.filter((val) => val.token?.amount)) {
1688
- utxoIds.push(token);
1689
- available += token.token?.amount!;
1690
- if (available >= fungibleBurnAmount) {
1691
- break;
1692
- }
1693
- }
1694
-
1695
- // if there are FT, reduce their amount
1696
- const newAmount = totalFungibleAmount - fungibleBurnAmount;
1697
- const safeNewAmount = newAmount < 0n ? 0n : newAmount;
1698
- changeSendRequests = [
1699
- new TokenSendRequest({
1700
- cashaddr: burnRequest.cashaddr || this.tokenaddr!,
1701
- tokenId: burnRequest.tokenId,
1702
- capability: burnRequest.capability,
1703
- commitment: burnRequest.commitment,
1704
- amount: safeNewAmount,
1705
- value: tokenUtxos[0].satoshis,
1706
- }),
1707
- ];
1708
- }
1709
- } else {
1710
- // if we are burning last fungible tokens, let us destroy the token completely
1711
- if (totalFungibleAmount === fungibleBurnAmount) {
1712
- changeSendRequests = [];
1713
- utxoIds.push(...tokenUtxos);
1714
- } else {
1715
- // add utxos to spend from
1716
- let available = 0n;
1717
- for (const token of tokenUtxos.filter((val) => val.token?.amount)) {
1718
- utxoIds.push(token);
1719
- available += token.token?.amount!;
1720
- if (available >= fungibleBurnAmount) {
1721
- break;
1722
- }
1723
- }
1724
-
1725
- // reduce the FT amount
1726
- const newAmount = available - fungibleBurnAmount;
1727
- const safeNewAmount = newAmount < 0n ? 0n : newAmount;
1728
- changeSendRequests = [
1729
- new TokenSendRequest({
1730
- cashaddr: burnRequest.cashaddr || this.tokenaddr!,
1731
- tokenId: burnRequest.tokenId,
1732
- amount: safeNewAmount,
1733
- value: tokenUtxos.reduce((a, c) => a + c.satoshis, 0),
1734
- }),
1735
- ];
1736
- }
1737
- }
1738
-
1739
- const opReturn = OpReturnData.fromString(message || "");
1740
- return this.send([opReturn, ...changeSendRequests], {
1741
- ...options,
1742
- checkTokenQuantities: false,
1743
- queryBalance: false,
1744
- ensureUtxos: utxoIds.length > 0 ? utxoIds : undefined,
1745
- tokenOperation: "burn",
1746
- });
1747
- }
1748
-
1749
- /**
1750
- * getTokenUtxos Get unspent token outputs for the wallet
1751
- * will return utxos only for the specified token if `tokenId` provided
1752
- * @param {string?} tokenId tokenId (category) to filter utxos by, if not set will return utxos from all tokens
1753
- * @returns {UtxoI[]} token utxos
1754
- */
1755
- public async getTokenUtxos(tokenId?: string): Promise<UtxoI[]> {
1756
- const utxos = await this.getAddressUtxos(this.address!);
1757
- return utxos.filter((val) =>
1758
- tokenId ? val.token?.tokenId === tokenId : val.token
1759
- );
1760
- }
1761
-
1762
- /**
1763
- * getTokenBalance Gets fungible token balance
1764
- * for NFT token balance see @ref getNftTokenBalance
1765
- * @param {string} tokenId tokenId to get balance for
1766
- * @returns {bigint} fungible token balance
1767
- */
1768
- public async getTokenBalance(tokenId: string): Promise<bigint> {
1769
- const utxos = (await this.getTokenUtxos(tokenId)).filter(
1770
- (val) => val.token?.amount
1771
- );
1772
- return sumTokenAmounts(utxos, tokenId);
1773
- }
1774
-
1775
- /**
1776
- * getNftTokenBalance Gets non-fungible token (NFT) balance for a particular tokenId
1777
- * disregards fungible token balances
1778
- * for fungible token balance see @ref getTokenBalance
1779
- * @param {string} tokenId tokenId to get balance for
1780
- * @returns {number} non-fungible token balance
1781
- */
1782
- public async getNftTokenBalance(tokenId: string): Promise<number> {
1783
- const utxos = (await this.getTokenUtxos(tokenId)).filter(
1784
- (val) => val.token?.commitment !== undefined
1785
- );
1786
- return utxos.length;
1787
- }
1788
-
1789
- /**
1790
- * getAllTokenBalances Gets all fungible token balances in this wallet
1791
- * @returns {Object} a map [tokenId => balance] for all tokens in this wallet
1792
- */
1793
- public async getAllTokenBalances(): Promise<{ [tokenId: string]: bigint }> {
1794
- const result = {};
1795
- const utxos = (await this.getTokenUtxos()).filter(
1796
- (val) => val.token?.amount
1797
- );
1798
- for (const utxo of utxos) {
1799
- if (!result[utxo.token!.tokenId]) {
1800
- result[utxo.token!.tokenId] = 0n;
1801
- }
1802
- result[utxo.token!.tokenId] += utxo.token!.amount;
1803
- }
1804
- return result;
1805
- }
1806
-
1807
- /**
1808
- * getAllNftTokenBalances Gets all non-fungible token (NFT) balances in this wallet
1809
- * @returns {Object} a map [tokenId => balance] for all NFTs in this wallet
1810
- */
1811
- public async getAllNftTokenBalances(): Promise<{
1812
- [tokenId: string]: number;
1813
- }> {
1814
- const result = {};
1815
- const utxos = (await this.getTokenUtxos()).filter(
1816
- (val) => val.token?.commitment !== undefined
1817
- );
1818
- for (const utxo of utxos) {
1819
- if (!result[utxo.token!.tokenId]) {
1820
- result[utxo.token!.tokenId] = 0;
1821
- }
1822
- result[utxo.token!.tokenId] += 1;
1823
- }
1824
- return result;
579
+ return await Wallet.signedMessage.sign(message, this.privateKey);
1825
580
  }
1826
- //#endregion Cashtokens
1827
581
  }
1828
582
 
1829
583
  /**
@@ -1835,11 +589,6 @@ export class TestNetWallet extends Wallet {
1835
589
  constructor(name = "") {
1836
590
  super(name, NetworkType.Testnet);
1837
591
  }
1838
-
1839
- // interface to static util functions. see Util.ts
1840
- public static get util() {
1841
- return TestNetUtil;
1842
- }
1843
592
  }
1844
593
 
1845
594
  /**
@@ -1850,11 +599,6 @@ export class RegTestWallet extends Wallet {
1850
599
  constructor(name = "") {
1851
600
  super(name, NetworkType.Regtest);
1852
601
  }
1853
-
1854
- // interface to static util functions. see Util.ts
1855
- public static get util() {
1856
- return RegTestUtil;
1857
- }
1858
602
  }
1859
603
 
1860
604
  /**
@@ -1866,11 +610,6 @@ export class WifWallet extends Wallet {
1866
610
  constructor(name = "") {
1867
611
  super(name, NetworkType.Mainnet, WalletTypeEnum.Wif);
1868
612
  }
1869
-
1870
- // interface to static util functions. see Util.ts
1871
- public static get util() {
1872
- return WifUtil;
1873
- }
1874
613
  }
1875
614
 
1876
615
  /**
@@ -1882,11 +621,6 @@ export class TestNetWifWallet extends Wallet {
1882
621
  constructor(name = "") {
1883
622
  super(name, NetworkType.Testnet, WalletTypeEnum.Wif);
1884
623
  }
1885
-
1886
- // interface to static util functions. see Util.ts
1887
- public static get util() {
1888
- return TestNetWifUtil;
1889
- }
1890
624
  }
1891
625
 
1892
626
  /**
@@ -1898,57 +632,4 @@ export class RegTestWifWallet extends Wallet {
1898
632
  constructor(name = "") {
1899
633
  super(name, NetworkType.Regtest, WalletTypeEnum.Wif);
1900
634
  }
1901
-
1902
- // interface to static util functions. see Util.ts
1903
- public static get util() {
1904
- return RegTestWifUtil;
1905
- }
1906
- }
1907
-
1908
- /**
1909
- * Class to manage a bitcoin cash watch wallet.
1910
- */
1911
- export class WatchWallet extends Wallet {
1912
- static networkPrefix = CashAddressNetworkPrefix.mainnet;
1913
- static walletType = WalletTypeEnum.Watch;
1914
- constructor(name = "") {
1915
- super(name, NetworkType.Mainnet, WalletTypeEnum.Watch);
1916
- }
1917
-
1918
- // interface to static util functions. see Util.ts
1919
- public static get util() {
1920
- return WatchUtil;
1921
- }
1922
- }
1923
-
1924
- /**
1925
- * Class to manage a testnet watch wallet.
1926
- */
1927
- export class TestNetWatchWallet extends Wallet {
1928
- static networkPrefix = CashAddressNetworkPrefix.testnet;
1929
- static walletType = WalletTypeEnum.Watch;
1930
- constructor(name = "") {
1931
- super(name, NetworkType.Testnet, WalletTypeEnum.Watch);
1932
- }
1933
-
1934
- // interface to static util functions. see Util.ts
1935
- public static get util() {
1936
- return TestNetWatchUtil;
1937
- }
1938
- }
1939
-
1940
- /**
1941
- * Class to manage a regtest watch wallet.
1942
- */
1943
- export class RegTestWatchWallet extends Wallet {
1944
- static networkPrefix = CashAddressNetworkPrefix.regtest;
1945
- static walletType = WalletTypeEnum.Watch;
1946
- constructor(name = "") {
1947
- super(name, NetworkType.Regtest, WalletTypeEnum.Watch);
1948
- }
1949
-
1950
- // interface to static util functions. see Util.ts
1951
- public static get util() {
1952
- return RegTestWatchUtil;
1953
- }
1954
635
  }