@arkade-os/sdk 0.4.45 → 0.4.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/adapters/expo.cjs +5 -5
  2. package/dist/adapters/expo.d.cts +2 -2
  3. package/dist/adapters/expo.d.ts +2 -2
  4. package/dist/adapters/expo.js +3 -3
  5. package/dist/adapters/indexedDB.cjs +5 -5
  6. package/dist/adapters/indexedDB.js +4 -4
  7. package/dist/{ark-C-PjOngm.d.cts → ark-CoJgwTi_.d.cts} +31 -3
  8. package/dist/{ark-C-PjOngm.d.ts → ark-CoJgwTi_.d.ts} +31 -3
  9. package/dist/{asyncStorageTaskQueue-BmBPvTyh.d.cts → asyncStorageTaskQueue-Dowyo59a.d.cts} +2 -2
  10. package/dist/{asyncStorageTaskQueue-CHpI3moJ.d.ts → asyncStorageTaskQueue-Z3aFoXCl.d.ts} +2 -2
  11. package/dist/{chunk-QZ6HZZZK.js → chunk-3N5I6GT6.js} +3 -3
  12. package/dist/{chunk-QZ6HZZZK.js.map → chunk-3N5I6GT6.js.map} +1 -1
  13. package/dist/{chunk-BOA4SHCD.js → chunk-AJQ4AOT3.js} +4 -4
  14. package/dist/{chunk-BOA4SHCD.js.map → chunk-AJQ4AOT3.js.map} +1 -1
  15. package/dist/{chunk-BE5MUB7T.js → chunk-C7TXXTMS.js} +9 -5
  16. package/dist/chunk-C7TXXTMS.js.map +1 -0
  17. package/dist/{chunk-YBQV7XBC.js → chunk-CDDWXBEM.js} +139 -21
  18. package/dist/chunk-CDDWXBEM.js.map +1 -0
  19. package/dist/{chunk-UHOHLN3C.cjs → chunk-HHV5FPC6.cjs} +148 -25
  20. package/dist/chunk-HHV5FPC6.cjs.map +1 -0
  21. package/dist/{chunk-HZ7VIN7P.cjs → chunk-JAYOXOLT.cjs} +14 -14
  22. package/dist/{chunk-HZ7VIN7P.cjs.map → chunk-JAYOXOLT.cjs.map} +1 -1
  23. package/dist/{chunk-HXDZ2QTW.cjs → chunk-L5ND2FJJ.cjs} +9 -5
  24. package/dist/chunk-L5ND2FJJ.cjs.map +1 -0
  25. package/dist/{chunk-TGRJ5WAX.js → chunk-OCS5S3L3.js} +1085 -103
  26. package/dist/chunk-OCS5S3L3.js.map +1 -0
  27. package/dist/chunk-RQSA7ADZ.cjs +2286 -0
  28. package/dist/chunk-RQSA7ADZ.cjs.map +1 -0
  29. package/dist/{chunk-OFQGANH2.cjs → chunk-VE67GMGM.cjs} +7 -7
  30. package/dist/{chunk-OFQGANH2.cjs.map → chunk-VE67GMGM.cjs.map} +1 -1
  31. package/dist/{chunk-SRCWBFSZ.cjs → chunk-WTTLIFS7.cjs} +1281 -289
  32. package/dist/chunk-WTTLIFS7.cjs.map +1 -0
  33. package/dist/chunk-XN3554FT.js +2244 -0
  34. package/dist/chunk-XN3554FT.js.map +1 -0
  35. package/dist/contracts/handlers/index.cjs +11 -7
  36. package/dist/contracts/handlers/index.d.cts +3 -3
  37. package/dist/contracts/handlers/index.d.ts +3 -3
  38. package/dist/contracts/handlers/index.js +2 -2
  39. package/dist/{delegate-BKNqva1j.d.ts → delegate-CuaDqqQi.d.ts} +1 -1
  40. package/dist/{delegate-DOEobYwH.d.cts → delegate-Dl87_zMA.d.cts} +1 -1
  41. package/dist/index-BC_G0VTh.d.cts +512 -0
  42. package/dist/index-CKwpZEpw.d.ts +512 -0
  43. package/dist/index.cjs +219 -159
  44. package/dist/index.d.cts +3097 -2090
  45. package/dist/index.d.ts +3097 -2090
  46. package/dist/index.js +4 -4
  47. package/dist/repositories/realm/index.cjs +23 -19
  48. package/dist/repositories/realm/index.cjs.map +1 -1
  49. package/dist/repositories/realm/index.d.cts +2 -2
  50. package/dist/repositories/realm/index.d.ts +2 -2
  51. package/dist/repositories/realm/index.js +9 -5
  52. package/dist/repositories/realm/index.js.map +1 -1
  53. package/dist/repositories/sqlite/index.cjs +22 -18
  54. package/dist/repositories/sqlite/index.cjs.map +1 -1
  55. package/dist/repositories/sqlite/index.d.cts +2 -2
  56. package/dist/repositories/sqlite/index.d.ts +2 -2
  57. package/dist/repositories/sqlite/index.js +9 -5
  58. package/dist/repositories/sqlite/index.js.map +1 -1
  59. package/dist/{taskRunner-BngWSFMi.d.ts → taskRunner-Dz-TI4Rb.d.ts} +2 -2
  60. package/dist/{taskRunner-DOiyRuJa.d.cts → taskRunner-Dz47k0lp.d.cts} +2 -2
  61. package/dist/wallet/expo/background.cjs +14 -14
  62. package/dist/wallet/expo/background.d.cts +3 -3
  63. package/dist/wallet/expo/background.d.ts +3 -3
  64. package/dist/wallet/expo/background.js +6 -6
  65. package/dist/wallet/expo/index.cjs +18 -14
  66. package/dist/wallet/expo/index.cjs.map +1 -1
  67. package/dist/wallet/expo/index.d.cts +4 -4
  68. package/dist/wallet/expo/index.d.ts +4 -4
  69. package/dist/wallet/expo/index.js +9 -5
  70. package/dist/wallet/expo/index.js.map +1 -1
  71. package/dist/{wallet-DBjumS42.d.cts → wallet-Oxz_wLxo.d.cts} +47 -3
  72. package/dist/{wallet-B3awP1Nj.d.ts → wallet-x7egmXq6.d.ts} +47 -3
  73. package/dist/worker/expo/index.cjs +10 -9
  74. package/dist/worker/expo/index.cjs.map +1 -1
  75. package/dist/worker/expo/index.d.cts +4 -4
  76. package/dist/worker/expo/index.d.ts +4 -4
  77. package/dist/worker/expo/index.js +6 -5
  78. package/dist/worker/expo/index.js.map +1 -1
  79. package/package.json +2 -2
  80. package/dist/chunk-32YKLQPS.cjs +0 -970
  81. package/dist/chunk-32YKLQPS.cjs.map +0 -1
  82. package/dist/chunk-BE5MUB7T.js.map +0 -1
  83. package/dist/chunk-HXDZ2QTW.cjs.map +0 -1
  84. package/dist/chunk-SRCWBFSZ.cjs.map +0 -1
  85. package/dist/chunk-TGRJ5WAX.js.map +0 -1
  86. package/dist/chunk-UHOHLN3C.cjs.map +0 -1
  87. package/dist/chunk-YBQV7XBC.js.map +0 -1
  88. package/dist/chunk-YNEDJTX2.js +0 -957
  89. package/dist/chunk-YNEDJTX2.js.map +0 -1
  90. package/dist/index-BlW4I30l.d.cts +0 -259
  91. package/dist/index-Cm2fGeox.d.ts +0 -259
@@ -1,11 +1,12 @@
1
- import { craftToSpendTx, Transaction, OP_RETURN_EMPTY_PKSCRIPT, baseFetch, Intent, getArkPsbtFields, CosignerPublicKey, setArkPsbtField, VtxoTaprootTree, maybeArkError, ProviderUnavailableError, FetchError, isFetchTimeoutError, AssetRef, AssetId, AssetOutput, AssetGroup, AssetInput, Packet, Metadata, isEventSourceError, RestArkProvider, RestIndexerProvider, ArkError, BufferReader } from './chunk-YBQV7XBC.js';
2
- import { contractHandlers, isMainnetDescriptor, descriptorIsOurs, DEFAULT_PAGE_SIZE, DelegateVtxo, WALLET_RECEIVE_SOURCE, deriveDescriptorLeafPubKey, DefaultVtxo, BoardingContractHandler } from './chunk-YNEDJTX2.js';
3
- import { VtxoScript, sequenceToTimelock, timelockToSequence, DEFAULT_NETWORK, DEFAULT_NETWORK_NAME, decodeTapscript, scriptFromTapLeafScript, CLTVMultisigTapscript, ArkAddress, CSVMultisigTapscript, getSequence, getNetwork, MultisigTapscript, networks as networks$1, DEFAULT_ARKADE_SERVER_URL } from './chunk-BE5MUB7T.js';
4
- import { sha256, hash160, sha256x2, concatBytes, randomPrivateKeyBytes, pubECDSA, pubSchnorr, equalBytes as equalBytes$1 } from '@scure/btc-signer/utils.js';
5
- import { SigHash, Script, p2tr, RawWitness, Address, OutScript, p2wpkh, TaprootControlBlock, DEFAULT_SEQUENCE, Transaction as Transaction$2 } from '@scure/btc-signer';
1
+ import { Transaction, setArkPsbtField, PrevArkTxField, ConditionWitness, AssetId, AssetGroup, AssetInput, AssetOutput, Metadata, Packet, craftToSpendTx, getArkPsbtFields, CosignerPublicKey, BufferReader, BufferWriter, OP_RETURN_EMPTY_PKSCRIPT, VtxoTaprootTree, baseFetch, Intent, maybeArkError, isArkError, ArkErrorName, ProviderUnavailableError, FetchError, isFetchTimeoutError, AssetRef, isEventSourceError, RestArkProvider, RestIndexerProvider, ArkError } from './chunk-CDDWXBEM.js';
2
+ import { witnessRefToBytes, validateProgram, toASM, stringifyArtifact, serializeArkadeContractParams, resolveAsm, parseArtifact, getOpcodeValue, getOpcodeName, fromASM, deserializeArkadeContractParams, computeArkadeScriptPublicKey, bytesToASM, asmToBytes, arkadeWitnessHash, arkadeScriptHash, SUPPORTED_PROGRAM_VERSION, OPCODE_VALUES, OPCODE_NAMES, bignum_exports, ArkadeScript, ArkadeProgramScript, ARKADE_OPS, ARKADE_OPCODE_VALUES, ARKADE_OPCODE_NAMES, ARKADE_OPCODES, ARKADE_OP, inputName, contractHandlers, isMainnetDescriptor, descriptorIsOurs, DEFAULT_PAGE_SIZE, DelegateVtxo, WALLET_RECEIVE_SOURCE, deriveDescriptorLeafPubKey, DefaultVtxo, BoardingContractHandler } from './chunk-XN3554FT.js';
3
+ import { VtxoScript, CSVMultisigTapscript, DEFAULT_NETWORK, ArkAddress, sequenceToTimelock, decodeTapscript, scriptFromTapLeafScript, CLTVMultisigTapscript, timelockToSequence, DEFAULT_NETWORK_NAME, getSequence, getNetwork, MultisigTapscript, networks as networks$1, DEFAULT_ARKADE_SERVER_URL } from './chunk-C7TXXTMS.js';
4
+ import { __export } from './chunk-NSBPE2FW.js';
5
+ import { sha256, equalBytes, hash160, sha256x2, concatBytes, randomPrivateKeyBytes, pubECDSA, pubSchnorr } from '@scure/btc-signer/utils.js';
6
+ import { SigHash, Script, OP, OutScript, Address, RawWitness, p2tr, p2wpkh, TaprootControlBlock, DEFAULT_SEQUENCE, Transaction as Transaction$2 } from '@scure/btc-signer';
6
7
  import { hex, base58, base64 } from '@scure/base';
7
8
  import * as musig2 from '@scure/btc-signer/musig2.js';
