mainnet-js 1.0.11 → 1.0.13

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.
@@ -50,6 +50,10 @@ export class BCMR {
50
50
  uri: string,
51
51
  contentHash?: string
52
52
  ): Promise<Registry> {
53
+ if (uri.indexOf("https://") < 0) {
54
+ uri = `https://${uri}`;
55
+ }
56
+
53
57
  // content hashes HTTPS Publication Outputs per spec
54
58
  if (contentHash) {
55
59
  // request as text and verify hash
@@ -110,7 +114,7 @@ export class BCMR {
110
114
 
111
115
  /**
112
116
  * buildAuthChain Build an authchain - Zeroth-Descendant Transaction Chain, refer to https://github.com/bitjson/chip-bcmr#zeroth-descendant-transaction-chains
113
- * The authchain in this implementation is specific to resolve to a valid metadata registy
117
+ * The authchain in this implementation is specific to resolve to a valid metadata registry
114
118
  *
115
119
  * @param {string} options.transactionHash (required) transaction hash from which to build the auth chain
116
120
  * @param {Network?} options.network (default=mainnet) network to query the data from
@@ -144,12 +148,52 @@ export class BCMR {
144
148
  const provider = getGlobalProvider(
145
149
  options.network
146
150
  ) as ElectrumNetworkProvider;
147
- if (!options.rawTx) {
151
+
152
+ if (options.rawTx === undefined) {
148
153
  options.rawTx = await provider.getRawTransactionObject(
149
154
  options.transactionHash
150
155
  );
151
156
  }
152
157
 
158
+ // figure out the autchain by moving towards authhead
159
+ const getAuthChainChild = async () => {
160
+ const history =
161
+ options.historyCache ||
162
+ (await provider.getHistory(
163
+ options.rawTx!.vout[0].scriptPubKey.addresses[0]
164
+ ));
165
+ const thisTx = history.find(
166
+ (val) => val.tx_hash === options.transactionHash
167
+ );
168
+ let filteredHistory = history.filter((val) =>
169
+ val.height > 0
170
+ ? val.height >= thisTx!.height || val.height <= 0
171
+ : val.height <= 0 && val.tx_hash !== thisTx!.tx_hash
172
+ );
173
+
174
+ for (const historyTx of filteredHistory) {
175
+ const historyRawTx = await provider.getRawTransactionObject(
176
+ historyTx.tx_hash
177
+ );
178
+ const authChainVin = historyRawTx.vin.find(
179
+ (val) => val.txid === options.transactionHash && val.vout === 0
180
+ );
181
+ // if we've found continuation of authchain, we shall recurse into it
182
+ if (authChainVin) {
183
+ // reuse queried address history if the next element in chain is the same address
184
+ const historyCache =
185
+ options.rawTx!.vout[0].scriptPubKey.addresses[0] ===
186
+ historyRawTx.vout[0].scriptPubKey.addresses[0]
187
+ ? filteredHistory
188
+ : undefined;
189
+
190
+ // combine the authchain element with the rest obtained
191
+ return { rawTx: historyRawTx, historyCache };
192
+ }
193
+ }
194
+ return undefined;
195
+ };
196
+
153
197
  // helper function to enforce the constraints on the 0th output, decode the BCMR's OP_RETURN data
154
198
  // returns resolved AuthChainElement
155
199
  const makeAuthChainElement = (
@@ -236,15 +280,31 @@ export class BCMR {
236
280
  // content hash is in OP_SHA256 byte order per spec
237
281
  result.contentHash = binToHex(chunks[1].slice().reverse());
238
282
  result.uri = binToUtf8(chunks[2]);
283
+ if (result.uri.indexOf("https://") < 0) {
284
+ result.uri = `https://${result.uri}`;
285
+ }
239
286
  }
240
287
  return result;
241
288
  };
242
289
 
243
290
  // make authchain element and combine with the rest obtained
244
- const element: AuthChainElement = makeAuthChainElement(
245
- options.rawTx,
246
- options.rawTx.hash
247
- );
291
+ let element: AuthChainElement;
292
+ try {
293
+ element = makeAuthChainElement(options.rawTx, options.rawTx.hash);
294
+ } catch (error) {
295
+ // follow authchain to head and look for BCMR outputs
296
+ const child = await getAuthChainChild();
297
+ if (child) {
298
+ return await BCMR.buildAuthChain({
299
+ ...options,
300
+ transactionHash: child.rawTx.hash,
301
+ rawTx: child.rawTx,
302
+ historyCache: child.historyCache,
303
+ });
304
+ } else {
305
+ throw error;
306
+ }
307
+ }
248
308
 
249
309
  let chainBase: AuthChain = [];
250
310
  if (options.resolveBase) {
@@ -294,49 +354,19 @@ export class BCMR {
294
354
  // if we follow to head, we need to locate the next transaction spending our 0th output
295
355
  // and repeat the building process recursively
296
356
  if (options.followToHead) {
297
- // let's figure out the autchain by moving towards authhead
298
- const history =
299
- options.historyCache ||
300
- (await provider.getHistory(
301
- options.rawTx.vout[0].scriptPubKey.addresses[0]
302
- ));
303
- const thisTx = history.find(
304
- (val) => val.tx_hash === options.transactionHash
305
- );
306
- let filteredHistory = history.filter((val) =>
307
- val.height > 0
308
- ? val.height >= thisTx!.height || val.height <= 0
309
- : val.height <= 0 && val.tx_hash !== thisTx!.tx_hash
310
- );
311
-
312
- for (const historyTx of filteredHistory) {
313
- const historyRawTx = await provider.getRawTransactionObject(
314
- historyTx.tx_hash
315
- );
316
- const authChainVin = historyRawTx.vin.find(
317
- (val) => val.txid === options.transactionHash && val.vout === 0
318
- );
319
- // if we've found continuation of authchain, we shall recurse into it
320
- if (authChainVin) {
321
- // reuse queried address history if the next element in chain is the same address
322
- const historyCache =
323
- options.rawTx.vout[0].scriptPubKey.addresses[0] ===
324
- historyRawTx.vout[0].scriptPubKey.addresses[0]
325
- ? filteredHistory
326
- : undefined;
327
- // query next chain element
328
- const chainHead = await BCMR.buildAuthChain({
329
- transactionHash: historyRawTx.hash,
330
- network: options.network,
331
- rawTx: historyRawTx,
332
- historyCache: historyCache,
333
- followToHead: options.followToHead,
334
- resolveBase: false,
335
- });
336
-
337
- // combine the authchain element with the rest obtained
338
- return [...chainBase, element, ...chainHead];
339
- }
357
+ const child = await getAuthChainChild();
358
+ if (child) {
359
+ const chainHead = await BCMR.buildAuthChain({
360
+ transactionHash: child.rawTx.hash,
361
+ network: options.network,
362
+ rawTx: child.rawTx,
363
+ historyCache: child.historyCache,
364
+ followToHead: options.followToHead,
365
+ resolveBase: false,
366
+ });
367
+
368
+ // combine the authchain element with the rest obtained
369
+ return [...chainBase, element, ...chainHead];
340
370
  }
341
371
  }
342
372
 
@@ -383,10 +413,10 @@ export class BCMR {
383
413
  for (const registry of this.metadataRegistries.slice().reverse()) {
384
414
  // registry identity is an authbase string pointer
385
415
  if (typeof registry.registryIdentity === "string") {
386
- // enforce spec, ensure identites have this authbase
416
+ // enforce spec, ensure identities have this authbase
387
417
  if (registry.identities?.[registry.registryIdentity]) {
388
418
  // find the latest identity in history and add it to the list
389
- const latestIdentityInHistory = registry.identities![tokenId][0];
419
+ const latestIdentityInHistory = registry.identities![tokenId]?.[0];
390
420
  if (latestIdentityInHistory) {
391
421
  return latestIdentityInHistory;
392
422
  }
@@ -398,7 +428,7 @@ export class BCMR {
398
428
  }
399
429
 
400
430
  // find the latest identity in history and add it to the list
401
- const latestIdentityInHistory = registry.identities![tokenId][0];
431
+ const latestIdentityInHistory = registry.identities![tokenId]?.[0];
402
432
  if (latestIdentityInHistory) {
403
433
  return latestIdentityInHistory;
404
434
  }
@@ -27,6 +27,19 @@ describe(`Test cashtokens`, () => {
27
27
  expect(utxos[0].token?.tokenId).toBeDefined();
28
28
  });
29
29
 
30
+ test("Test token genesis and max amount to send", async () => {
31
+ const alice = await RegTestWallet.fromId(process.env.ALICE_ID!);
32
+ const bob = await RegTestWallet.newRandom();
33
+ await alice.send([[bob.cashaddr!, 0.101, "bch"]]);
34
+ const genesisResponse = await bob.tokenGenesis({
35
+ amount: 100,
36
+ });
37
+
38
+ const maxAmountToSend = await bob.getMaxAmountToSend();
39
+ await bob.send([[alice.cashaddr!, maxAmountToSend.sat!, "sat"]]);
40
+ expect(await bob.getBalance("sat")).toBe(0);
41
+ });
42
+
30
43
  test("Test tokens will not be burned when sending bch value", async () => {
31
44
  const alice = await RegTestWallet.fromId(process.env.ALICE_ID!);
32
45
  const bob = await RegTestWallet.newRandom();
@@ -53,8 +66,6 @@ describe(`Test cashtokens`, () => {
53
66
  ]);
54
67
  expect(await bob.getTokenBalance(tokenId)).toBe(25);
55
68
  expect(await bob.getBalance("sat")).toBe(5000);
56
- console.log(await bob.getAddressUtxos());
57
- console.log(await bob.getMaxAmountToSend());
58
69
 
59
70
  await bob.send(
60
71
  new SendRequest({
@@ -604,7 +615,7 @@ describe(`Test cashtokens`, () => {
604
615
  expect(tokenUtxos2.length).toBe(1);
605
616
  });
606
617
 
607
- test("Test enforcing tokenaddresses", async () => {
618
+ test("Test enforcing token addresses", async () => {
608
619
  const bob = await RegTestWallet.newRandom();
609
620
 
610
621
  const previousValue = Config.EnforceCashTokenReceiptAddresses;
package/src/wallet/Wif.ts CHANGED
@@ -1126,6 +1126,14 @@ export class Wallet extends BaseWallet {
1126
1126
  utxos = await this.getAddressUtxos(this.cashaddr);
1127
1127
  }
1128
1128
 
1129
+ // filter out token utxos if there are no token requests
1130
+ if (
1131
+ checkTokenQuantities &&
1132
+ !sendRequests.some((val) => val instanceof TokenSendRequest)
1133
+ ) {
1134
+ utxos = utxos.filter((val) => !val.token);
1135
+ }
1136
+
1129
1137
  const addTokenChangeOutputs = (
1130
1138
  inputs: UtxoI[],
1131
1139
  outputs: SendRequestType[]