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.
- package/dist/index.html +1 -1
- package/dist/{mainnet-2.6.7.js → mainnet-2.7.1.js} +446 -156
- package/dist/module/cli.js +0 -4
- package/dist/module/cli.js.map +1 -1
- package/dist/module/index.d.ts +1 -3
- package/dist/module/index.d.ts.map +1 -1
- package/dist/module/index.js +1 -3
- package/dist/module/index.js.map +1 -1
- package/dist/module/network/ElectrumNetworkProvider.d.ts +21 -27
- package/dist/module/network/ElectrumNetworkProvider.d.ts.map +1 -1
- package/dist/module/network/ElectrumNetworkProvider.js +92 -102
- package/dist/module/network/ElectrumNetworkProvider.js.map +1 -1
- package/dist/module/network/NetworkProvider.d.ts +9 -22
- package/dist/module/network/NetworkProvider.d.ts.map +1 -1
- package/dist/module/network/constant.d.ts +0 -21
- package/dist/module/network/constant.d.ts.map +1 -1
- package/dist/module/network/constant.js +0 -21
- package/dist/module/network/constant.js.map +1 -1
- package/dist/module/network/default.d.ts +2 -2
- package/dist/module/network/default.d.ts.map +1 -1
- package/dist/module/network/default.js +15 -45
- package/dist/module/network/default.js.map +1 -1
- package/dist/module/network/interface.d.ts +2 -8
- package/dist/module/network/interface.d.ts.map +1 -1
- package/dist/module/network/util.d.ts.map +1 -1
- package/dist/module/network/util.js +4 -5
- package/dist/module/network/util.js.map +1 -1
- package/dist/module/rate/ExchangeRate.js +2 -1
- package/dist/module/rate/ExchangeRate.js.map +1 -1
- package/dist/module/transaction/Wif.d.ts.map +1 -1
- package/dist/module/transaction/Wif.js +1 -1
- package/dist/module/transaction/Wif.js.map +1 -1
- package/dist/module/wallet/Base.d.ts +282 -88
- package/dist/module/wallet/Base.d.ts.map +1 -1
- package/dist/module/wallet/Base.js +1058 -215
- package/dist/module/wallet/Base.js.map +1 -1
- package/dist/module/wallet/Util.d.ts +7 -54
- package/dist/module/wallet/Util.d.ts.map +1 -1
- package/dist/module/wallet/Util.js +12 -79
- package/dist/module/wallet/Util.js.map +1 -1
- package/dist/module/wallet/Wif.d.ts +46 -251
- package/dist/module/wallet/Wif.d.ts.map +1 -1
- package/dist/module/wallet/Wif.js +126 -1026
- package/dist/module/wallet/Wif.js.map +1 -1
- package/dist/module/wallet/createWallet.d.ts +2 -1
- package/dist/module/wallet/createWallet.d.ts.map +1 -1
- package/dist/module/wallet/createWallet.js +2 -3
- package/dist/module/wallet/createWallet.js.map +1 -1
- package/dist/module/wallet/interface.d.ts +2 -4
- package/dist/module/wallet/interface.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -12
- package/src/cli.ts +0 -4
- package/src/index.ts +1 -5
- package/src/network/ElectrumNetworkProvider.ts +133 -188
- package/src/network/NetworkProvider.ts +9 -30
- package/src/network/Rpc.test.ts +14 -5
- package/src/network/constant.ts +0 -23
- package/src/network/default.ts +26 -66
- package/src/network/electrum.test.ts +2 -4
- package/src/network/interface.ts +2 -9
- package/src/network/util.ts +6 -7
- package/src/rate/ExchangeRate.test.ts +1 -1
- package/src/rate/ExchangeRate.ts +2 -1
- package/{polyfill/json.js → src/test/json.test.ts} +7 -1
- package/src/transaction/Wif.ts +2 -1
- package/src/wallet/Base.ts +1520 -273
- package/src/wallet/Cashtokens.test.headless.js +1 -1
- package/src/wallet/Cashtokens.test.ts +7 -8
- package/src/wallet/Util.ts +20 -102
- package/src/wallet/Wif.bip39.test.ts +3 -3
- package/src/wallet/Wif.test.ts +31 -25
- package/src/wallet/Wif.ts +174 -1493
- package/src/wallet/Wif.watchOnly.test.ts +5 -5
- package/src/wallet/createWallet.ts +11 -10
- package/src/wallet/interface.ts +3 -4
- package/webpack.config.cjs +4 -55
- package/dist/module/qr/Qr.d.ts +0 -9
- package/dist/module/qr/Qr.d.ts.map +0 -1
- package/dist/module/qr/Qr.js +0 -22
- package/dist/module/qr/Qr.js.map +0 -1
- package/dist/module/qr/interface.d.ts +0 -6
- package/dist/module/qr/interface.d.ts.map +0 -1
- package/dist/module/qr/interface.js +0 -2
- package/dist/module/qr/interface.js.map +0 -1
- package/dist/module/util/eventsource.d.ts +0 -3
- package/dist/module/util/eventsource.d.ts.map +0 -1
- package/dist/module/util/eventsource.js +0 -11
- package/dist/module/util/eventsource.js.map +0 -1
- package/polyfill/README.md +0 -1
- package/polyfill/eventsource.js +0 -6
- package/polyfill/support/types.js +0 -286
- package/polyfill/util.cjs +0 -249
- package/src/network/default.test.ts +0 -37
- package/src/qr/Qr.test.ts +0 -14
- package/src/qr/Qr.ts +0 -24
- package/src/qr/interface.ts +0 -5
- package/src/util/eventsource.ts +0 -12
|
@@ -1,94 +1,42 @@
|
|
|
1
1
|
//#region Imports
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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
|
-
//
|
|
46
|
-
|
|
47
|
-
if (!this.
|
|
48
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
158
|
-
|
|
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.
|
|
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
|
-
*
|
|
150
|
+
* fromSeed - create a wallet using the seed phrase and derivation path
|
|
176
151
|
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
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
|
|
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
|
|
185
|
-
|
|
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
|
-
*
|
|
164
|
+
* newRandom - create a random wallet
|
|
191
165
|
*
|
|
192
|
-
*
|
|
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
|
|
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
|
|
200
|
-
|
|
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
|
|
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
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
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
|
-
//
|
|
339
|
+
// by the name key
|
|
409
340
|
toString() {
|
|
410
|
-
|
|
411
|
-
|
|
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
|
-
|
|
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.
|
|
436
|
-
|
|
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
|
|
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
|
-
*
|
|
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
|
-
|
|
448
|
-
if (
|
|
449
|
-
|
|
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
|
-
|
|
364
|
+
else if (this.walletType === WalletTypeEnum.Wif) {
|
|
365
|
+
return `${this.walletType}:${this.network}:${this.privateKeyWif}`;
|
|
588
366
|
}
|
|
589
|
-
|
|
590
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|