8
- import { equalBytes, bytesToNumberBE } from '@noble/curves/utils.js';
9
+ import { equalBytes as equalBytes$1, bytesToNumberBE } from '@noble/curves/utils.js';
9
10
  import { signAsync, schnorr as schnorr$1, Point } from '@noble/secp256k1';
10
11
  import { secp256k1, schnorr } from '@noble/curves/secp256k1.js';
11
12
  import { Script as Script$1 } from '@scure/btc-signer/script.js';
@@ -62,6 +63,8 @@ var PartialSig = class _PartialSig {
62
63
  throw new PartialSignatureError("Invalid R length");
63
64
  }
64
65
  }
66
+ s;
67
+ R;
65
68
  /**
66
69
  * Encodes the partial signature into bytes
67
70
  * Returns a 32-byte array containing just the s value
@@ -111,6 +114,7 @@ var TreeSignerSession = class _TreeSignerSession {
111
114
  constructor(secretKey) {
112
115
  this.secretKey = secretKey;
113
116
  }
117
+ secretKey;
114
118
  static NOT_INITIALIZED = new Error("session not initialized, call init method");
115
119
  myNonces = null;
116
120
  aggregateNonces = null;
@@ -337,6 +341,7 @@ var ReadonlySingleKey = class _ReadonlySingleKey {
337
341
  throw new Error("Invalid public key length");
338
342
  }
339
343
  }
344
+ publicKey;
340
345
  /**
341
346
  * Create a ReadonlySingleKey from a compressed public key.
342
347
  *
@@ -948,6 +953,7 @@ var RestDelegateProvider = class {
948
953
  constructor(url) {
949
954
  this.url = url;
950
955
  }
956
+ url;
951
957
  /**
952
958
  * Submit a delegation request to the remote delegation service.
953
959
  *
@@ -1017,6 +1023,7 @@ var EsploraProvider = class {
1017
1023
  this.pollingInterval = opts?.pollingInterval ?? 15e3;
1018
1024
  this.forcePolling = opts?.forcePolling ?? false;
1019
1025
  }
1026
+ baseUrl;
1020
1027
  pollingInterval;
1021
1028
  forcePolling;
1022
1029
  async getCoins(address) {
@@ -1496,6 +1503,8 @@ var UnknownPacket = class {
1496
1503
  this.packetType = packetType;
1497
1504
  this.data = data;
1498
1505
  }
1506
+ packetType;
1507
+ data;
1499
1508
  type() {
1500
1509
  return this.packetType;
1501
1510
  }
@@ -1504,6 +1513,62 @@ var UnknownPacket = class {
1504
1513
  }
1505
1514
  };
1506
1515
 
1516
+ // src/extension/emulator/packet.ts
1517
+ var EmulatorPacket = class _EmulatorPacket {
1518
+ constructor(entries) {
1519
+ this.entries = entries;
1520
+ }
1521
+ entries;
1522
+ /** PACKET_TYPE is the 1-byte TLV type tag used in the Extension envelope. */
1523
+ static PACKET_TYPE = 1;
1524
+ static create(entries) {
1525
+ if (entries.length === 0) {
1526
+ throw new Error("empty emulator packet");
1527
+ }
1528
+ for (const entry of entries) {
1529
+ if (entry.script.length === 0) {
1530
+ throw new Error(`empty script for vin ${entry.vin}`);
1531
+ }
1532
+ }
1533
+ const seen = /* @__PURE__ */ new Set();
1534
+ for (const entry of entries) {
1535
+ if (seen.has(entry.vin)) {
1536
+ throw new Error(`duplicate vin ${entry.vin}`);
1537
+ }
1538
+ seen.add(entry.vin);
1539
+ }
1540
+ return new _EmulatorPacket(entries);
1541
+ }
1542
+ static fromBytes(data) {
1543
+ const reader = new BufferReader(data);
1544
+ const entryCount = reader.readCompactSize();
1545
+ const entries = [];
1546
+ for (let i = 0; i < entryCount; i++) {
1547
+ const vin = reader.readUint16LE();
1548
+ const script = reader.readCompactSlice();
1549
+ const witness = reader.readCompactSlice();
1550
+ entries.push({ vin, script, witness });
1551
+ }
1552
+ if (reader.remaining() > 0) {
1553
+ throw new Error(`unexpected ${reader.remaining()} trailing bytes`);
1554
+ }
1555
+ return _EmulatorPacket.create(entries);
1556
+ }
1557
+ type() {
1558
+ return _EmulatorPacket.PACKET_TYPE;
1559
+ }
1560
+ serialize() {
1561
+ const writer = new BufferWriter();
1562
+ writer.writeCompactSize(this.entries.length);
1563
+ for (const entry of this.entries) {
1564
+ writer.writeUint16LE(entry.vin);
1565
+ writer.writeCompactSlice(entry.script);
1566
+ writer.writeCompactSlice(entry.witness ?? new Uint8Array(0));
1567
+ }
1568
+ return writer.toBytes();
1569
+ }
1570
+ };
1571
+
1507
1572
  // src/extension/index.ts
1508
1573
  var ARKADE_MAGIC = new Uint8Array([65, 82, 75]);
