mainnet-js 1.0.7 → 1.0.9
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-1.0.7.js → mainnet-1.0.9.js} +19 -9
- package/dist/module/config.d.ts +4 -0
- package/dist/module/config.d.ts.map +1 -0
- package/dist/module/config.js +5 -0
- package/dist/module/config.js.map +1 -0
- package/dist/module/index.d.ts +1 -0
- package/dist/module/index.d.ts.map +1 -1
- package/dist/module/index.js +2 -0
- package/dist/module/index.js.map +1 -1
- package/dist/module/mine/mine.d.ts.map +1 -1
- package/dist/module/mine/mine.js +2 -2
- package/dist/module/mine/mine.js.map +1 -1
- package/dist/module/network/ElectrumNetworkProvider.d.ts +6 -2
- package/dist/module/network/ElectrumNetworkProvider.d.ts.map +1 -1
- package/dist/module/network/ElectrumNetworkProvider.js +69 -45
- package/dist/module/network/ElectrumNetworkProvider.js.map +1 -1
- package/dist/module/network/interface.d.ts +11 -8
- package/dist/module/network/interface.d.ts.map +1 -1
- package/dist/module/transaction/Wif.d.ts +1 -1
- package/dist/module/transaction/Wif.d.ts.map +1 -1
- package/dist/module/transaction/Wif.js +17 -9
- package/dist/module/transaction/Wif.js.map +1 -1
- package/dist/module/util/deriveCashaddr.d.ts +1 -0
- package/dist/module/util/deriveCashaddr.d.ts.map +1 -1
- package/dist/module/util/deriveCashaddr.js +5 -0
- package/dist/module/util/deriveCashaddr.js.map +1 -1
- package/dist/module/util/index.d.ts +1 -0
- package/dist/module/util/index.d.ts.map +1 -1
- package/dist/module/util/index.js +1 -0
- package/dist/module/util/index.js.map +1 -1
- package/dist/module/wallet/Util.d.ts.map +1 -1
- package/dist/module/wallet/Util.js +1 -0
- package/dist/module/wallet/Util.js.map +1 -1
- package/dist/module/wallet/Wif.d.ts +3 -0
- package/dist/module/wallet/Wif.d.ts.map +1 -1
- package/dist/module/wallet/Wif.js +20 -26
- package/dist/module/wallet/Wif.js.map +1 -1
- package/dist/module/wallet/interface.d.ts +1 -0
- package/dist/module/wallet/interface.d.ts.map +1 -1
- package/dist/module/wallet/model.d.ts.map +1 -1
- package/dist/module/wallet/model.js +4 -3
- package/dist/module/wallet/model.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/config.ts +4 -0
- package/src/index.ts +3 -0
- package/src/mine/mine.ts +3 -2
- package/src/network/ElectrumNetworkProvider.ts +99 -51
- package/src/network/interface.ts +12 -12
- package/src/transaction/Wif.ts +25 -10
- package/src/util/deriveCashaddr.ts +6 -0
- package/src/util/index.ts +1 -0
- package/src/wallet/Cashtokens.test.ts +149 -6
- package/src/wallet/Util.ts +1 -0
- package/src/wallet/Wif.test.ts +5 -4
- package/src/wallet/Wif.ts +35 -27
- package/src/wallet/interface.ts +1 -0
- package/src/wallet/model.ts +6 -3
package/src/network/interface.ts
CHANGED
|
@@ -19,23 +19,21 @@ export interface ElectrumClusterParams {
|
|
|
19
19
|
timeout: number;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
export interface ElectrumTokenData {
|
|
23
|
+
amount: string;
|
|
24
|
+
category: string;
|
|
25
|
+
nft?: {
|
|
26
|
+
capability?: NFTCapability;
|
|
27
|
+
commitment?: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
22
31
|
export interface ElectrumUtxo {
|
|
23
32
|
tx_pos: number;
|
|
24
33
|
value: number;
|
|
25
34
|
tx_hash: string;
|
|
26
35
|
height: number;
|
|
27
|
-
token_data
|
|
28
|
-
| {
|
|
29
|
-
amount: string;
|
|
30
|
-
category: string;
|
|
31
|
-
nft:
|
|
32
|
-
| {
|
|
33
|
-
capability: NFTCapability | undefined;
|
|
34
|
-
commitment: string | undefined;
|
|
35
|
-
}
|
|
36
|
-
| undefined;
|
|
37
|
-
}
|
|
38
|
-
| undefined;
|
|
36
|
+
token_data?: ElectrumTokenData;
|
|
39
37
|
}
|
|
40
38
|
|
|
41
39
|
export interface ElectrumRawTransaction {
|
|
@@ -65,12 +63,14 @@ export interface ElectrumRawTransactionVin {
|
|
|
65
63
|
vout: number;
|
|
66
64
|
value?: number; // optional extention by mainnet.cash
|
|
67
65
|
address?: string; // optional extension by mainnet.cash
|
|
66
|
+
tokenData?: ElectrumTokenData; // optional extension by mainnet.cash
|
|
68
67
|
}
|
|
69
68
|
|
|
70
69
|
export interface ElectrumRawTransactionVout {
|
|
71
70
|
n: number;
|
|
72
71
|
scriptPubKey: ElectrumRawTransactionVoutScriptPubKey;
|
|
73
72
|
value: number;
|
|
73
|
+
tokenData?: ElectrumTokenData;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
export interface ElectrumRawTransactionVoutScriptPubKey {
|
package/src/transaction/Wif.ts
CHANGED
|
@@ -240,8 +240,8 @@ export function prepareTokenOutputs(
|
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
if (
|
|
243
|
-
token
|
|
244
|
-
|
|
243
|
+
tokenInputs[0].token?.capability === NFTCapability.none &&
|
|
244
|
+
tokenInputs[0].token?.commitment !== token.commitment
|
|
245
245
|
) {
|
|
246
246
|
throw new Error("Can not change the commitment of an immutable token");
|
|
247
247
|
}
|
|
@@ -286,7 +286,8 @@ export async function getSuitableUtxos(
|
|
|
286
286
|
amountRequired: BigInt | undefined,
|
|
287
287
|
bestHeight: number,
|
|
288
288
|
feePaidBy: FeePaidByEnum,
|
|
289
|
-
requests: SendRequestType[]
|
|
289
|
+
requests: SendRequestType[],
|
|
290
|
+
ensureUtxos: UtxoI[] = []
|
|
290
291
|
): Promise<UtxoI[]> {
|
|
291
292
|
let suitableUtxos: UtxoI[] = [];
|
|
292
293
|
let amountAvailable = BigInt(0);
|
|
@@ -294,6 +295,16 @@ export async function getSuitableUtxos(
|
|
|
294
295
|
const tokenRequests = requests.filter(
|
|
295
296
|
(val) => val instanceof TokenSendRequest
|
|
296
297
|
) as TokenSendRequest[];
|
|
298
|
+
|
|
299
|
+
// if we do a new token genesis, we shall filter all token utxos out
|
|
300
|
+
const isTokenGenesis = tokenRequests.some(
|
|
301
|
+
(val) => !val.tokenId || (val as any)._isGenesis
|
|
302
|
+
);
|
|
303
|
+
const bchOnlyTransfer = tokenRequests.length === 0;
|
|
304
|
+
let filteredInputs =
|
|
305
|
+
isTokenGenesis || bchOnlyTransfer
|
|
306
|
+
? inputs.slice(0).filter((val) => !val.token)
|
|
307
|
+
: inputs.slice();
|
|
297
308
|
const tokenIds = tokenRequests
|
|
298
309
|
.map((val) => val.tokenId)
|
|
299
310
|
.filter((value, index, array) => array.indexOf(value) === index);
|
|
@@ -304,8 +315,6 @@ export async function getSuitableUtxos(
|
|
|
304
315
|
tokenAmountsRequired.push({ tokenId, requiredAmount });
|
|
305
316
|
}
|
|
306
317
|
|
|
307
|
-
let filteredInputs = inputs.slice(0);
|
|
308
|
-
|
|
309
318
|
// find suitable token inputs first
|
|
310
319
|
for (const { tokenId, requiredAmount } of tokenAmountsRequired) {
|
|
311
320
|
let tokenAmountAvailable = 0;
|
|
@@ -325,10 +334,10 @@ export async function getSuitableUtxos(
|
|
|
325
334
|
}
|
|
326
335
|
}
|
|
327
336
|
|
|
328
|
-
// find plain outputs
|
|
337
|
+
// find plain bch outputs
|
|
329
338
|
for (const u of filteredInputs) {
|
|
330
339
|
if (u.token) {
|
|
331
|
-
|
|
340
|
+
continue;
|
|
332
341
|
}
|
|
333
342
|
|
|
334
343
|
if (u.coinbase && u.height && bestHeight) {
|
|
@@ -347,14 +356,20 @@ export async function getSuitableUtxos(
|
|
|
347
356
|
}
|
|
348
357
|
}
|
|
349
358
|
|
|
359
|
+
const addEnsured = (suitableUtxos) => {
|
|
360
|
+
return [...suitableUtxos, ...ensureUtxos].filter(
|
|
361
|
+
(val, index, array) => array.indexOf(val) === index
|
|
362
|
+
);
|
|
363
|
+
};
|
|
364
|
+
|
|
350
365
|
// if the fee is split with a feePaidBy option, skip checking change.
|
|
351
366
|
if (feePaidBy && feePaidBy != FeePaidByEnum.change) {
|
|
352
|
-
return suitableUtxos;
|
|
367
|
+
return addEnsured(suitableUtxos);
|
|
353
368
|
}
|
|
354
369
|
|
|
355
370
|
// If the amount needed is met, or no amount is given, return
|
|
356
371
|
if (typeof amountRequired === "undefined") {
|
|
357
|
-
return suitableUtxos;
|
|
372
|
+
return addEnsured(suitableUtxos);
|
|
358
373
|
} else if (amountAvailable < amountRequired) {
|
|
359
374
|
let e = Error(
|
|
360
375
|
`Amount required was not met, ${amountRequired} satoshis needed, ${amountAvailable} satoshis available`
|
|
@@ -365,7 +380,7 @@ export async function getSuitableUtxos(
|
|
|
365
380
|
};
|
|
366
381
|
throw e;
|
|
367
382
|
} else {
|
|
368
|
-
return suitableUtxos;
|
|
383
|
+
return addEnsured(suitableUtxos);
|
|
369
384
|
}
|
|
370
385
|
}
|
|
371
386
|
|
|
@@ -131,3 +131,9 @@ export function isTokenaddr(address: string): boolean {
|
|
|
131
131
|
].indexOf(result.version) !== -1
|
|
132
132
|
);
|
|
133
133
|
}
|
|
134
|
+
|
|
135
|
+
export function checkTokenaddr(cashaddr: string, enforce: boolean) {
|
|
136
|
+
if (enforce && !isTokenaddr(cashaddr)) {
|
|
137
|
+
throw new Error("Error trying to send to a non-tokenaware cash address");
|
|
138
|
+
}
|
|
139
|
+
}
|
package/src/util/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ export { convert, convertObject } from "./convert.js";
|
|
|
12
12
|
export { delay } from "./delay.js";
|
|
13
13
|
export { derivedNetwork } from "./deriveNetwork.js";
|
|
14
14
|
export { derivePublicKeyHash } from "./derivePublicKeyHash.js";
|
|
15
|
+
export * from "./deriveCashaddr.js";
|
|
15
16
|
export {
|
|
16
17
|
getAddrsByXpubKey,
|
|
17
18
|
getAddrsByXpubKeyObject,
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { RegTestWallet, TestNetWallet, Wallet } from "./Wif";
|
|
2
2
|
import { initProviders, disconnectProviders } from "../network/Connection";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
SendRequest,
|
|
5
|
+
SendResponse,
|
|
6
|
+
TokenMintRequest,
|
|
7
|
+
TokenSendRequest,
|
|
8
|
+
} from "./model";
|
|
4
9
|
import { Network, NFTCapability } from "../interface";
|
|
5
10
|
import { binToHex, utf8ToBin } from "@bitauth/libauth";
|
|
6
11
|
import { delay } from "../util";
|
|
12
|
+
import { Config } from "../config";
|
|
7
13
|
|
|
8
14
|
beforeAll(async () => {
|
|
9
15
|
await initProviders();
|
|
@@ -194,9 +200,12 @@ describe(`Test cashtokens`, () => {
|
|
|
194
200
|
const response = await alice.tokenMint(tokenId, [
|
|
195
201
|
new TokenMintRequest({
|
|
196
202
|
cashaddr: alice.cashaddr!,
|
|
203
|
+
commitment: "test",
|
|
204
|
+
capability: NFTCapability.none,
|
|
197
205
|
}),
|
|
198
206
|
new TokenMintRequest({
|
|
199
207
|
cashaddr: alice.cashaddr!,
|
|
208
|
+
commitment: "test2",
|
|
200
209
|
}),
|
|
201
210
|
]);
|
|
202
211
|
expect(await alice.getTokenBalance(tokenId)).toBe(0);
|
|
@@ -313,7 +322,7 @@ describe(`Test cashtokens`, () => {
|
|
|
313
322
|
expect(tokenId).toEqual(response.tokenIds![0]);
|
|
314
323
|
});
|
|
315
324
|
|
|
316
|
-
test("Test explicit burning of FT
|
|
325
|
+
test("Test explicit burning of FT and NFT", async () => {
|
|
317
326
|
const alice = await RegTestWallet.fromId(process.env.ALICE_ID!);
|
|
318
327
|
const genesisResponse = await alice.tokenGenesis({
|
|
319
328
|
cashaddr: alice.cashaddr!,
|
|
@@ -439,13 +448,15 @@ describe(`Test cashtokens`, () => {
|
|
|
439
448
|
expect(bobUtxos[1].satoshis).toBe(3349);
|
|
440
449
|
});
|
|
441
450
|
|
|
442
|
-
test("Test cashtoken waiting and watching
|
|
451
|
+
test("Test cashtoken waiting and watching", async () => {
|
|
443
452
|
const alice = await RegTestWallet.fromId(process.env.ALICE_ID!);
|
|
444
453
|
const bob = await RegTestWallet.newRandom();
|
|
445
454
|
|
|
446
455
|
const genesisResponse = await alice.tokenGenesis({
|
|
447
456
|
amount: 100,
|
|
448
457
|
value: 5000,
|
|
458
|
+
capability: NFTCapability.minting,
|
|
459
|
+
commitment: "test",
|
|
449
460
|
cashaddr: alice.cashaddr!,
|
|
450
461
|
});
|
|
451
462
|
|
|
@@ -457,16 +468,17 @@ describe(`Test cashtokens`, () => {
|
|
|
457
468
|
expect(tokenUtxos[0].satoshis).toBe(5000);
|
|
458
469
|
|
|
459
470
|
let seenBalance = 0;
|
|
471
|
+
let sendResponse: SendResponse = {};
|
|
460
472
|
setTimeout(
|
|
461
|
-
() =>
|
|
462
|
-
alice.send([
|
|
473
|
+
async () =>
|
|
474
|
+
(sendResponse = await alice.send([
|
|
463
475
|
new TokenSendRequest({
|
|
464
476
|
cashaddr: bob.cashaddr!,
|
|
465
477
|
amount: 100,
|
|
466
478
|
tokenId: tokenId,
|
|
467
479
|
value: 1500,
|
|
468
480
|
}),
|
|
469
|
-
]),
|
|
481
|
+
])),
|
|
470
482
|
0
|
|
471
483
|
);
|
|
472
484
|
|
|
@@ -474,10 +486,141 @@ describe(`Test cashtokens`, () => {
|
|
|
474
486
|
seenBalance = balance;
|
|
475
487
|
});
|
|
476
488
|
|
|
489
|
+
let bobTxId = ".";
|
|
490
|
+
const txCancel = bob.watchAddressTokenTransactions((tx) => {
|
|
491
|
+
bobTxId = tx.txid;
|
|
492
|
+
});
|
|
493
|
+
|
|
477
494
|
const balance = await bob.waitForTokenBalance(tokenId, 100);
|
|
495
|
+
await delay(500);
|
|
478
496
|
expect(balance).toBe(100);
|
|
479
497
|
expect(seenBalance).toBe(100);
|
|
498
|
+
expect(sendResponse.txId).toBe(bobTxId);
|
|
480
499
|
await cancel();
|
|
500
|
+
await txCancel();
|
|
481
501
|
await delay(500);
|
|
482
502
|
});
|
|
503
|
+
|
|
504
|
+
test("Test double genesis should not burn tokens", async () => {
|
|
505
|
+
const alice = await RegTestWallet.fromId(process.env.ALICE_ID!);
|
|
506
|
+
const bob = await RegTestWallet.newRandom();
|
|
507
|
+
|
|
508
|
+
// prepare inputs for two token geneses
|
|
509
|
+
await alice.send({ cashaddr: bob.cashaddr!, value: 10000, unit: "sat" });
|
|
510
|
+
await alice.send({ cashaddr: bob.cashaddr!, value: 10000, unit: "sat" });
|
|
511
|
+
|
|
512
|
+
const genesisResponse = await bob.tokenGenesis({
|
|
513
|
+
amount: 100,
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
const tokenId = genesisResponse.tokenIds![0];
|
|
517
|
+
const tokenBalance = await bob.getTokenBalance(tokenId);
|
|
518
|
+
|
|
519
|
+
expect(tokenBalance).toBe(100);
|
|
520
|
+
const tokenUtxos = await bob.getTokenUtxos(tokenId);
|
|
521
|
+
expect(tokenUtxos.length).toBe(1);
|
|
522
|
+
|
|
523
|
+
const genesis2Response = await bob.tokenGenesis({
|
|
524
|
+
amount: 200,
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
const tokenId2 = genesis2Response.tokenIds![0];
|
|
528
|
+
const tokenBalance2 = await bob.getTokenBalance(tokenId2);
|
|
529
|
+
expect(tokenBalance2).toBe(200);
|
|
530
|
+
const tokenUtxos2 = await bob.getTokenUtxos(tokenId2);
|
|
531
|
+
expect(tokenUtxos2.length).toBe(1);
|
|
532
|
+
|
|
533
|
+
expect((await bob.getTokenUtxos()).length).toBe(2);
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
test("Test sending tokens should not burn tokens", async () => {
|
|
537
|
+
const alice = await RegTestWallet.fromId(process.env.ALICE_ID!);
|
|
538
|
+
const bob = await RegTestWallet.newRandom();
|
|
539
|
+
|
|
540
|
+
// prepare inputs for two token geneses
|
|
541
|
+
await alice.send({ cashaddr: bob.cashaddr!, value: 10000, unit: "sat" });
|
|
542
|
+
await alice.send({ cashaddr: bob.cashaddr!, value: 10000, unit: "sat" });
|
|
543
|
+
|
|
544
|
+
const genesisResponse = await bob.tokenGenesis({
|
|
545
|
+
amount: 100,
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
const tokenId = genesisResponse.tokenIds![0];
|
|
549
|
+
const tokenBalance = await bob.getTokenBalance(tokenId);
|
|
550
|
+
|
|
551
|
+
expect(tokenBalance).toBe(100);
|
|
552
|
+
const tokenUtxos = await bob.getTokenUtxos(tokenId);
|
|
553
|
+
expect(tokenUtxos.length).toBe(1);
|
|
554
|
+
|
|
555
|
+
const genesis2Response = await bob.tokenGenesis({
|
|
556
|
+
amount: 200,
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
const tokenId2 = genesis2Response.tokenIds![0];
|
|
560
|
+
const tokenBalance2 = await bob.getTokenBalance(tokenId2);
|
|
561
|
+
expect(tokenBalance2).toBe(200);
|
|
562
|
+
const tokenUtxos2 = await bob.getTokenUtxos(tokenId2);
|
|
563
|
+
expect(tokenUtxos2.length).toBe(1);
|
|
564
|
+
|
|
565
|
+
expect((await bob.getTokenUtxos()).length).toBe(2);
|
|
566
|
+
|
|
567
|
+
const charlie = await RegTestWallet.newRandom();
|
|
568
|
+
await bob.send({
|
|
569
|
+
cashaddr: charlie.cashaddr!,
|
|
570
|
+
tokenId: tokenId,
|
|
571
|
+
amount: 50,
|
|
572
|
+
});
|
|
573
|
+
expect((await bob.getTokenUtxos()).length).toBe(2);
|
|
574
|
+
expect((await charlie.getTokenUtxos()).length).toBe(1);
|
|
575
|
+
expect(await bob.getTokenBalance(tokenId)).toBe(50);
|
|
576
|
+
expect(await charlie.getTokenBalance(tokenId)).toBe(50);
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
test("Test sending bch should not burn tokens", async () => {
|
|
580
|
+
const alice = await RegTestWallet.fromId(process.env.ALICE_ID!);
|
|
581
|
+
const bob = await RegTestWallet.newRandom();
|
|
582
|
+
|
|
583
|
+
// prepare inputs for two token geneses
|
|
584
|
+
await alice.send({ cashaddr: bob.cashaddr!, value: 10000, unit: "sat" });
|
|
585
|
+
|
|
586
|
+
const genesisResponse = await bob.tokenGenesis({
|
|
587
|
+
amount: 100,
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
const tokenId = genesisResponse.tokenIds![0];
|
|
591
|
+
const tokenBalance = await bob.getTokenBalance(tokenId);
|
|
592
|
+
|
|
593
|
+
expect(tokenBalance).toBe(100);
|
|
594
|
+
const tokenUtxos = await bob.getTokenUtxos(tokenId);
|
|
595
|
+
expect(tokenUtxos.length).toBe(1);
|
|
596
|
+
|
|
597
|
+
await bob.send({ cashaddr: alice.cashaddr!, value: 1000, unit: "sat" });
|
|
598
|
+
|
|
599
|
+
const tokenBalance2 = await bob.getTokenBalance(tokenId);
|
|
600
|
+
expect(tokenBalance2).toBe(100);
|
|
601
|
+
const tokenUtxos2 = await bob.getTokenUtxos(tokenId);
|
|
602
|
+
expect(tokenUtxos2.length).toBe(1);
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
test("Test enforcing tokenaddresses", async () => {
|
|
606
|
+
const bob = await RegTestWallet.newRandom();
|
|
607
|
+
|
|
608
|
+
const previousValue = Config.EnforceCashTokenReceiptAddresses;
|
|
609
|
+
|
|
610
|
+
const wrap = (addr) => {
|
|
611
|
+
return new Promise(() => {
|
|
612
|
+
return new TokenSendRequest({ cashaddr: addr, tokenId: "" });
|
|
613
|
+
});
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
Config.EnforceCashTokenReceiptAddresses = false;
|
|
617
|
+
expect(wrap(bob.cashaddr)).resolves.not.toThrow();
|
|
618
|
+
expect(wrap(bob.tokenaddr)).resolves.not.toThrow();
|
|
619
|
+
|
|
620
|
+
Config.EnforceCashTokenReceiptAddresses = true;
|
|
621
|
+
expect(wrap(bob.cashaddr)).rejects.toThrow();
|
|
622
|
+
expect(wrap(bob.tokenaddr)).resolves.not.toThrow();
|
|
623
|
+
|
|
624
|
+
Config.EnforceCashTokenReceiptAddresses = previousValue;
|
|
625
|
+
});
|
|
483
626
|
});
|
package/src/wallet/Util.ts
CHANGED
package/src/wallet/Wif.test.ts
CHANGED
|
@@ -806,11 +806,11 @@ describe(`Wallet subscriptions`, () => {
|
|
|
806
806
|
});
|
|
807
807
|
|
|
808
808
|
let bobWatchResult = false;
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
).watchAddressStatus(bob.getDepositAddress(), (_status) => {
|
|
809
|
+
let bobTransactionId = "";
|
|
810
|
+
const bobWatchCancel = bob.watchAddress((txHash) => {
|
|
812
811
|
bobWatchCancel();
|
|
813
812
|
bobWatchResult = true;
|
|
813
|
+
bobTransactionId = txHash;
|
|
814
814
|
});
|
|
815
815
|
|
|
816
816
|
let bobBalanceWatchResult = false;
|
|
@@ -847,7 +847,7 @@ describe(`Wallet subscriptions`, () => {
|
|
|
847
847
|
blockNumberWaitResult = true;
|
|
848
848
|
}, 0);
|
|
849
849
|
|
|
850
|
-
await alice.send({
|
|
850
|
+
const sendResponse = await alice.send({
|
|
851
851
|
cashaddr: bob.getDepositAddress(),
|
|
852
852
|
value: 0.001,
|
|
853
853
|
unit: "bch",
|
|
@@ -862,6 +862,7 @@ describe(`Wallet subscriptions`, () => {
|
|
|
862
862
|
expect(waitBalanceResult).toBe(true);
|
|
863
863
|
expect(aliceWatchResult).toBe(true);
|
|
864
864
|
expect(bobWatchResult).toBe(true);
|
|
865
|
+
expect(bobTransactionId).toBe(sendResponse.txId);
|
|
865
866
|
expect(bobBalanceWatchResult).toBe(true);
|
|
866
867
|
expect(blockWatchResult).toBe(true);
|
|
867
868
|
expect(blockWaitResult).toBe(true);
|
package/src/wallet/Wif.ts
CHANGED
|
@@ -711,25 +711,32 @@ export class Wallet extends BaseWallet {
|
|
|
711
711
|
return this.getBalanceFromUtxos();
|
|
712
712
|
}
|
|
713
713
|
|
|
714
|
-
//
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
//
|
|
714
|
+
// watching for any transaction hash of this wallet
|
|
715
|
+
public watchAddress(callback: (txHash: string) => void): CancelWatchFn {
|
|
716
|
+
return (this.provider! as ElectrumNetworkProvider).watchAddress(
|
|
717
|
+
this.getDepositAddress(),
|
|
718
|
+
callback
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// watching for any transaction of this wallet
|
|
723
|
+
public watchAddressTransactions(
|
|
724
|
+
callback: (tx: ElectrumRawTransaction) => void
|
|
725
|
+
): CancelWatchFn {
|
|
726
|
+
return (this.provider! as ElectrumNetworkProvider).watchAddressTransactions(
|
|
727
|
+
this.getDepositAddress(),
|
|
728
|
+
callback
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
// watching for cashtoken transaction of this wallet
|
|
733
|
+
public watchAddressTokenTransactions(
|
|
734
|
+
callback: (tx: ElectrumRawTransaction) => void
|
|
735
|
+
): CancelWatchFn {
|
|
736
|
+
return (
|
|
737
|
+
this.provider! as ElectrumNetworkProvider
|
|
738
|
+
).watchAddressTokenTransactions(this.getDepositAddress(), callback);
|
|
739
|
+
}
|
|
733
740
|
|
|
734
741
|
// sets up a callback to be called upon wallet's balance change
|
|
735
742
|
// can be cancelled by calling the function returned from this one
|
|
@@ -1157,7 +1164,7 @@ export class Wallet extends BaseWallet {
|
|
|
1157
1164
|
if (change > 0) {
|
|
1158
1165
|
outputs.push(
|
|
1159
1166
|
new TokenSendRequest({
|
|
1160
|
-
cashaddr: changeAddress || this.
|
|
1167
|
+
cashaddr: changeAddress || this.tokenaddr!,
|
|
1161
1168
|
amount: change,
|
|
1162
1169
|
tokenId: tokenId,
|
|
1163
1170
|
commitment: tokenOutputs[0].commitment,
|
|
@@ -1195,7 +1202,8 @@ export class Wallet extends BaseWallet {
|
|
|
1195
1202
|
BigInt(spendAmount) + BigInt(feeEstimate),
|
|
1196
1203
|
bestHeight,
|
|
1197
1204
|
feePaidBy,
|
|
1198
|
-
sendRequests
|
|
1205
|
+
sendRequests,
|
|
1206
|
+
options?.ensureUtxos || []
|
|
1199
1207
|
);
|
|
1200
1208
|
if (fundingUtxos.length === 0) {
|
|
1201
1209
|
throw Error(
|
|
@@ -1437,7 +1445,7 @@ export class Wallet extends BaseWallet {
|
|
|
1437
1445
|
): Promise<SendResponse> {
|
|
1438
1446
|
return this.send(
|
|
1439
1447
|
new TokenSendRequest({
|
|
1440
|
-
cashaddr: genesisRequest.cashaddr || this.
|
|
1448
|
+
cashaddr: genesisRequest.cashaddr || this.tokenaddr!,
|
|
1441
1449
|
amount: genesisRequest.amount,
|
|
1442
1450
|
value: genesisRequest.value,
|
|
1443
1451
|
capability: genesisRequest.capability,
|
|
@@ -1488,7 +1496,7 @@ export class Wallet extends BaseWallet {
|
|
|
1488
1496
|
: nftUtxos[0].token!.amount;
|
|
1489
1497
|
const safeNewAmount = Math.max(0, newAmount);
|
|
1490
1498
|
const mintingInput = new TokenSendRequest({
|
|
1491
|
-
cashaddr: this.
|
|
1499
|
+
cashaddr: this.tokenaddr!,
|
|
1492
1500
|
tokenId: tokenId,
|
|
1493
1501
|
capability: nftUtxos[0].token!.capability,
|
|
1494
1502
|
commitment: nftUtxos[0].token!.commitment,
|
|
@@ -1501,7 +1509,7 @@ export class Wallet extends BaseWallet {
|
|
|
1501
1509
|
...mintRequests.map(
|
|
1502
1510
|
(val) =>
|
|
1503
1511
|
new TokenSendRequest({
|
|
1504
|
-
cashaddr: val.cashaddr || this.
|
|
1512
|
+
cashaddr: val.cashaddr || this.tokenaddr!,
|
|
1505
1513
|
amount: 0,
|
|
1506
1514
|
tokenId: tokenId,
|
|
1507
1515
|
value: val.value,
|
|
@@ -1570,7 +1578,7 @@ export class Wallet extends BaseWallet {
|
|
|
1570
1578
|
const safeNewAmount = Math.max(0, newAmount);
|
|
1571
1579
|
changeSendRequests = [
|
|
1572
1580
|
new TokenSendRequest({
|
|
1573
|
-
cashaddr: burnRequest.cashaddr || this.
|
|
1581
|
+
cashaddr: burnRequest.cashaddr || this.tokenaddr!,
|
|
1574
1582
|
tokenId: burnRequest.tokenId,
|
|
1575
1583
|
capability: burnRequest.capability,
|
|
1576
1584
|
commitment: burnRequest.commitment,
|
|
@@ -1590,7 +1598,7 @@ export class Wallet extends BaseWallet {
|
|
|
1590
1598
|
const safeNewAmount = Math.max(0, newAmount);
|
|
1591
1599
|
changeSendRequests = [
|
|
1592
1600
|
new TokenSendRequest({
|
|
1593
|
-
cashaddr: burnRequest.cashaddr || this.
|
|
1601
|
+
cashaddr: burnRequest.cashaddr || this.tokenaddr!,
|
|
1594
1602
|
tokenId: burnRequest.tokenId,
|
|
1595
1603
|
amount: safeNewAmount,
|
|
1596
1604
|
value: tokenUtxos[0].satoshis,
|
|
@@ -1603,7 +1611,7 @@ export class Wallet extends BaseWallet {
|
|
|
1603
1611
|
return this.send([opReturn, ...changeSendRequests], {
|
|
1604
1612
|
checkTokenQuantities: false,
|
|
1605
1613
|
queryBalance: false,
|
|
1606
|
-
|
|
1614
|
+
ensureUtxos: utxoIds.length > 0 ? utxoIds : undefined,
|
|
1607
1615
|
});
|
|
1608
1616
|
}
|
|
1609
1617
|
|
package/src/wallet/interface.ts
CHANGED
|
@@ -54,6 +54,7 @@ export interface SendRequestOptionsI {
|
|
|
54
54
|
awaitTransactionPropagation?: boolean;
|
|
55
55
|
feePaidBy?: FeePaidByEnum;
|
|
56
56
|
checkTokenQuantities?: boolean; // true
|
|
57
|
+
ensureUtxos?: UtxoI[]; // ensure these inputs will be consumed in the transaction
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
export interface MnemonicI {
|
package/src/wallet/model.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { UnitEnum } from "../enum.js";
|
|
|
4
4
|
import { NFTCapability, UtxoI } from "../interface.js";
|
|
5
5
|
import { DELIMITER } from "../constant.js";
|
|
6
6
|
import { utf8ToBin } from "@bitauth/libauth";
|
|
7
|
+
import { Config } from "../config.js";
|
|
8
|
+
import { checkTokenaddr } from "../util/deriveCashaddr.js";
|
|
7
9
|
|
|
8
10
|
// These are the minimal models used to provide types for the express server
|
|
9
11
|
//
|
|
@@ -94,7 +96,7 @@ export class TokenBurnRequest {
|
|
|
94
96
|
}
|
|
95
97
|
|
|
96
98
|
export class TokenSendRequest {
|
|
97
|
-
cashaddr: string;
|
|
99
|
+
cashaddr: string; // cashaddr or tokenaddr to send tokens to
|
|
98
100
|
value?: number; // satoshi value
|
|
99
101
|
amount: number; // fungible token amount
|
|
100
102
|
tokenId: string;
|
|
@@ -116,6 +118,8 @@ export class TokenSendRequest {
|
|
|
116
118
|
capability?: NFTCapability;
|
|
117
119
|
commitment?: string;
|
|
118
120
|
}) {
|
|
121
|
+
checkTokenaddr(cashaddr, Config.EnforceCashTokenReceiptAddresses);
|
|
122
|
+
|
|
119
123
|
this.cashaddr = cashaddr;
|
|
120
124
|
this.value = value;
|
|
121
125
|
this.amount = amount || 0;
|
|
@@ -142,8 +146,7 @@ export class TokenMintRequest {
|
|
|
142
146
|
cashaddr?: string;
|
|
143
147
|
value?: number;
|
|
144
148
|
}) {
|
|
145
|
-
|
|
146
|
-
this.capability = capability || NFTCapability.none;
|
|
149
|
+
this.capability = capability;
|
|
147
150
|
this.commitment = commitment;
|
|
148
151
|
this.cashaddr = cashaddr;
|
|
149
152
|
this.value = value;
|