nexa-wallet-sdk 0.4.2 → 0.5.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.cjs CHANGED
@@ -149,7 +149,7 @@ $parcel$export(module.exports, "SighashType", () => $l50U0$libnexats.SighashType
149
149
  $parcel$export(module.exports, "AccountKeysUtils", () => $4d68f29c39c1a3e4$export$94f569bf4eb0f6f6);
150
150
  // @ts-ignore
151
151
  var $2dd241e44b9dc3c2$exports = {};
152
- $2dd241e44b9dc3c2$exports = JSON.parse("{\"name\":\"nexa-wallet-sdk\",\"version\":\"0.4.2\",\"type\":\"module\",\"source\":\"src/index.ts\",\"types\":\"dist/index.d.ts\",\"main\":\"dist/index.cjs\",\"module\":\"dist/index.mjs\",\"browser\":\"dist/index.web.mjs\",\"exports\":{\"types\":\"./dist/index.d.ts\",\"node\":{\"import\":\"./dist/index.mjs\",\"require\":\"./dist/index.cjs\"},\"browser\":\"./dist/index.web.mjs\",\"default\":\"./dist/index.mjs\"},\"scripts\":{\"build\":\"parcel build\",\"lint\":\"eslint .\",\"fix-lint\":\"eslint --fix .\",\"dev\":\"parcel watch\",\"test\":\"vitest run\",\"clean\":\"rm -rf dist .parcel-cache\",\"docs\":\"typedoc\",\"docs:serve\":\"typedoc && npx serve docs -l 8080\",\"docs:mkdocs\":\"typedoc && mkdocs serve\",\"docs:build\":\"typedoc && mkdocs build\",\"docs:setup\":\"./scripts/setup-docs.sh\",\"wallet-cli\":\"node examples/wallet-cli.cjs\"},\"repository\":{\"type\":\"git\",\"url\":\"git+ssh://git@gitlab.com/nexa/wallet-sdk-ts.git\"},\"keywords\":[\"nexa\",\"wallet\",\"web3\",\"crypto\",\"dapp\",\"walletcomms\",\"walletsdk\"],\"contributors\":[{\"name\":\"Dolaned\"},{\"name\":\"Griffith\"},{\"name\":\"Vgrunner\"},{\"name\":\"myendy\"}],\"author\":\"Dolaned\",\"license\":\"MIT\",\"bugs\":{\"url\":\"https://gitlab.com/nexa/wallet-sdk-ts/issues\"},\"homepage\":\"https://gitlab.com/nexa/wallet-sdk-ts#readme\",\"description\":\"Wallet SDK for the Nexa blockchain\",\"devDependencies\":{\"@parcel/packager-ts\":\"^2.15.4\",\"@parcel/transformer-typescript-types\":\"^2.15.4\",\"@types/lodash-es\":\"^4.17.12\",\"@types/node\":\"^22.13.1\",\"eslint\":\"^9.20.1\",\"parcel\":\"^2.15.4\",\"typedoc\":\"^0.28.7\",\"typedoc-plugin-markdown\":\"^4.7.0\",\"typedoc-plugin-rename-defaults\":\"^0.7.3\",\"typescript\":\"^5.8.3\",\"typescript-eslint\":\"^8.24.1\",\"vitest\":\"^3.0.8\"},\"targets\":{\"main\":{\"context\":\"node\",\"outputFormat\":\"commonjs\",\"distDir\":\"dist\",\"isLibrary\":true,\"includeNodeModules\":[\"lodash-es\"]},\"module\":{\"context\":\"node\",\"outputFormat\":\"esmodule\",\"distDir\":\"dist\",\"isLibrary\":true},\"browser\":{\"context\":\"browser\",\"outputFormat\":\"esmodule\",\"distDir\":\"dist\",\"isLibrary\":true}},\"dependencies\":{\"@vgrunner/electrum-cash\":\"^2.0.12\",\"bip39\":\"^3.1.0\",\"js-big-decimal\":\"^2.2.0\",\"libnexa-ts\":\"^1.0.5\",\"lodash-es\":\"^4.17.21\",\"prompt-sync\":\"^4.2.0\",\"wallet-comms-sdk\":\"^0.6.0\"},\"files\":[\"dist\"],\"directories\":{\"test\":\"tests\"},\"@parcel/resolver-default\":{\"packageExports\":true}}");
152
+ $2dd241e44b9dc3c2$exports = JSON.parse("{\"name\":\"nexa-wallet-sdk\",\"version\":\"0.5.1\",\"type\":\"module\",\"source\":\"src/index.ts\",\"types\":\"dist/index.d.ts\",\"main\":\"dist/index.cjs\",\"module\":\"dist/index.mjs\",\"browser\":\"dist/index.web.mjs\",\"exports\":{\"types\":\"./dist/index.d.ts\",\"node\":{\"import\":\"./dist/index.mjs\",\"require\":\"./dist/index.cjs\"},\"browser\":\"./dist/index.web.mjs\",\"default\":\"./dist/index.mjs\"},\"scripts\":{\"build\":\"parcel build\",\"lint\":\"eslint .\",\"fix-lint\":\"eslint --fix .\",\"dev\":\"parcel watch\",\"test\":\"vitest run\",\"clean\":\"rm -rf dist .parcel-cache\",\"docs\":\"typedoc\",\"docs:serve\":\"typedoc && npx serve docs -l 8080\",\"docs:mkdocs\":\"typedoc && mkdocs serve\",\"docs:build\":\"typedoc && mkdocs build\",\"docs:setup\":\"./scripts/setup-docs.sh\",\"wallet-cli\":\"node examples/wallet-cli.cjs\"},\"repository\":{\"type\":\"git\",\"url\":\"git+ssh://git@gitlab.com/nexa/wallet-sdk-ts.git\"},\"keywords\":[\"nexa\",\"wallet\",\"web3\",\"crypto\",\"dapp\",\"walletcomms\",\"walletsdk\"],\"contributors\":[{\"name\":\"Dolaned\"},{\"name\":\"Griffith\"},{\"name\":\"Vgrunner\"},{\"name\":\"myendy\"}],\"author\":\"Dolaned\",\"license\":\"MIT\",\"bugs\":{\"url\":\"https://gitlab.com/nexa/wallet-sdk-ts/issues\"},\"homepage\":\"https://gitlab.com/nexa/wallet-sdk-ts#readme\",\"description\":\"Wallet SDK for the Nexa blockchain\",\"devDependencies\":{\"@parcel/packager-ts\":\"^2.15.4\",\"@parcel/transformer-typescript-types\":\"^2.15.4\",\"@types/lodash-es\":\"^4.17.12\",\"@types/node\":\"^22.13.1\",\"eslint\":\"^9.20.1\",\"parcel\":\"^2.15.4\",\"typedoc\":\"^0.28.7\",\"typedoc-plugin-markdown\":\"^4.7.0\",\"typedoc-plugin-rename-defaults\":\"^0.7.3\",\"typescript\":\"^5.8.3\",\"typescript-eslint\":\"^8.24.1\",\"vitest\":\"^3.0.8\"},\"targets\":{\"main\":{\"context\":\"node\",\"outputFormat\":\"commonjs\",\"distDir\":\"dist\",\"isLibrary\":true,\"includeNodeModules\":[\"lodash-es\"]},\"module\":{\"context\":\"node\",\"outputFormat\":\"esmodule\",\"distDir\":\"dist\",\"isLibrary\":true},\"browser\":{\"context\":\"browser\",\"outputFormat\":\"esmodule\",\"distDir\":\"dist\",\"isLibrary\":true}},\"dependencies\":{\"@vgrunner/electrum-cash\":\"^2.0.12\",\"bip39\":\"^3.1.0\",\"js-big-decimal\":\"^2.2.0\",\"libnexa-ts\":\"^1.0.5\",\"lodash-es\":\"^4.17.21\",\"prompt-sync\":\"^4.2.0\",\"wallet-comms-sdk\":\"^0.6.1\"},\"files\":[\"dist\"],\"directories\":{\"test\":\"tests\"},\"@parcel/resolver-default\":{\"packageExports\":true}}");
153
153
 
154
154
 
155
155
 
@@ -651,6 +651,11 @@ function $bc5ca2c06b1affa3$export$254a5c7330bbfd41(token) {
651
651
  if ((0, $l50U0$libnexats.CommonUtils).isHexa(token)) return token;
652
652
  return $bc5ca2c06b1affa3$export$23010fd5dda8dec1(token).toString('hex');
653
653
  }
654
+ function $bc5ca2c06b1affa3$export$f12d707d2b261fb6(txIdem, outputIndex) {
655
+ const writer = new (0, $l50U0$libnexats.BufferWriter)(undefined);
656
+ const outpoint = writer.write(Buffer.from(txIdem, 'hex').reverse()).writeUInt32LE(outputIndex).toBuffer();
657
+ return (0, $l50U0$libnexats.Hash).sha256(outpoint).reverse().toString('hex');
658
+ }
654
659
 
655
660
 
656
661
  var $0d59d2bcffd646c5$export$dcc1fb6ad5308e56 = /*#__PURE__*/ function(TxTokenType) {
@@ -1291,7 +1296,6 @@ var $90290c84737dbb50$export$2e2bcd8739ae039 = $90290c84737dbb50$var$isString;
1291
1296
 
1292
1297
 
1293
1298
 
1294
-
1295
1299
  class $4d68f29c39c1a3e4$export$94f569bf4eb0f6f6 {
1296
1300
  static getAllKeys(keys) {
1297
1301
  return keys.receiveKeys.concat(keys.changeKeys);
@@ -1358,7 +1362,7 @@ function $22e282633cad8f97$export$e240c810a53c3a0c(perms) {
1358
1362
 
1359
1363
 
1360
1364
  /** Maximum number of inputs/outputs allowed in a single transaction */ const $b4a043612f1c4089$var$MAX_INPUTS_OUTPUTS = 250;
1361
- async function $b4a043612f1c4089$export$afd979971a55acfc(txBuilder, keys, totalTxValue, options) {
1365
+ async function $b4a043612f1c4089$export$afd979971a55acfc(txBuilder, keys, totalTxValue, options, spentOutpoints) {
1362
1366
  let rKeys = keys.receiveKeys.filter((k)=>BigInt(k.balance) > 0n);
1363
1367
  let cKeys = keys.changeKeys.filter((k)=>BigInt(k.balance) > 0n);
1364
1368
  let allKeys = rKeys.concat(cKeys);
@@ -1368,6 +1372,8 @@ async function $b4a043612f1c4089$export$afd979971a55acfc(txBuilder, keys, totalT
1368
1372
  for (let key of allKeys){
1369
1373
  let utxos = await (0, $b5bfd17fdf06d231$export$eaa49f0478d81b9d).getNexaUtxos(key.address);
1370
1374
  for (let utxo of utxos){
1375
+ // Skip if this outpoint is already spent
1376
+ if (spentOutpoints && spentOutpoints.has(utxo.outpoint_hash)) continue;
1371
1377
  let input = {
1372
1378
  outpoint: utxo.outpoint_hash,
1373
1379
  address: key.address,
@@ -1375,6 +1381,8 @@ async function $b4a043612f1c4089$export$afd979971a55acfc(txBuilder, keys, totalT
1375
1381
  templateData: options.templateData
1376
1382
  };
1377
1383
  txBuilder.from(input);
1384
+ // Add to spent outpoints set
1385
+ if (spentOutpoints) spentOutpoints.add(utxo.outpoint_hash);
1378
1386
  if (!usedKeys.has(key.address)) usedKeys.set(key.address, key.key.privateKey);
1379
1387
  if (options.isConsolidate) {
1380
1388
  txBuilder.change(options.toChange ?? keys.receiveKeys[keys.receiveKeys.length - 1].address);
@@ -1418,7 +1426,7 @@ async function $b4a043612f1c4089$export$afd979971a55acfc(txBuilder, keys, totalT
1418
1426
  };
1419
1427
  throw new Error(JSON.stringify(err));
1420
1428
  }
1421
- async function $b4a043612f1c4089$export$931e37acdaa22dcf(txBuilder, keys, token, outTokenAmount) {
1429
+ async function $b4a043612f1c4089$export$931e37acdaa22dcf(txBuilder, keys, token, outTokenAmount, spentOutpoints) {
1422
1430
  let tokenHex = (0, $bc5ca2c06b1affa3$export$254a5c7330bbfd41)(token);
1423
1431
  let rKeys = keys.receiveKeys.filter((k)=>Object.keys(k.tokensBalance).includes(tokenHex));
1424
1432
  let cKeys = keys.changeKeys.filter((k)=>Object.keys(k.tokensBalance).includes(tokenHex));
@@ -1430,6 +1438,8 @@ async function $b4a043612f1c4089$export$931e37acdaa22dcf(txBuilder, keys, token,
1430
1438
  let utxos = await (0, $b5bfd17fdf06d231$export$eaa49f0478d81b9d).getTokenUtxos(key.address, token);
1431
1439
  for (let utxo of utxos){
1432
1440
  if (utxo.token_amount < 0) continue;
1441
+ // Skip if this outpoint is already spent
1442
+ if (spentOutpoints && spentOutpoints.has(utxo.outpoint_hash)) continue;
1433
1443
  txBuilder.from({
1434
1444
  outpoint: utxo.outpoint_hash,
1435
1445
  address: key.address,
@@ -1437,6 +1447,8 @@ async function $b4a043612f1c4089$export$931e37acdaa22dcf(txBuilder, keys, token,
1437
1447
  groupId: utxo.group,
1438
1448
  groupAmount: BigInt(utxo.token_amount)
1439
1449
  });
1450
+ // Add to spent outpoints set
1451
+ if (spentOutpoints) spentOutpoints.add(utxo.outpoint_hash);
1440
1452
  inTokenAmount = inTokenAmount + BigInt(utxo.token_amount);
1441
1453
  if (!usedKeys.has(key.address)) usedKeys.set(key.address, key.key.privateKey);
1442
1454
  if (inTokenAmount > (0, $bc5ca2c06b1affa3$export$8ba128bc85947a2a)) throw new Error("Token inputs exceeded max amount. Consider sending in small chunks");
@@ -1452,7 +1464,7 @@ async function $b4a043612f1c4089$export$931e37acdaa22dcf(txBuilder, keys, token,
1452
1464
  }
1453
1465
  throw new Error("Not enough token balance");
1454
1466
  }
1455
- async function $b4a043612f1c4089$export$d7f2b844e1d59768(txBuilder, keys, opReturnData, network) {
1467
+ async function $b4a043612f1c4089$export$d7f2b844e1d59768(txBuilder, keys, opReturnData, network, spentOutpoints) {
1456
1468
  // TODO validate opreturn data
1457
1469
  const allKeys = (0, $4d68f29c39c1a3e4$export$94f569bf4eb0f6f6).getAllKeys(keys);
1458
1470
  let outpoint = '';
@@ -1461,11 +1473,15 @@ async function $b4a043612f1c4089$export$d7f2b844e1d59768(txBuilder, keys, opRetu
1461
1473
  for (let key of allKeys){
1462
1474
  let utxos = await (0, $b5bfd17fdf06d231$export$eaa49f0478d81b9d).getNexaUtxos(key.address);
1463
1475
  for (let utxo of utxos){
1476
+ // Skip if this outpoint is already spent
1477
+ if (spentOutpoints && spentOutpoints.has(utxo.outpoint_hash)) continue;
1464
1478
  txBuilder.from({
1465
1479
  outpoint: utxo.outpoint_hash,
1466
1480
  address: key.address,
1467
1481
  satoshis: utxo.value
1468
1482
  });
1483
+ // Add to spent outpoints set
1484
+ if (spentOutpoints) spentOutpoints.add(utxo.outpoint_hash);
1469
1485
  if ((0, $bc5ca2c06b1affa3$export$c8733ae29fb53302)(outpoint)) {
1470
1486
  outpoint = utxo.outpoint_hash;
1471
1487
  let id = (0, $l50U0$libnexats.GroupToken).findGroupId(outpoint, Buffer.from(opReturnData, 'hex'), (0, $l50U0$libnexats.GroupToken).authFlags.ACTIVE_FLAG_BITS);
@@ -1494,17 +1510,21 @@ async function $b4a043612f1c4089$export$48a48877d6df17e9(txBuilder, keys, outpoi
1494
1510
  addrKey.key.privateKey
1495
1511
  ];
1496
1512
  }
1497
- async function $b4a043612f1c4089$export$1dfa5d829fc95097(txBuilder, keys, token, perm, subgroup = '', subgroupAddr = '', quantity) {
1513
+ async function $b4a043612f1c4089$export$1dfa5d829fc95097(txBuilder, keys, token, perm, subgroup = '', subgroupAddr = '', quantity, spentOutpoints) {
1498
1514
  let allKeys = (0, $4d68f29c39c1a3e4$export$94f569bf4eb0f6f6).getAllKeys(keys);
1499
1515
  for (let key of allKeys){
1500
1516
  let utxos = await (0, $b5bfd17fdf06d231$export$eaa49f0478d81b9d).getTokenUtxos(key.address, token);
1501
1517
  for (let utxo of utxos){
1502
1518
  if (!(0, $22e282633cad8f97$export$7f7cffd29bf2d96d)(utxo.token_amount, perm)) continue;
1519
+ // Skip if this outpoint is already spent
1520
+ if (spentOutpoints && spentOutpoints.has(utxo.outpoint_hash)) continue;
1503
1521
  txBuilder.from({
1504
1522
  outpoint: utxo.outpoint_hash,
1505
1523
  address: key.address,
1506
1524
  satoshis: utxo.value
1507
1525
  });
1526
+ // Add to spent outpoints set
1527
+ if (spentOutpoints) spentOutpoints.add(utxo.outpoint_hash);
1508
1528
  if (perm === 'subgroup') {
1509
1529
  const subgroupQuantity = quantity ?? (0, $22e282633cad8f97$export$636fb0b03b94ac81)(utxo.token_amount, false);
1510
1530
  txBuilder.to(subgroupAddr, (0, $l50U0$libnexats.Transaction).DUST_AMOUNT, subgroup, subgroupQuantity);
@@ -1521,7 +1541,7 @@ async function $b4a043612f1c4089$export$1dfa5d829fc95097(txBuilder, keys, token,
1521
1541
  }
1522
1542
  throw new Error("The requested authority not found");
1523
1543
  }
1524
- async function $b4a043612f1c4089$export$28a843ca046a6b3f(txBuilder, keys, token, perms, toAddr) {
1544
+ async function $b4a043612f1c4089$export$28a843ca046a6b3f(txBuilder, keys, token, perms, toAddr, spentOutpoints) {
1525
1545
  let allKeys = (0, $4d68f29c39c1a3e4$export$94f569bf4eb0f6f6).getAllKeys(keys);
1526
1546
  let usedKeys = [];
1527
1547
  let reqiredPerms = new Set(perms);
@@ -1530,6 +1550,8 @@ async function $b4a043612f1c4089$export$28a843ca046a6b3f(txBuilder, keys, token,
1530
1550
  let utxos = await (0, $b5bfd17fdf06d231$export$eaa49f0478d81b9d).getTokenUtxos(key.address, token);
1531
1551
  for (let utxo of utxos){
1532
1552
  if (utxo.token_amount > 0) continue;
1553
+ // Skip if this outpoint is already spent
1554
+ if (spentOutpoints && spentOutpoints.has(utxo.outpoint_hash)) continue;
1533
1555
  let found = false;
1534
1556
  for (let perm of reqiredPerms)if ((0, $22e282633cad8f97$export$7f7cffd29bf2d96d)(utxo.token_amount, perm)) {
1535
1557
  reqiredPerms.delete(perm);
@@ -1542,6 +1564,8 @@ async function $b4a043612f1c4089$export$28a843ca046a6b3f(txBuilder, keys, token,
1542
1564
  satoshis: utxo.value
1543
1565
  });
1544
1566
  usedKeys.push(key.key.privateKey);
1567
+ // Add to spent outpoints set
1568
+ if (spentOutpoints) spentOutpoints.add(utxo.outpoint_hash);
1545
1569
  // duplicate
1546
1570
  const duplicateAddress = toAddr != null ? toAddr : keys.changeKeys.length > 0 ? keys.changeKeys[keys.changeKeys.length - 1].address : keys.receiveKeys[keys.receiveKeys.length - 1].address;
1547
1571
  txBuilder.to(duplicateAddress, (0, $l50U0$libnexats.Transaction).DUST_AMOUNT, token, (0, $22e282633cad8f97$export$636fb0b03b94ac81)(utxo.token_amount));
@@ -1680,6 +1704,7 @@ var $ed110f8656071ee4$export$2e2bcd8739ae039 = $ed110f8656071ee4$var$parseInt;
1680
1704
 
1681
1705
 
1682
1706
 
1707
+
1683
1708
  class $57b0117fe8233fef$export$bba690fb5c12ba04 {
1684
1709
  /**
1685
1710
  * Creates a new TransactionCreator instance
@@ -1689,10 +1714,80 @@ class $57b0117fe8233fef$export$bba690fb5c12ba04 {
1689
1714
  /** Total value of NEXA being sent in this transaction */ this._totalValue = BigInt(0);
1690
1715
  /** Network this transaction will be broadcast on */ this._network = (0, $l50U0$libnexats.Networks).mainnet;
1691
1716
  /** Transaction options for customizing behavior */ this._txOptions = {};
1717
+ /** Set of outpoints already spent in this transaction to prevent double-spending */ this._spentOutpoints = new Set();
1692
1718
  if (tx instanceof (0, $l50U0$libnexats.TransactionBuilder)) this.transactionBuilder = tx;
1693
1719
  this.tokens = new Set();
1694
1720
  this.transactionBuilder = new (0, $l50U0$libnexats.TransactionBuilder)();
1695
1721
  }
1722
+ /**
1723
+ * Parse transaction from hex string with common logic
1724
+ * @param tx Transaction hex string
1725
+ * @returns This instance for chaining
1726
+ */ parseTxHex(tx) {
1727
+ // Clear any existing builder operations since we're dealing with a pre-built transaction
1728
+ this.builder = [];
1729
+ // Add the parsing operation to the builder queue
1730
+ this.builder.push(async ()=>{
1731
+ try {
1732
+ const txBuilder = new (0, $l50U0$libnexats.TransactionBuilder)(tx);
1733
+ const newTxBuilder = new (0, $l50U0$libnexats.TransactionBuilder)();
1734
+ const oldInputs = txBuilder.transaction.inputs;
1735
+ // Reconstruct inputs with proper script template data if needed
1736
+ for(let i = 0; i < oldInputs.length; i++){
1737
+ const input = oldInputs[i];
1738
+ const utxo = await (0, $b5bfd17fdf06d231$export$eaa49f0478d81b9d).getUtxo(input.outpoint.toString('hex'));
1739
+ const scriptPubkey = (0, $l50U0$libnexats.Script).fromHex(utxo.scriptpubkey);
1740
+ if (scriptPubkey.isScriptTemplateOut()) {
1741
+ const scriptSig = input.scriptSig;
1742
+ newTxBuilder.transaction.addInput(new (0, $l50U0$libnexats.ScriptTemplateInput)({
1743
+ amount: input.amount,
1744
+ outpoint: input.outpoint.toString('hex'),
1745
+ scriptSig: input.scriptSig,
1746
+ templateData: {
1747
+ templateScript: (0, $l50U0$libnexats.BufferUtils).isHashBuffer(scriptPubkey.getTemplateHash()) ? (0, $l50U0$libnexats.Script).fromBuffer(scriptSig.chunks[0].buf) : (0, $l50U0$libnexats.Script).empty(),
1748
+ constraintScript: (0, $l50U0$libnexats.BufferUtils).isHashBuffer(scriptPubkey.getConstraintHash()) ? (0, $l50U0$libnexats.Script).fromBuffer(scriptSig.chunks[1].buf) : (0, $l50U0$libnexats.Opcode).OP_FALSE
1749
+ },
1750
+ output: {
1751
+ type: input.type,
1752
+ value: utxo.amount,
1753
+ scriptPubKey: utxo.scriptpubkey
1754
+ }
1755
+ }));
1756
+ } else // For non-template inputs, add them normally
1757
+ newTxBuilder.from({
1758
+ outpoint: input.outpoint.toString('hex'),
1759
+ satoshis: input.amount,
1760
+ address: utxo.addresses[0],
1761
+ scriptPubKey: utxo.scriptpubkey
1762
+ });
1763
+ // Hook for subclasses to handle input-specific logic (like key management)
1764
+ await this.handleParsedInput(input, utxo, i);
1765
+ }
1766
+ // Copy the outputs from the original transaction
1767
+ newTxBuilder.transaction.outputs = txBuilder.transaction.outputs;
1768
+ this.transactionBuilder = newTxBuilder;
1769
+ // Hook for subclasses to perform post-processing
1770
+ await this.handleParsingComplete();
1771
+ } catch (error) {
1772
+ console.error('parseTxHex: Error parsing transaction:', error);
1773
+ throw error;
1774
+ }
1775
+ });
1776
+ return this;
1777
+ }
1778
+ /**
1779
+ * Hook for subclasses to handle individual parsed inputs
1780
+ * @param input The original input from the transaction
1781
+ * @param utxo UTXO data for this input
1782
+ * @param index Input index
1783
+ */ async handleParsedInput(input, utxo, index) {
1784
+ // Default implementation does nothing - subclasses can override
1785
+ }
1786
+ /**
1787
+ * Hook for subclasses to perform post-processing after parsing is complete
1788
+ */ async handleParsingComplete() {
1789
+ // Default implementation does nothing - subclasses can override
1790
+ }
1696
1791
  /**
1697
1792
  * Sets the network for this transaction
1698
1793
  * @param network Network name or Networkish object
@@ -1737,6 +1832,17 @@ class $57b0117fe8233fef$export$bba690fb5c12ba04 {
1737
1832
  /** Sets the total NEXA value being sent */ set totalValue(value) {
1738
1833
  this._totalValue = value;
1739
1834
  }
1835
+ /** Gets the set of spent outpoints */ get spentOutpoints() {
1836
+ return this._spentOutpoints;
1837
+ }
1838
+ /**
1839
+ * Updates the spent outpoints set with current transaction inputs
1840
+ * @returns Set of outpoint hashes that are already spent
1841
+ */ updateSpentOutpoints() {
1842
+ this._spentOutpoints.clear();
1843
+ for (const input of this.transactionBuilder.transaction.inputs)this._spentOutpoints.add(input.outpoint.toString('hex'));
1844
+ return this._spentOutpoints;
1845
+ }
1740
1846
  /**
1741
1847
  * Validates and creates a token action
1742
1848
  * @param toAddr Destination address
@@ -1789,10 +1895,27 @@ class $57b0117fe8233fef$export$bba690fb5c12ba04 {
1789
1895
  * @param toAddr Destination address
1790
1896
  * @param amount Amount of tokens to send
1791
1897
  * @param token Token ID
1898
+ * @param dustAmount Optional dust amount for the output (defaults to Transaction.DUST_AMOUNT)
1792
1899
  * @returns This instance for chaining
1793
- */ sendToToken(toAddr, amount, token) {
1900
+ */ sendToToken(toAddr, amount, token, dustAmount = (0, $l50U0$libnexats.Transaction).DUST_AMOUNT) {
1794
1901
  this.builder.push(async ()=>{
1795
- this.tokenAction(toAddr, amount, token, 'send');
1902
+ // Validate destination address
1903
+ if (!(0, $0d59d2bcffd646c5$export$8d986bd2866fe6ab)(toAddr, this.network) && !(0, $0d59d2bcffd646c5$export$8d986bd2866fe6ab)(toAddr, this.network, (0, $l50U0$libnexats.AddressType).PayToPublicKeyHash)) throw new Error('Invalid Address.');
1904
+ // Validate amount ranges
1905
+ if (BigInt(amount) < 1n) throw new Error("The amount is too low.");
1906
+ if (BigInt(amount) > (0, $bc5ca2c06b1affa3$export$8ba128bc85947a2a)) throw new Error("The amount is too high.");
1907
+ // Validate token ID
1908
+ if (!(0, $0d59d2bcffd646c5$export$8d986bd2866fe6ab)(token, this.network, (0, $l50U0$libnexats.AddressType).GroupIdAddress)) throw new Error('Invalid Token ID');
1909
+ // Ensure tokens are sent to script template addresses
1910
+ if ((0, $l50U0$libnexats.Address).getOutputType(toAddr) === 0) throw new Error('Token must be sent to script template address');
1911
+ // Add output to transaction with configurable dust amount
1912
+ this.transactionBuilder.to(toAddr, dustAmount, token, BigInt(amount));
1913
+ // Record the token action
1914
+ this.tokens.add({
1915
+ token: token,
1916
+ amount: BigInt(amount),
1917
+ action: 'send'
1918
+ });
1796
1919
  });
1797
1920
  return this;
1798
1921
  }
@@ -1941,9 +2064,10 @@ class $57b0117fe8233fef$export$bba690fb5c12ba04 {
1941
2064
  let opReturn = (0, $l50U0$libnexats.ScriptFactory).buildNFTDescription(zipUrl, zipHash);
1942
2065
  this.transactionBuilder.addData(opReturn, true);
1943
2066
  // generate subgroup ID
1944
- const subGroup = (0, $l50U0$libnexats.GroupToken).generateSubgroupId(parent, opReturn.toBuffer()).toString('hex');
2067
+ const subGroupBuffer = (0, $l50U0$libnexats.GroupToken).generateSubgroupId(parent, zipHash);
2068
+ const subGroupAddress = new (0, $l50U0$libnexats.Address)(subGroupBuffer, (0, $l50U0$libnexats.Networks).get(this.network) || (0, $l50U0$libnexats.Networks).mainnet, (0, $l50U0$libnexats.AddressType).GroupIdAddress).toString();
1945
2069
  this.tokens.add({
1946
- token: subGroup,
2070
+ token: subGroupAddress,
1947
2071
  parentToken: parent,
1948
2072
  amount: BigInt((0, $l50U0$libnexats.Transaction).DUST_AMOUNT),
1949
2073
  action: 'subgroup',
@@ -1967,9 +2091,10 @@ class $57b0117fe8233fef$export$bba690fb5c12ba04 {
1967
2091
  let opReturn = (0, $l50U0$libnexats.ScriptFactory).buildNFTDescription(zipUrl, zipHash);
1968
2092
  this.transactionBuilder.addData(opReturn, true);
1969
2093
  // generate subgroup ID
1970
- const subGroup = (0, $l50U0$libnexats.GroupToken).generateSubgroupId(parent, opReturn.toBuffer()).toString('hex');
2094
+ const subGroupBuffer = (0, $l50U0$libnexats.GroupToken).generateSubgroupId(parent, zipHash);
2095
+ const subGroupAddress = new (0, $l50U0$libnexats.Address)(subGroupBuffer, (0, $l50U0$libnexats.Networks).get(this.network) || (0, $l50U0$libnexats.Networks).mainnet, (0, $l50U0$libnexats.AddressType).GroupIdAddress).toString();
1971
2096
  this.tokens.add({
1972
- token: subGroup,
2097
+ token: subGroupAddress,
1973
2098
  parentToken: parent,
1974
2099
  amount: BigInt((0, $l50U0$libnexats.Transaction).DUST_AMOUNT),
1975
2100
  action: 'subgroup',
@@ -2034,28 +2159,33 @@ class $e1896e59040fa3c5$export$2e2bcd8739ae039 extends (0, $57b0117fe8233fef$exp
2034
2159
  return this;
2035
2160
  }
2036
2161
  parseTxHex(tx) {
2037
- this.builder.push(async ()=>{
2038
- const txBuilder = new (0, $l50U0$libnexats.TransactionBuilder)(tx);
2039
- const newTxBuilder = new (0, $l50U0$libnexats.TransactionBuilder)();
2040
- const oldInputs = txBuilder.transaction.inputs;
2041
- for (const input of oldInputs){
2042
- const utxo = await (0, $b5bfd17fdf06d231$export$eaa49f0478d81b9d).getUtxo(input.outpoint.toString('hex'));
2043
- newTxBuilder.from({
2044
- outpoint: utxo.tx_hash,
2045
- amount: utxo.amount,
2046
- scriptPubKey: utxo.scriptpubkey
2047
- });
2048
- const foundKey = this.findPrivateKeyFromAddress(utxo.addresses[0]);
2049
- if (foundKey) this._keysToSign.push(foundKey.key.privateKey);
2050
- }
2051
- newTxBuilder.transaction.outputs = txBuilder.transaction.outputs;
2052
- });
2053
- return this;
2162
+ // Call parent's parseTxHex which handles the common parsing logic
2163
+ return super.parseTxHex(tx);
2164
+ }
2165
+ /**
2166
+ * Handle wallet-specific logic for each parsed input (find and store private keys)
2167
+ */ async handleParsedInput(input, utxo, index) {
2168
+ // Find and store the private key for this input
2169
+ const foundKey = this.findPrivateKeyFromAddress(utxo.addresses[0]);
2170
+ if (foundKey) this._keysToSign.push(foundKey.key.privateKey);
2171
+ }
2172
+ /**
2173
+ * Handle wallet-specific post-processing after parsing is complete
2174
+ */ async handleParsingComplete() {
2175
+ // If no keys found, use the primary account key as fallback
2176
+ if (this._keysToSign.length == 0) this._keysToSign.push(this._account.getPrimaryAddressKey().key.privateKey);
2054
2177
  }
2055
2178
  parseTxBuffer(tx) {
2056
- this.builder.push(async ()=>{
2057
- this.transactionBuilder = new (0, $l50U0$libnexats.TransactionBuilder)(tx);
2058
- });
2179
+ // Clear any existing builder operations since we're dealing with a pre-built transaction
2180
+ this.builder = [];
2181
+ // For buffer, we can parse immediately since it doesn't require async operations
2182
+ this.transactionBuilder = new (0, $l50U0$libnexats.TransactionBuilder)(tx);
2183
+ // Extract all possible keys from the account that might be needed for signing
2184
+ // Since we don't have UTXO data, we'll add all account keys
2185
+ const addresses = this._account.getAddresses();
2186
+ for (const addressKey of addresses)if (!this._keysToSign.includes(addressKey.key.privateKey)) this._keysToSign.push(addressKey.key.privateKey);
2187
+ // If no keys found, use the primary account key as fallback
2188
+ if (this._keysToSign.length == 0) this._keysToSign.push(this._account.getPrimaryAddressKey().key.privateKey);
2059
2189
  return this;
2060
2190
  }
2061
2191
  mint(token, amount) {
@@ -2078,63 +2208,236 @@ class $e1896e59040fa3c5$export$2e2bcd8739ae039 extends (0, $57b0117fe8233fef$exp
2078
2208
  let tK = [];
2079
2209
  let nK = [];
2080
2210
  if (this.tokens.size > 0) for (const tokenAction of this.tokens){
2081
- if (tokenAction.action == 'mint' || tokenAction.action == 'melt') tK = tK.concat(await (0, $b4a043612f1c4089$export$1dfa5d829fc95097)(this.transactionBuilder, this._account.accountKeys, tokenAction.token, tokenAction.action));
2082
- else if (tokenAction.action == 'group') tK = tK.concat(await (0, $b4a043612f1c4089$export$d7f2b844e1d59768)(this.transactionBuilder, this._account.accountKeys, tokenAction.extraData?.opReturnData, this.network));
2083
- else if (tokenAction.action == 'subgroup') tK = tK.concat(await (0, $b4a043612f1c4089$export$1dfa5d829fc95097)(this.transactionBuilder, this._account.accountKeys, tokenAction.parentToken, 'subgroup', tokenAction.token, this._account.accountKeys.receiveKeys.at(-1).address, tokenAction.extraData?.quantity));
2084
- else if (tokenAction.action == 'renew') tK = tK.concat(await (0, $b4a043612f1c4089$export$28a843ca046a6b3f)(this.transactionBuilder, this._account.accountKeys, tokenAction.token, tokenAction.extraData.perms, tokenAction.extraData.address));
2211
+ if (tokenAction.action == 'mint' || tokenAction.action == 'melt') tK = tK.concat(await (0, $b4a043612f1c4089$export$1dfa5d829fc95097)(this.transactionBuilder, this._account.accountKeys, tokenAction.token, tokenAction.action, undefined, undefined, undefined, this.spentOutpoints));
2212
+ else if (tokenAction.action == 'group') tK = tK.concat(await (0, $b4a043612f1c4089$export$d7f2b844e1d59768)(this.transactionBuilder, this._account.accountKeys, tokenAction.extraData?.opReturnData, this.network, this.spentOutpoints));
2213
+ else if (tokenAction.action == 'subgroup') tK = tK.concat(await (0, $b4a043612f1c4089$export$1dfa5d829fc95097)(this.transactionBuilder, this._account.accountKeys, tokenAction.parentToken, 'subgroup', tokenAction.token, this._account.accountKeys.receiveKeys.at(-1).address, tokenAction.extraData?.quantity, this.spentOutpoints));
2214
+ else if (tokenAction.action == 'renew') tK = tK.concat(await (0, $b4a043612f1c4089$export$28a843ca046a6b3f)(this.transactionBuilder, this._account.accountKeys, tokenAction.token, tokenAction.extraData.perms, tokenAction.extraData.address, this.spentOutpoints));
2085
2215
  else if (tokenAction.action == 'delete') tK = tK.concat(await (0, $b4a043612f1c4089$export$48a48877d6df17e9)(this.transactionBuilder, this._account.accountKeys, tokenAction.extraData.outpoint));
2086
- else tK = tK.concat(await (0, $b4a043612f1c4089$export$931e37acdaa22dcf)(this.transactionBuilder, this._account.accountKeys, tokenAction.token, tokenAction.amount));
2216
+ else tK = tK.concat(await (0, $b4a043612f1c4089$export$931e37acdaa22dcf)(this.transactionBuilder, this._account.accountKeys, tokenAction.token, tokenAction.amount, this.spentOutpoints));
2087
2217
  this._keysToSign.concat(tK);
2088
2218
  }
2089
- nK = nK.concat(await (0, $b4a043612f1c4089$export$afd979971a55acfc)(this.transactionBuilder, this._account.accountKeys, this.totalValue, this.txOptions));
2219
+ nK = nK.concat(await (0, $b4a043612f1c4089$export$afd979971a55acfc)(this.transactionBuilder, this._account.accountKeys, this.totalValue, this.txOptions, this.spentOutpoints));
2090
2220
  this._keysToSign = tK.concat(nK);
2091
2221
  });
2092
2222
  return this;
2093
2223
  }
2094
2224
  sign() {
2095
2225
  this.builder.push(async ()=>{
2096
- const blockHeight = await (0, $b5bfd17fdf06d231$export$eaa49f0478d81b9d).getBlockTip();
2097
- this.transactionBuilder.lockUntilBlockHeight(blockHeight.height).sign(this._keysToSign);
2226
+ // Process each transaction input using placeholder-based signing
2227
+ const inputs = this.transactionBuilder.transaction.inputs;
2228
+ for(let inputIndex = 0; inputIndex < inputs.length; inputIndex++){
2229
+ const input = inputs[inputIndex];
2230
+ // Analyze the scriptSig to determine signing strategy
2231
+ const analysis = this.analyzeScriptSig(input.scriptSig);
2232
+ if (analysis.strategy === 'skip') {
2233
+ console.log(`Skipping input ${inputIndex} - no placeholder found`);
2234
+ continue;
2235
+ }
2236
+ // Determine sighash type from placeholder bytes (or default to SIGHASH_ALL)
2237
+ const sighashType = analysis.sighashType || new (0, $l50U0$libnexats.SighashType)();
2238
+ // Try signing with all available keys until one works
2239
+ let signed = false;
2240
+ const allAddressKeys = this._account.getAddresses();
2241
+ for (const addressKey of allAddressKeys)try {
2242
+ // Sign the input
2243
+ const sig = (0, $l50U0$libnexats.TxSigner).sign(this.transactionBuilder.transaction, inputIndex, sighashType, this.transactionBuilder.transaction.inputs[inputIndex].getSubscript(), addressKey.key.privateKey);
2244
+ // Create signature buffer (Schnorr signature for Nexa)
2245
+ const signatureBuffer = new (0, $l50U0$libnexats.TxSignature)({
2246
+ publicKey: addressKey.key.publicKey,
2247
+ sigType: sighashType,
2248
+ signature: sig,
2249
+ subscript: this.transactionBuilder.transaction.inputs[inputIndex].getSubscript(),
2250
+ inputIndex: inputIndex
2251
+ }).toTxSatisfier();
2252
+ if (analysis.strategy === 'sign_all') this.transactionBuilder.signInput(inputIndex, addressKey.key.privateKey, sighashType);
2253
+ else if (analysis.strategy === 'replace_placeholder' && analysis.placeholderIndex !== undefined) {
2254
+ // Handle script template with mixed satisfier elements
2255
+ if (input instanceof (0, $l50U0$libnexats.ScriptTemplateInput) && analysis.satisfierElements && analysis.satisfierElements.length > 0) {
2256
+ // Reconstruct script template with preserved satisfier elements plus signature
2257
+ const templateInput = input;
2258
+ const allSatisfierElements = [
2259
+ ...analysis.satisfierElements
2260
+ ];
2261
+ // Insert signature at the correct position (where placeholder was)
2262
+ const relativeIndex = analysis.placeholderIndex - 2; // Adjust for template and constraint chunks
2263
+ allSatisfierElements.splice(relativeIndex, 0, signatureBuffer);
2264
+ this.transactionBuilder.transaction.inputs[inputIndex].scriptSig = this.buildScriptSig(templateInput.templateScript, templateInput.constraintScript, allSatisfierElements);
2265
+ } else if (input instanceof (0, $l50U0$libnexats.ScriptTemplateInput)) {
2266
+ // Script template with only placeholder (no other satisfier elements)
2267
+ const templateInput = input;
2268
+ this.transactionBuilder.transaction.inputs[inputIndex].scriptSig = this.buildScriptSig(templateInput.templateScript, templateInput.constraintScript, [
2269
+ signatureBuffer
2270
+ ]);
2271
+ } else // P2PKH script: signature placeholder + public key
2272
+ this.transactionBuilder.transaction.inputs[inputIndex].scriptSig = (0, $l50U0$libnexats.ScriptFactory).buildPublicKeyHashIn(addressKey.key.publicKey, sig);
2273
+ }
2274
+ signed = true;
2275
+ break;
2276
+ } catch (error) {
2277
+ // console.log(`Key for address ${addressKey.address} failed to sign input ${inputIndex}:`, (error as Error).message)
2278
+ }
2279
+ signed;
2280
+ }
2098
2281
  });
2099
2282
  return this;
2100
2283
  }
2284
+ buildSatisfier(elements) {
2285
+ let script = new (0, $l50U0$libnexats.Script)();
2286
+ for (const element of elements)script = script.add(element);
2287
+ return script;
2288
+ }
2289
+ buildScriptSig(template, constraint, satisfierElements) {
2290
+ const satisfierScript = this.buildSatisfier(satisfierElements);
2291
+ return (0, $l50U0$libnexats.ScriptFactory).buildScriptTemplateIn(template, constraint, satisfierScript);
2292
+ }
2101
2293
  /**
2102
- * Sign the transaction with specific sighash specification for inputs/outputs
2103
- * @param sighashSpec - Specification of which inputs and outputs to sign
2104
- * @returns This instance for chaining
2105
- */ signWithSpec(sighashSpec) {
2106
- this.builder.push(async ()=>{
2107
- const blockHeight = await (0, $b5bfd17fdf06d231$export$eaa49f0478d81b9d).getBlockTip();
2108
- this.transactionBuilder.lockUntilBlockHeight(blockHeight.height);
2109
- // If sighashSpec is provided, use selective signing
2110
- if (sighashSpec) {
2111
- // Parse sighashSpec and create appropriate SighashType
2112
- const sighashType = this.parseSighashSpec(sighashSpec);
2113
- // Sign each input with the specified sighash type
2114
- for(let i = 0; i < this._keysToSign.length; i++)if (this._keysToSign[i]) this.transactionBuilder.signInput(i, this._keysToSign[i], sighashType);
2115
- } else // Fall back to default signing if no spec provided
2116
- this.transactionBuilder.sign(this._keysToSign);
2117
- });
2118
- return this;
2294
+ * Check if a buffer is a 64-byte placeholder (all zeros)
2295
+ * @param buf - Buffer to check
2296
+ * @returns true if it's a 64-byte zero placeholder
2297
+ */ isPlaceholder(buf) {
2298
+ if (!buf || buf.length < 64) return false;
2299
+ const placeholderBytes = Buffer.alloc(64, 0);
2300
+ return buf.subarray(0, 64).equals(placeholderBytes);
2119
2301
  }
2120
2302
  /**
2121
- * Parse sighash specification into SighashType instance
2122
- * @param sighashSpec - The sighash specification from wallet-comms-sdk
2123
- * @returns SighashType instance
2124
- */ parseSighashSpec(sighashSpec) {
2125
- // If sighashSpec is already a SighashType, return it
2126
- if (sighashSpec instanceof (0, $l50U0$libnexats.SighashType)) return sighashSpec;
2127
- // Create new SighashType based on specification
2303
+ * Extract sighash type from placeholder buffer (bytes after the 64-byte placeholder)
2304
+ * @param buf - Buffer that may contain sighash type after placeholder
2305
+ * @returns SighashType or null if using default SIGHASH_ALL (empty sighash = 0)
2306
+ */ extractSighashFromPlaceholder(buf) {
2307
+ if (buf.length <= 64) // Empty SigHash - use single byte 0 (sign all inputs and outputs)
2308
+ return (0, $l50U0$libnexats.SighashType).ALL; // Use default SIGHASH_ALL
2309
+ // Parse the sighash bytes starting at offset 64
2310
+ const sighashBytes = buf.subarray(64);
2311
+ if (sighashBytes.length === 0) return (0, $l50U0$libnexats.SighashType).ALL; // Default SIGHASH_ALL
2312
+ // Get the sighashtype flag byte
2313
+ const flagByte = sighashBytes[0];
2314
+ // Upper 4 bits = input type, Lower 4 bits = output type
2315
+ const inputType = flagByte >> 4 & 0x0F;
2316
+ const outputType = flagByte & 0x0F;
2128
2317
  const sighashType = new (0, $l50U0$libnexats.SighashType)();
2129
- // Handle the v0.6.0 format with inType, outType, inData, outData
2130
- if (typeof sighashSpec === 'object' && sighashSpec !== null) {
2131
- if (sighashSpec.inType !== undefined) sighashType.inType = sighashSpec.inType;
2132
- if (sighashSpec.outType !== undefined) sighashType.outType = sighashSpec.outType;
2133
- if (sighashSpec.inData !== undefined) sighashType.inData = sighashSpec.inData;
2134
- if (sighashSpec.outData !== undefined) sighashType.outData = sighashSpec.outData;
2318
+ let byteIndex = 1;
2319
+ // Parse input type flags
2320
+ switch(inputType){
2321
+ case 0:
2322
+ sighashType.inType = 0; // ALL
2323
+ break;
2324
+ case 1:
2325
+ sighashType.inType = 1; // FIRST_N
2326
+ if (byteIndex < sighashBytes.length) {
2327
+ sighashType.inData = [
2328
+ sighashBytes[byteIndex]
2329
+ ];
2330
+ byteIndex++;
2331
+ }
2332
+ break;
2333
+ case 2:
2334
+ sighashType.inType = 2; // THIS
2335
+ break;
2336
+ default:
2337
+ // Unknown input type, default to ALL
2338
+ sighashType.inType = 0;
2339
+ }
2340
+ // Parse output type flags
2341
+ switch(outputType){
2342
+ case 0:
2343
+ sighashType.outType = 0; // ALL
2344
+ break;
2345
+ case 1:
2346
+ sighashType.outType = 1; // FIRST_N
2347
+ if (byteIndex < sighashBytes.length) {
2348
+ sighashType.outData = [
2349
+ sighashBytes[byteIndex]
2350
+ ];
2351
+ byteIndex++;
2352
+ }
2353
+ break;
2354
+ case 2:
2355
+ sighashType.outType = 2; // TWO_OUTPUTS (if supported)
2356
+ const outData = [];
2357
+ if (byteIndex < sighashBytes.length) {
2358
+ outData.push(sighashBytes[byteIndex]);
2359
+ byteIndex++;
2360
+ }
2361
+ if (byteIndex < sighashBytes.length) {
2362
+ outData.push(sighashBytes[byteIndex]);
2363
+ byteIndex++;
2364
+ }
2365
+ sighashType.outData = outData;
2366
+ break;
2367
+ default:
2368
+ // Unknown output type, default to ALL
2369
+ sighashType.outType = 0;
2135
2370
  }
2136
- // If no valid sighash data was parsed, default to ALL
2137
- return sighashType.isInvalid() ? (0, $l50U0$libnexats.SighashType).ALL : sighashType;
2371
+ return sighashType;
2372
+ }
2373
+ /**
2374
+ * Determine signing behavior based on scriptSig content
2375
+ * @param scriptSig - The scriptSig to analyze
2376
+ * @returns Object with signing strategy and sighash type
2377
+ */ analyzeScriptSig(scriptSig) {
2378
+ const chunks = scriptSig.chunks;
2379
+ // Empty scriptSig - sign with SIGHASH_ALL
2380
+ if (chunks.length === 0) return {
2381
+ strategy: 'sign_all',
2382
+ sighashType: (0, $l50U0$libnexats.SighashType).ALL
2383
+ };
2384
+ // Single chunk
2385
+ if (chunks.length === 1) {
2386
+ const chunk = chunks[0];
2387
+ if (chunk.buf && this.isPlaceholder(chunk.buf)) {
2388
+ // Single placeholder - treat same as empty, sign with SIGHASH_ALL or extract sighash
2389
+ const sighashType = this.extractSighashFromPlaceholder(chunk.buf);
2390
+ return {
2391
+ strategy: 'sign_all',
2392
+ sighashType: sighashType
2393
+ };
2394
+ }
2395
+ // Single non-placeholder chunk - skip signing
2396
+ return {
2397
+ strategy: 'skip',
2398
+ sighashType: null
2399
+ };
2400
+ }
2401
+ // For script templates, check if this follows the 3-part structure: template, constraint, satisfier elements
2402
+ // If chunks.length >= 3, assume it's a script template where chunks[2:] are satisfier elements
2403
+ if (chunks.length >= 3) {
2404
+ // Extract satisfier elements (chunks starting from index 2)
2405
+ const satisfierElements = [];
2406
+ let placeholderIndex;
2407
+ let sighashType = null;
2408
+ for(let i = 2; i < chunks.length; i++){
2409
+ const chunk = chunks[i];
2410
+ if (chunk.buf && this.isPlaceholder(chunk.buf)) {
2411
+ // Found placeholder in satisfier elements
2412
+ placeholderIndex = i;
2413
+ sighashType = this.extractSighashFromPlaceholder(chunk.buf);
2414
+ } else // Non-placeholder satisfier element - preserve it
2415
+ satisfierElements.push(chunk);
2416
+ }
2417
+ if (placeholderIndex !== undefined) return {
2418
+ strategy: 'replace_placeholder',
2419
+ sighashType: sighashType,
2420
+ placeholderIndex: placeholderIndex,
2421
+ satisfierElements: satisfierElements
2422
+ };
2423
+ }
2424
+ // Multiple chunks - look for placeholder (fallback for non-template scripts)
2425
+ for(let i = 0; i < chunks.length; i++){
2426
+ const chunk = chunks[i];
2427
+ if (chunk.buf && this.isPlaceholder(chunk.buf)) {
2428
+ const sighashType = this.extractSighashFromPlaceholder(chunk.buf);
2429
+ return {
2430
+ strategy: 'replace_placeholder',
2431
+ sighashType: sighashType,
2432
+ placeholderIndex: i
2433
+ };
2434
+ }
2435
+ }
2436
+ // Multiple chunks without placeholder - skip signing
2437
+ return {
2438
+ strategy: 'skip',
2439
+ sighashType: null
2440
+ };
2138
2441
  }
2139
2442
  /**
2140
2443
  * Validates that the account has the necessary keys before performing operations
@@ -2184,10 +2487,14 @@ class $7fcf3b163a95e69a$export$2e2bcd8739ae039 {
2184
2487
  * Creates a new account of the specified type
2185
2488
  * @param accountType Type of account to create (DAPP, VAULT, or DEFAULT)
2186
2489
  * @param masterKey Master HD private key for deriving account keys
2490
+ * @param forceNextIndex If true, forces the next sequential index regardless of blockchain usage
2187
2491
  * @returns Promise resolving to the created account
2188
- */ async createAccount(accountType, masterKey) {
2189
- // Get the next available index for this account type
2190
- const nextIndex = await (0, $0d59d2bcffd646c5$export$ef13479e8d3251d7)(accountType, masterKey);
2492
+ */ async createAccount(accountType, masterKey, forceNextIndex = false) {
2493
+ let nextIndex;
2494
+ if (forceNextIndex) // Force next sequential index considering both blockchain and store
2495
+ nextIndex = await this.getNextSequentialIndex(accountType, masterKey);
2496
+ else // Get the next available index for this account type (discovery-based)
2497
+ nextIndex = await (0, $0d59d2bcffd646c5$export$ef13479e8d3251d7)(accountType, masterKey);
2191
2498
  const accountStoreKey = this.getAccountStoreKey(accountType, nextIndex);
2192
2499
  // Check if account already exists
2193
2500
  const indexExists = this._accounts.get(String(accountStoreKey));
@@ -2298,6 +2605,41 @@ class $7fcf3b163a95e69a$export$2e2bcd8739ae039 {
2298
2605
  }
2299
2606
  return accounts;
2300
2607
  }
2608
+ /**
2609
+ * Gets the next sequential index for an account type, considering both blockchain
2610
+ * discovery and accounts already in the store
2611
+ * @param accountType The account type to get the next index for
2612
+ * @param masterKey Master HD private key for blockchain discovery
2613
+ * @returns Promise resolving to the next safe sequential index
2614
+ */ async getNextSequentialIndex(accountType, masterKey) {
2615
+ // First, get the discovery-based index (what exists on blockchain)
2616
+ const discoveryIndex = await (0, $0d59d2bcffd646c5$export$ef13479e8d3251d7)(accountType, masterKey);
2617
+ // Then, check what's the next index based on current store
2618
+ const storeBasedIndex = this.getNextStoreIndex(accountType);
2619
+ // Use whichever is higher to ensure we don't conflict with either
2620
+ return Math.max(discoveryIndex, storeBasedIndex);
2621
+ }
2622
+ /**
2623
+ * Gets the next index based only on accounts currently in the store
2624
+ * @param accountType The account type to check
2625
+ * @returns The next available index in the store
2626
+ */ getNextStoreIndex(accountType) {
2627
+ const indexes = [];
2628
+ for (const [key, _] of this._accounts)switch(accountType){
2629
+ case (0, $0d59d2bcffd646c5$export$b8ca5fa4899cbfc7).DAPP_ACCOUNT:
2630
+ if (key.startsWith('2.')) indexes.push(parseInt(key.split('.')[1]));
2631
+ break;
2632
+ case (0, $0d59d2bcffd646c5$export$b8ca5fa4899cbfc7).VAULT_ACCOUNT:
2633
+ if (key.startsWith('1.')) indexes.push(parseInt(key.split('.')[1]));
2634
+ break;
2635
+ case (0, $0d59d2bcffd646c5$export$b8ca5fa4899cbfc7).NEXA_ACCOUNT:
2636
+ if (!key.includes('.')) indexes.push(parseInt(key));
2637
+ break;
2638
+ }
2639
+ if (indexes.length === 0) return 0; // First account of this type
2640
+ // Return the next sequential index after the highest existing one
2641
+ return Math.max(...indexes) + 1;
2642
+ }
2301
2643
  }
2302
2644
 
2303
2645
 
@@ -2389,6 +2731,7 @@ class $b76d3bd5024ccfc0$export$2e2bcd8739ae039 {
2389
2731
  * const wallet = new Wallet(undefined, 'testnet');
2390
2732
  * ```
2391
2733
  */ constructor(data, network){
2734
+ /** Whether to force sequential account indexing instead of discovery-based indexing */ this._forceSequentialIndexing = false;
2392
2735
  this._network = (0, $l50U0$libnexats.Networks).get(network) ?? (0, $l50U0$libnexats.Networks).mainnet;
2393
2736
  this._accountStore = new (0, $7fcf3b163a95e69a$export$2e2bcd8739ae039)();
2394
2737
  if ((0, $54dde1e65fb8e5b8$export$2e2bcd8739ae039)(data)) {
@@ -2542,7 +2885,7 @@ class $b76d3bd5024ccfc0$export$2e2bcd8739ae039 {
2542
2885
  * const dappAccount = await wallet.newAccount('DappAccount');
2543
2886
  * ```
2544
2887
  */ async newAccount(accountType) {
2545
- return await this.accountStore.createAccount(accountType, this.masterKey);
2888
+ return await this.accountStore.createAccount(accountType, this.masterKey, this._forceSequentialIndexing);
2546
2889
  }
2547
2890
  /**
2548
2891
  * Broadcast a signed transaction to the Nexa network
@@ -2760,6 +3103,47 @@ class $b76d3bd5024ccfc0$export$2e2bcd8739ae039 {
2760
3103
  */ get network() {
2761
3104
  return this._network;
2762
3105
  }
3106
+ /**
3107
+ * Get the current sequential indexing setting
3108
+ *
3109
+ * @returns true if forcing sequential indexing, false if using discovery-based indexing
3110
+ *
3111
+ * @example
3112
+ * ```typescript
3113
+ * const isSequential = wallet.forceSequentialIndexing;
3114
+ * console.log('Sequential indexing:', isSequential);
3115
+ * ```
3116
+ */ get forceSequentialIndexing() {
3117
+ return this._forceSequentialIndexing;
3118
+ }
3119
+ /**
3120
+ * Set whether to force sequential account indexing
3121
+ *
3122
+ * When enabled, new accounts will use sequential indexes (0, 1, 2, ...) regardless
3123
+ * of blockchain activity. This is useful for multi-user wallets where each user
3124
+ * needs a unique account even if they haven't used it yet.
3125
+ *
3126
+ * When disabled (default), account creation uses discovery-based indexing which
3127
+ * scans the blockchain for existing activity before assigning indexes.
3128
+ *
3129
+ * @param force - true to force sequential indexing, false for discovery-based
3130
+ *
3131
+ * @example
3132
+ * ```typescript
3133
+ * // Enable sequential indexing for multi-user scenarios
3134
+ * wallet.forceSequentialIndexing = true;
3135
+ *
3136
+ * // Create accounts - they'll be 2.0, 2.1, 2.2 regardless of usage
3137
+ * const alice = await wallet.newAccount(AccountType.DAPP_ACCOUNT);
3138
+ * const bob = await wallet.newAccount(AccountType.DAPP_ACCOUNT);
3139
+ * const charlie = await wallet.newAccount(AccountType.DAPP_ACCOUNT);
3140
+ *
3141
+ * // Disable for normal discovery-based indexing
3142
+ * wallet.forceSequentialIndexing = false;
3143
+ * ```
3144
+ */ set forceSequentialIndexing(force) {
3145
+ this._forceSequentialIndexing = force;
3146
+ }
2763
3147
  }
2764
3148
 
2765
3149
 
@@ -2771,13 +3155,15 @@ class $b76d3bd5024ccfc0$export$2e2bcd8739ae039 {
2771
3155
 
2772
3156
 
2773
3157
  const $52632971edbdb934$var$MAX_INPUTS_OUTPUTS = 250;
2774
- async function $52632971edbdb934$export$20e004915450ed44(txBuilder, addresses, totalTxValue, options) {
3158
+ async function $52632971edbdb934$export$20e004915450ed44(txBuilder, addresses, totalTxValue, options, spentOutpoints) {
2775
3159
  if ((0, $bc5ca2c06b1affa3$export$c8733ae29fb53302)(addresses)) throw new Error("Not enough Nexa balance.");
2776
3160
  let usedAddresses = new Set();
2777
3161
  let origAmount = options.isConsolidate ? 0 : Number(totalTxValue);
2778
3162
  for (let item of addresses){
2779
3163
  let utxos = await (0, $b5bfd17fdf06d231$export$eaa49f0478d81b9d).getNexaUtxos(item.address);
2780
3164
  for (let utxo of utxos){
3165
+ // Skip if this outpoint is already spent
3166
+ if (spentOutpoints && spentOutpoints.has(utxo.outpoint_hash)) continue;
2781
3167
  let input = {
2782
3168
  outpoint: utxo.outpoint_hash,
2783
3169
  address: item.address,
@@ -2785,6 +3171,8 @@ async function $52632971edbdb934$export$20e004915450ed44(txBuilder, addresses, t
2785
3171
  templateData: options.templateData
2786
3172
  };
2787
3173
  txBuilder.from(input);
3174
+ // Add to spent outpoints set
3175
+ if (spentOutpoints) spentOutpoints.add(utxo.outpoint_hash);
2788
3176
  if (!usedAddresses.has(item.address)) usedAddresses.add(item.address);
2789
3177
  if (options.isConsolidate) {
2790
3178
  // need to handle change
@@ -2828,7 +3216,7 @@ async function $52632971edbdb934$export$20e004915450ed44(txBuilder, addresses, t
2828
3216
  };
2829
3217
  throw new Error(JSON.stringify(err));
2830
3218
  }
2831
- async function $52632971edbdb934$export$49bc96b87058cba4(txBuilder, addresses, token, outTokenAmount) {
3219
+ async function $52632971edbdb934$export$49bc96b87058cba4(txBuilder, addresses, token, outTokenAmount, spentOutpoints) {
2832
3220
  if ((0, $bc5ca2c06b1affa3$export$c8733ae29fb53302)(addresses)) throw new Error("Not enough token balance.");
2833
3221
  let usedKeys = new Set();
2834
3222
  let inTokenAmount = 0n;
@@ -2836,6 +3224,8 @@ async function $52632971edbdb934$export$49bc96b87058cba4(txBuilder, addresses, t
2836
3224
  let utxos = await (0, $b5bfd17fdf06d231$export$eaa49f0478d81b9d).getTokenUtxos(item.address, token);
2837
3225
  for (let utxo of utxos){
2838
3226
  if (utxo.token_amount < 0) continue;
3227
+ // Skip if this outpoint is already spent
3228
+ if (spentOutpoints && spentOutpoints.has(utxo.outpoint_hash)) continue;
2839
3229
  txBuilder.from({
2840
3230
  outpoint: utxo.outpoint_hash,
2841
3231
  address: item.address,
@@ -2843,6 +3233,8 @@ async function $52632971edbdb934$export$49bc96b87058cba4(txBuilder, addresses, t
2843
3233
  groupId: utxo.group,
2844
3234
  groupAmount: BigInt(utxo.token_amount)
2845
3235
  });
3236
+ // Add to spent outpoints set
3237
+ if (spentOutpoints) spentOutpoints.add(utxo.outpoint_hash);
2846
3238
  inTokenAmount = inTokenAmount + BigInt(utxo.token_amount);
2847
3239
  if (!usedKeys.has(item.address)) usedKeys.add(item.address);
2848
3240
  if (inTokenAmount > (0, $bc5ca2c06b1affa3$export$8ba128bc85947a2a)) throw new Error("Token inputs exceeded max amount. Consider sending in small chunks");
@@ -2857,17 +3249,21 @@ async function $52632971edbdb934$export$49bc96b87058cba4(txBuilder, addresses, t
2857
3249
  }
2858
3250
  throw new Error("Not enough token balance");
2859
3251
  }
2860
- async function $52632971edbdb934$export$74e83de914f372c5(txBuilder, addresses, opReturnData, network) {
3252
+ async function $52632971edbdb934$export$74e83de914f372c5(txBuilder, addresses, opReturnData, network, spentOutpoints) {
2861
3253
  let outpoint = '';
2862
3254
  let usedKeys = [];
2863
3255
  for (let item of addresses){
2864
3256
  let utxos = await (0, $b5bfd17fdf06d231$export$eaa49f0478d81b9d).getNexaUtxos(item.address);
2865
3257
  for (let utxo of utxos){
3258
+ // Skip if this outpoint is already spent
3259
+ if (spentOutpoints && spentOutpoints.has(utxo.outpoint_hash)) continue;
2866
3260
  txBuilder.from({
2867
3261
  outpoint: utxo.outpoint_hash,
2868
3262
  address: item.address,
2869
3263
  satoshis: utxo.value
2870
3264
  });
3265
+ // Add to spent outpoints set
3266
+ if (spentOutpoints) spentOutpoints.add(utxo.outpoint_hash);
2871
3267
  if ((0, $bc5ca2c06b1affa3$export$c8733ae29fb53302)(outpoint) && !utxo.has_token) {
2872
3268
  outpoint = utxo.outpoint_hash;
2873
3269
  let id = (0, $l50U0$libnexats.GroupToken).findGroupId(outpoint, Buffer.from(opReturnData, 'hex'), (0, $l50U0$libnexats.GroupToken).authFlags.ACTIVE_FLAG_BITS);
@@ -2894,16 +3290,20 @@ async function $52632971edbdb934$export$1eb54f2f084fd3c6(txBuilder, addresses, o
2894
3290
  addrKey.address
2895
3291
  ];
2896
3292
  }
2897
- async function $52632971edbdb934$export$d7c9c386067a6463(txBuilder, addresses, token, perm, subgroup = '', quantity) {
3293
+ async function $52632971edbdb934$export$d7c9c386067a6463(txBuilder, addresses, token, perm, subgroup = '', quantity, spentOutpoints) {
2898
3294
  for (let item of addresses){
2899
3295
  let utxos = await (0, $b5bfd17fdf06d231$export$eaa49f0478d81b9d).getTokenUtxos(item.address, token);
2900
3296
  for (let utxo of utxos){
2901
3297
  if (!(0, $22e282633cad8f97$export$7f7cffd29bf2d96d)(utxo.token_amount, perm)) continue;
3298
+ // Skip if this outpoint is already spent
3299
+ if (spentOutpoints && spentOutpoints.has(utxo.outpoint_hash)) continue;
2902
3300
  txBuilder.from({
2903
3301
  outpoint: utxo.outpoint_hash,
2904
3302
  address: item.address,
2905
3303
  satoshis: utxo.value
2906
3304
  });
3305
+ // Add to spent outpoints set
3306
+ if (spentOutpoints) spentOutpoints.add(utxo.outpoint_hash);
2907
3307
  if (perm === 'subgroup') {
2908
3308
  const subgroupQuantity = quantity ?? (0, $22e282633cad8f97$export$636fb0b03b94ac81)(utxo.token_amount, false);
2909
3309
  txBuilder.to(item.address, (0, $l50U0$libnexats.Transaction).DUST_AMOUNT, subgroup, subgroupQuantity);
@@ -2917,7 +3317,7 @@ async function $52632971edbdb934$export$d7c9c386067a6463(txBuilder, addresses, t
2917
3317
  }
2918
3318
  throw new Error("The requested authority not found");
2919
3319
  }
2920
- async function $52632971edbdb934$export$5c44e04d8c04c292(txBuilder, addresses, token, perms, toAddr) {
3320
+ async function $52632971edbdb934$export$5c44e04d8c04c292(txBuilder, addresses, token, perms, toAddr, spentOutpoints) {
2921
3321
  let usedAddresses = [];
2922
3322
  let reqiredPerms = new Set(perms);
2923
3323
  reqiredPerms.add('authorise');
@@ -2925,6 +3325,8 @@ async function $52632971edbdb934$export$5c44e04d8c04c292(txBuilder, addresses, t
2925
3325
  let utxos = await (0, $b5bfd17fdf06d231$export$eaa49f0478d81b9d).getTokenUtxos(item.address, token);
2926
3326
  for (let utxo of utxos){
2927
3327
  if (utxo.token_amount > 0) continue;
3328
+ // Skip if this outpoint is already spent
3329
+ if (spentOutpoints && spentOutpoints.has(utxo.outpoint_hash)) continue;
2928
3330
  let found = false;
2929
3331
  for (let perm of reqiredPerms)if ((0, $22e282633cad8f97$export$7f7cffd29bf2d96d)(utxo.token_amount, perm)) {
2930
3332
  reqiredPerms.delete(perm);
@@ -2937,6 +3339,8 @@ async function $52632971edbdb934$export$5c44e04d8c04c292(txBuilder, addresses, t
2937
3339
  satoshis: utxo.value
2938
3340
  });
2939
3341
  usedAddresses.push(item.address);
3342
+ // Add to spent outpoints set
3343
+ if (spentOutpoints) spentOutpoints.add(utxo.outpoint_hash);
2940
3344
  // duplicate
2941
3345
  txBuilder.to(toAddr != null ? toAddr : item.address, (0, $l50U0$libnexats.Transaction).DUST_AMOUNT, token, (0, $22e282633cad8f97$export$636fb0b03b94ac81)(utxo.token_amount));
2942
3346
  if (reqiredPerms.size === 0) return usedAddresses;
@@ -2948,7 +3352,6 @@ async function $52632971edbdb934$export$5c44e04d8c04c292(txBuilder, addresses, t
2948
3352
 
2949
3353
 
2950
3354
 
2951
-
2952
3355
  class $2ce5b75d10954bb1$export$2e2bcd8739ae039 extends (0, $57b0117fe8233fef$export$bba690fb5c12ba04) {
2953
3356
  /**
2954
3357
  * Creates a new WatchOnlyTransactionCreator
@@ -3018,22 +3421,22 @@ class $2ce5b75d10954bb1$export$2e2bcd8739ae039 extends (0, $57b0117fe8233fef$exp
3018
3421
  // Process token operations if any are configured
3019
3422
  if (this.tokens.size > 0) for (const tokenAction of this.tokens){
3020
3423
  if (tokenAction.action == 'mint' || tokenAction.action == 'melt') // Handle token minting/melting - requires authority
3021
- tokenAddresses = tokenAddresses.concat(await (0, $52632971edbdb934$export$d7c9c386067a6463)(this.transactionBuilder, this._availableAddresses, tokenAction.token, tokenAction.action));
3424
+ tokenAddresses = tokenAddresses.concat(await (0, $52632971edbdb934$export$d7c9c386067a6463)(this.transactionBuilder, this._availableAddresses, tokenAction.token, tokenAction.action, undefined, undefined, this.spentOutpoints));
3022
3425
  else if (tokenAction.action == 'group') // Handle group token creation
3023
- tokenAddresses = tokenAddresses.concat(await (0, $52632971edbdb934$export$74e83de914f372c5)(this.transactionBuilder, this._availableAddresses, tokenAction.extraData?.opReturnData, this.network));
3426
+ tokenAddresses = tokenAddresses.concat(await (0, $52632971edbdb934$export$74e83de914f372c5)(this.transactionBuilder, this._availableAddresses, tokenAction.extraData?.opReturnData, this.network, this.spentOutpoints));
3024
3427
  else if (tokenAction.action == 'subgroup') // Handle subgroup token creation
3025
- tokenAddresses = tokenAddresses.concat(await (0, $52632971edbdb934$export$d7c9c386067a6463)(this.transactionBuilder, this._availableAddresses, tokenAction.parentToken, tokenAction.action, tokenAction.token, tokenAction.extraData?.quantity));
3428
+ tokenAddresses = tokenAddresses.concat(await (0, $52632971edbdb934$export$d7c9c386067a6463)(this.transactionBuilder, this._availableAddresses, tokenAction.parentToken, tokenAction.action, tokenAction.token, tokenAction.extraData?.quantity, this.spentOutpoints));
3026
3429
  else if (tokenAction.action == 'renew') // Handle authority renewal
3027
- tokenAddresses = tokenAddresses.concat(await (0, $52632971edbdb934$export$5c44e04d8c04c292)(this.transactionBuilder, this._availableAddresses, tokenAction.token, tokenAction.extraData.perms, tokenAction.extraData?.address));
3430
+ tokenAddresses = tokenAddresses.concat(await (0, $52632971edbdb934$export$5c44e04d8c04c292)(this.transactionBuilder, this._availableAddresses, tokenAction.token, tokenAction.extraData.perms, tokenAction.extraData?.address, this.spentOutpoints));
3028
3431
  else if (tokenAction.action == 'delete') // Handle authority deletion
3029
3432
  tokenAddresses = tokenAddresses.concat(await (0, $52632971edbdb934$export$1eb54f2f084fd3c6)(this.transactionBuilder, this._availableAddresses, tokenAction.extraData.outpoint));
3030
3433
  else // Handle regular token transfers
3031
- tokenAddresses = tokenAddresses.concat(await (0, $52632971edbdb934$export$49bc96b87058cba4)(this.transactionBuilder, this._availableAddresses, tokenAction.token, tokenAction.amount));
3434
+ tokenAddresses = tokenAddresses.concat(await (0, $52632971edbdb934$export$49bc96b87058cba4)(this.transactionBuilder, this._availableAddresses, tokenAction.token, tokenAction.amount, this.spentOutpoints));
3032
3435
  // Accumulate addresses that need signing
3033
3436
  this._addressesToSignWith.concat(tokenAddresses);
3034
3437
  }
3035
3438
  // Populate NEXA inputs for transaction fees and change
3036
- nexaAddresses = nexaAddresses.concat(await (0, $52632971edbdb934$export$20e004915450ed44)(this.transactionBuilder, this._availableAddresses, this.totalValue, this.txOptions));
3439
+ nexaAddresses = nexaAddresses.concat(await (0, $52632971edbdb934$export$20e004915450ed44)(this.transactionBuilder, this._availableAddresses, this.totalValue, this.txOptions, this.spentOutpoints));
3037
3440
  // Combine all addresses that need signing
3038
3441
  this._addressesToSignWith = tokenAddresses.concat(nexaAddresses);
3039
3442
  });
@@ -3051,27 +3454,21 @@ class $2ce5b75d10954bb1$export$2e2bcd8739ae039 extends (0, $57b0117fe8233fef$exp
3051
3454
  return this;
3052
3455
  }
3053
3456
  /**
3054
- * Parse transaction from hex string (not implemented for watch-only)
3457
+ * Parse transaction from hex string using enhanced base class implementation
3055
3458
  * @param tx Transaction hex string
3056
3459
  * @returns This instance for chaining
3057
- * @throws Error indicating method not implemented
3058
3460
  */ parseTxHex(tx) {
3059
- this.builder.push(async ()=>{
3060
- const txBuilder = new (0, $l50U0$libnexats.TransactionBuilder)(tx);
3061
- const newTxBuilder = new (0, $l50U0$libnexats.TransactionBuilder)();
3062
- const oldInputs = txBuilder.transaction.inputs;
3063
- for (const input of oldInputs){
3064
- const utxo = await (0, $b5bfd17fdf06d231$export$eaa49f0478d81b9d).getUtxo(input.outpoint.toString('hex'));
3065
- newTxBuilder.from({
3066
- outpoint: utxo.tx_hash,
3067
- amount: utxo.amount,
3068
- scriptPubKey: utxo.scriptpubkey
3069
- });
3070
- }
3071
- newTxBuilder.transaction.outputs = txBuilder.transaction.outputs;
3072
- this.transactionBuilder = newTxBuilder;
3073
- });
3074
- return this;
3461
+ // Call parent's parseTxHex which handles script template reconstruction
3462
+ return super.parseTxHex(tx);
3463
+ }
3464
+ /**
3465
+ * Handle watch-only specific logic for each parsed input (track addresses for signing)
3466
+ */ async handleParsedInput(_input, utxo, _index) {
3467
+ // Track addresses that will need to be signed with external wallet
3468
+ if (utxo.addresses && utxo.addresses.length > 0) {
3469
+ const address = utxo.addresses[0];
3470
+ if (!this._addressesToSignWith.includes(address)) this._addressesToSignWith.push(address);
3471
+ }
3075
3472
  }
3076
3473
  }
3077
3474
 
@@ -3250,6 +3647,39 @@ const $7af6fdca1cf5d9d6$export$618de809a659cb44 = {
3250
3647
  };
3251
3648
 
3252
3649
 
3650
+ /**
3651
+ * Extract all token outputs and amounts from transaction hex
3652
+ * @param txHex - Transaction hex string
3653
+ * @param network - Network to use for token address creation
3654
+ * @returns Array of token outputs with tokenId and amount
3655
+ */ const $23698d921173fdf9$var$getTokenOutputsFromTx = (txHex, network)=>{
3656
+ const tokenOutputs = [];
3657
+ try {
3658
+ const transaction = new (0, $l50U0$libnexats.Transaction)(txHex);
3659
+ for (const output of transaction.outputs)if (output.type !== 0 && output.scriptPubKey.chunks[0]?.opcodenum >= 32) {
3660
+ const tokenIdHex = output.scriptPubKey.chunks[0].buf?.toString('hex');
3661
+ if (tokenIdHex && output.scriptPubKey.chunks[1]?.buf) {
3662
+ const tokenAddress = (0, $l50U0$libnexats.Address).fromObject({
3663
+ data: tokenIdHex,
3664
+ network: network.toString(),
3665
+ type: (0, $l50U0$libnexats.AddressType).GroupIdAddress
3666
+ });
3667
+ if (tokenAddress !== null) {
3668
+ const tokenAddressStr = tokenAddress.toString();
3669
+ // Extract token amount
3670
+ const tokenAmount = (0, $l50U0$libnexats.BNExtended).fromScriptNumBuffer(output.scriptPubKey.chunks[1].buf, false, 8).toBigInt();
3671
+ tokenOutputs.push({
3672
+ tokenId: tokenAddressStr,
3673
+ amount: tokenAmount.toString()
3674
+ });
3675
+ }
3676
+ }
3677
+ }
3678
+ } catch (error) {
3679
+ console.warn('Failed to extract token data from transaction:', error);
3680
+ }
3681
+ return tokenOutputs;
3682
+ };
3253
3683
  class $23698d921173fdf9$export$11e896a2f3ae4119 {
3254
3684
  constructor(wallet, approvalCallbacks){
3255
3685
  this.provider = null;
@@ -3389,23 +3819,23 @@ class $23698d921173fdf9$export$11e896a2f3ae4119 {
3389
3819
  // If parsing fails, keep default "Unknown" values
3390
3820
  console.warn('Failed to parse transaction for approval details:', error);
3391
3821
  }
3822
+ // Extract token outputs from transaction hex
3823
+ const tokenOutputs = $23698d921173fdf9$var$getTokenOutputsFromTx(request.hex, this.wallet.network);
3392
3824
  const approvalDetails = {
3393
3825
  dApp: this.connectedDApp,
3394
3826
  account: request.account,
3395
3827
  transactionHex: request.hex,
3396
3828
  broadcast: request.broadcast || false,
3397
- sighashSpec: request.sighashSpec,
3398
3829
  totalAmount: totalAmount,
3399
- fees: fees
3830
+ fees: fees,
3831
+ tokenOutputs: tokenOutputs.length > 0 ? tokenOutputs : undefined
3400
3832
  };
3401
3833
  const approved = await this.approvalCallbacks.approveTransaction(approvalDetails);
3402
3834
  if (!approved) throw new (0, $l50U0$walletcommssdk.JsonRpcError)((0, $l50U0$walletcommssdk.JsonRpcErrorCode).RequestRejected, 'Transaction signing rejected by user');
3403
3835
  }
3404
3836
  // Parse and sign the transaction
3405
3837
  let txBuilder = this.wallet.newTransaction(account, request.hex);
3406
- // Use signWithSpec if sighashSpec is provided, otherwise use regular sign
3407
- if (request.sighashSpec) txBuilder = txBuilder.signWithSpec(request.sighashSpec);
3408
- else txBuilder = txBuilder.sign();
3838
+ txBuilder = txBuilder.sign();
3409
3839
  const tx = await txBuilder.build();
3410
3840
  // Broadcast if requested
3411
3841
  if (request.broadcast) return await this.wallet.sendTransaction(tx); // Return just the txId string
@@ -3581,12 +4011,12 @@ class $4d217697e55d958e$export$80793d8292a1630a {
3581
4011
  */ async sendTransaction(toAddress, amount, opReturnData, broadcast = true) {
3582
4012
  if (!this.watchOnlyWallet || !this.connectedWalletAddress) throw new Error('No wallet connected');
3583
4013
  // Create unsigned transaction using WatchOnlyWallet
3584
- let txBuilder = this.watchOnlyWallet.newTransaction().onNetwork(this.currentNetwork).sendTo(toAddress, amount);
4014
+ let txBuilder = this.watchOnlyWallet.newTransaction().sendTo(toAddress, amount);
3585
4015
  // Add OP_RETURN data if provided
3586
4016
  if (opReturnData) txBuilder = txBuilder.addOpReturn(opReturnData);
3587
4017
  // Populate and build unsigned transaction
3588
4018
  const unsignedTx = await txBuilder.populate().build();
3589
- return await this.provider.signTransaction(this.connectedWalletAddress, unsignedTx, undefined, broadcast);
4019
+ return await this.provider.signTransaction(this.connectedWalletAddress, unsignedTx, broadcast);
3590
4020
  }
3591
4021
  /**
3592
4022
  * Send tokens to an address
@@ -3599,12 +4029,12 @@ class $4d217697e55d958e$export$80793d8292a1630a {
3599
4029
  */ async sendTokenTransaction(toAddress, amount, tokenId, opReturnData, broadcast = true) {
3600
4030
  if (!this.watchOnlyWallet || !this.connectedWalletAddress) throw new Error('No wallet connected');
3601
4031
  // Create unsigned token transaction
3602
- let txBuilder = this.watchOnlyWallet.newTransaction().onNetwork(this.currentNetwork).sendToToken(toAddress, amount, tokenId);
4032
+ let txBuilder = this.watchOnlyWallet.newTransaction().sendToToken(toAddress, amount, tokenId);
3603
4033
  // Add OP_RETURN data if provided
3604
4034
  if (opReturnData) txBuilder = txBuilder.addOpReturn(opReturnData);
3605
4035
  // Populate and build unsigned transaction
3606
4036
  const unsignedTx = await txBuilder.populate().build();
3607
- return await this.provider.signTransaction(this.connectedWalletAddress, unsignedTx, undefined, broadcast);
4037
+ return await this.provider.signTransaction(this.connectedWalletAddress, unsignedTx, broadcast);
3608
4038
  }
3609
4039
  /**
3610
4040
  * Create and mint a new token
@@ -3618,8 +4048,8 @@ class $4d217697e55d958e$export$80793d8292a1630a {
3618
4048
  */ async createToken(name, ticker, decimals, docUrl, docHash, broadcast = true) {
3619
4049
  if (!this.watchOnlyWallet || !this.connectedWalletAddress) throw new Error('No wallet connected');
3620
4050
  // Create token creation transaction
3621
- const unsignedTx = await this.watchOnlyWallet.newTransaction().onNetwork(this.currentNetwork).token(name, ticker, decimals, docUrl, docHash).populate().build();
3622
- return await this.provider.signTransaction(this.connectedWalletAddress, unsignedTx, undefined, broadcast);
4051
+ const unsignedTx = await this.watchOnlyWallet.newTransaction().token(name, ticker, decimals, docUrl, docHash).populate().build();
4052
+ return await this.provider.signTransaction(this.connectedWalletAddress, unsignedTx, broadcast);
3623
4053
  }
3624
4054
  /**
3625
4055
  * Create and mint a new NFT collection
@@ -3632,8 +4062,8 @@ class $4d217697e55d958e$export$80793d8292a1630a {
3632
4062
  */ async createCollection(name, ticker, docUrl, docHash, broadcast = true) {
3633
4063
  if (!this.watchOnlyWallet || !this.connectedWalletAddress) throw new Error('No wallet connected');
3634
4064
  // Create collection creation transaction
3635
- const unsignedTx = await this.watchOnlyWallet.newTransaction().onNetwork(this.currentNetwork).collection(name, ticker, docUrl, docHash).populate().build();
3636
- return await this.provider.signTransaction(this.connectedWalletAddress, unsignedTx, undefined, broadcast);
4065
+ const unsignedTx = await this.watchOnlyWallet.newTransaction().collection(name, ticker, docUrl, docHash).populate().build();
4066
+ return await this.provider.signTransaction(this.connectedWalletAddress, unsignedTx, broadcast);
3637
4067
  }
3638
4068
  /**
3639
4069
  * Create and mint an NFT within a collection
@@ -3645,8 +4075,8 @@ class $4d217697e55d958e$export$80793d8292a1630a {
3645
4075
  */ async createNFT(parentCollection, zipUrl, zipHash, broadcast = true) {
3646
4076
  if (!this.watchOnlyWallet || !this.connectedWalletAddress) throw new Error('No wallet connected');
3647
4077
  // Create NFT creation transaction
3648
- const unsignedTx = await this.watchOnlyWallet.newTransaction().onNetwork(this.currentNetwork).nft(parentCollection, zipUrl, zipHash).populate().build();
3649
- return await this.provider.signTransaction(this.connectedWalletAddress, unsignedTx, undefined, broadcast);
4078
+ const unsignedTx = await this.watchOnlyWallet.newTransaction().nft(parentCollection, zipUrl, zipHash).populate().build();
4079
+ return await this.provider.signTransaction(this.connectedWalletAddress, unsignedTx, broadcast);
3650
4080
  }
3651
4081
  /**
3652
4082
  * Create and mint an SFT (Semi-Fungible Token) within a collection with specified quantity
@@ -3659,8 +4089,85 @@ class $4d217697e55d958e$export$80793d8292a1630a {
3659
4089
  */ async createSFT(parentCollection, zipUrl, zipHash, quantity, broadcast = true) {
3660
4090
  if (!this.watchOnlyWallet || !this.connectedWalletAddress) throw new Error('No wallet connected');
3661
4091
  // Create SFT creation transaction
3662
- const unsignedTx = await this.watchOnlyWallet.newTransaction().onNetwork(this.currentNetwork).sft(parentCollection, zipUrl, zipHash, quantity).populate().build();
3663
- return await this.provider.signTransaction(this.connectedWalletAddress, unsignedTx, undefined, broadcast);
4092
+ const unsignedTx = await this.watchOnlyWallet.newTransaction().sft(parentCollection, zipUrl, zipHash, quantity).populate().build();
4093
+ return await this.provider.signTransaction(this.connectedWalletAddress, unsignedTx, broadcast);
4094
+ }
4095
+ /**
4096
+ * Sign an arbitrary unsigned transaction
4097
+ * @param unsignedTx Unsigned transaction hex string
4098
+ * @param sighashSpec Optional sighash specification for selective signing
4099
+ * @param broadcast Whether to broadcast immediately (default false)
4100
+ * @returns Promise resolving to transaction ID (if broadcast) or signed hex
4101
+ */ async signTransaction(unsignedTx, sighashSpec, broadcast = false) {
4102
+ if (!this.connectedWalletAddress) throw new Error('No wallet connected');
4103
+ return await this.provider.signTransaction(this.connectedWalletAddress, unsignedTx, broadcast);
4104
+ }
4105
+ /**
4106
+ * Build an unsigned transaction without signing
4107
+ * @param toAddress Destination address
4108
+ * @param amount Amount in satoshis (as string)
4109
+ * @param tokenId Optional token ID for token transfers
4110
+ * @param opReturnData Optional OP_RETURN data
4111
+ * @returns Promise resolving to unsigned transaction hex
4112
+ */ async buildTransaction(toAddress, amount, tokenId, opReturnData) {
4113
+ if (!this.watchOnlyWallet) throw new Error('No wallet connected');
4114
+ let txBuilder = this.watchOnlyWallet.newTransaction();
4115
+ if (tokenId) txBuilder = txBuilder.sendToToken(toAddress, amount, tokenId);
4116
+ else txBuilder = txBuilder.sendTo(toAddress, amount);
4117
+ if (opReturnData) txBuilder = txBuilder.addOpReturn(opReturnData);
4118
+ return await txBuilder.populate().build();
4119
+ }
4120
+ /**
4121
+ * Build an unsigned token creation transaction
4122
+ * @param name Token name
4123
+ * @param ticker Token ticker symbol
4124
+ * @param decimals Number of decimal places
4125
+ * @param docUrl URL to token documentation
4126
+ * @param docHash Hash of token documentation
4127
+ * @returns Promise resolving to unsigned transaction hex
4128
+ */ async buildTokenCreation(name, ticker, decimals, docUrl, docHash) {
4129
+ if (!this.watchOnlyWallet) throw new Error('No wallet connected');
4130
+ return await this.watchOnlyWallet.newTransaction().token(name, ticker, decimals, docUrl, docHash).populate().build();
4131
+ }
4132
+ /**
4133
+ * Build an unsigned collection creation transaction
4134
+ * @param name Collection name
4135
+ * @param ticker Collection ticker symbol
4136
+ * @param docUrl URL to collection documentation
4137
+ * @param docHash Hash of collection documentation
4138
+ * @returns Promise resolving to unsigned transaction hex
4139
+ */ async buildCollectionCreation(name, ticker, docUrl, docHash) {
4140
+ if (!this.watchOnlyWallet) throw new Error('No wallet connected');
4141
+ return await this.watchOnlyWallet.newTransaction().collection(name, ticker, docUrl, docHash).populate().build();
4142
+ }
4143
+ /**
4144
+ * Build an unsigned NFT creation transaction
4145
+ * @param parentCollection Parent collection token ID
4146
+ * @param zipUrl URL to NFT content ZIP file
4147
+ * @param zipHash Hash of NFT content ZIP file
4148
+ * @returns Promise resolving to unsigned transaction hex
4149
+ */ async buildNFTCreation(parentCollection, zipUrl, zipHash) {
4150
+ if (!this.watchOnlyWallet) throw new Error('No wallet connected');
4151
+ return await this.watchOnlyWallet.newTransaction().nft(parentCollection, zipUrl, zipHash).populate().build();
4152
+ }
4153
+ /**
4154
+ * Build an unsigned SFT creation transaction
4155
+ * @param parentCollection Parent collection token ID
4156
+ * @param zipUrl URL to SFT content ZIP file
4157
+ * @param zipHash Hash of SFT content ZIP file
4158
+ * @param quantity Quantity of SFTs to create
4159
+ * @returns Promise resolving to unsigned transaction hex
4160
+ */ async buildSFTCreation(parentCollection, zipUrl, zipHash, quantity) {
4161
+ if (!this.watchOnlyWallet) throw new Error('No wallet connected');
4162
+ return await this.watchOnlyWallet.newTransaction().sft(parentCollection, zipUrl, zipHash, quantity).populate().build();
4163
+ }
4164
+ /**
4165
+ * Initialize WatchOnlyWallet with multiple addresses
4166
+ * @param addresses Array of addresses to watch
4167
+ */ initializeWatchOnly(addresses) {
4168
+ this.watchOnlyWallet = new (0, $f4ad400261a08cba$export$2e2bcd8739ae039)(addresses.map((addr)=>({
4169
+ address: addr
4170
+ })), this.currentNetwork);
3664
4171
  }
3665
4172
  /**
3666
4173
  * Create a custom unsigned transaction
@@ -3671,10 +4178,10 @@ class $4d217697e55d958e$export$80793d8292a1630a {
3671
4178
  */ async createCustomTransaction(txBuilder, sighashSpec, broadcast = true) {
3672
4179
  if (!this.watchOnlyWallet || !this.connectedWalletAddress) throw new Error('No wallet connected');
3673
4180
  // Create custom transaction using provided builder function
3674
- const builder = this.watchOnlyWallet.newTransaction().onNetwork(this.currentNetwork);
4181
+ const builder = this.watchOnlyWallet.newTransaction();
3675
4182
  const configuredBuilder = txBuilder(builder);
3676
4183
  const unsignedTx = await configuredBuilder.populate().build();
3677
- return await this.provider.signTransaction(this.connectedWalletAddress, unsignedTx, sighashSpec, broadcast);
4184
+ return await this.provider.signTransaction(this.connectedWalletAddress, unsignedTx, broadcast);
3678
4185
  }
3679
4186
  /**
3680
4187
  * Sign a message with the connected wallet