1509
1574
  var ExtensionNotFoundError = class extends Error {
@@ -1516,16 +1581,21 @@ var Extension = class _Extension {
1516
1581
  constructor(packets) {
1517
1582
  this.packets = packets;
1518
1583
  }
1584
+ packets;
1519
1585
  static create(packets) {
1520
1586
  if (packets.length === 0) {
1521
1587
  throw new Error("missing packets");
1522
1588
  }
1523
1589
  const seen = /* @__PURE__ */ new Set();
1524
1590
  for (const p of packets) {
1525
- if (seen.has(p.type())) {
1526
- throw new Error(`duplicate packet type ${p.type()}`);
1591
+ const type = p.type();
1592
+ if (!Number.isInteger(type) || type < 0 || type > 255) {
1593
+ throw new Error(`invalid packet type ${type}: must be an integer in [0, 255]`);
1527
1594
  }
1528
- seen.add(p.type());
1595
+ if (seen.has(type)) {
1596
+ throw new Error(`duplicate packet type ${type}`);
1597
+ }
1598
+ seen.add(type);
1529
1599
  }
1530
1600
  return new _Extension(packets);
1531
1601
  }
@@ -1539,7 +1609,7 @@ var Extension = class _Extension {
1539
1609
  if (decoded.length < 2 || decoded[0] !== "RETURN") return false;
1540
1610
  const data = decoded[1];
1541
1611
  if (!(data instanceof Uint8Array)) return false;
1542
- return data.length >= ARKADE_MAGIC.length && equalBytes$1(data.slice(0, ARKADE_MAGIC.length), ARKADE_MAGIC);
1612
+ return data.length >= ARKADE_MAGIC.length && equalBytes(data.slice(0, ARKADE_MAGIC.length), ARKADE_MAGIC);
1543
1613
  } catch {
1544
1614
  return false;
1545
1615
  }
@@ -1570,7 +1640,7 @@ var Extension = class _Extension {
1570
1640
  payload.set(d, offset);
1571
1641
  offset += d.length;
1572
1642
  }
1573
- if (payload.length < ARKADE_MAGIC.length || !equalBytes$1(payload.slice(0, ARKADE_MAGIC.length), ARKADE_MAGIC)) {
1643
+ if (payload.length < ARKADE_MAGIC.length || !equalBytes(payload.slice(0, ARKADE_MAGIC.length), ARKADE_MAGIC)) {
1574
1644
  throw new Error(
1575
1645
  `expected magic prefix ${hex.encode(ARKADE_MAGIC)}, got ${hex.encode(payload.slice(0, Math.min(payload.length, ARKADE_MAGIC.length)))}`
1576
1646
  );
@@ -1655,12 +1725,45 @@ var Extension = class _Extension {
1655
1725
  }
1656
1726
  return null;
1657
1727
  }
1728
+ /**
1729
+ * getEmulatorPacket returns the embedded EmulatorPacket, or null if not present.
1730
+ */
1731
+ getEmulatorPacket() {
1732
+ for (const p of this.packets) {
1733
+ if (p instanceof EmulatorPacket) {
1734
+ return p;
1735
+ }
1736
+ }
1737
+ return null;
1738
+ }
1739
+ /**
1740
+ * getPacketByType returns the first packet matching the given type tag, or null.
1741
+ */
1742
+ getPacketByType(packetType) {
1743
+ for (const p of this.packets) {
1744
+ if (p.type() === packetType) {
1745
+ return p;
1746
+ }
1747
+ }
1748
+ return null;
1749
+ }
1750
+ /**
1751
+ * Returns all embedded packets in insertion order. Used when callers need
1752
+ * to rebuild an Extension from an existing one (e.g. appending a new packet).
1753
+ */
1754
+ getPackets() {
1755
+ return this.packets;
1756
+ }
1658
1757
  };
1659
1758
  function parsePacket(packetType, data) {
1660
- if (packetType === Packet.PACKET_TYPE) {
1661
- return Packet.fromBytes(data);
1759
+ switch (packetType) {
1760
+ case Packet.PACKET_TYPE:
1761
+ return Packet.fromBytes(data);
1762
+ case EmulatorPacket.PACKET_TYPE:
1763
+ return EmulatorPacket.fromBytes(data);
1764
+ default:
1765
+ return new UnknownPacket(packetType, data);
1662
1766
  }
1663
- return new UnknownPacket(packetType, data);
1664
1767
  }
1665
1768
  function encodeVarUint(value) {
1666
1769
  const bytes = [];
@@ -1741,7 +1844,7 @@ function validateOnchainRecipient(commitmentTx, recipient, network, usedOutputs)
1741
1844
  if (!output?.script || output.script.length === 0) {
1742
1845
  continue;
1743
1846
  }
1744
- if (equalBytes$1(output.script, expectedPkScript)) {
1847
+ if (equalBytes(output.script, expectedPkScript)) {
1745
1848
  if (output.amount !== BigInt(recipient.amount)) {
1746
1849
  continue;
1747
1850
  }
@@ -1765,7 +1868,7 @@ function validateOffchainRecipient(leaves, arkAddress, recipient, usedOutputs) {
1765
1868
  if (!output?.script || output.script.length === 0) {
1766
1869
  continue;
1767
1870
  }
1768
- if (!equalBytes$1(output.script, expectedPkScript)) {
1871
+ if (!equalBytes(output.script, expectedPkScript)) {
1769
1872
  continue;
1770
1873
  }
1771
1874
  if (output.amount !== expectedAmount) {
@@ -1954,6 +2057,7 @@ function isExpired(vtxo) {
1954
2057
  return expiry <= Date.now();
1955
2058
  }
1956
2059
  function isSubdust(vtxo, dust) {
2060
+ if (typeof vtxo === "bigint") return vtxo < dust;
1957
2061
  return vtxo.value < dust;
1958
2062
  }
1959
2063
 
@@ -2293,6 +2397,46 @@ function isValidArkAddress(address) {
2293
2397
  return false;
2294
2398
  }
2295
2399
  }
2400
+ async function submitOffchainTx(provider, offchainTx, signer, hooks) {
2401
+ const { arkTx: signedArkTx, userSignedCheckpoints } = await signer.signArkTx(
2402
+ offchainTx.arkTx,
2403
+ offchainTx.checkpoints
2404
+ );
2405
+ if (userSignedCheckpoints && userSignedCheckpoints.length !== offchainTx.checkpoints.length) {
2406
+ throw new Error(
2407
+ `signer returned ${userSignedCheckpoints.length} signed checkpoints, expected ${offchainTx.checkpoints.length}`
2408
+ );
2409
+ }
2410
+ await hooks?.beforeSubmit?.();
2411
+ const { arkTxid, signedCheckpointTxs } = await provider.submitTx(
2412
+ base64.encode(signedArkTx.toPSBT()),
2413
+ offchainTx.checkpoints.map((c) => base64.encode(c.toPSBT()))
2414
+ );
2415
+ if (signedCheckpointTxs.length !== offchainTx.checkpoints.length) {
2416
+ throw new Error(
2417
+ `submitTx returned ${signedCheckpointTxs.length} checkpoints, expected ${offchainTx.checkpoints.length}`
2418
+ );
2419
+ }
2420
+ let finalCheckpoints;
2421
+ if (userSignedCheckpoints) {
2422
+ finalCheckpoints = signedCheckpointTxs.map((c, i) => {
2423
+ const serverSigned = Transaction.fromPSBT(base64.decode(c));
2424
+ combineTapscriptSigs(userSignedCheckpoints[i], serverSigned);
2425
+ return base64.encode(serverSigned.toPSBT());
2426
+ });
2427
+ } else {
2428
+ finalCheckpoints = await Promise.all(
2429
+ signedCheckpointTxs.map(async (c) => {
2430
+ const tx = Transaction.fromPSBT(base64.decode(c));
2431
+ const signed = await signer.signCheckpoint(tx);
2432
+ return base64.encode(signed.toPSBT());
2433
+ })
2434
+ );
2435
+ }
2436
+ await provider.finalizeTx(arkTxid, finalCheckpoints);
2437
+ await hooks?.afterFinalize?.();
2438
+ return { arkTxid, signedCheckpointTxs };
2439
+ }
2296
2440
  var AmountVariableName = "amount";
2297
2441
  var ExpiryVariableName = "expiry";
2298
2442
  var BirthVariableName = "birth";
@@ -2312,6 +2456,7 @@ var FeeAmount = class _FeeAmount {
2312
2456
  constructor(value) {
2313
2457
  this.value = value;
2314
2458
  }
2459
+ value;
2315
2460
  static ZERO = new _FeeAmount(0);
2316
2461
  /** Returns the fee amount rounded up to whole satoshis. */
2317
2462
  get satoshis() {
@@ -2336,6 +2481,7 @@ var Estimator = class {
2336
2481
  this.intentOffchainOutput = config.offchainOutput ? parseProgram(config.offchainOutput, IntentOutputEnv) : void 0;
2337
2482
  this.intentOnchainOutput = config.onchainOutput ? parseProgram(config.onchainOutput, IntentOutputEnv) : void 0;
2338
2483
  }
2484
+ config;
2339
2485
  intentOffchainInput;
2340
2486
  intentOnchainInput;
2341
2487
  intentOffchainOutput;
@@ -2446,6 +2592,14 @@ function parseProgram(text, env) {
2446
2592
  }
2447
2593
  return { program, text };
2448
2594
  }
2595
+
2596
+ // src/contracts/types.ts
2597
+ function isDiscoverable(handler) {
2598
+ return !!handler && typeof handler.discoverAt === "function";
2599
+ }
2600
+ function isTapscriptDeriving(handler) {
2601
+ return !!handler && typeof handler.deriveTapscripts === "function";
2602
+ }
2449
2603
  var DUST_AMOUNT = 546;
2450
2604
  var FALLBACK_WALLET_DUST_AMOUNT = 330n;
2451
2605
  function getDustAmount(wallet) {
@@ -2465,10 +2619,14 @@ function deriveContractTapscripts(contract) {
2465
2619
  throw new Error(`No handler for contract type '${contract.type}'`);
2466
2620
  }
2467
2621
  const script = handler.createScript(contract.params);
2622
+ if (isTapscriptDeriving(handler)) {
2623
+ return handler.deriveTapscripts(script, contract);
2624
+ }
2625
+ const legacy = script;
2468
2626
  return {
2469
- forfeitTapLeafScript: script.forfeit(),
2470
- intentTapLeafScript: script.forfeit(),
2471
- tapTree: script.encode()
2627
+ forfeitTapLeafScript: legacy.forfeit(),
2628
+ intentTapLeafScript: legacy.forfeit(),
2629
+ tapTree: legacy.encode()
2472
2630
  };
2473
2631
  }
2474
2632
  function cloneTapLeafScript([
@@ -2738,6 +2896,8 @@ var VtxoManager = class _VtxoManager {
2738
2896
  }
2739
2897
  this.contractEventsSubscriptionReady = this.initializeSubscription();
2740
2898
  }
2899
+ wallet;
2900
+ renewalConfig;
2741
2901
  settlementConfig;
2742
2902
  contractEventsSubscriptionReady;
2743
2903
  disposePromise;
@@ -3775,7 +3935,7 @@ var VtxoManager = class _VtxoManager {
3775
3935
  */
3776
3936
  extractSpentOutpoint(error) {
3777
3937
  const ark = maybeArkError(error);
3778
- if (!ark || ark.name !== "VTXO_ALREADY_SPENT") return void 0;
3938
+ if (!isArkError(ark, ArkErrorName.VTXO_ALREADY_SPENT)) return void 0;
3779
3939
  const raw = ark.metadata?.vtxo_outpoint;
3780
3940
  if (typeof raw !== "string") return void 0;
3781
3941
  const [txid, voutStr] = raw.split(":");
@@ -4079,6 +4239,9 @@ var ArkNote = class _ArkNote {
4079
4239
  this.status = { confirmed: true };
4080
4240
  this.extraWitness = [this.preimage];
4081
4241
  }
4242
+ preimage;
4243
+ value;
4244
+ HRP;
4082
4245
  static DefaultHRP = "arknote";
4083
4246
  static PreimageLength = 32;
4084
4247
  // 32 bytes for the preimage
@@ -4455,6 +4618,7 @@ var IndexerExitDataSource = class {
4455
4618
  constructor(indexer) {
4456
4619
  this.indexer = indexer;
4457
4620
  }
4621
+ indexer;
4458
4622
  name = "indexer";
4459
4623
  async getVtxoChain(vtxo) {
4460
4624
  const chain = [];
@@ -4516,6 +4680,7 @@ var RepositoryExitDataSource = class {
4516
4680
  constructor(repo) {
4517
4681
  this.repo = repo;
4518
4682
  }
4683
+ repo;
4519
4684
  name = "repository";
4520
4685
  async getVtxoChain(vtxo) {
4521
4686
  const branch = await this.repo.getBranch(vtxo);
@@ -4553,6 +4718,8 @@ var OrderedExitChainResolver = class {
4553
4718
  this.sources = sources;
4554
4719
  this.persist = persist;
4555
4720
  }
4721
+ sources;
4722
+ persist;
4556
4723
  async getVtxoChain(vtxo) {
4557
4724
  let lastErr;
4558
4725
  for (const source of this.sources) {
@@ -4878,6 +5045,8 @@ var TxTree = class {
4878
5045
  this.root = root;
4879
5046
  this.children = children;
4880
5047
  }
5048
+ root;
5049
+ children;
4881
5050
  static create(chunks) {
4882
5051
  if (chunks.length === 0) {
4883
5052
  throw new Error("empty chunks");
@@ -5349,6 +5518,7 @@ var ReadonlyAssetManager = class {
5349
5518
  constructor(indexer) {
5350
5519
  this.indexer = indexer;
5351
5520
  }
5521
+ indexer;
5352
5522
  async getAssetDetails(assetId) {
5353
5523
  return this.indexer.getAssetDetails(assetId);
5354
5524
  }
@@ -5358,6 +5528,7 @@ var AssetManager = class extends ReadonlyAssetManager {
5358
5528
  super(wallet.indexerProvider);
5359
5529
  this.wallet = wallet;
5360
5530
  }
5531
+ wallet;
5361
5532
  /**
5362
5533
  * Issue a new asset.
5363
5534
  * @param params - Parameters for asset issuance
@@ -5679,6 +5850,9 @@ var DelegateManagerImpl = class {
5679
5850
  this.arkInfoProvider = arkInfoProvider;
5680
5851
  this.identity = identity;
5681
5852
  }
5853
+ delegateProvider;
5854
+ arkInfoProvider;
5855
+ identity;
5682
5856
  async getDelegateInfo() {
5683
5857
  return this.delegateProvider.getDelegateInfo();
5684
5858
  }
@@ -5845,12 +6019,12 @@ async function delegate(identity, delegateProvider, arkInfo, delegateInfo, vtxos
5845
6019
  script: hex.encode(output.script)
5846
6020
  }).satoshis;
5847
6021
  }, 0);
5848
- if (amount - BigInt(outputFee) <= dust) {
6022
+ if (isSubdust(amount - BigInt(outputFee), dust)) {
5849
6023
  throw new Error("Amount is below dust limit, cannot delegate");
5850
6024
  }
5851
6025
  amount -= BigInt(outputFee);
5852
6026
  amount -= delegateFee;
5853
- if (amount <= dust) {
6027
+ if (isSubdust(amount, dust)) {
5854
6028
  throw new Error("Amount is below dust limit, cannot delegate");
5855
6029
  }
5856
6030
  outputs.push({
@@ -5961,7 +6135,7 @@ async function makeSignedDelegateIntent(identity, coins, outputs, onchainOutputs
5961
6135
  };
5962
6136
  }
5963
6137
  function findDestinationOutputIndex(outputs, destinationScript) {
5964
- return outputs.findIndex((o) => o.script && equalBytes$1(o.script, destinationScript));
6138
+ return outputs.findIndex((o) => o.script && equalBytes(o.script, destinationScript));
5965
6139
  }
5966
6140
  function getDayTimestamp(timestamp) {
5967
6141
  const date = new Date(timestamp);
@@ -6447,6 +6621,7 @@ var IndexedDBVirtualTxRepository = class {
6447
6621
  constructor(dbName = DEFAULT_DB_NAME) {
6448
6622
  this.dbName = dbName;
6449
6623
  }
6624
+ dbName;
6450
6625
  version = 1;
6451
6626
  db = null;
6452
6627
  async getDB() {
@@ -6576,6 +6751,7 @@ var IndexedDBIntentRepository = class {
6576
6751
  constructor(dbName = DEFAULT_DB_NAME) {
6577
6752
  this.dbName = dbName;
6578
6753
  }
6754
+ dbName;
6579
6755
  version = 1;
6580
6756
  db = null;
6581
6757
  async getDB() {
@@ -6886,6 +7062,7 @@ var IndexedDBContractRepository = class {
6886
7062
  constructor(dbName = DEFAULT_DB_NAME) {
6887
7063
  this.dbName = dbName;
6888
7064
  }
7065
+ dbName;
6889
7066
  version = 1;
6890
7067
  db = null;
6891
7068
  async clear() {
@@ -7052,6 +7229,7 @@ var IndexedDBWalletRepository = class {
7052
7229
  constructor(dbName = DEFAULT_DB_NAME) {
7053
7230
  this.dbName = dbName;
7054
7231
  }
7232
+ dbName;
7055
7233
  version = 1;
7056
7234
  db = null;
7057
7235
  async clear() {
@@ -7809,11 +7987,6 @@ var ContractRepositoryImpl = class {
7809
7987
  }
7810
7988
  };
7811
7989
 
7812
- // src/contracts/types.ts
7813
- function isDiscoverable(handler) {
7814
- return !!handler && typeof handler.discoverAt === "function";
7815
- }
7816
-
7817
7990
  // src/contracts/contractWatcher.ts
7818
7991
  function computeReconnectDelay(attempt, baseMs, maxMs) {
7819
7992
  return Math.min(baseMs * Math.pow(2, attempt - 1), maxMs);
@@ -9197,6 +9370,8 @@ var HDDescriptorProvider = class _HDDescriptorProvider {
9197
9370
  this.identity = identity;
9198
9371
  this.walletRepository = walletRepository;
9199
9372
  }
9373
+ identity;
9374
+ walletRepository;
9200
9375
  /**
9201
9376
  * Construct an HDDescriptorProvider. No I/O is performed here;
9202
9377
  * persisted state is read lazily on the first call to
@@ -9389,6 +9564,7 @@ var WalletReceiveRotator = class _WalletReceiveRotator {
9389
9564
  this.currentTaggedScript = priorTaggedScript;
9390
9565
  this.logger = logger ?? console;
9391
9566
  }
9567
+ provider;
9392
9568
  unsubscribe;
9393
9569
  chain = Promise.resolve();
9394
9570
  /**
@@ -9460,7 +9636,7 @@ var WalletReceiveRotator = class _WalletReceiveRotator {
9460
9636
  throw e;
9461
9637
  }
9462
9638
  if (!boot) return void 0;
9463
- const offchainTapscript = equalBytes$1(
9639
+ const offchainTapscript = equalBytes(
9464
9640
  boot.receivePubkey,
9465
9641
  setup.offchainTapscript.options.pubKey
9466
9642
  ) ? setup.offchainTapscript : rebuildTapscript(setup.offchainTapscript, boot.receivePubkey);
@@ -9742,6 +9918,8 @@ var MissingSigningDescriptorError = class extends Error {
9742
9918
  this.contractScript = contractScript;
9743
9919
  this.contractType = contractType;
9744
9920
  }
9921
+ contractScript;
9922
+ contractType;
9745
9923
  name = "MissingSigningDescriptorError";
9746
9924
  };
9747
9925
  var DescriptorSigningProviderMissingError = class extends Error {
@@ -9757,6 +9935,7 @@ var InputSignerRouter = class {
9757
9935
  constructor(deps) {
9758
9936
  this.deps = deps;
9759
9937
  }
9938
+ deps;
9760
9939
  /**
9761
9940
  * Resolve each job to its target signer without invoking signing. The
9762
9941
  * returned plan is the single source of truth for both {@link sign} and
@@ -9975,6 +10154,14 @@ var ReadonlyWallet = class _ReadonlyWallet {
9975
10154
  this._assetManager = new ReadonlyAssetManager(this.indexerProvider);
9976
10155
  this.walletContractTimelocks = walletContractTimelocks && walletContractTimelocks.length > 0 ? dedupeTimelocks(walletContractTimelocks) : [this.offchainTapscript.options.csvTimelock];
9977
10156
  }
10157
+ identity;
10158
+ network;
10159
+ onchainProvider;
10160
+ indexerProvider;
10161
+ dustAmount;
10162
+ walletRepository;
10163
+ contractRepository;
10164
+ delegateProvider;
9978
10165
  _contractManager;
9979
10166
  _contractManagerInitializing;
9980
10167
  watcherConfig;
@@ -11081,6 +11268,9 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
11081
11268
  boardingPkScript: boardingTapscript.pkScript
11082
11269
  });
11083
11270
  }
11271
+ arkProvider;
11272
+ forfeitOutputScript;
11273
+ forfeitPubkey;
11084
11274
  static MIN_FEE_RATE = 1;
11085
11275
  // sats/vbyte
11086
11276
  identity;
@@ -11293,7 +11483,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
11293
11483
  } catch (e) {
11294
11484
  throw new Error("Invalid checkpointTapscript from server");
11295
11485
  }
11296
- if (equalBytes$1(xonly, this.arkServerPublicKey)) return;
11486
+ if (equalBytes(xonly, this.arkServerPublicKey)) return;
11297
11487
  if (this._receiveRotator) {
11298
11488
  await this._receiveRotator.runExclusive(
11299
11489
  () => this._doRotateServerSigner(xonly, newServerUnrollScript)
@@ -11308,7 +11498,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
11308
11498
  return run;
11309
11499
  }
11310
11500
  async _doRotateServerSigner(xonly, newServerUnrollScript) {
11311
- if (equalBytes$1(xonly, this.arkServerPublicKey)) return;
11501
+ if (equalBytes(xonly, this.arkServerPublicKey)) return;
11312
11502
  const manager = await this.getContractManager();
11313
11503
  const [currentOffchainRow] = await manager.getContracts({
11314
11504
  script: this.defaultContractScript
@@ -11845,7 +12035,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
11845
12035
  script: offchainOutputScript
11846
12036
  });
11847
12037
  output.amount -= BigInt(outputFee.satoshis);
11848
- if (output.amount <= this.dustAmount) {
12038
+ if (isSubdust(output.amount, this.dustAmount)) {
11849
12039
  throw new Error("Output amount is below dust limit");
11850
12040
  }
11851
12041
  params = {
@@ -12093,7 +12283,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
12093
12283
  }
12094
12284
  continue;
12095
12285
  }
12096
- if (isRecoverable(input) || isSubdust(input, this.dustAmount)) {
12286
+ if (isRecoverable(input) || isSubdust({ value: input.value }, this.dustAmount)) {
12097
12287
  continue;
12098
12288
  }
12099
12289
  if (connectorsLeaves.length === 0) {
@@ -12655,9 +12845,24 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
12655
12845
  }
12656
12846
  const assetInputs = selectedCoinsToAssetInputs(selectedCoins);
12657
12847
  const hasAssets = assetInputs.size > 0 || recipients.some((r) => r.assets && r.assets.length > 0);
12848
+ const customExtPackets = [];
12849
+ for (const r of args) {
12850
+ if (r.extensions) {
12851
+ for (const ext of r.extensions) {
12852
+ customExtPackets.push({
12853
+ type: () => ext.type,
12854
+ serialize: () => ext.payload
12855
+ });
12856
+ }
12857
+ }
12858
+ }
12859
+ const allExtPackets = [];
12658
12860
  if (hasAssets) {
12659
- const assetPacket = createAssetPacket(assetInputs, recipients, changeReceiver);
12660
- outputs.push(Extension.create([assetPacket]).txOut());
12861
+ allExtPackets.push(createAssetPacket(assetInputs, recipients, changeReceiver));
12862
+ }
12863
+ allExtPackets.push(...customExtPackets);
12864
+ if (allExtPackets.length > 0) {
12865
+ outputs.push(Extension.create(allExtPackets).txOut());
12661
12866
  }
12662
12867
  const sentAmount = recipients.reduce((sum, r) => sum + r.amount, 0);
12663
12868
  return this._submitOffchainSpend(selectedCoins, outputs, {
@@ -12796,72 +13001,52 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
12796
13001
  index,
12797
13002
  lookupScript: VtxoScript.decode(input.tapTree).pkScript
12798
13003
  }));
12799
- const checkpointJobs = offchainTx.checkpoints.map(
12800
- (c) => this.inputSigningJobsFromWitnessUtxos(c)
12801
- );
12802
- let signedVirtualTx;
12803
- let userSignedCheckpoints;
12804
13004
  const identity = this.identity;
12805
- const batchEligible = isBatchSignable(identity) && await this._signerRouter.canBatch(arkTxJobs, ...checkpointJobs);
12806
- if (batchEligible) {
12807
- const requests = [
12808
- {
12809
- tx: offchainTx.arkTx.clone(),
12810
- inputIndexes: arkTxJobs.map((j) => j.index)
12811
- },
12812
- ...offchainTx.checkpoints.map((c, i) => ({
12813
- tx: c.clone(),
12814
- inputIndexes: checkpointJobs[i].map((j) => j.index)
12815
- }))
12816
- ];
12817
- const signed = await identity.signMultiple(requests);
12818
- if (signed.length !== requests.length) {
12819
- throw new Error(
12820
- `signMultiple returned ${signed.length} transactions, expected ${requests.length}`
12821
- );
12822
- }
12823
- const [firstSignedTx, ...signedCheckpoints] = signed;
12824
- signedVirtualTx = firstSignedTx;
12825
- userSignedCheckpoints = signedCheckpoints;
12826
- } else {
12827
- signedVirtualTx = await this._signerRouter.sign(offchainTx.arkTx, arkTxJobs);
12828
- }
12829
- await this.setPendingTxFlag(true);
12830
- const { arkTxid, signedCheckpointTxs } = await this.arkProvider.submitTx(
12831
- base64.encode(signedVirtualTx.toPSBT()),
12832
- offchainTx.checkpoints.map((c) => base64.encode(c.toPSBT()))
12833
- );
12834
- let finalCheckpoints;
12835
- if (userSignedCheckpoints) {
12836
- if (signedCheckpointTxs.length !== userSignedCheckpoints.length) {
12837
- throw new Error(
12838
- `submitTx returned ${signedCheckpointTxs.length} checkpoints, expected ${userSignedCheckpoints.length}`
13005
+ const signer = {
13006
+ signArkTx: async (arkTx, checkpoints) => {
13007
+ const checkpointJobs = checkpoints.map(
13008
+ (c) => this.inputSigningJobsFromWitnessUtxos(c)
12839
13009
  );
13010
+ const batchEligible = isBatchSignable(identity) && await this._signerRouter.canBatch(arkTxJobs, ...checkpointJobs);
13011
+ if (batchEligible) {
13012
+ const requests = [
13013
+ {
13014
+ tx: arkTx.clone(),
13015
+ inputIndexes: arkTxJobs.map((j) => j.index)
13016
+ },
13017
+ ...checkpoints.map((c, i) => ({
13018
+ tx: c.clone(),
13019
+ inputIndexes: checkpointJobs[i].map((j) => j.index)
13020
+ }))
13021
+ ];
13022
+ const signed = await identity.signMultiple(requests);
13023
+ if (signed.length !== requests.length) {
13024
+ throw new Error(
13025
+ `signMultiple returned ${signed.length} transactions, expected ${requests.length}`
13026
+ );
13027
+ }
13028
+ const [firstSignedTx, ...signedCheckpoints] = signed;
13029
+ return { arkTx: firstSignedTx, userSignedCheckpoints: signedCheckpoints };
13030
+ }
13031
+ return { arkTx: await this._signerRouter.sign(arkTx, arkTxJobs) };
13032
+ },
13033
+ signCheckpoint: (checkpoint) => this._signerRouter.sign(
13034
+ checkpoint,
13035
+ this.inputSigningJobsFromWitnessUtxos(checkpoint)
13036
+ )
13037
+ };
13038
+ return submitOffchainTx(this.arkProvider, offchainTx, signer, {
13039
+ // Mark pending before submitting — if we crash between submit and
13040
+ // finalize, the next init recovers via finalizePendingTxs.
13041
+ beforeSubmit: () => this.setPendingTxFlag(true),
13042
+ afterFinalize: async () => {
13043
+ try {
13044
+ await this.setPendingTxFlag(false);
13045
+ } catch (error) {
13046
+ console.error("Failed to clear pending tx flag:", error);
13047
+ }
12840
13048
  }
12841
- finalCheckpoints = signedCheckpointTxs.map((c, i) => {
12842
- const serverSigned = Transaction$2.fromPSBT(base64.decode(c));
12843
- combineTapscriptSigs(userSignedCheckpoints[i], serverSigned);
12844
- return base64.encode(serverSigned.toPSBT());
12845
- });
12846
- } else {
12847
- finalCheckpoints = await Promise.all(
12848
- signedCheckpointTxs.map(async (c) => {
12849
- const tx = Transaction$2.fromPSBT(base64.decode(c));
12850
- const signedCheckpoint = await this._signerRouter.sign(
12851
- tx,
12852
- this.inputSigningJobsFromWitnessUtxos(tx)
12853
- );
12854
- return base64.encode(signedCheckpoint.toPSBT());
12855
- })
12856
- );
12857
- }
12858
- await this.arkProvider.finalizeTx(arkTxid, finalCheckpoints);
12859
- try {
12860
- await this.setPendingTxFlag(false);
12861
- } catch (error) {
12862
- console.error("Failed to clear pending tx flag:", error);
12863
- }
12864
- return { arkTxid, signedCheckpointTxs };
13049
+ });
12865
13050
  }
12866
13051
  // mark virtual outputs as spent, save change outputs if any.
12867
13052
  // `offchainTapscript` and `serverPubKey` are the epoch snapshot the
@@ -13172,6 +13357,8 @@ var MessageBus = class {
13172
13357
  this.debug = debug;
13173
13358
  this.buildServicesFn = buildServices ?? this.buildServices.bind(this);
13174
13359
  }
13360
+ walletRepository;
13361
+ contractRepository;
13175
13362
  handlers;
13176
13363
  tickIntervalMs;
13177
13364
  messageTimeoutMs;
@@ -13673,6 +13860,7 @@ var Ramps = class {
13673
13860
  constructor(wallet) {
13674
13861
  this.wallet = wallet;
13675
13862
  }
13863
+ wallet;
13676
13864
  /**
13677
13865
  * Onboard boarding inputs.
13678
13866
  *
@@ -15019,6 +15207,8 @@ var ServiceWorkerReadonlyAssetManager = class {
15019
15207
  this.sendMessage = sendMessage;
15020
15208
  this.messageTag = messageTag;
15021
15209
  }
15210
+ sendMessage;
15211
+ messageTag;
15022
15212
  async getAssetDetails(assetId) {
15023
15213
  const message = {
15024
15214
  tag: this.messageTag,
@@ -15103,6 +15293,8 @@ var ServiceWorkerReadonlyWallet = class _ServiceWorkerReadonlyWallet {
15103
15293
  messageTag
15104
15294
  );
15105
15295
  }
15296
+ serviceWorker;
15297
+ messageTag;
15106
15298
  walletRepository;
15107
15299
  contractRepository;
15108
15300
  identity;
@@ -15855,6 +16047,7 @@ var ServiceWorkerWallet = class _ServiceWorkerWallet extends ServiceWorkerReadon
15855
16047
  );
15856
16048
  this.hasDelegate = hasDelegate;
15857
16049
  }
16050
+ serviceWorker;
15858
16051
  walletRepository;
15859
16052
  contractRepository;
15860
16053
  identity;
@@ -16236,6 +16429,7 @@ var OnchainWallet = class _OnchainWallet {
16236
16429
  this.onchainP2TR = onchainP2TR;
16237
16430
  this.provider = provider;
16238
16431
  }
16432
+ identity;
16239
16433
  static MIN_FEE_RATE = 1;
16240
16434
  // sat/vbyte
16241
16435
  onchainP2TR;
@@ -16537,6 +16731,8 @@ var WsElectrumChainSource = class {
16537
16731
  this.ws = ws;
16538
16732
  this.network = network;
16539
16733
  }
16734
+ ws;
16735
+ network;
16540
16736
  // Cached chain tip kept fresh by the headers subscription. Initialized
16541
16737
  // lazily on first call to subscribeHeaders().
16542
16738
  cachedTip = null;
@@ -16819,6 +17015,8 @@ var ElectrumOnchainProvider = class {
16819
17015
  this.network = network;
16820
17016
  this.chain = new WsElectrumChainSource(ws, network);
16821
17017
  }
17018
+ ws;
17019
+ network;
16822
17020
  chain;
16823
17021
  async getCoins(address) {
16824
17022
  const script = this.encodeAddress(address);
@@ -17254,7 +17452,7 @@ function verifyP2WPKH(message, witnessItems, pkScript, addressHash) {
17254
17452
  return false;
17255
17453
  }
17256
17454
  const derived = p2wpkh(pubkey);
17257
- if (!equalBytes(derived.hash, addressHash)) {
17455
+ if (!equalBytes$1(derived.hash, addressHash)) {
17258
17456
  return false;
17259
17457
  }
17260
17458
  const sighashType = sigWithHash[sigWithHash.length - 1];
@@ -17284,7 +17482,7 @@ function verifyLegacy(message, sigBytes, addressHash) {
17284
17482
  const sig = secp256k1.Signature.fromBytes(compactSig, "compact").addRecoveryBit(recoveryId);
17285
17483
  const point = sig.recoverPublicKey(msgHash);
17286
17484
  const pubkeyBytes = point.toBytes(compressed);
17287
- return equalBytes(hash160(pubkeyBytes), addressHash);
17485
+ return equalBytes$1(hash160(pubkeyBytes), addressHash);
17288
17486
  } catch {
17289
17487
  return false;
17290
17488
  }
@@ -17347,6 +17545,132 @@ function craftBIP322ToSignSimple(toSpend, pkScript) {
17347
17545
  });
17348
17546
  return tx;
17349
17547
  }
17548
+
17549
+ // src/providers/emulator.ts
17550
+ var RestEmulatorProvider = class {
17551
+ constructor(serverUrl) {
17552
+ this.serverUrl = serverUrl;
17553
+ }
17554
+ serverUrl;
17555
+ async getInfo() {
17556
+ const url = `${this.serverUrl}/v1/info`;
17557
+ const response = await fetch(url);
17558
+ if (!response.ok) {
17559
+ const errorText2 = await response.text();
17560
+ throw new Error(`Failed to get emulator info: ${errorText2}`);
17561
+ }
17562
+ const data = await response.json();
17563
+ const signerPubkey = data.signerPubkey;
17564
+ if (typeof signerPubkey !== "string" || !signerPubkey) {
17565
+ throw new Error("Invalid emulator info response: missing signerPubkey");
17566
+ }
17567
+ return { signerPubkey };
17568
+ }
17569
+ async submitTx(arkTx, checkpointTxs) {
17570
+ const url = `${this.serverUrl}/v1/tx`;
17571
+ const response = await fetch(url, {
17572
+ method: "POST",
17573
+ headers: { "Content-Type": "application/json" },
17574
+ body: JSON.stringify({
17575
+ arkTx,
17576
+ checkpointTxs
17577
+ })
17578
+ });
17579
+ if (!response.ok) {
17580
+ const errorText2 = await response.text();
17581
+ throw new Error(`Failed to submit tx to emulator: ${errorText2}`);
17582
+ }
17583
+ const data = await response.json();
17584
+ if (typeof data.signedArkTx !== "string" || !data.signedArkTx) {
17585
+ throw new Error("Invalid emulator submitTx response: missing signedArkTx");
17586
+ }
17587
+ if (!Array.isArray(data.signedCheckpointTxs) || !data.signedCheckpointTxs.every((item) => typeof item === "string")) {
17588
+ throw new Error(
17589
+ "Invalid emulator submitTx response: signedCheckpointTxs must be an array of strings"
17590
+ );
17591
+ }
17592
+ return {
17593
+ signedArkTx: data.signedArkTx,
17594
+ signedCheckpointTxs: data.signedCheckpointTxs
17595
+ };
17596
+ }
17597
+ async submitIntent(intent) {
17598
+ const url = `${this.serverUrl}/v1/intent`;
17599
+ const response = await fetch(url, {
17600
+ method: "POST",
17601
+ headers: { "Content-Type": "application/json" },
17602
+ body: JSON.stringify({
17603
+ intent: {
17604
+ proof: intent.proof,
17605
+ message: JSON.stringify(intent.message)
17606
+ }
17607
+ })
17608
+ });
17609
+ if (!response.ok) {
17610
+ const errorText2 = await response.text();
17611
+ throw new Error(`Failed to submit intent to emulator: ${errorText2}`);
17612
+ }
17613
+ const data = await response.json();
17614
+ if (typeof data.signedProof !== "string" || !data.signedProof) {
17615
+ throw new Error("Invalid emulator submitIntent response: missing signedProof");
17616
+ }
17617
+ return data.signedProof;
17618
+ }
17619
+ async submitFinalization(intent, forfeits, connectorTree, commitmentTx) {
17620
+ const url = `${this.serverUrl}/v1/finalization`;
17621
+ const response = await fetch(url, {
17622
+ method: "POST",
17623
+ headers: { "Content-Type": "application/json" },
17624
+ // Uses "signedIntent" (not "intent") because the proof was already
17625
+ // co-signed by the emulator via submitIntent in a prior step.
17626
+ body: JSON.stringify({
17627
+ signedIntent: {
17628
+ proof: intent.proof,
17629
+ message: JSON.stringify(intent.message)
17630
+ },
17631
+ forfeits,
17632
+ connectorTree,
17633
+ commitmentTx
17634
+ })
17635
+ });
17636
+ if (!response.ok) {
17637
+ const errorText2 = await response.text();
17638
+ throw new Error(`Failed to submit finalization to emulator: ${errorText2}`);
17639
+ }
17640
+ const data = await response.json();
17641
+ if (!Array.isArray(data.signedForfeits) || !data.signedForfeits.every((item) => typeof item === "string")) {
17642
+ throw new Error(
17643
+ "Invalid emulator submitFinalization response: signedForfeits must be an array of strings"
17644
+ );
17645
+ }
17646
+ if ("signedCommitmentTx" in data && typeof data.signedCommitmentTx !== "string") {
17647
+ throw new Error(
17648
+ "Invalid emulator submitFinalization response: invalid signedCommitmentTx"
17649
+ );
17650
+ }
17651
+ return {
17652
+ signedForfeits: data.signedForfeits,
17653
+ signedCommitmentTx: data.signedCommitmentTx
17654
+ };
17655
+ }
17656
+ async submitOnchainTx(tx) {
17657
+ const url = `${this.serverUrl}/v1/onchain-tx`;
17658
+ const response = await fetch(url, {
17659
+ method: "POST",
17660
+ headers: { "Content-Type": "application/json" },
17661
+ body: JSON.stringify({ tx })
17662
+ });
17663
+ if (!response.ok) {
17664
+ const errorText2 = await response.text();
17665
+ throw new Error(`Failed to submit onchain tx to emulator: ${errorText2}`);
17666
+ }
17667
+ const data = await response.json();
17668
+ if (typeof data.signedTx !== "string" || !data.signedTx) {
17669
+ throw new Error("Invalid emulator submitOnchainTx response: missing signedTx");
17670
+ }
17671
+ return { signedTx: data.signedTx };
17672
+ }
17673
+ };
17350
17674
  function finalizeVirtualTx(type, psbtBase64) {
17351
17675
  const tx = Transaction.fromPSBT(base64.decode(psbtBase64));
17352
17676
  if (type === "INDEXER_CHAINED_TX_TYPE_TREE" /* TREE */) {
@@ -17372,6 +17696,7 @@ var ExitPathError = class extends Error {
17372
17696
  this.reason = reason;
17373
17697
  this.name = "ExitPathError";
17374
17698
  }
17699
+ reason;
17375
17700
  };
17376
17701
  function delayWeight(sequence) {
17377
17702
  const t = sequenceToTimelock(sequence);
@@ -17455,6 +17780,11 @@ var Unroll;
17455
17780
  this.indexer = indexer;
17456
17781
  this.virtualTxRepository = virtualTxRepository;
17457
17782
  }
17783
+ toUnroll;
17784
+ bumper;
17785
+ explorer;
17786
+ indexer;
17787
+ virtualTxRepository;
17458
17788
  /** Create an unroll session by loading the virtual output chain from the indexer. */
17459
17789
  static async create(toUnroll, bumper, explorer, indexer, virtualTxRepository) {
17460
17790
  const { chain } = await indexer.getVtxoChain(toUnroll);
@@ -17935,6 +18265,8 @@ var Executor = class {
17935
18265
  this.pollIntervalMs = opts?.pollIntervalMs ?? 5e3;
17936
18266
  this.feeWallet = opts?.feeWallet;
17937
18267
  }
18268
+ pkg;
18269
+ provider;
17938
18270
  pollIntervalMs;
17939
18271
  feeWallet;
17940
18272
  sleep() {
@@ -18371,6 +18703,656 @@ var UnilateralExit = {
18371
18703
  prepare,
18372
18704
  Executor
18373
18705
  };
18706
+
18707
+ // src/arkade/index.ts
18708
+ var arkade_exports = {};
18709
+ __export(arkade_exports, {
18710
+ ARKADE_OP: () => ARKADE_OP,
18711
+ ARKADE_OPCODES: () => ARKADE_OPCODES,
18712
+ ARKADE_OPCODE_NAMES: () => ARKADE_OPCODE_NAMES,
18713
+ ARKADE_OPCODE_VALUES: () => ARKADE_OPCODE_VALUES,
18714
+ ARKADE_OPS: () => ARKADE_OPS,
18715
+ Arkade: () => Arkade,
18716
+ ArkadeContract: () => ArkadeContract,
18717
+ ArkadeProgramScript: () => ArkadeProgramScript,
18718
+ ArkadeScript: () => ArkadeScript,
18719
+ ArkadeTransactionBuilder: () => ArkadeTransactionBuilder,
18720
+ BigNum: () => bignum_exports,
18721
+ OP: () => OP,
18722
+ OPCODE_NAMES: () => OPCODE_NAMES,
18723
+ OPCODE_VALUES: () => OPCODE_VALUES,
18724
+ SUPPORTED_PROGRAM_VERSION: () => SUPPORTED_PROGRAM_VERSION,
18725
+ Script: () => Script,
18726
+ arkadeScriptHash: () => arkadeScriptHash,
18727
+ arkadeWitnessHash: () => arkadeWitnessHash,
18728
+ asmToBytes: () => asmToBytes,
18729
+ bytesToASM: () => bytesToASM,
18730
+ computeArkadeScriptPublicKey: () => computeArkadeScriptPublicKey,
18731
+ createArkadeBatchHandler: () => createArkadeBatchHandler,
18732
+ deserializeArkadeContractParams: () => deserializeArkadeContractParams,
18733
+ fromASM: () => fromASM,
18734
+ getOpcodeName: () => getOpcodeName,
18735
+ getOpcodeValue: () => getOpcodeValue,
18736
+ parseArtifact: () => parseArtifact,
18737
+ resolveAsm: () => resolveAsm,
18738
+ serializeArkadeContractParams: () => serializeArkadeContractParams,
18739
+ stringifyArtifact: () => stringifyArtifact,
18740
+ toASM: () => toASM,
18741
+ validateProgram: () => validateProgram,
18742
+ witnessRefToBytes: () => witnessRefToBytes
18743
+ });
18744
+ var isVtxoCoin = (input) => "virtualStatus" in input;
18745
+ function createArkadeBatchHandler(intentId, inputs, signer, signedProof, message, session, arkProvider, emulator, network, recipients) {
18746
+ let batchId;
18747
+ let sweepTapTreeRoot;
18748
+ return {
18749
+ onBatchStarted: async (event) => {
18750
+ const utf8IntentId = new TextEncoder().encode(intentId);
18751
+ const intentIdHash = sha256(utf8IntentId);
18752
+ const intentIdHashStr = hex.encode(intentIdHash);
18753
+ if (!event.intentIdHashes.includes(intentIdHashStr)) return { skip: true };
18754
+ await arkProvider.confirmRegistration(intentId);
18755
+ batchId = event.id;
18756
+ const sweepTapscript = CSVMultisigTapscript.encode({
18757
+ timelock: {
18758
+ value: event.batchExpiry,
18759
+ // BIP-65: values >= 512 are interpreted as seconds, below as blocks
18760
+ type: event.batchExpiry >= 512n ? "seconds" : "blocks"
18761
+ },
18762
+ pubkeys: [hex.decode((await arkProvider.getInfo()).forfeitPubkey).subarray(1)]
18763
+ }).script;
18764
+ sweepTapTreeRoot = tapLeafHash(sweepTapscript);
18765
+ return { skip: false };
18766
+ },
18767
+ onTreeSigningStarted: async (event, vtxoTree) => {
18768
+ const signerPubKey = await session.getPublicKey();
18769
+ const xonlySignerPubKey = signerPubKey.subarray(1);
18770
+ const xOnlyPubkeys = event.cosignersPublicKeys.map((k) => k.slice(2));
18771
+ if (!xOnlyPubkeys.includes(hex.encode(xonlySignerPubKey))) {
18772
+ return { skip: true };
18773
+ }
18774
+ const commitmentTx = Transaction.fromPSBT(base64.decode(event.unsignedCommitmentTx));
18775
+ validateVtxoTxGraph(vtxoTree, commitmentTx, sweepTapTreeRoot);
18776
+ if (recipients && recipients.length > 0) {
18777
+ validateBatchRecipients(commitmentTx, vtxoTree.leaves(), recipients, network);
18778
+ }
18779
+ const sharedOutput = commitmentTx.getOutput(0);
18780
+ if (!sharedOutput?.amount) {
18781
+ throw new Error("Shared output not found");
18782
+ }
18783
+ await session.init(vtxoTree, sweepTapTreeRoot, sharedOutput.amount);
18784
+ const pubkey = hex.encode(await session.getPublicKey());
18785
+ const nonces = await session.getNonces();
18786
+ await arkProvider.submitTreeNonces(batchId, pubkey, nonces);
18787
+ return { skip: false };
18788
+ },
18789
+ onTreeNonces: async (event) => {
18790
+ const { hasAllNonces } = await session.aggregatedNonces(event.txid, event.nonces);
18791
+ if (!hasAllNonces) return { fullySigned: false };
18792
+ const signatures = await session.sign();
18793
+ const pubkey = hex.encode(await session.getPublicKey());
18794
+ await arkProvider.submitTreeSignatures(batchId, pubkey, signatures);
18795
+ return { fullySigned: true };
18796
+ },
18797
+ onBatchFinalization: async (event, _vtxoTree, connectorTree) => {
18798
+ const info = await arkProvider.getInfo();
18799
+ const forfeitOutputScript = OutScript.encode(
18800
+ Address(network).decode(info.forfeitAddress)
18801
+ );
18802
+ if (connectorTree) {
18803
+ validateConnectorsTxGraph(event.commitmentTx, connectorTree);
18804
+ }
18805
+ let commitmentPsbt = Transaction.fromPSBT(base64.decode(event.commitmentTx));
18806
+ const signedForfeits = [];
18807
+ let connectorIndex = 0;
18808
+ const connectorLeaves = connectorTree?.leaves() || [];
18809
+ const boardingIndices = [];
18810
+ for (const input of inputs) {
18811
+ if (!isVtxoCoin(input)) {
18812
+ let boardingIdx = null;
18813
+ for (let i = 0; i < commitmentPsbt.inputsLength; i++) {
18814
+ const psbtInput = commitmentPsbt.getInput(i);
18815
+ if (!psbtInput.txid) continue;
18816
+ if (hex.encode(psbtInput.txid) === input.txid && psbtInput.index === input.vout) {
18817
+ boardingIdx = i;
18818
+ break;
18819
+ }
18820
+ }
18821
+ if (boardingIdx === null) continue;
18822
+ commitmentPsbt.updateInput(boardingIdx, {
18823
+ tapLeafScript: [input.forfeitTapLeafScript]
18824
+ });
18825
+ boardingIndices.push(boardingIdx);
18826
+ continue;
18827
+ }
18828
+ if (isRecoverable(input) || isSubdust(input, info.dust)) {
18829
+ continue;
18830
+ }
18831
+ if (connectorIndex >= connectorLeaves.length) {
18832
+ throw new Error("not enough connectors received");
18833
+ }
18834
+ const connectorLeaf = connectorLeaves[connectorIndex++];
18835
+ const connectorTxId = connectorLeaf.id;
18836
+ const connectorOutput = connectorLeaf.getOutput(0);
18837
+ if (!connectorOutput?.amount || !connectorOutput?.script) {
18838
+ throw new Error(
18839
+ `Invalid connector output at index ${connectorIndex - 1}: missing amount or script`
18840
+ );
18841
+ }
18842
+ let forfeitTx = buildForfeitTx(
18843
+ [
18844
+ {
18845
+ txid: input.txid,
18846
+ index: input.vout,
18847
+ witnessUtxo: {
18848
+ amount: BigInt(input.value),
18849
+ script: VtxoScript.decode(input.tapTree).pkScript
18850
+ },
18851
+ sighashType: SigHash.DEFAULT,
18852
+ tapLeafScript: [input.forfeitTapLeafScript]
18853
+ },
18854
+ {
18855
+ txid: connectorTxId,
18856
+ index: 0,
18857
+ witnessUtxo: {
18858
+ amount: connectorOutput.amount,
18859
+ script: connectorOutput.script
18860
+ }
18861
+ }
18862
+ ],
18863
+ forfeitOutputScript
18864
+ );
18865
+ forfeitTx = await signer.sign(forfeitTx, [0]);
18866
+ signedForfeits.push(base64.encode(forfeitTx.toPSBT()));
18867
+ }
18868
+ if (boardingIndices.length > 0) {
18869
+ commitmentPsbt = await signer.sign(commitmentPsbt, boardingIndices);
18870
+ }
18871
+ let connectorTreeNodes = null;
18872
+ if (connectorTree) {
18873
+ connectorTreeNodes = [];
18874
+ for (const subtree of connectorTree.iterator()) {
18875
+ const children = {};
18876
+ for (const [outputIndex, child] of subtree.children) {
18877
+ children[String(outputIndex)] = child.txid;
18878
+ }
18879
+ connectorTreeNodes.push({
18880
+ txid: subtree.txid,
18881
+ tx: base64.encode(subtree.root.toPSBT()),
18882
+ children
18883
+ });
18884
+ }
18885
+ }
18886
+ const hasBoardingInputs = boardingIndices.length > 0;
18887
+ const commitmentB64 = hasBoardingInputs ? base64.encode(commitmentPsbt.toPSBT()) : event.commitmentTx;
18888
+ const emuResult = await emulator.submitFinalization(
18889
+ { proof: signedProof, message },
18890
+ signedForfeits,
18891
+ connectorTreeNodes,
18892
+ commitmentB64
18893
+ );
18894
+ await arkProvider.submitSignedForfeitTxs(
18895
+ emuResult.signedForfeits,
18896
+ emuResult.signedCommitmentTx || (hasBoardingInputs ? commitmentB64 : void 0)
18897
+ );
18898
+ }
18899
+ };
18900
+ }
18901
+ var Arkade = class _Arkade {
18902
+ arkProvider;
18903
+ /** The co-signing service, or undefined for emulator-less (pure tapscript) usage. */
18904
+ emulator;
18905
+ network;
18906
+ serverKey;
18907
+ /** The co-signer's x-only key, present only when an emulator is configured. */
18908
+ emulatorKey;
18909
+ checkpoint;
18910
+ indexer;
18911
+ identity;
18912
+ /** The signing identity's x-only public key, resolved at connect — identifies which inputs the wallet signs. */
18913
+ userKey;
18914
+ /** The wallet's contract manager, when contract persistence is wired up. */
18915
+ contractManager;
18916
+ constructor(fields) {
18917
+ this.arkProvider = fields.arkProvider;
18918
+ this.emulator = fields.emulator;
18919
+ this.network = fields.network;
18920
+ this.serverKey = fields.serverKey;
18921
+ this.emulatorKey = fields.emulatorKey;
18922
+ this.checkpoint = fields.checkpoint;
18923
+ this.indexer = fields.indexer;
18924
+ this.identity = fields.identity;
18925
+ this.userKey = fields.userKey;
18926
+ this.contractManager = fields.contractManager;
18927
+ }
18928
+ /** Connect and resolve the server key, checkpoint closure and (if present) the co-signer key. */
18929
+ static async connect(opts) {
18930
+ const info = await opts.arkade.getInfo();
18931
+ const serverKey = hex.decode(info.signerPubkey).slice(1);
18932
+ const checkpoint = CSVMultisigTapscript.decode(hex.decode(info.checkpointTapscript));
18933
+ let emulatorKey;
18934
+ if (opts.emulator) {
18935
+ const eInfo = await opts.emulator.getInfo();
18936
+ emulatorKey = hex.decode(eInfo.signerPubkey);
18937
+ }
18938
+ let userKey;
18939
+ if (opts.identity) {
18940
+ const pub = await opts.identity.xOnlyPublicKey();
18941
+ userKey = pub.length === 33 ? pub.slice(1) : pub;
18942
+ }
18943
+ return new _Arkade({
18944
+ arkProvider: opts.arkade,
18945
+ emulator: opts.emulator,
18946
+ network: opts.network ?? DEFAULT_NETWORK,
18947
+ serverKey,
18948
+ emulatorKey,
18949
+ checkpoint,
18950
+ indexer: opts.indexer,
18951
+ identity: opts.identity,
18952
+ userKey,
18953
+ contractManager: opts.contractManager
18954
+ });
18955
+ }
18956
+ /**
18957
+ * Instantiate a contract from a program and its constructor arguments. The
18958
+ * `program`'s literal type is preserved (`const` inference) so the resulting
18959
+ * contract's `functions` map is strongly typed — `functions.<name>(...)`
18960
+ * knows each argument's type from the function's `inputs` descriptors.
18961
+ *
18962
+ * When the program declares a `server` or `user` param and the caller does
18963
+ * not bind it, it defaults to the client's server key or the identity's
18964
+ * key respectively; explicit args always win.
18965
+ */
18966
+ contract(program, args = {}) {
18967
+ const declared = (program.params ?? []).map(inputName);
18968
+ if (declared.includes("server") && args.server === void 0) {
18969
+ args = { ...args, server: this.serverKey };
18970
+ }
18971
+ if (declared.includes("user") && args.user === void 0 && this.userKey) {
18972
+ args = { ...args, user: this.userKey };
18973
+ }
18974
+ return new ArkadeContract(this, program, args);
18975
+ }
18976
+ };
18977
+ var ArkadeContract = class _ArkadeContract {
18978
+ constructor(client, program, args = {}, keys) {
18979
+ this.client = client;
18980
+ this.program = program;
18981
+ this.args = args;
18982
+ this.keys = keys ?? {
18983
+ serverKey: client.serverKey,
18984
+ userKey: client.userKey,
18985
+ emulatorKey: client.emulatorKey
18986
+ };
18987
+ this.vtxoScript = new ArkadeProgramScript(program, args, this.keys);
18988
+ this.tapTree = this.vtxoScript.encode();
18989
+ this.compiled = this.vtxoScript.compiled;
18990
+ }
18991
+ client;
18992
+ program;
18993
+ args;
18994
+ /** The compiled taproot tree of spending-path leaves. */
18995
+ vtxoScript;
18996
+ /** Encoded taproot tree (shared spend context for every path). */
18997
+ tapTree;
18998
+ /** The signer keys the program was compiled against. */
18999
+ keys;
19000
+ /** Spending paths in declaration order. */
19001
+ compiled;
19002
+ /**
19003
+ * Rebuild a callable contract from a persisted `"arkade"` contract row
19004
+ * (see {@link ArkadeContract.register}). The stored keys are used for
19005
+ * compilation — not the client's current ones — so the derived script and
19006
+ * address stay identical to the registered contract even after a server
19007
+ * signer rotation.
19008
+ */
19009
+ static fromContract(client, contract) {
19010
+ if (contract.type !== "arkade") {
19011
+ throw new Error(
19012
+ `ArkadeContract.fromContract: expected contract type 'arkade', got '${contract.type}'`
19013
+ );
19014
+ }
19015
+ const typed = deserializeArkadeContractParams(contract.params);
19016
+ return new _ArkadeContract(client, typed.program, typed.args, {
19017
+ serverKey: typed.serverKey,
19018
+ userKey: typed.userKey,
19019
+ emulatorKey: typed.emulatorKey
19020
+ });
19021
+ }
19022
+ /** Resolve the {@link TapLeafScript} for a spending path by its index. */
19023
+ leafScript(index) {
19024
+ const fn = this.compiled[index];
19025
+ if (!fn) throw new Error(`leaf index ${index} out of range`);
19026
+ return fn.tapLeafScript;
19027
+ }
19028
+ /** Arkade funding address. */
19029
+ get address() {
19030
+ return this.vtxoScript.address(this.client.network.hrp, this.keys.serverKey).encode();
19031
+ }
19032
+ /** Taproot output script. */
19033
+ get pkScript() {
19034
+ return this.vtxoScript.pkScript;
19035
+ }
19036
+ /**
19037
+ * Callable spending paths: `contract.functions.<name>(...args)`. Strongly
19038
+ * typed from the program's literal type — each function's argument types are
19039
+ * derived from its `inputs` descriptors (see {@link ContractFunctions}).
19040
+ */
19041
+ get functions() {
19042
+ const out = {};
19043
+ for (const fn of this.compiled) {
19044
+ out[fn.name] = (...callArgs) => new ArkadeTransactionBuilder(this, fn, bindInputs(fn, callArgs));
19045
+ }
19046
+ return out;
19047
+ }
19048
+ /**
19049
+ * The `createContract` payload for this contract — the serialized program,
19050
+ * args and keys plus the derived script/address. Useful when registering
19051
+ * through a manager the client does not hold.
19052
+ */
19053
+ toContractParams() {
19054
+ return {
19055
+ type: "arkade",
19056
+ params: serializeArkadeContractParams({
19057
+ program: this.program,
19058
+ args: this.args,
19059
+ serverKey: this.keys.serverKey,
19060
+ userKey: this.keys.userKey,
19061
+ emulatorKey: this.keys.emulatorKey
19062
+ }),
19063
+ script: hex.encode(this.pkScript),
19064
+ address: this.address
19065
+ };
19066
+ }
19067
+ /**
19068
+ * Persist this contract through the wallet's {@link IContractManager} so it
19069
+ * is tracked like any other contract type: stored in the contract
19070
+ * repository, watched for VTXO events, counted in repository-backed
19071
+ * balances, and re-derivable offline via the `"arkade"` contract handler.
19072
+ * Idempotent — re-registering the same script is a no-op.
19073
+ */
19074
+ async register(options) {
19075
+ const manager = this.client.contractManager;
19076
+ if (!manager) {
19077
+ throw new Error(
19078
+ "ArkadeContract.register requires a `contractManager` on the Arkade client \u2014 pass one to Arkade.connect"
19079
+ );
19080
+ }
19081
+ return manager.createContract({
19082
+ ...this.toContractParams(),
19083
+ label: options?.label,
19084
+ metadata: options?.metadata
19085
+ });
19086
+ }
19087
+ /**
19088
+ * Spendable VTXOs locked by this contract.
19089
+ *
19090
+ * When a `contractManager` is configured and this contract is registered,
19091
+ * reads the repository-backed state (offline-first, kept fresh by the
19092
+ * contract watcher). Otherwise falls back to a direct indexer query.
19093
+ */
19094
+ async getUtxos() {
19095
+ const manager = this.client.contractManager;
19096
+ const scriptHex = hex.encode(this.pkScript);
19097
+ if (manager) {
19098
+ const [registered] = await manager.getContracts({ script: scriptHex });
19099
+ if (registered) {
19100
+ const [withVtxos] = await manager.getContractsWithVtxos({ script: scriptHex });
19101
+ return (withVtxos?.vtxos ?? []).filter(isSpendable);
19102
+ }
19103
+ }
19104
+ if (!this.client.indexer) {
19105
+ throw new Error("ArkadeContract.getUtxos: an indexer is required");
19106
+ }
19107
+ const { vtxos } = await this.client.indexer.getVtxos({
19108
+ scripts: [scriptHex],
19109
+ spendableOnly: true
19110
+ });
19111
+ return vtxos;
19112
+ }
19113
+ /** Total spendable balance (requires an indexer). */
19114
+ async getBalance() {
19115
+ const utxos = await this.getUtxos();
19116
+ return utxos.reduce((sum, u) => sum + BigInt(u.value), 0n);
19117
+ }
19118
+ };
19119
+ var ArkadeTransactionBuilder = class {
19120
+ /** @internal */
19121
+ constructor(contract, fn, args) {
19122
+ this.contract = contract;
19123
+ this.fn = fn;
19124
+ this.args = args;
19125
+ }
19126
+ contract;
19127
+ fn;
19128
+ args;
19129
+ outputs = [];
19130
+ fundingCoins = [];
19131
+ assetSpecs = [];
19132
+ coin;
19133
+ changeScript;
19134
+ from(coin) {
19135
+ this.coin = coin;
19136
+ return this;
19137
+ }
19138
+ /**
19139
+ * Add extra inputs the caller funds (e.g. a taker's own coins in a swap).
19140
+ * These become inputs 1..n and are signed with the client identity.
19141
+ */
19142
+ fund(coins) {
19143
+ this.fundingCoins.push(...coins);
19144
+ return this;
19145
+ }
19146
+ /** Transfer an asset group: which inputs supply it and which outputs receive it. */
19147
+ withAsset(spec) {
19148
+ this.assetSpecs.push(spec);
19149
+ return this;
19150
+ }
19151
+ /** Destination for any surplus (inputs − outputs). Required when the spend is not exact. */
19152
+ change(script) {
19153
+ this.changeScript = script;
19154
+ return this;
19155
+ }
19156
+ to(scriptOrOutputs, amount) {
19157
+ if (Array.isArray(scriptOrOutputs)) {
19158
+ for (const [i, out] of scriptOrOutputs.entries()) {
19159
+ if (out.amount === void 0) {
19160
+ throw new Error(`to(outputs): output ${i} is missing an amount`);
19161
+ }
19162
+ }
19163
+ this.outputs.push(...scriptOrOutputs);
19164
+ } else {
19165
+ if (amount === void 0) throw new Error("to(script, amount): amount is required");
19166
+ this.outputs.push({ script: scriptOrOutputs, amount });
19167
+ }
19168
+ return this;
19169
+ }
19170
+ /** Assemble the unsigned ark transaction and its checkpoints. */
19171
+ async build() {
19172
+ if (this.outputs.length === 0) {
19173
+ throw new Error("ArkadeTransactionBuilder: at least one output is required");
19174
+ }
19175
+ const outputsSum = this.outputs.reduce((s, o) => s + (o.amount ?? 0n), 0n);
19176
+ const coin = this.coin ?? await this.selectCoin(outputsSum);
19177
+ const def = this.fn.def;
19178
+ const outputs = [...this.outputs];
19179
+ const fundingSum = this.fundingCoins.reduce((s, f) => s + BigInt(f.value), 0n);
19180
+ const surplus = BigInt(coin.value) + fundingSum - outputsSum;
19181
+ if (surplus < 0n) {
19182
+ throw new Error(
19183
+ `ArkadeTransactionBuilder: insufficient inputs \u2014 outputs ${outputsSum} exceed inputs ${BigInt(coin.value) + fundingSum}`
19184
+ );
19185
+ }
19186
+ if (surplus > 0n) {
19187
+ if (!this.changeScript) {
19188
+ throw new Error(
19189
+ `ArkadeTransactionBuilder: ${surplus} sats surplus with no change output \u2014 call .change(script)`
19190
+ );
19191
+ }
19192
+ outputs.push({ script: this.changeScript, amount: surplus });
19193
+ }
19194
+ const { arkTx, checkpoints } = buildOffchainTx(
19195
+ [
19196
+ {
19197
+ txid: coin.txid,
19198
+ vout: coin.vout,
19199
+ value: coin.value,
19200
+ tapLeafScript: this.fn.tapLeafScript,
19201
+ tapTree: this.contract.tapTree
19202
+ },
19203
+ ...this.fundingCoins
19204
+ ],
19205
+ outputs,
19206
+ this.contract.client.checkpoint
19207
+ );
19208
+ if (coin.sourceTx) {
19209
+ setArkPsbtField(arkTx, 0, PrevArkTxField, coin.sourceTx);
19210
+ }
19211
+ const condition = (def.tapscript.witness ?? []).map((w) => this.witnessBytes(w));
19212
+ if (condition.length > 0) {
19213
+ setArkPsbtField(arkTx, 0, ConditionWitness, condition);
19214
+ setArkPsbtField(checkpoints[0], 0, ConditionWitness, condition);
19215
+ }
19216
+ const packets = [];
19217
+ if (this.assetSpecs.length > 0) {
19218
+ packets.push(this.buildAssetPacket());
19219
+ }
19220
+ const arkadeScript = this.fn.arkadeScript;
19221
+ if (arkadeScript) {
19222
+ const stack = (def.arkadeScript?.witness ?? []).map((w) => this.witnessBytes(w));
19223
+ packets.push(
19224
+ EmulatorPacket.create([
19225
+ { vin: 0, script: arkadeScript, witness: RawWitness.encode(stack) }
19226
+ ])
19227
+ );
19228
+ }
19229
+ if (packets.length > 0) {
19230
+ attachExtension(arkTx, packets);
19231
+ }
19232
+ return { arkTx, checkpoints };
19233
+ }
19234
+ /** Build, submit and return the finalized transaction. */
19235
+ async send() {
19236
+ const { arkTx, checkpoints } = await this.build();
19237
+ const client = this.contract.client;
19238
+ const userInputs = this.userInputIndexes();
19239
+ if (this.fn.arkadeScript) {
19240
+ if (!client.emulator) {
19241
+ throw new Error("covenant spends require an `emulator` on the Arkade client");
19242
+ }
19243
+ const signedArk2 = await this.signArk(arkTx, userInputs);
19244
+ const signedCps = userInputs.length > 0 ? await Promise.all(
19245
+ checkpoints.map(
19246
+ (c, i) => userInputs.includes(i) ? client.identity.sign(c, [0]) : c
19247
+ )
19248
+ ) : checkpoints;
19249
+ const res2 = await client.emulator.submitTx(
19250
+ base64.encode(signedArk2.toPSBT()),
19251
+ signedCps.map((c) => base64.encode(c.toPSBT()))
19252
+ );
19253
+ const txid = Transaction.fromPSBT(base64.decode(res2.signedArkTx)).id;
19254
+ return {
19255
+ txid,
19256
+ signedArkTx: res2.signedArkTx,
19257
+ signedCheckpointTxs: res2.signedCheckpointTxs
19258
+ };
19259
+ }
19260
+ if (!client.identity) {
19261
+ throw new Error("a signing identity is required for non-covenant spends");
19262
+ }
19263
+ const signedArk = await this.signArk(arkTx, userInputs);
19264
+ const res = await client.arkProvider.submitTx(
19265
+ base64.encode(signedArk.toPSBT()),
19266
+ checkpoints.map((c) => base64.encode(c.toPSBT()))
19267
+ );
19268
+ const finalCps = await Promise.all(
19269
+ res.signedCheckpointTxs.map(
19270
+ async (b) => base64.encode(
19271
+ (await client.identity.sign(Transaction.fromPSBT(base64.decode(b)), [0])).toPSBT()
19272
+ )
19273
+ )
19274
+ );
19275
+ await client.arkProvider.finalizeTx(res.arkTxid, finalCps);
19276
+ return {
19277
+ txid: res.arkTxid,
19278
+ signedArkTx: res.finalArkTx,
19279
+ signedCheckpointTxs: res.signedCheckpointTxs
19280
+ };
19281
+ }
19282
+ /** Sign the client-owned inputs of the ark tx (no-op when there are none). */
19283
+ async signArk(arkTx, userInputs) {
19284
+ if (userInputs.length === 0) return arkTx;
19285
+ const { identity } = this.contract.client;
19286
+ if (!identity) {
19287
+ throw new Error("this spend requires an `identity` to sign its user/funding inputs");
19288
+ }
19289
+ return identity.sign(arkTx, userInputs);
19290
+ }
19291
+ /** Indexes of inputs the client owns and must sign (contract input + funded inputs). */
19292
+ userInputIndexes() {
19293
+ const idxs = [];
19294
+ const userKey = this.contract.keys.userKey;
19295
+ if (userKey && this.fn.signerKeys.some((k) => equalBytes(k, userKey))) {
19296
+ idxs.push(0);
19297
+ }
19298
+ for (let i = 0; i < this.fundingCoins.length; i++) {
19299
+ idxs.push(i + 1);
19300
+ }
19301
+ return idxs;
19302
+ }
19303
+ async selectCoin(amount) {
19304
+ const utxos = await this.contract.getUtxos();
19305
+ if (utxos.length === 0) throw new Error("no spendable coins for this contract");
19306
+ const covering = utxos.filter((u) => BigInt(u.value) >= amount).sort((a, b) => a.value - b.value);
19307
+ if (covering.length > 0) return covering[0];
19308
+ return [...utxos].sort((a, b) => b.value - a.value)[0];
19309
+ }
19310
+ buildAssetPacket() {
19311
+ const groups = this.assetSpecs.map((s) => {
19312
+ const id = typeof s.assetId === "string" ? AssetId.fromString(s.assetId) : AssetId.fromBytes(s.assetId);
19313
+ return AssetGroup.create(
19314
+ id,
19315
+ null,
19316
+ s.inputs.map((i) => AssetInput.create(i.vin, i.amount)),
19317
+ s.outputs.map((o) => AssetOutput.create(o.vout, o.amount)),
19318
+ (s.metadata ?? []).map((m) => Metadata.create(m.key, m.value))
19319
+ );
19320
+ });
19321
+ return Packet.create(groups);
19322
+ }
19323
+ witnessBytes(ref) {
19324
+ return witnessRefToBytes(ref, this.args, this.contract.args);
19325
+ }
19326
+ };
19327
+ function bindInputs(fn, callArgs) {
19328
+ const names = (fn.def.inputs ?? []).map(inputName);
19329
+ if (callArgs.length !== names.length) {
19330
+ throw new Error(`${fn.name}: expected ${names.length} argument(s), got ${callArgs.length}`);
19331
+ }
19332
+ const bound = {};
19333
+ names.forEach((n, i) => bound[n] = callArgs[i]);
19334
+ return bound;
19335
+ }
19336
+ function attachExtension(tx, newPackets) {
19337
+ for (let i = 0; i < tx.outputsLength; i++) {
19338
+ const out = tx.getOutput(i);
19339
+ if (!out?.script || !Extension.isExtension(out.script)) continue;
19340
+ const existing = Extension.fromBytes(out.script);
19341
+ const merged = Extension.create([...existing.getPackets(), ...newPackets]);
19342
+ tx.updateOutput(i, { script: merged.serialize(), amount: 0n });
19343
+ return;
19344
+ }
19345
+ const ext = Extension.create(newPackets);
19346
+ const newOut = ext.txOut();
19347
+ const lastIdx = tx.outputsLength - 1;
19348
+ const lastOut = tx.getOutput(lastIdx);
19349
+ if (lastOut?.script && lastOut.script.length === ANCHOR_PKSCRIPT.length && lastOut.script.every((b, j) => b === ANCHOR_PKSCRIPT[j])) {
19350
+ tx.updateOutput(lastIdx, { script: newOut.script, amount: newOut.amount });
19351
+ tx.addOutput({ script: lastOut.script, amount: lastOut.amount ?? 0n });
19352
+ return;
19353
+ }
19354
+ tx.addOutput({ script: newOut.script, amount: newOut.amount });
19355
+ }
18374
19356
  var ARKCONTRACT_PREFIX = "arkcontract";
18375
19357
  function encodeArkContract(contract) {
18376
19358
  const params = new URLSearchParams();
@@ -18430,6 +19412,6 @@ function isArkContract(str) {
18430
19412
  return str.startsWith(ARKCONTRACT_PREFIX + "=");
18431
19413
  }
18432
19414
 
18433
- export { ActivityRegistry, ArkNote, AssetManager, BIP322, Batch, ChainedTxType, ContractManager, ContractRepositoryImpl, ContractWatcher, DB_VERSION, DEFAULT_MESSAGE_TIMEOUTS, DelegateManagerImpl, DelegateNotConfiguredError, DelegatorManagerImpl, DelegatorNotConfiguredError, DescriptorSigningProviderMissingError, DustChangeError, ELECTRUM_TCP_HOST, ELECTRUM_WS_URL, ESPLORA_URL, ElectrumOnchainProvider, EsploraProvider, Estimator, HDDescriptorProvider, INTENT_TERMINAL_STATES, InMemoryContractRepository, InMemoryIntentRepository, InMemoryVirtualTxRepository, InMemoryWalletRepository, IndexedDBContractRepository, IndexedDBIntentRepository, IndexedDBVirtualTxRepository, IndexedDBWalletRepository, MESSAGE_BUS_INITIALIZING, MESSAGE_BUS_NOT_INITIALIZED, MIGRATION_KEY, MessageBus, MessageBusInitializingError, MessageBusNotInitializedError, MissingSigningDescriptorError, MnemonicIdentity, OnchainWallet, P2A, Ramps, ReadonlyAssetManager, ReadonlyDescriptorIdentity, ReadonlySingleKey, ReadonlyWallet, ReadonlyWalletError, RestDelegateProvider, RestDelegatorProvider, SeedIdentity, ServiceWorkerReadonlyWallet, ServiceWorkerTimeoutError, ServiceWorkerWallet, SingleKey, TxTree, TxType, TxWeightEstimator, UnilateralExit, Unroll, VtxoManager, Wallet2 as Wallet, WalletMessageHandler, WalletNotInitializedError, WalletRepositoryImpl, WsElectrumChainSource, assertIntentIdUnique, boardingResolver, buildForfeitTx, buildOffchainTx, classifyAgainstSignerSet, classifyContractSigner, closeDatabase, combineTapscriptSigs, contractFromArkContract, contractFromArkContractWithAddress, createDefaultActivityRegistry, createExitChainResolver, decodeArkContract, deserializeAssets, deserializeExitPackage, deserializeUtxo, deserializeVtxo, encodeArkContract, extendVirtualCoinForContract, getMigrationStatus, getRandomId, hasBoardingTxExpired, intentMatchesFilter, intentPageBounds, isArkContract, isBatchSignable, isCooperativelyMigratable, isDiscoverable, isExpired, isRecoverable, isRetryableProviderError, isSpendable, isSubdust, isTerminalIntentState, isValidArkAddress, isVtxoExpiringSoon, isVtxoForScript, mergeChainedTxType, migrateWalletRepository, openDatabase, requiresMigration, rollbackMigration, saveVtxosForContract, scriptFromArkAddress, serializeAssets, serializeExitPackage, serializeUtxo, serializeVtxo, setupServiceWorker, signerSetFromInfo, toXOnlySignerHex, validateConnectorsTxGraph, validateVtxoTxGraph, verifyTapscriptSignatures, waitForIncomingFunds, warnAndFilterVtxosForScript };
18434
- //# sourceMappingURL=chunk-TGRJ5WAX.js.map
18435
- //# sourceMappingURL=chunk-TGRJ5WAX.js.map
19415
+ export { ARKADE_MAGIC, ActivityRegistry, ArkNote, AssetManager, BIP322, Batch, ChainedTxType, ContractManager, ContractRepositoryImpl, ContractWatcher, DB_VERSION, DEFAULT_MESSAGE_TIMEOUTS, DelegateManagerImpl, DelegateNotConfiguredError, DelegatorManagerImpl, DelegatorNotConfiguredError, DescriptorSigningProviderMissingError, DustChangeError, ELECTRUM_TCP_HOST, ELECTRUM_WS_URL, ESPLORA_URL, ElectrumOnchainProvider, EmulatorPacket, EsploraProvider, Estimator, Extension, ExtensionNotFoundError, HDDescriptorProvider, INTENT_TERMINAL_STATES, InMemoryContractRepository, InMemoryIntentRepository, InMemoryVirtualTxRepository, InMemoryWalletRepository, IndexedDBContractRepository, IndexedDBIntentRepository, IndexedDBVirtualTxRepository, IndexedDBWalletRepository, MESSAGE_BUS_INITIALIZING, MESSAGE_BUS_NOT_INITIALIZED, MIGRATION_KEY, MessageBus, MessageBusInitializingError, MessageBusNotInitializedError, MissingSigningDescriptorError, MnemonicIdentity, OnchainWallet, P2A, Ramps, ReadonlyAssetManager, ReadonlyDescriptorIdentity, ReadonlySingleKey, ReadonlyWallet, ReadonlyWalletError, RestDelegateProvider, RestDelegatorProvider, RestEmulatorProvider, SeedIdentity, ServiceWorkerReadonlyWallet, ServiceWorkerTimeoutError, ServiceWorkerWallet, SingleKey, TxTree, TxType, TxWeightEstimator, UnilateralExit, UnknownPacket, Unroll, VtxoManager, Wallet2 as Wallet, WalletMessageHandler, WalletNotInitializedError, WalletRepositoryImpl, WsElectrumChainSource, arkade_exports, assertIntentIdUnique, boardingResolver, buildForfeitTx, buildOffchainTx, classifyAgainstSignerSet, classifyContractSigner, closeDatabase, combineTapscriptSigs, contractFromArkContract, contractFromArkContractWithAddress, createAssetPacket, createDefaultActivityRegistry, createExitChainResolver, decodeArkContract, deserializeAssets, deserializeExitPackage, deserializeUtxo, deserializeVtxo, encodeArkContract, extendVirtualCoinForContract, getMigrationStatus, getRandomId, hasBoardingTxExpired, intentMatchesFilter, intentPageBounds, isArkContract, isBatchSignable, isCooperativelyMigratable, isDiscoverable, isExpired, isRecoverable, isRetryableProviderError, isSpendable, isSubdust, isTerminalIntentState, isValidArkAddress, isVtxoExpiringSoon, isVtxoForScript, mergeChainedTxType, migrateWalletRepository, openDatabase, requiresMigration, rollbackMigration, saveVtxosForContract, scriptFromArkAddress, selectCoinsWithAsset, selectVirtualCoins, serializeAssets, serializeExitPackage, serializeUtxo, serializeVtxo, setupServiceWorker, signerSetFromInfo, toXOnlySignerHex, validateConnectorsTxGraph, validateVtxoTxGraph, verifyTapscriptSignatures, waitForIncomingFunds, warnAndFilterVtxosForScript };
19416
+ //# sourceMappingURL=chunk-OCS5S3L3.js.map
19417
+ //# sourceMappingURL=chunk-OCS5S3L3.js.map