@arkade-os/sdk 0.4.76 → 0.4.78

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 (87) 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 +3 -5
  6. package/dist/adapters/indexedDB.cjs.map +1 -1
  7. package/dist/adapters/indexedDB.d.cts +1 -1
  8. package/dist/adapters/indexedDB.d.ts +1 -1
  9. package/dist/adapters/indexedDB.js +3 -5
  10. package/dist/adapters/indexedDB.js.map +1 -1
  11. package/dist/{asyncStorageTaskQueue-D7BU-BdP.d.cts → asyncStorageTaskQueue-DiXxP-nQ.d.cts} +2 -2
  12. package/dist/{asyncStorageTaskQueue-DrKbfleJ.d.ts → asyncStorageTaskQueue-YW2t31_o.d.ts} +2 -2
  13. package/dist/{chunk-F6YOCYWU.js → chunk-7KIGEOYT.js} +29 -12
  14. package/dist/chunk-7KIGEOYT.js.map +1 -0
  15. package/dist/{chunk-54W6QBXL.js → chunk-CTFYT24G.js} +3 -3
  16. package/dist/{chunk-54W6QBXL.js.map → chunk-CTFYT24G.js.map} +1 -1
  17. package/dist/{chunk-2KFXM3N5.cjs → chunk-DFWLP3MD.cjs} +17 -17
  18. package/dist/{chunk-2KFXM3N5.cjs.map → chunk-DFWLP3MD.cjs.map} +1 -1
  19. package/dist/{chunk-T327OEMV.cjs → chunk-IAM5IEVT.cjs} +17 -17
  20. package/dist/{chunk-T327OEMV.cjs.map → chunk-IAM5IEVT.cjs.map} +1 -1
  21. package/dist/{chunk-LQM6RHNT.cjs → chunk-KEEZSFLI.cjs} +10 -10
  22. package/dist/{chunk-LQM6RHNT.cjs.map → chunk-KEEZSFLI.cjs.map} +1 -1
  23. package/dist/{chunk-WMHELOHH.js → chunk-LKAASQTL.js} +604 -138
  24. package/dist/chunk-LKAASQTL.js.map +1 -0
  25. package/dist/{chunk-MUBPGYUE.js → chunk-OUHUC26M.js} +4 -4
  26. package/dist/{chunk-MUBPGYUE.js.map → chunk-OUHUC26M.js.map} +1 -1
  27. package/dist/{chunk-O2BIWLZC.js → chunk-P766MOPU.js} +4 -4
  28. package/dist/{chunk-O2BIWLZC.js.map → chunk-P766MOPU.js.map} +1 -1
  29. package/dist/{chunk-P53OHGXY.cjs → chunk-PWIESIO6.cjs} +30 -11
  30. package/dist/chunk-PWIESIO6.cjs.map +1 -0
  31. package/dist/{chunk-LHSVPDFJ.cjs → chunk-VJ7M6WKB.cjs} +1031 -566
  32. package/dist/chunk-VJ7M6WKB.cjs.map +1 -0
  33. package/dist/{chunk-CQNX4BTA.cjs → chunk-Y7BVR6SQ.cjs} +89 -57
  34. package/dist/chunk-Y7BVR6SQ.cjs.map +1 -0
  35. package/dist/{chunk-7UJLIZQU.js → chunk-ZOCJ7E6M.js} +43 -11
  36. package/dist/chunk-ZOCJ7E6M.js.map +1 -0
  37. package/dist/contracts/handlers/index.cjs +9 -9
  38. package/dist/contracts/handlers/index.d.cts +3 -3
  39. package/dist/contracts/handlers/index.d.ts +3 -3
  40. package/dist/contracts/handlers/index.js +2 -2
  41. package/dist/{delegate-CR7msIXK.d.cts → delegate-DaNNUI_B.d.cts} +1 -1
  42. package/dist/{delegate-BAYpqkEx.d.ts → delegate-b2Wiymoj.d.ts} +1 -1
  43. package/dist/{hdWalletCapable-BSF29IjK.d.ts → hdWalletCapable-B2XVTOvu.d.ts} +6 -4
  44. package/dist/{hdWalletCapable-BbZxCUeZ.d.cts → hdWalletCapable-D6PqWpuW.d.cts} +6 -4
  45. package/dist/{index-4NF3srcg.d.cts → index-BlB97fYl.d.cts} +26 -8
  46. package/dist/{index-B_RYn9rg.d.cts → index-CAS4GyN-.d.cts} +36 -9
  47. package/dist/{index-B_RYn9rg.d.ts → index-CAS4GyN-.d.ts} +36 -9
  48. package/dist/{index-Dj2yOhQ2.d.ts → index-CYsKAXog.d.ts} +26 -8
  49. package/dist/index.cjs +307 -303
  50. package/dist/index.d.cts +75 -13
  51. package/dist/index.d.ts +75 -13
  52. package/dist/index.js +4 -4
  53. package/dist/repositories/realm/index.cjs +36 -19
  54. package/dist/repositories/realm/index.cjs.map +1 -1
  55. package/dist/repositories/realm/index.d.cts +6 -3
  56. package/dist/repositories/realm/index.d.ts +6 -3
  57. package/dist/repositories/realm/index.js +22 -5
  58. package/dist/repositories/realm/index.js.map +1 -1
  59. package/dist/repositories/sqlite/index.cjs +42 -18
  60. package/dist/repositories/sqlite/index.cjs.map +1 -1
  61. package/dist/repositories/sqlite/index.d.cts +5 -2
  62. package/dist/repositories/sqlite/index.d.ts +5 -2
  63. package/dist/repositories/sqlite/index.js +29 -5
  64. package/dist/repositories/sqlite/index.js.map +1 -1
  65. package/dist/{taskRunner-TzGUhxP-.d.ts → taskRunner-BJj7xe35.d.ts} +2 -2
  66. package/dist/{taskRunner-DGxde-YU.d.cts → taskRunner-HS2d2oda.d.cts} +2 -2
  67. package/dist/wallet/expo/background.cjs +14 -14
  68. package/dist/wallet/expo/background.d.cts +3 -3
  69. package/dist/wallet/expo/background.d.ts +3 -3
  70. package/dist/wallet/expo/background.js +6 -6
  71. package/dist/wallet/expo/index.cjs +14 -14
  72. package/dist/wallet/expo/index.cjs.map +1 -1
  73. package/dist/wallet/expo/index.d.cts +5 -5
  74. package/dist/wallet/expo/index.d.ts +5 -5
  75. package/dist/wallet/expo/index.js +5 -5
  76. package/dist/wallet/expo/index.js.map +1 -1
  77. package/dist/worker/expo/index.cjs +9 -9
  78. package/dist/worker/expo/index.d.cts +4 -4
  79. package/dist/worker/expo/index.d.ts +4 -4
  80. package/dist/worker/expo/index.js +5 -5
  81. package/package.json +1 -1
  82. package/dist/chunk-7UJLIZQU.js.map +0 -1
  83. package/dist/chunk-CQNX4BTA.cjs.map +0 -1
  84. package/dist/chunk-F6YOCYWU.js.map +0 -1
  85. package/dist/chunk-LHSVPDFJ.cjs.map +0 -1
  86. package/dist/chunk-P53OHGXY.cjs.map +0 -1
  87. package/dist/chunk-WMHELOHH.js.map +0 -1
@@ -1,6 +1,6 @@
1
- import { AssetId, AssetGroup, AssetInput, AssetOutput, Metadata, Packet, BufferReader, BufferWriter, AssetRef, RestIndexerProvider } from './chunk-54W6QBXL.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, toXOnly, inputName, contractHandlers, isMainnetDescriptor, descriptorIsOurs, deriveDescriptorLeafPubKey, deriveDescriptorLeafCompressedPubKey, parseHDDescriptor, identityDescriptor, DefaultVtxo, speculativeReceiveMetadata, DelegateVtxo, WALLET_RECEIVE_SOURCE, BoardingContractHandler } from './chunk-7UJLIZQU.js';
3
- import { VtxoScript, Transaction, isRecoverable, CSVMultisigTapscript, networks, getNetwork, DEFAULT_NETWORK, resolveEmulatorPubkey, hasTerminalSpend, getNormalizedVtxos, setArkPsbtField, PrevArkTxField, ConditionWitness, craftToSpendTx, ArkAddress, isVirtualCoin, getArkPsbtFields, CosignerPublicKey, ServerResponseMismatchError, OP_RETURN_EMPTY_PKSCRIPT, canSweepOnchain, sequenceToTimelock, decodeTapscript, scriptFromTapLeafScript, CLTVMultisigTapscript, VtxoTaprootTree, timelockToSequence, assertAllowedSighashTypes, baseFetch, Intent, rateGate, DEFAULT_NETWORK_NAME, formatSighash, normalizeVtxo, getSequence, toBatchExpiry, maybeArkError, isArkError, ArkErrorName, toOffchainInputFeeParams, canRecoverOnchain, MultisigTapscript, isEventSourceError, isEventSourceUnavailableError, getAllNormalizedVtxos, isPastExpiry, isRetryableProviderError, DEFAULT_PAGE_SIZE, RestArkProvider, toVirtualStatus, ArkError, TapTreeCoder, resolveTimeHeight, canSpendOffchain, parseLegacyExpiry, isExpired, PrevoutTxField, fetchVtxoCreatedAtByTxid, DEFAULT_ARKADE_SERVER_URL, ProviderUnavailableError } from './chunk-F6YOCYWU.js';
1
+ import { AssetId, AssetGroup, AssetInput, AssetOutput, Metadata, Packet, BufferReader, BufferWriter, AssetRef, RestIndexerProvider } from './chunk-CTFYT24G.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, toXOnly, inputName, contractHandlers, isMainnetDescriptor, descriptorIsOurs, deriveDescriptorLeafPubKey, deriveDescriptorLeafCompressedPubKey, parseHDDescriptor, identityDescriptor, DefaultVtxo, speculativeReceiveMetadata, DelegateVtxo, WALLET_RECEIVE_SOURCE, BoardingContractHandler } from './chunk-ZOCJ7E6M.js';
3
+ import { VtxoScript, Transaction, requiresForfeit, CSVMultisigTapscript, networks, getNetwork, DEFAULT_NETWORK, resolveEmulatorPubkey, isVtxoSpent, getNormalizedVtxos, setArkPsbtField, PrevArkTxField, ConditionWitness, craftToSpendTx, ArkAddress, isVirtualCoin, getArkPsbtFields, CosignerPublicKey, ServerResponseMismatchError, OP_RETURN_EMPTY_PKSCRIPT, canSweepOnchain, sequenceToTimelock, timelockToSequence, decodeTapscript, scriptFromTapLeafScript, CLTVMultisigTapscript, VtxoTaprootTree, assertAllowedSighashTypes, baseFetch, Intent, rateGate, DEFAULT_NETWORK_NAME, formatSighash, normalizeVtxo, getSequence, toBatchExpiry, maybeArkError, isArkError, ArkErrorName, toOffchainInputFeeParams, canRecoverOnchain, MultisigTapscript, isEventSourceError, isEventSourceUnavailableError, getAllNormalizedVtxos, isPastExpiry, isRetryableProviderError, DEFAULT_PAGE_SIZE, RestArkProvider, toVirtualStatus, ArkError, TapTreeCoder, resolveTimeHeight, canSpendOffchain, parseLegacyExpiry, isRecoverable, isExpired, PrevoutTxField, fetchVtxoCreatedAtByTxid, DEFAULT_ARKADE_SERVER_URL, ProviderUnavailableError } from './chunk-7KIGEOYT.js';
4
4
  import { __export } from './chunk-NSBPE2FW.js';
5
5
  import { sha256 as sha256$1, equalBytes, hash160, sha256x2, concatBytes, randomPrivateKeyBytes, pubECDSA, pubSchnorr } from '@scure/btc-signer/utils.js';
6
6
  import { SigHash, Script, OP, OutScript, Address, RawWitness, p2tr, p2wpkh, TaprootControlBlock, DEFAULT_SEQUENCE, Transaction as Transaction$2 } from '@scure/btc-signer';
@@ -1334,7 +1334,7 @@ async function provisionClaimSecret(wallet, opts = {}) {
1334
1334
  const descriptor = await provisionDescriptor(wallet);
1335
1335
  const signer = await contractSigner(wallet, descriptor);
1336
1336
  const pubkey = await signer.xOnlyPublicKey();
1337
- const claim = async () => {
1337
+ const claim2 = async () => {
1338
1338
  if (opts.preimage) return { preimage: opts.preimage, mustPersistPreimage: true };
1339
1339
  if (isPerArtifactDescriptor(descriptor)) {
1340
1340
  return {
@@ -1356,7 +1356,7 @@ async function provisionClaimSecret(wallet, opts = {}) {
1356
1356
  return { preimage: randomBytes(32), mustPersistPreimage: true };
1357
1357
  }
1358
1358
  };
1359
- const { preimage, preimageSalt, mustPersistPreimage } = await claim();
1359
+ const { preimage, preimageSalt, mustPersistPreimage } = await claim2();
1360
1360
  return {
1361
1361
  descriptor,
1362
1362
  pubkey,
@@ -4128,7 +4128,7 @@ function selectPendingRecoveryOutpoints(contractsWithVtxos, signerSet, nowSecond
4128
4128
  continue;
4129
4129
  }
4130
4130
  for (const v of vtxos) {
4131
- if (!hasTerminalSpend(v) && !v.isSwept && !v.isUnrolled) {
4131
+ if (!isVtxoSpent(v) && !v.isSwept && !v.isUnrolled) {
4132
4132
  out.add(`${v.txid}:${v.vout}`);
4133
4133
  }
4134
4134
  }
@@ -4655,7 +4655,10 @@ var VtxoManager = class _VtxoManager {
4655
4655
  if (this.settlementConfig === false && thresholdMs === void 0) {
4656
4656
  return [];
4657
4657
  }
4658
- const vtxos = await this.wallet.getSpendableVtxos({ withRecoverable: true });
4658
+ const vtxos = await this.wallet.getSpendableVtxos({
4659
+ withRecoverable: true,
4660
+ genericallySpendableOnly: true
4661
+ });
4659
4662
  let threshold;
4660
4663
  if (thresholdMs !== void 0) {
4661
4664
  threshold = thresholdMs;
@@ -5152,8 +5155,8 @@ var VtxoManager = class _VtxoManager {
5152
5155
  const cls = classifyAgainstSignerSet(serverPubKey, signerSet, nowSeconds);
5153
5156
  if (cls.status === "CURRENT") continue;
5154
5157
  const live = vtxos.filter((v) => !v.isUnrolled);
5155
- const recoverable = live.filter((v) => v.isSwept && !hasTerminalSpend(v));
5156
- const spendable = live.filter((v) => !hasTerminalSpend(v) && !v.isSwept);
5158
+ const recoverable = live.filter((v) => v.isSwept && !isVtxoSpent(v));
5159
+ const spendable = live.filter((v) => !isVtxoSpent(v) && !v.isSwept);
5157
5160
  const value = spendable.reduce((sum, v) => sum + v.value, 0);
5158
5161
  let recoverableCount = 0;
5159
5162
  let recoverableValue = 0;
@@ -5954,8 +5957,8 @@ function noteTapscript(preimageHash) {
5954
5957
  return Script.encode(["SHA256", preimageHash, "EQUAL"]);
5955
5958
  }
5956
5959
  var ArkadeCash = class _ArkadeCash {
5957
- constructor(privateKey, serverPubKey, csvTimelock, hrp = _ArkadeCash.DefaultHRP) {
5958
- this.csvTimelock = csvTimelock;
5960
+ constructor(privateKey, serverPubKey, csvTimelock2, hrp = _ArkadeCash.DefaultHRP) {
5961
+ this.csvTimelock = csvTimelock2;
5959
5962
  this.hrp = hrp;
5960
5963
  if (privateKey.length !== 32) {
5961
5964
  throw new Error(
@@ -5995,8 +5998,8 @@ var ArkadeCash = class _ArkadeCash {
5995
5998
  get publicKey() {
5996
5999
  return this._publicKey.slice();
5997
6000
  }
5998
- static generate(serverPubKey, csvTimelock, hrp) {
5999
- return new _ArkadeCash(randomPrivateKeyBytes(), serverPubKey, csvTimelock, hrp);
6001
+ static generate(serverPubKey, csvTimelock2, hrp) {
6002
+ return new _ArkadeCash(randomPrivateKeyBytes(), serverPubKey, csvTimelock2, hrp);
6000
6003
  }
6001
6004
  static fromString(encoded) {
6002
6005
  const decoded = bech32m.decodeUnsafe(encoded.trim().toLowerCase(), 1023);
@@ -6016,8 +6019,8 @@ var ArkadeCash = class _ArkadeCash {
6016
6019
  const privateKey = data.slice(1, 33);
6017
6020
  const serverPubKey = data.slice(33, 65);
6018
6021
  const sequence = new DataView(data.buffer, data.byteOffset + 65, 4).getUint32(0, false);
6019
- const csvTimelock = sequenceToTimelock(sequence);
6020
- return new _ArkadeCash(privateKey, serverPubKey, csvTimelock, decoded.prefix);
6022
+ const csvTimelock2 = sequenceToTimelock(sequence);
6023
+ return new _ArkadeCash(privateKey, serverPubKey, csvTimelock2, decoded.prefix);
6021
6024
  }
6022
6025
  toString() {
6023
6026
  const data = new Uint8Array(_ArkadeCash.PayloadLength);
@@ -7319,7 +7322,8 @@ var AssetManager = class extends ReadonlyAssetManager {
7319
7322
  }
7320
7323
  const metadata = castMetadata(params.metadata);
7321
7324
  const virtualCoins = await this.wallet.getSpendableVtxos({
7322
- withRecoverable: false
7325
+ withRecoverable: false,
7326
+ genericallySpendableOnly: true
7323
7327
  });
7324
7328
  const controlAssetRef = params.controlAssetId ? AssetRef.fromId(AssetId.fromString(params.controlAssetId)) : null;
7325
7329
  const coinSelection = selectVirtualCoins(virtualCoins, Number(this.wallet.dustAmount));
@@ -7408,7 +7412,8 @@ var AssetManager = class extends ReadonlyAssetManager {
7408
7412
  throw new Error(`Asset ${params.assetId} is not reissuable`);
7409
7413
  }
7410
7414
  const virtualCoins = await this.wallet.getSpendableVtxos({
7411
- withRecoverable: false
7415
+ withRecoverable: false,
7416
+ genericallySpendableOnly: true
7412
7417
  });
7413
7418
  const assetChanges = /* @__PURE__ */ new Map();
7414
7419
  const { selected: controlCoins } = selectCoinsWithAsset(virtualCoins, controlAssetId, 1n);
@@ -7514,7 +7519,8 @@ var AssetManager = class extends ReadonlyAssetManager {
7514
7519
  throw new Error(`Burn amount must be greater than 0, got ${params.amount}`);
7515
7520
  }
7516
7521
  const virtualCoins = await this.wallet.getSpendableVtxos({
7517
- withRecoverable: false
7522
+ withRecoverable: false,
7523
+ genericallySpendableOnly: true
7518
7524
  });
7519
7525
  const assetChanges = /* @__PURE__ */ new Map();
7520
7526
  const { selected: assetCoins } = selectCoinsWithAsset(
@@ -7797,7 +7803,7 @@ async function delegate(identity, delegateProvider, arkInfo, delegateInfo, vtxos
7797
7803
  Address(getNetwork(network)).decode(forfeitAddress)
7798
7804
  );
7799
7805
  const forfeits = await Promise.all(
7800
- vtxos.filter((v) => !canRecoverOnchain(v, { timestamp: /* @__PURE__ */ new Date() })).map(async (coin) => {
7806
+ vtxos.filter((v) => requiresForfeit(v)).map(async (coin) => {
7801
7807
  const forfeit = await makeDelegateForfeitTx(
7802
7808
  coin,
7803
7809
  dust,
@@ -8203,7 +8209,7 @@ var STORE_INTENTS = "intents";
8203
8209
  var STORE_VIRTUAL_TXS = "virtualTxs";
8204
8210
  var STORE_VTXO_BRANCHES = "vtxoBranches";
8205
8211
  var LEGACY_STORE_CONTRACT_COLLECTIONS = "contractsCollections";
8206
- var DB_VERSION = 3;
8212
+ var DB_VERSION = 4;
8207
8213
  var INTENT_DB_VERSION = 5;
8208
8214
  function initDatabase(db, oldVersion, transaction) {
8209
8215
  if (!db.objectStoreNames.contains(STORE_VTXOS)) {
@@ -8266,6 +8272,7 @@ function initDatabase(db, oldVersion, transaction) {
8266
8272
  unique: false
8267
8273
  });
8268
8274
  }
8275
+ vtxosStore.createIndex("scriptUnspent", ["script", "unspent"], { unique: false });
8269
8276
  }
8270
8277
  if (!db.objectStoreNames.contains(STORE_UTXOS)) {
8271
8278
  const utxosStore = db.createObjectStore(STORE_UTXOS, {
@@ -8348,14 +8355,18 @@ function initDatabase(db, oldVersion, transaction) {
8348
8355
  keyPath: "key"
8349
8356
  });
8350
8357
  }
8351
- if (oldVersion >= 1 && oldVersion < 3 && transaction) {
8358
+ if (oldVersion >= 1 && oldVersion < 4 && transaction) {
8352
8359
  const vtxosStore = transaction.objectStore(STORE_VTXOS);
8353
8360
  if (!vtxosStore.indexNames.contains("script")) {
8354
8361
  vtxosStore.createIndex("script", "script", { unique: false });
8355
8362
  }
8356
- backfillVtxoScripts(transaction);
8363
+ if (!vtxosStore.indexNames.contains("scriptUnspent")) {
8364
+ vtxosStore.createIndex("scriptUnspent", ["script", "unspent"], { unique: false });
8365
+ }
8366
+ backfillVtxoIndexFields(transaction);
8357
8367
  }
8358
8368
  }
8369
+ var unspentFlag = (vtxo) => isVtxoSpent(vtxo) ? {} : { unspent: 1 };
8359
8370
  function initDatabaseWithIntents(db, oldVersion, transaction) {
8360
8371
  initDatabase(db, oldVersion, transaction);
8361
8372
  if (!db.objectStoreNames.contains(STORE_INTENTS)) {
@@ -8405,19 +8416,28 @@ function dedupeIntentIds(store, onComplete) {
8405
8416
  cursor.continue();
8406
8417
  };
8407
8418
  }
8408
- function backfillVtxoScripts(transaction) {
8419
+ function backfillVtxoIndexFields(transaction) {
8409
8420
  const store = transaction.objectStore(STORE_VTXOS);
8410
- const cursorRequest = store.openCursor();
8411
- cursorRequest.onsuccess = () => {
8412
- const cursor = cursorRequest.result;
8413
- if (!cursor) return;
8414
- const value = cursor.value;
8415
- if (!value.script) {
8416
- value.script = scriptFromArkAddress(value.address);
8417
- cursor.update(value);
8418
- }
8419
- cursor.continue();
8421
+ const pageSize = 1e3;
8422
+ const readPage = (after) => {
8423
+ const request = store.getAll(after && IDBKeyRange.lowerBound(after, true), pageSize);
8424
+ request.onsuccess = () => {
8425
+ const rows = request.result;
8426
+ for (const value of rows) {
8427
+ const next = {
8428
+ ...value,
8429
+ script: value.script || scriptFromArkAddress(value.address),
8430
+ ...unspentFlag(value)
8431
+ };
8432
+ if (next.script !== value.script || next.unspent !== value.unspent) {
8433
+ store.put(next);
8434
+ }
8435
+ }
8436
+ const last = rows.at(-1);
8437
+ if (rows.length === pageSize && last) readPage([last.address, last.txid, last.vout]);
8438
+ };
8420
8439
  };
8440
+ readPage();
8421
8441
  }
8422
8442
 
8423
8443
  // src/worker/browser/utils.ts
@@ -8683,9 +8703,10 @@ function validateVtxosForScript(vtxos, script, context) {
8683
8703
  `${context}: refusing to persist ${mismatches.length} VTXO(s) whose script does not match ${script}: ${detail}`
8684
8704
  );
8685
8705
  }
8686
- async function getVtxosForContract(repo, contract) {
8687
- const vtxos = repo.getVtxosForScript ? await repo.getVtxosForScript(contract.script) : filterVtxosForScript(await repo.getVtxos(contract.address), contract.script);
8688
- return vtxos.map(normalizeVtxo);
8706
+ async function getVtxosForContract(repo, contract, options) {
8707
+ const vtxos = options?.unspentOnly && repo.getVtxosForScripts ? await repo.getVtxosForScripts([contract.script], options) : repo.getVtxosForScript ? await repo.getVtxosForScript(contract.script) : filterVtxosForScript(await repo.getVtxos(contract.address), contract.script);
8708
+ const normalized = vtxos.map(normalizeVtxo);
8709
+ return options?.unspentOnly ? normalized.filter((vtxo) => !isVtxoSpent(vtxo)) : normalized;
8689
8710
  }
8690
8711
  function hasRecordedSpend(vtxo) {
8691
8712
  return vtxo.isSpent === true && (!!vtxo.spentBy || !!vtxo.arkTxId || !!vtxo.settledBy);
@@ -8776,15 +8797,21 @@ var InMemoryWalletRepository = class {
8776
8797
  this.vtxosByAddress.delete(address);
8777
8798
  }
8778
8799
  async getVtxosForScript(script) {
8779
- const allMatches = [];
8800
+ return this.getVtxosForScripts([script]);
8801
+ }
8802
+ async getVtxosForScripts(scripts, options) {
8803
+ if (scripts.length === 0) return [];
8804
+ const selected = new Set(scripts);
8805
+ const byOutpoint = /* @__PURE__ */ new Map();
8780
8806
  for (const bucket of this.vtxosByAddress.values()) {
8781
8807
  for (const vtxo of bucket) {
8782
- if (isVtxoForScript(vtxo, script)) {
8783
- allMatches.push(vtxo);
8808
+ if (vtxo.script && selected.has(vtxo.script)) {
8809
+ byOutpoint.set(`${vtxo.script}:${vtxo.txid}:${vtxo.vout}`, vtxo);
8784
8810
  }
8785
8811
  }
8786
8812
  }
8787
- return mergeByKey([], allMatches, (item) => `${item.txid}:${item.vout}`);
8813
+ const rows = [...byOutpoint.values()];
8814
+ return options?.unspentOnly ? rows.filter((vtxo) => !isVtxoSpent(vtxo)) : rows;
8788
8815
  }
8789
8816
  async saveVtxosForScript(key, vtxos) {
8790
8817
  if (!key.address) {
@@ -9565,7 +9592,7 @@ var ContractWatcher = class {
9565
9592
  }
9566
9593
  if (target === byWatchedScript && eventType === "vtxo_received") {
9567
9594
  const n = normalizeVtxo(vtxo);
9568
- if (hasTerminalSpend(n) || n.isSwept || isPastExpiry(n, now)) continue;
9595
+ if (isVtxoSpent(n) || n.isSwept || isPastExpiry(n, now)) continue;
9569
9596
  }
9570
9597
  let bucket = target.get(vtxo.script);
9571
9598
  if (!bucket) {
@@ -9664,7 +9691,7 @@ var NON_TERMINAL_INTENT_STATES = ALL_INTENT_STATES.filter(
9664
9691
  (s) => !isTerminalIntentState(s)
9665
9692
  );
9666
9693
  function isConsumed(vtxo) {
9667
- return vtxo !== void 0 && hasTerminalSpend(vtxo);
9694
+ return vtxo !== void 0 && isVtxoSpent(vtxo);
9668
9695
  }
9669
9696
  async function classifyIntent(intent, vtxosByOutpoint, now) {
9670
9697
  if (isTerminalIntentState(intent.state)) return void 0;
@@ -10483,9 +10510,15 @@ var ContractManager = class _ContractManager {
10483
10510
  const dbFilter = this.buildContractsDbFilter(filter ?? {});
10484
10511
  return await this.config.contractRepository.getContracts(dbFilter);
10485
10512
  }
10486
- async getContractsWithVtxos(filter, pageSize) {
10513
+ async getContractsWithVtxos(filter, pageSize, options) {
10514
+ if (options?.maxSyncAgeMs !== void 0 && (!Number.isSafeInteger(options.maxSyncAgeMs) || options.maxSyncAgeMs < 0)) {
10515
+ throw new Error("maxSyncAgeMs must be a non-negative safe integer");
10516
+ }
10487
10517
  const contracts = await this.getContracts(filter);
10488
- if (this.syncedWithin(contracts, this.config.vtxoSyncMaxAgeMs ?? 0)) {
10518
+ if (this.syncedWithin(
10519
+ contracts,
10520
+ options?.maxSyncAgeMs ?? (options?.requireSynced ? 0 : this.config.vtxoSyncMaxAgeMs ?? 0)
10521
+ )) {
10489
10522
  await this.demoteFundedAwaitingContracts(contracts);
10490
10523
  } else {
10491
10524
  try {
@@ -10494,12 +10527,23 @@ var ContractManager = class _ContractManager {
10494
10527
  } catch (err) {
10495
10528
  if (!isRetryableProviderError(err)) throw err;
10496
10529
  this.markSyncDegraded(err);
10530
+ if (options?.requireSynced) {
10531
+ throw new Error("Spendable VTXO read requires an online contract sync", {
10532
+ cause: err
10533
+ });
10534
+ }
10497
10535
  }
10498
10536
  }
10499
- const vtxos = await this.getVtxosForContracts(contracts);
10537
+ const vtxos = await this.getVtxosForContracts(contracts, options);
10538
+ const vtxosByScript = /* @__PURE__ */ new Map();
10539
+ for (const vtxo of vtxos) {
10540
+ const group = vtxosByScript.get(vtxo.contractScript) ?? [];
10541
+ group.push(vtxo);
10542
+ vtxosByScript.set(vtxo.contractScript, group);
10543
+ }
10500
10544
  return contracts.map((contract) => ({
10501
10545
  contract,
10502
- vtxos: vtxos.filter((vtxo) => vtxo.contractScript === contract.script)
10546
+ vtxos: vtxosByScript.get(contract.script)?.slice() ?? []
10503
10547
  }));
10504
10548
  }
10505
10549
  async annotateVtxos(vtxos, tapscripts) {
@@ -10929,20 +10973,28 @@ var ContractManager = class _ContractManager {
10929
10973
  }
10930
10974
  this.emitEvent(event);
10931
10975
  }
10932
- async getVtxosForContracts(contracts) {
10933
- const res = await Promise.all(
10934
- contracts.map(
10935
- (contract) => getVtxosForContract(this.config.walletRepository, contract).then(
10936
- (vtxos) => vtxos.map(
10937
- (vtxo) => ({
10938
- ...vtxo,
10939
- contractScript: contract.script
10940
- })
10976
+ async getVtxosForContracts(contracts, options) {
10977
+ if (contracts.length === 0) return [];
10978
+ let rows;
10979
+ if (this.config.walletRepository.getVtxosForScripts) {
10980
+ const byScript = new Set(contracts.map((contract) => contract.script));
10981
+ rows = (await this.config.walletRepository.getVtxosForScripts([...byScript], options)).filter((vtxo) => vtxo.script !== void 0 && byScript.has(vtxo.script)).map((vtxo) => ({ ...normalizeVtxo(vtxo), contractScript: vtxo.script }));
10982
+ } else {
10983
+ const res = await Promise.all(
10984
+ contracts.map(
10985
+ (contract) => getVtxosForContract(this.config.walletRepository, contract).then(
10986
+ (vtxos) => vtxos.map(
10987
+ (vtxo) => ({
10988
+ ...vtxo,
10989
+ contractScript: contract.script
10990
+ })
10991
+ )
10941
10992
  )
10942
10993
  )
10943
- )
10944
- );
10945
- return res.flat();
10994
+ );
10995
+ rows = res.flat();
10996
+ }
10997
+ return options?.unspentOnly ? rows.filter((vtxo) => !isVtxoSpent(vtxo)) : rows;
10946
10998
  }
10947
10999
  /**
10948
11000
  * Sync virtual outputs for the given contracts against the indexer.
@@ -11443,7 +11495,7 @@ var IndexedDBWalletRepository = class {
11443
11495
  const store = transaction.objectStore(STORE_VTXOS);
11444
11496
  for (const vtxo of vtxos) {
11445
11497
  const serialized = serializeVtxo(vtxo);
11446
- store.put({ address, ...serialized });
11498
+ store.put({ address, ...serialized, ...unspentFlag(vtxo) });
11447
11499
  }
11448
11500
  await awaitTransaction(transaction);
11449
11501
  } catch (error) {
@@ -11463,31 +11515,52 @@ var IndexedDBWalletRepository = class {
11463
11515
  }
11464
11516
  }
11465
11517
  async getVtxosForScript(script) {
11518
+ return this.getVtxosForScripts([script]);
11519
+ }
11520
+ async getVtxosForScripts(scripts, options) {
11521
+ const unique = [...new Set(scripts)];
11522
+ if (unique.length === 0) return [];
11466
11523
  try {
11467
11524
  const db = await this.getDB();
11468
- const store = db.transaction([STORE_VTXOS], "readonly").objectStore(STORE_VTXOS);
11469
- const results = await promisifyRequest(
11470
- store.index("script").getAll(script)
11471
- );
11472
- const matching = (results || []).filter((r) => r.script === script);
11525
+ const rows = options?.unspentOnly ? await getAllByIndexValues(
11526
+ this.vtxoStore(db),
11527
+ "scriptUnspent",
11528
+ unique.map((script) => [script, 1])
11529
+ ) : await getAllByIndexValues(this.vtxoStore(db), "script", unique);
11530
+ const selected = new Set(unique);
11473
11531
  const byOutpoint = /* @__PURE__ */ new Map();
11474
- for (const row of matching) {
11475
- const outpoint = `${row.txid}:${row.vout}`;
11476
- const existing = byOutpoint.get(outpoint);
11477
- if (!existing) {
11478
- byOutpoint.set(outpoint, row);
11532
+ for (const row of rows) {
11533
+ if (!selected.has(row.script)) continue;
11534
+ const key = `${row.script}:${row.txid}:${row.vout}`;
11535
+ const existing = byOutpoint.get(key);
11536
+ if (!existing || shouldReplaceVtxo(existing, row)) byOutpoint.set(key, row);
11537
+ }
11538
+ if (options?.unspentOnly && byOutpoint.size > 0) {
11539
+ const txids = [...new Set([...byOutpoint.values()].map((row) => row.txid))];
11540
+ const store = this.vtxoStore(db);
11541
+ for (const row of await getAllByIndexValues(store, "txid", txids)) {
11542
+ const key = `${row.script}:${row.txid}:${row.vout}`;
11543
+ const existing = byOutpoint.get(key);
11544
+ if (existing && shouldReplaceVtxo(existing, row)) byOutpoint.set(key, row);
11545
+ }
11546
+ }
11547
+ const result = [];
11548
+ for (const row of byOutpoint.values()) {
11549
+ if (options?.unspentOnly && (row.isSpent || row.spentBy || row.settledBy)) {
11479
11550
  continue;
11480
11551
  }
11481
- if (shouldReplaceVtxo(existing, row)) {
11482
- byOutpoint.set(outpoint, row);
11483
- }
11552
+ const vtxo = deserializeVtxoWithBackfill(row);
11553
+ if (!options?.unspentOnly || !isVtxoSpent(vtxo)) result.push(vtxo);
11484
11554
  }
11485
- return Array.from(byOutpoint.values()).map(deserializeVtxoWithBackfill);
11555
+ return result;
11486
11556
  } catch (error) {
11487
- console.error(`Failed to get VTXOs for script ${script}:`, error);
11557
+ console.error("Failed to get VTXOs for scripts:", error);
11488
11558
  throw error;
11489
11559
  }
11490
11560
  }
11561
+ vtxoStore(db) {
11562
+ return db.transaction([STORE_VTXOS], "readonly").objectStore(STORE_VTXOS);
11563
+ }
11491
11564
  async saveVtxosForScript(key, vtxos) {
11492
11565
  if (!key.address) {
11493
11566
  throw new Error("IndexedDBWalletRepository requires an address");
@@ -11618,7 +11691,7 @@ var IndexedDBWalletRepository = class {
11618
11691
  return this.connection.get();
11619
11692
  }
11620
11693
  };
11621
- function deserializeVtxoWithBackfill(o) {
11694
+ function deserializeVtxoWithBackfill({ unspent: _unspent, ...o }) {
11622
11695
  if (!o.script) {
11623
11696
  o = { ...o, script: scriptFromArkAddress(o.address) };
11624
11697
  }
@@ -12451,7 +12524,7 @@ function buildReceiveContract(current, descriptor, hrp, tagSource) {
12451
12524
  const pubKey = deriveLeafPubkey(descriptor);
12452
12525
  const tapscript = rebuildTapscript(current, pubKey);
12453
12526
  const serverPubKey = tapscript.options.serverPubKey;
12454
- const csvTimelock = timelockToSequence(tapscript.options.csvTimelock).toString();
12527
+ const csvTimelock2 = timelockToSequence(tapscript.options.csvTimelock).toString();
12455
12528
  const base = {
12456
12529
  script: hex.encode(tapscript.pkScript),
12457
12530
  address: tapscript.address(hrp, serverPubKey).encode(),
@@ -12473,7 +12546,7 @@ function buildReceiveContract(current, descriptor, hrp, tagSource) {
12473
12546
  pubKey: hex.encode(pubKey),
12474
12547
  serverPubKey: hex.encode(serverPubKey),
12475
12548
  delegatePubKey: hex.encode(tapscript.options.delegatePubKey),
12476
- csvTimelock
12549
+ csvTimelock: csvTimelock2
12477
12550
  }
12478
12551
  } : {
12479
12552
  ...base,
@@ -12481,7 +12554,7 @@ function buildReceiveContract(current, descriptor, hrp, tagSource) {
12481
12554
  params: {
12482
12555
  pubKey: hex.encode(pubKey),
12483
12556
  serverPubKey: hex.encode(serverPubKey),
12484
- csvTimelock
12557
+ csvTimelock: csvTimelock2
12485
12558
  }
12486
12559
  };
12487
12560
  return { tapscript, params };
@@ -12555,7 +12628,7 @@ function computeOffchainBalance(vtxos, caps) {
12555
12628
  }
12556
12629
  };
12557
12630
  for (const vtxo of vtxos) {
12558
- if (hasTerminalSpend(vtxo)) continue;
12631
+ if (isVtxoSpent(vtxo)) continue;
12559
12632
  addAssets(owned, vtxo);
12560
12633
  if (vtxo.isUnrolled) {
12561
12634
  unrolled += vtxo.value;
@@ -12827,7 +12900,7 @@ function filterSnapshotVtxos(snapshot, filter, pendingSpendOutpoints) {
12827
12900
  if (vtxo.isUnrolled) {
12828
12901
  return !!f.withUnrolled;
12829
12902
  }
12830
- if (hasTerminalSpend(vtxo)) {
12903
+ if (isVtxoSpent(vtxo)) {
12831
12904
  return false;
12832
12905
  }
12833
12906
  if (!f.withRecoverable && canRecoverOnchain(vtxo, now)) {
@@ -12836,6 +12909,34 @@ function filterSnapshotVtxos(snapshot, filter, pendingSpendOutpoints) {
12836
12909
  return true;
12837
12910
  });
12838
12911
  }
12912
+ async function refreshSweptStateOfExpiredInputs(wallet, inputs) {
12913
+ const candidates = inputs.filter(isVirtualCoin).map(normalizeVtxo).filter((v) => !v.isSwept);
12914
+ if (candidates.length === 0) return inputs;
12915
+ const now = candidates.some((v) => v.expiresAtHeight !== void 0) ? await resolveTimeHeight(wallet.onchainProvider) : { timestamp: /* @__PURE__ */ new Date() };
12916
+ const suspects = candidates.filter(
12917
+ (v) => isPastExpiry(v, now) || v.expiresAtHeight !== void 0 && now.height === void 0
12918
+ );
12919
+ if (suspects.length === 0) return inputs;
12920
+ let sweptNow;
12921
+ try {
12922
+ const manager = await wallet.getContractManager();
12923
+ await manager.refreshOutpoints(suspects.map(({ txid, vout }) => ({ txid, vout })));
12924
+ const suspectKeys = new Set(suspects.map((v) => `${v.txid}:${v.vout}`));
12925
+ const fresh = await wallet.getVtxos({ withRecoverable: true });
12926
+ sweptNow = new Set(
12927
+ fresh.filter((v) => v.isSwept && suspectKeys.has(`${v.txid}:${v.vout}`)).map((v) => `${v.txid}:${v.vout}`)
12928
+ );
12929
+ } catch (e) {
12930
+ const message = "could not confirm the swept state of expired settlement inputs; retry the settlement";
12931
+ throw isRetryableProviderError(e) ? new ProviderUnavailableError(message, { cause: e }) : new Error(message, { cause: e });
12932
+ }
12933
+ if (sweptNow.size === 0) return inputs;
12934
+ return inputs.map((input) => {
12935
+ if (!isVirtualCoin(input) || !sweptNow.has(`${input.txid}:${input.vout}`)) return input;
12936
+ const swept = { ...normalizeVtxo(input), isSwept: true };
12937
+ return { ...swept, virtualStatus: toVirtualStatus(swept) };
12938
+ });
12939
+ }
12839
12940
  var PENDING_RECOVERY_REASON = "is past its operator signer's rotation cutoff \u2014 the operator will not co-sign it until it recovers";
12840
12941
  var UNROLLED_REASON = "was unilaterally exited and lives onchain \u2014 `Unroll.completeUnroll` is the only spend that reaches it";
12841
12942
  function omittedOutpoints(before, after) {
@@ -13335,7 +13436,8 @@ var ReadonlyWallet = class _ReadonlyWallet {
13335
13436
  async getBalance() {
13336
13437
  const [boardingUtxos, snapshot] = await Promise.all([
13337
13438
  this.getBoardingUtxos(),
13338
- this.contractSnapshot()
13439
+ // The bucketer drops every spent coin and the gate reads contracts only.
13440
+ this.contractSnapshot(void 0, { unspentOnly: true })
13339
13441
  ]);
13340
13442
  const vtxos = filterSnapshotVtxos(
13341
13443
  snapshot,
@@ -13388,14 +13490,17 @@ var ReadonlyWallet = class _ReadonlyWallet {
13388
13490
  */
13389
13491
  async getVtxos(filter) {
13390
13492
  return filterSnapshotVtxos(
13391
- await this.contractSnapshot(),
13493
+ // Only an unrolled coin is returned spent, so no other read needs spent rows.
13494
+ await this.contractSnapshot(void 0, { unspentOnly: !filter?.withUnrolled }),
13392
13495
  filter,
13393
13496
  this._pendingSpendOutpoints
13394
13497
  );
13395
13498
  }
13396
13499
  /** @inheritdoc */
13397
13500
  async getSpendableVtxos(filter) {
13398
- const snapshot = await this.contractSnapshot();
13501
+ const snapshot = await this.contractSnapshot(filter, {
13502
+ unspentOnly: !filter?.withUnrolled
13503
+ });
13399
13504
  const vtxos = filterSnapshotVtxos(snapshot, filter, this._pendingSpendOutpoints);
13400
13505
  const { gated, pendingRecovery } = this.spendabilityView(snapshot);
13401
13506
  const selectable = vtxos.filter(
@@ -13413,7 +13518,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
13413
13518
  if (!filter?.withUnrolled) {
13414
13519
  logExcludedVtxos(
13415
13520
  "getSpendableVtxos",
13416
- snapshot.flatMap((contract) => contract.vtxos).filter((vtxo) => vtxo.isUnrolled && !hasTerminalSpend(vtxo)),
13521
+ snapshot.flatMap((contract) => contract.vtxos).filter((vtxo) => vtxo.isUnrolled && !isVtxoSpent(vtxo)),
13417
13522
  [() => UNROLLED_REASON]
13418
13523
  );
13419
13524
  }
@@ -13487,9 +13592,25 @@ var ReadonlyWallet = class _ReadonlyWallet {
13487
13592
  * otherwise the gate and the pending-recovery set answer about two different
13488
13593
  * points in time, and each read costs another sync.
13489
13594
  */
13490
- async contractSnapshot() {
13595
+ async contractSnapshot(filter, options) {
13491
13596
  const contractManager = await this.getContractManager();
13492
- return contractManager.getContractsWithVtxos();
13597
+ const scope = filter?.watchedOnly ? { watch: ["watched", "awaiting-funds"] } : void 0;
13598
+ let query = scope;
13599
+ if (filter?.genericallySpendableOnly) {
13600
+ const scripts = (await contractManager.getContracts(scope)).filter(isContractGenericallySpendable).map((contract) => contract.script);
13601
+ if (scripts.length === 0) {
13602
+ if (filter.requireSynced) {
13603
+ throw new Error("No generically spendable contracts to sync");
13604
+ }
13605
+ return [];
13606
+ }
13607
+ query = { ...scope, script: scripts };
13608
+ }
13609
+ return contractManager.getContractsWithVtxos(query, void 0, {
13610
+ maxSyncAgeMs: filter?.maxSyncAgeMs,
13611
+ unspentOnly: options?.unspentOnly,
13612
+ requireSynced: filter?.requireSynced
13613
+ });
13493
13614
  }
13494
13615
  /**
13495
13616
  * The two exclusion sets the gate is made of, derived from one snapshot so
@@ -14050,12 +14171,12 @@ var ReadonlyWallet = class _ReadonlyWallet {
14050
14171
  ];
14051
14172
  const seenBaselineScripts = /* @__PURE__ */ new Set();
14052
14173
  for (const serverPubKey of baselineSigners) {
14053
- for (const csvTimelock of this.walletContractTimelocks) {
14054
- const csvTimelockStr = timelockToSequence(csvTimelock).toString();
14174
+ for (const csvTimelock2 of this.walletContractTimelocks) {
14175
+ const csvTimelockStr = timelockToSequence(csvTimelock2).toString();
14055
14176
  const defaultScript = new DefaultVtxo.Script({
14056
14177
  pubKey: baselinePubkey,
14057
14178
  serverPubKey,
14058
- csvTimelock
14179
+ csvTimelock: csvTimelock2
14059
14180
  });
14060
14181
  const defaultScriptHex = hex.encode(defaultScript.pkScript);
14061
14182
  if (!seenBaselineScripts.has(defaultScriptHex)) {
@@ -14077,7 +14198,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
14077
14198
  pubKey: baselinePubkey,
14078
14199
  serverPubKey,
14079
14200
  delegatePubKey,
14080
- csvTimelock
14201
+ csvTimelock: csvTimelock2
14081
14202
  });
14082
14203
  const delegateScriptHex = hex.encode(delegateScript.pkScript);
14083
14204
  if (seenBaselineScripts.has(delegateScriptHex)) continue;
@@ -14622,7 +14743,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
14622
14743
  ...this._boardingTapscript.options,
14623
14744
  pubKey
14624
14745
  });
14625
- const csvTimelock = tapscript.options.csvTimelock ?? DefaultVtxo.Script.DEFAULT_TIMELOCK;
14746
+ const csvTimelock2 = tapscript.options.csvTimelock ?? DefaultVtxo.Script.DEFAULT_TIMELOCK;
14626
14747
  return {
14627
14748
  tapscript,
14628
14749
  params: {
@@ -14630,7 +14751,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
14630
14751
  params: {
14631
14752
  pubKey: hex.encode(pubKey),
14632
14753
  serverPubKey: hex.encode(tapscript.options.serverPubKey),
14633
- csvTimelock: timelockToSequence(csvTimelock).toString()
14754
+ csvTimelock: timelockToSequence(csvTimelock2).toString()
14634
14755
  },
14635
14756
  script: hex.encode(tapscript.pkScript),
14636
14757
  address: tapscript.address(this.network.hrp, this.arkServerPublicKey).encode(),
@@ -15318,7 +15439,10 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
15318
15439
  filteredBoardingUtxos.push(utxo);
15319
15440
  amount += utxo.value - inputFee.satoshis;
15320
15441
  }
15321
- const vtxos = await this.getSpendableVtxos({ withRecoverable: true });
15442
+ const vtxos = await this.getSpendableVtxos({
15443
+ withRecoverable: true,
15444
+ genericallySpendableOnly: true
15445
+ });
15322
15446
  const filteredVtxos = [];
15323
15447
  for (const vtxo of byValueDescending(vtxos)) {
15324
15448
  if (filteredVtxos.length >= MAX_VTXOS_PER_SETTLEMENT) {
@@ -15363,6 +15487,10 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
15363
15487
  outputs: [output]
15364
15488
  };
15365
15489
  }
15490
+ params = {
15491
+ ...params,
15492
+ inputs: await refreshSweptStateOfExpiredInputs(this, params.inputs)
15493
+ };
15366
15494
  const onchainOutputIndexes = [];
15367
15495
  const outputs = [];
15368
15496
  let hasOffchainOutputs = false;
@@ -15635,7 +15763,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
15635
15763
  }
15636
15764
  continue;
15637
15765
  }
15638
- if (canRecoverOnchain(input, { timestamp: /* @__PURE__ */ new Date() }) || isSubdust({ value: input.value }, this.dustAmount)) {
15766
+ if (!requiresForfeit(input) || isSubdust({ value: input.value }, this.dustAmount)) {
15639
15767
  continue;
15640
15768
  }
15641
15769
  if (connectorsLeaves.length === 0) {
@@ -16231,7 +16359,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
16231
16359
  const spendable = [];
16232
16360
  const unclaimed = [];
16233
16361
  for (const vtxo of vtxos) {
16234
- if (hasTerminalSpend(vtxo)) {
16362
+ if (isVtxoSpent(vtxo)) {
16235
16363
  unclaimed.push(cashReport(vtxo, "already-spent"));
16236
16364
  } else if (vtxo.isUnrolled) {
16237
16365
  unclaimed.push(cashReport(vtxo, "exited"));
@@ -16422,7 +16550,8 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
16422
16550
  let virtualCoins = [];
16423
16551
  if (!selectedVtxos) {
16424
16552
  virtualCoins = await this.getSpendableVtxos({
16425
- withRecoverable: false
16553
+ withRecoverable: false,
16554
+ genericallySpendableOnly: true
16426
16555
  });
16427
16556
  }
16428
16557
  const assetChanges = /* @__PURE__ */ new Map();
@@ -16454,6 +16583,15 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
16454
16583
  }
16455
16584
  }
16456
16585
  } else {
16586
+ const coinsByAsset = /* @__PURE__ */ new Map();
16587
+ for (const coin of virtualCoins) {
16588
+ for (const assetId of new Set(coin.assets?.map((asset) => asset.assetId))) {
16589
+ const coins = coinsByAsset.get(assetId) ?? [];
16590
+ coins.push(coin);
16591
+ coinsByAsset.set(assetId, coins);
16592
+ }
16593
+ }
16594
+ const selectedOutpoints2 = /* @__PURE__ */ new Set();
16457
16595
  for (const recipient of recipients) {
16458
16596
  if (!recipient.assets) {
16459
16597
  continue;
@@ -16472,8 +16610,8 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
16472
16610
  amountToSelect -= existingChange;
16473
16611
  assetChanges.delete(receiverAsset.assetId);
16474
16612
  }
16475
- const availableCoins = virtualCoins.filter(
16476
- (c) => !selectedCoins.find((sc) => sc.txid === c.txid && sc.vout === c.vout)
16613
+ const availableCoins = (coinsByAsset.get(receiverAsset.assetId) ?? []).filter(
16614
+ (coin) => !selectedOutpoints2.has(vtxoOutpoint(coin))
16477
16615
  );
16478
16616
  const { selected, totalAssetAmount } = selectCoinsWithAsset(
16479
16617
  availableCoins,
@@ -16482,6 +16620,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
16482
16620
  );
16483
16621
  for (const coin of selected) {
16484
16622
  selectedCoins.push(coin);
16623
+ selectedOutpoints2.add(vtxoOutpoint(coin));
16485
16624
  btcAmountToSelect -= coin.value;
16486
16625
  if (coin.assets) {
16487
16626
  for (const a of coin.assets) {
@@ -16502,7 +16641,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
16502
16641
  }
16503
16642
  if (btcAmountToSelect > 0) {
16504
16643
  const availableCoins = virtualCoins.filter(
16505
- (c) => !selectedCoins.find((sc) => sc.txid === c.txid && sc.vout === c.vout)
16644
+ (coin) => !selectedOutpoints2.has(vtxoOutpoint(coin))
16506
16645
  );
16507
16646
  const { inputs: btcCoins } = selectVirtualCoins(availableCoins, btcAmountToSelect);
16508
16647
  for (const coin of btcCoins) {
@@ -16514,6 +16653,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
16514
16653
  }
16515
16654
  }
16516
16655
  selectedCoins = [...selectedCoins, ...btcCoins];
16656
+ for (const coin of btcCoins) selectedOutpoints2.add(vtxoOutpoint(coin));
16517
16657
  }
16518
16658
  }
16519
16659
  let totalBtcSelected = selectedCoins.reduce((sum, c) => sum + c.value, 0);
@@ -16530,19 +16670,58 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
16530
16670
  `send({ selectedVtxos }): inputs total ${totalBtcSelected} sats, outputs need ${totalBtcOutput}`
16531
16671
  );
16532
16672
  }
16533
- if (assetChanges.size > 0 && changeAmount < Number(this.dustAmount)) {
16534
- if (selectedVtxos) {
16535
- throw new Error(
16536
- `send({ selectedVtxos }): ${changeAmount} sats of change cannot carry ${assetChanges.size} asset change(s), needs ${this.dustAmount}`
16537
- );
16538
- }
16539
- const availableCoins = virtualCoins.filter(
16540
- (c) => !selectedCoins.find((sc) => sc.txid === c.txid && sc.vout === c.vout)
16673
+ if (selectedVtxos && assetChanges.size > 0 && changeAmount < Number(this.dustAmount)) {
16674
+ throw new Error(
16675
+ `send({ selectedVtxos }): ${changeAmount} sats of change cannot carry ${assetChanges.size} asset change(s), needs ${this.dustAmount}`
16541
16676
  );
16542
- const { inputs: extraCoins } = selectVirtualCoins(
16543
- availableCoins,
16544
- Number(this.dustAmount) - changeAmount
16677
+ }
16678
+ const vtxoMinAmount = changeAmount > 0 || assetChanges.size > 0 ? (await this.arkProvider.getInfo()).vtxoMinAmount ?? 0n : 0n;
16679
+ if (selectedVtxos && changeAmount > 0 && BigInt(changeAmount) < vtxoMinAmount) {
16680
+ throw new Error(
16681
+ `send({ selectedVtxos }): ${changeAmount} sats of change is below the operator minimum of ${vtxoMinAmount} sats`
16545
16682
  );
16683
+ }
16684
+ const selectedOutpoints = new Set(selectedCoins.map(vtxoOutpoint));
16685
+ while (!selectedVtxos && (changeAmount > 0 && BigInt(changeAmount) < vtxoMinAmount || assetChanges.size > 0 && BigInt(changeAmount) < this.dustAmount)) {
16686
+ const minimumChange = assetChanges.size > 0 && this.dustAmount > vtxoMinAmount ? this.dustAmount : vtxoMinAmount;
16687
+ const availableCoins = virtualCoins.filter(
16688
+ (coin) => !selectedOutpoints.has(vtxoOutpoint(coin))
16689
+ );
16690
+ let extraCoins;
16691
+ try {
16692
+ ({ inputs: extraCoins } = selectVirtualCoins(
16693
+ availableCoins,
16694
+ Number(minimumChange) - changeAmount
16695
+ ));
16696
+ } catch (error) {
16697
+ if (!(error instanceof Error) || error.message !== "Insufficient funds") {
16698
+ throw error;
16699
+ }
16700
+ if (recipients.every((r) => r.assets.length === 0)) {
16701
+ const plainCoins = virtualCoins.filter((coin) => !coin.assets?.length);
16702
+ const exact = plainCoins.find((coin) => coin.value === totalBtcOutput);
16703
+ let exactCoins = exact ? [exact] : void 0;
16704
+ if (!exactCoins) {
16705
+ const byAmount = /* @__PURE__ */ new Map();
16706
+ for (const coin of plainCoins) {
16707
+ const partner = byAmount.get(totalBtcOutput - coin.value);
16708
+ if (partner) {
16709
+ exactCoins = [partner, coin];
16710
+ break;
16711
+ }
16712
+ byAmount.set(coin.value, coin);
16713
+ }
16714
+ }
16715
+ if (exactCoins) {
16716
+ selectedCoins = exactCoins;
16717
+ assetChanges.clear();
16718
+ totalBtcSelected = totalBtcOutput;
16719
+ changeAmount = 0;
16720
+ break;
16721
+ }
16722
+ }
16723
+ throw new Error(`Cannot form minimum change amount of ${minimumChange} sats`);
16724
+ }
16546
16725
  for (const coin of extraCoins) {
16547
16726
  if (coin.assets) {
16548
16727
  for (const asset of coin.assets) {
@@ -16552,6 +16731,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
16552
16731
  }
16553
16732
  }
16554
16733
  selectedCoins = [...selectedCoins, ...extraCoins];
16734
+ for (const coin of extraCoins) selectedOutpoints.add(vtxoOutpoint(coin));
16555
16735
  totalBtcSelected += extraCoins.reduce((sum, c) => sum + c.value, 0);
16556
16736
  changeAmount = totalBtcSelected - totalBtcOutput;
16557
16737
  }
@@ -17938,7 +18118,8 @@ var Ramps = class {
17938
18118
  if (vtxos) reportUngatedInputs(this.wallet, vtxos);
17939
18119
  const spendable = vtxos ?? await this.wallet.getSpendableVtxos({
17940
18120
  withRecoverable: true,
17941
- withUnrolled: false
18121
+ withUnrolled: false,
18122
+ genericallySpendableOnly: true
17942
18123
  });
17943
18124
  const estimator = new Estimator(feeInfo?.intentFee ?? {});
17944
18125
  const { inputs, subtotal } = filterOffboardInputs(spendable, feeInfo, named);
@@ -17980,7 +18161,8 @@ var Ramps = class {
17980
18161
  if (vtxos) reportUngatedInputs(this.wallet, vtxos);
17981
18162
  const spendable = vtxos ?? await this.wallet.getSpendableVtxos({
17982
18163
  withRecoverable: true,
17983
- withUnrolled: false
18164
+ withUnrolled: false,
18165
+ genericallySpendableOnly: true
17984
18166
  });
17985
18167
  const estimator = new Estimator(feeInfo?.intentFee ?? {});
17986
18168
  const { inputs, subtotal } = filterOffboardInputs(spendable, feeInfo, named);
@@ -18367,7 +18549,7 @@ var WalletMessageHandler = class {
18367
18549
  return this.tagged({
18368
18550
  id,
18369
18551
  type: "SPENDABLE_VTXOS",
18370
- payload: { vtxos }
18552
+ payload: { vtxos, filterApplied: true }
18371
18553
  });
18372
18554
  }
18373
18555
  case "GET_BOARDING_UTXOS": {
@@ -18448,11 +18630,15 @@ var WalletMessageHandler = class {
18448
18630
  }
18449
18631
  case "GET_CONTRACTS_WITH_VTXOS": {
18450
18632
  const manager = await this.readonlyWallet.getContractManager();
18451
- const contracts = await manager.getContractsWithVtxos(message.payload.filter);
18633
+ const contracts = await manager.getContractsWithVtxos(
18634
+ message.payload.filter,
18635
+ void 0,
18636
+ message.payload.options
18637
+ );
18452
18638
  return this.tagged({
18453
18639
  id,
18454
18640
  type: "CONTRACTS_WITH_VTXOS",
18455
- payload: { contracts }
18641
+ payload: { contracts, filterApplied: true }
18456
18642
  });
18457
18643
  }
18458
18644
  case "WATCH_SCRIPT": {
@@ -18849,7 +19035,8 @@ var WalletMessageHandler = class {
18849
19035
  async handleGetBalance() {
18850
19036
  const [boardingUtxos, { snapshot, vtxos: allVtxos }] = await Promise.all([
18851
19037
  this.getAllBoardingUtxos(),
18852
- this.repoSnapshot()
19038
+ // The bucketer drops every spent coin and the gate reads contracts only.
19039
+ this.repoSnapshot({ unspentOnly: true })
18853
19040
  ]);
18854
19041
  const pendingOutpoints = this.readonlyWallet?.pendingRecoveryOutpointsIn(snapshot) ?? /* @__PURE__ */ new Set();
18855
19042
  let confirmed = 0;
@@ -19116,15 +19303,15 @@ var WalletMessageHandler = class {
19116
19303
  if (!this.readonlyWallet) {
19117
19304
  throw new WalletNotInitializedError();
19118
19305
  }
19119
- const allVtxos = await this.getVtxosFromRepo();
19120
- const dustAmount = this.readonlyWallet.dustAmount;
19121
19306
  const withUnrolled = message.payload.filter?.withUnrolled ?? false;
19307
+ const allVtxos = await this.getVtxosFromRepo({ unspentOnly: !withUnrolled });
19308
+ const dustAmount = this.readonlyWallet.dustAmount;
19122
19309
  const includeRecoverable = message.payload.filter?.withRecoverable ?? false;
19123
19310
  return allVtxos.filter((v) => {
19124
19311
  if (v.isUnrolled) {
19125
19312
  return withUnrolled;
19126
19313
  }
19127
- if (hasTerminalSpend(v)) {
19314
+ if (isVtxoSpent(v)) {
19128
19315
  return false;
19129
19316
  }
19130
19317
  if (includeRecoverable) {
@@ -19168,8 +19355,8 @@ var WalletMessageHandler = class {
19168
19355
  * Read all virtual outputs from the repository, aggregated across all contract
19169
19356
  * addresses and the wallet's primary address, with deduplication.
19170
19357
  */
19171
- async getVtxosFromRepo() {
19172
- return (await this.repoSnapshot()).vtxos;
19358
+ async getVtxosFromRepo(options) {
19359
+ return (await this.repoSnapshot(options)).vtxos;
19173
19360
  }
19174
19361
  /**
19175
19362
  * The worker's equivalent of `ReadonlyWallet.contractSnapshot`: contracts
@@ -19184,7 +19371,7 @@ var WalletMessageHandler = class {
19184
19371
  * what it knows is closed — so the coins of a just-registered escrowed
19185
19372
  * contract would count as available until the next poll.
19186
19373
  */
19187
- async repoSnapshot() {
19374
+ async repoSnapshot(options) {
19188
19375
  if (!this.walletRepository || !this.readonlyWallet) return { snapshot: [], vtxos: [] };
19189
19376
  const seen = /* @__PURE__ */ new Set();
19190
19377
  const allVtxos = [];
@@ -19206,7 +19393,9 @@ var WalletMessageHandler = class {
19206
19393
  for (const contract of contracts) {
19207
19394
  snapshot.push({
19208
19395
  contract,
19209
- vtxos: addVtxos(await getVtxosForContract(this.walletRepository, contract))
19396
+ vtxos: addVtxos(
19397
+ await getVtxosForContract(this.walletRepository, contract, options)
19398
+ )
19210
19399
  });
19211
19400
  }
19212
19401
  const walletAddress = await this.readonlyWallet.getAddress();
@@ -19219,10 +19408,11 @@ var WalletMessageHandler = class {
19219
19408
  );
19220
19409
  }
19221
19410
  addVtxos(
19222
- await getVtxosForContract(this.walletRepository, {
19223
- script: walletScript,
19224
- address: walletAddress
19225
- })
19411
+ await getVtxosForContract(
19412
+ this.walletRepository,
19413
+ { script: walletScript, address: walletAddress },
19414
+ options
19415
+ )
19226
19416
  );
19227
19417
  return { snapshot, vtxos: allVtxos };
19228
19418
  }
@@ -19977,7 +20167,13 @@ var ServiceWorkerReadonlyWallet = class _ServiceWorkerReadonlyWallet {
19977
20167
  };
19978
20168
  try {
19979
20169
  const response = await this.sendMessage(message);
19980
- return response.payload.vtxos.map(normalizeVtxo);
20170
+ const payload = response.payload;
20171
+ if ((filter?.watchedOnly || filter?.genericallySpendableOnly || filter?.requireSynced) && payload.filterApplied !== true) {
20172
+ throw new Error(
20173
+ "Service worker does not support the requested contract scope or freshness check"
20174
+ );
20175
+ }
20176
+ return payload.vtxos.map(normalizeVtxo);
19981
20177
  } catch (error) {
19982
20178
  throw new Error(`Failed to get spendable vtxos: ${error}`);
19983
20179
  }
@@ -20062,15 +20258,20 @@ var ServiceWorkerReadonlyWallet = class _ServiceWorkerReadonlyWallet {
20062
20258
  throw new Error("Failed to get contracts");
20063
20259
  }
20064
20260
  },
20065
- async getContractsWithVtxos(filter) {
20261
+ async getContractsWithVtxos(filter, _pageSize, options) {
20066
20262
  const message = {
20067
20263
  type: "GET_CONTRACTS_WITH_VTXOS",
20068
20264
  id: getRandomId(),
20069
20265
  tag: messageTag,
20070
- payload: { filter }
20266
+ payload: { filter, options }
20071
20267
  };
20072
20268
  try {
20073
20269
  const response = await sendContractMessage(message);
20270
+ if (options?.requireSynced && response.payload.filterApplied !== true) {
20271
+ throw new Error(
20272
+ "Service worker does not support the requested freshness check"
20273
+ );
20274
+ }
20074
20275
  await refreshSyncState();
20075
20276
  return response.payload.contracts;
20076
20277
  } catch (e) {
@@ -23548,7 +23749,9 @@ __export(arkade_exports, {
23548
23749
  fromASM: () => fromASM,
23549
23750
  getOpcodeName: () => getOpcodeName,
23550
23751
  getOpcodeValue: () => getOpcodeValue,
23752
+ isContractArtifact: () => isContractArtifact,
23551
23753
  parseArtifact: () => parseArtifact,
23754
+ programFromArtifact: () => programFromArtifact,
23552
23755
  resolveAsm: () => resolveAsm,
23553
23756
  serializeArkadeContractParams: () => serializeArkadeContractParams,
23554
23757
  stringifyArtifact: () => stringifyArtifact,
@@ -23673,7 +23876,7 @@ function createArkadeBatchHandler(intentId, inputs, signer, signedProof, message
23673
23876
  boardingIndices.push(boardingIdx);
23674
23877
  continue;
23675
23878
  }
23676
- if (isRecoverable(input) || isSubdust(input, info.dust)) {
23879
+ if (!requiresForfeit(input) || isSubdust(input, info.dust)) {
23677
23880
  continue;
23678
23881
  }
23679
23882
  if (connectorIndex >= connectorLeaves.length) {
@@ -23746,6 +23949,271 @@ function createArkadeBatchHandler(intentId, inputs, signer, signedProof, message
23746
23949
  }
23747
23950
  };
23748
23951
  }
23952
+ var NATIVE_STRUCTS = {
23953
+ AssetId: [
23954
+ ["txid", "bytes32"],
23955
+ ["gidx", "int"]
23956
+ ],
23957
+ Outpoint: [
23958
+ ["txid", "bytes32"],
23959
+ ["vout", "int"]
23960
+ ],
23961
+ ECPoint: [
23962
+ ["x", "int"],
23963
+ ["y", "int"]
23964
+ ]
23965
+ };
23966
+ var HASH_OPCODES = /* @__PURE__ */ new Set(["OP_SHA256", "OP_HASH160", "OP_HASH256", "OP_RIPEMD160"]);
23967
+ function isRecord(value) {
23968
+ return typeof value === "object" && value !== null && !Array.isArray(value);
23969
+ }
23970
+ function isNamed(value) {
23971
+ return isRecord(value) && typeof value.name === "string" && typeof value.type === "string" && value.type.length > 0;
23972
+ }
23973
+ function isContractArtifact(value) {
23974
+ if (!isRecord(value) || typeof value.contractName !== "string") return false;
23975
+ if (!Array.isArray(value.constructorInputs)) return false;
23976
+ if (value.structs !== void 0 && !Array.isArray(value.structs)) return false;
23977
+ if (!Array.isArray(value.functions) || value.functions.length === 0) return false;
23978
+ return value.functions.every(
23979
+ (group) => isRecord(group) && typeof group.name === "string" && Array.isArray(group.leaves) && group.leaves.length > 0
23980
+ );
23981
+ }
23982
+ function fail(detail) {
23983
+ throw new Error(`programFromArtifact: ${detail}`);
23984
+ }
23985
+ function claim(seen, name, kind) {
23986
+ if (seen.has(name)) fail(`duplicate ${kind} '${name}'`);
23987
+ seen.add(name);
23988
+ }
23989
+ var SCALAR_TYPES = {
23990
+ pubkey: "pubkey",
23991
+ signature: "sig",
23992
+ bytes: "bytes",
23993
+ bytes20: "hash",
23994
+ bytes32: "hash",
23995
+ asset: "hash",
23996
+ int: "int",
23997
+ bool: "int"
23998
+ };
23999
+ function argType(arkType) {
24000
+ if (!Object.hasOwn(SCALAR_TYPES, arkType)) fail(`unknown type '${arkType}'`);
24001
+ return SCALAR_TYPES[arkType];
24002
+ }
24003
+ function arrayParts(type) {
24004
+ const open = type.indexOf("[");
24005
+ if (open < 0 || !type.endsWith("]")) return [type, 0];
24006
+ const length = Number(type.slice(open + 1, -1));
24007
+ return Number.isInteger(length) && length > 0 ? [type.slice(0, open), length] : [type, 0];
24008
+ }
24009
+ function flatten(param, structs, stack = []) {
24010
+ if (!isNamed(param)) fail("parameter needs a name and a type");
24011
+ if (Object.hasOwn(NATIVE_STRUCTS, param.type)) {
24012
+ return NATIVE_STRUCTS[param.type].map(([field, type]) => ({
24013
+ name: `${param.name}.${field}`,
24014
+ type: argType(type)
24015
+ }));
24016
+ }
24017
+ const declared = structs.find((struct) => struct?.name === param.type);
24018
+ if (declared) {
24019
+ if (stack.includes(param.type)) fail(`recursive struct layout '${param.type}'`);
24020
+ return declared.fields.flatMap(
24021
+ (field) => flatten({ name: `${param.name}.${field.name}`, type: field.type }, structs, [
24022
+ ...stack,
24023
+ param.type
24024
+ ])
24025
+ );
24026
+ }
24027
+ const [element, length] = arrayParts(param.type);
24028
+ if (length === 0) {
24029
+ if (param.type.includes("[") || param.type.includes("]")) {
24030
+ fail(`invalid array type '${param.type}'`);
24031
+ }
24032
+ return [{ name: param.name, type: argType(param.type) }];
24033
+ }
24034
+ if (Object.hasOwn(NATIVE_STRUCTS, element) || structs.some((struct) => struct.name === element)) {
24035
+ fail(`arrays of structs are not supported: '${param.type}'`);
24036
+ }
24037
+ return Array.from({ length }, (_, index) => ({
24038
+ name: `${param.name}.${index}`,
24039
+ type: argType(element)
24040
+ }));
24041
+ }
24042
+ function opcodeToken(token) {
24043
+ const base = token.slice(3);
24044
+ const name = base === "0" || /^([1-9]|1[0-6])$/.test(base) ? token : base;
24045
+ if (!Object.hasOwn(ARKADE_OPS, name)) {
24046
+ fail(`opcode '${token}' is not in this SDK's table`);
24047
+ }
24048
+ return name;
24049
+ }
24050
+ function instantiationParam(body) {
24051
+ return `contract_${body.replace(/[^A-Za-z0-9]+/g, "_")}`.replace(/_+$/, "");
24052
+ }
24053
+ function asmToken(token, instantiations) {
24054
+ if (token.startsWith("OP_")) return opcodeToken(token);
24055
+ if (token.startsWith("<") && token.endsWith(">")) {
24056
+ const inner = token.slice(1, -1);
24057
+ if (inner.startsWith("CONTRACT:")) {
24058
+ const body = inner.slice("CONTRACT:".length);
24059
+ const name = instantiationParam(body);
24060
+ const seen = instantiations.get(name);
24061
+ if (seen !== void 0 && seen !== body) {
24062
+ fail(`instantiations '${seen}' and '${body}' both map to parameter '${name}'`);
24063
+ }
24064
+ instantiations.set(name, body);
24065
+ return `$${name}`;
24066
+ }
24067
+ if (inner.startsWith("VTXO:")) {
24068
+ fail(`'${token}' is not a placeholder; the compiler emits <CONTRACT:\u2026>`);
24069
+ }
24070
+ if (inner === "SERVER_KEY" || inner.startsWith("EMULATOR_KEY:")) {
24071
+ fail(`${token} is a signer role and cannot appear in a covenant`);
24072
+ }
24073
+ return `$${inner}`;
24074
+ }
24075
+ if (token.startsWith("0x")) return hex.decode(token.slice(2));
24076
+ try {
24077
+ return BigInt(token);
24078
+ } catch {
24079
+ fail(`unrecognized assembly token '${token}'`);
24080
+ }
24081
+ }
24082
+ function providedWitness(witness) {
24083
+ if (witness === void 0) return [];
24084
+ if (!Array.isArray(witness)) fail("leaf witness must be an array");
24085
+ return witness.filter((item) => {
24086
+ if (!isNamed(item)) fail("witness item needs a name and a type");
24087
+ return !item.injected && item.type !== "signature";
24088
+ });
24089
+ }
24090
+ function csvTimelock(leafName, operand) {
24091
+ if (typeof operand === "string") return { type: "blocks", value: operand };
24092
+ const timelock = operand >= 0n && operand < 0x80000000n ? sequenceToTimelock(Number(operand)) : void 0;
24093
+ if (!timelock || BigInt(timelockToSequence(timelock)) !== operand) {
24094
+ fail(`leaf '${leafName}': CSV literal ${operand} is not a canonical BIP68 sequence`);
24095
+ }
24096
+ return timelock;
24097
+ }
24098
+ function parseLeaf(leaf, hasCovenant, instantiations, extras) {
24099
+ const asm = leaf.asm;
24100
+ let index = 0;
24101
+ let condition;
24102
+ if (asm.length >= 4 && HASH_OPCODES.has(asm[0]) && asm[2] === "OP_EQUAL") {
24103
+ if (asm[3] !== "OP_VERIFY")
24104
+ fail(`leaf '${leaf.name}': hash condition must end in OP_VERIFY`);
24105
+ condition = asm.slice(0, 3).map((token) => asmToken(token, instantiations));
24106
+ index = 4;
24107
+ }
24108
+ let csv;
24109
+ let cltv;
24110
+ if (asm.length >= index + 3 && asm[index + 2] === "OP_DROP") {
24111
+ const operand = asm[index].startsWith("<") && asm[index].endsWith(">") ? `$${asm[index].slice(1, -1)}` : BigInt(asm[index]);
24112
+ if (asm[index + 1] === "OP_CHECKSEQUENCEVERIFY") csv = csvTimelock(leaf.name, operand);
24113
+ else if (asm[index + 1] === "OP_CHECKLOCKTIMEVERIFY") cltv = operand;
24114
+ else fail(`leaf '${leaf.name}': unexpected timelock opcode ${asm[index + 1]}`);
24115
+ index += 3;
24116
+ }
24117
+ const signers = [];
24118
+ let emulator;
24119
+ while (index < asm.length) {
24120
+ const key = asm[index];
24121
+ const terminator = asm[index + 1];
24122
+ const last = terminator === "OP_CHECKSIG";
24123
+ if (!last && terminator !== "OP_CHECKSIGVERIFY") {
24124
+ fail(`leaf '${leaf.name}': expected CHECKSIG after '${key}', found '${terminator}'`);
24125
+ }
24126
+ if (!key.startsWith("<") || !key.endsWith(">")) {
24127
+ fail(`leaf '${leaf.name}': unsupported key operand '${key}'`);
24128
+ }
24129
+ const inner = key.slice(1, -1);
24130
+ if (inner === "SERVER_KEY") signers.push("$server");
24131
+ else if (inner.startsWith("TWEAK:")) {
24132
+ const [, base, fn] = inner.match(/^TWEAK:([^:]+):([^:]+)$/) ?? [];
24133
+ if (!base || !fn) fail(`leaf '${leaf.name}': malformed tweak '${key}'`);
24134
+ signers.push({ tweak: `$${base}`, fn });
24135
+ } else if (inner.startsWith("EMULATOR_KEY:")) {
24136
+ if (!hasCovenant) fail(`leaf '${leaf.name}': ${key} needs a covenant`);
24137
+ if (!last) fail(`leaf '${leaf.name}': ${key} must be the last signer`);
24138
+ emulator = inner.slice("EMULATOR_KEY:".length);
24139
+ } else signers.push(`$${inner}`);
24140
+ index += 2;
24141
+ }
24142
+ const constructorTweak = signers.some((signer) => typeof signer !== "string");
24143
+ if (hasCovenant && emulator === void 0 && !constructorTweak) {
24144
+ fail(
24145
+ `leaf '${leaf.name}': covenant leaf must sign with the emulator or a tweaked constructor key`
24146
+ );
24147
+ }
24148
+ if (signers.length === 0) fail(`leaf '${leaf.name}': at least one named signer is required`);
24149
+ return {
24150
+ signers,
24151
+ ...emulator !== void 0 ? { emulator } : hasCovenant ? { emulator: null } : {},
24152
+ ...condition ? { asm: condition } : {},
24153
+ ...csv ? { csv } : {},
24154
+ ...cltv !== void 0 ? { cltv } : {},
24155
+ ...extras.length > 0 ? { witness: extras.map((item) => item.name) } : {}
24156
+ };
24157
+ }
24158
+ function programFromArtifact(artifact) {
24159
+ if (!isContractArtifact(artifact)) {
24160
+ fail(
24161
+ "expected a complete arkadec artifact with contractName, constructorInputs, and non-empty functions"
24162
+ );
24163
+ }
24164
+ const structs = artifact.structs ?? [];
24165
+ const structNames = /* @__PURE__ */ new Set();
24166
+ for (const struct of structs) {
24167
+ if (!isRecord(struct) || typeof struct.name !== "string") continue;
24168
+ if (Object.hasOwn(SCALAR_TYPES, struct.name) || Object.hasOwn(NATIVE_STRUCTS, struct.name)) {
24169
+ fail(`struct name '${struct.name}' shadows a built-in type`);
24170
+ }
24171
+ claim(structNames, struct.name, "struct");
24172
+ }
24173
+ const instantiations = /* @__PURE__ */ new Map();
24174
+ const functions = {};
24175
+ const groups = /* @__PURE__ */ new Set();
24176
+ const leafNames = /* @__PURE__ */ new Set();
24177
+ for (const group of artifact.functions) {
24178
+ claim(groups, group.name, "spend group");
24179
+ const covenantInputs = (group.arkade?.inputs ?? []).flatMap(
24180
+ (input) => flatten(input, structs)
24181
+ );
24182
+ const arkadeScript = group.arkade ? {
24183
+ asm: group.arkade.asm.map((token) => asmToken(token, instantiations)),
24184
+ // Covenant witness is reverse declaration order.
24185
+ witness: covenantInputs.map((field) => field.name).reverse()
24186
+ } : void 0;
24187
+ for (const [index, leaf] of group.leaves.entries()) {
24188
+ const name = index === 0 ? group.name : `${group.name}/${index}:${leaf.name}`;
24189
+ claim(leafNames, name, "function name");
24190
+ const extras = providedWitness(leaf.witness);
24191
+ const tapscript = parseLeaf(leaf, group.arkade !== void 0, instantiations, extras);
24192
+ const inputs = [
24193
+ ...covenantInputs,
24194
+ ...extras.map((item) => ({ name: item.name, type: argType(item.type) }))
24195
+ ];
24196
+ functions[name] = {
24197
+ ...inputs.length > 0 ? { inputs } : {},
24198
+ tapscript,
24199
+ ...arkadeScript ? { arkadeScript } : {}
24200
+ };
24201
+ }
24202
+ }
24203
+ const params = [
24204
+ ...artifact.constructorInputs.flatMap((input) => flatten(input, structs)),
24205
+ { name: "server", type: "pubkey" },
24206
+ ...[...instantiations.keys()].map((name) => ({ name, type: "hash" }))
24207
+ ];
24208
+ const paramNames = /* @__PURE__ */ new Set();
24209
+ for (const param of params) claim(paramNames, param.name, "program parameter");
24210
+ return {
24211
+ version: SUPPORTED_PROGRAM_VERSION,
24212
+ name: artifact.contractName,
24213
+ params,
24214
+ functions
24215
+ };
24216
+ }
23749
24217
  var Arkade = class _Arkade {
23750
24218
  arkProvider;
23751
24219
  /** The co-signing service, or undefined for emulator-less (pure tapscript) usage. */
@@ -23958,9 +24426,7 @@ var ArkadeContract = class _ArkadeContract {
23958
24426
  const [registered] = await manager.getContracts({ script: scriptHex });
23959
24427
  if (registered) {
23960
24428
  const [withVtxos] = await manager.getContractsWithVtxos({ script: scriptHex });
23961
- return (withVtxos?.vtxos ?? []).filter(
23962
- (v) => !hasTerminalSpend(v) && !v.isUnrolled
23963
- );
24429
+ return (withVtxos?.vtxos ?? []).filter((v) => !isVtxoSpent(v) && !v.isUnrolled);
23964
24430
  }
23965
24431
  }
23966
24432
  if (!this.client.indexer) {
@@ -23970,7 +24436,7 @@ var ArkadeContract = class _ArkadeContract {
23970
24436
  scripts: [scriptHex],
23971
24437
  spendableOnly: true
23972
24438
  });
23973
- return vtxos.filter((v) => !hasTerminalSpend(v) && !v.isUnrolled);
24439
+ return vtxos.filter((v) => !isVtxoSpent(v) && !v.isUnrolled);
23974
24440
  }
23975
24441
  /** Total spendable balance (requires an indexer). */
23976
24442
  async getBalance() {
@@ -24694,6 +25160,6 @@ function createDefaultPaymentRouter(wallet) {
24694
25160
  (*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
24695
25161
  */
24696
25162
 
24697
- export { ARKADE_MAGIC, ARKADE_SALTED_PREIMAGE_TAG, ARKADE_SWAP_PREIMAGE_TAG, ASSET_CARRIER_SATS, AbortError, ActivityRegistry, AmbiguousRouteError, ArkNote, ArkadeCash, ArkadeCashCreateError, AssetManager, BIP21, BIP21Error, BIP322, BLOCKED_UPGRADE_TIMEOUT_MS, Batch, CachingArkProvider, ChainedTxType, ConnectionDisposedError, ContractManager, ContractRepositoryImpl, ContractWatcher, DB_VERSION, DEFAULT_MESSAGE_TIMEOUTS, DEFAULT_MIN_BATCH_EXPIRY_SECONDS, DEFAULT_MIN_CHECKPOINT_EXIT_DELAY_SECONDS, DatabaseUpgradeBlockedError, DelegateManagerImpl, DelegateNotConfiguredError, DelegatorManagerImpl, DelegatorNotConfiguredError, DescriptorIdentity, DescriptorSigningProviderMissingError, DustChangeError, ELECTRUM_TCP_HOST, ELECTRUM_WS_URL, ESPLORA_URL, ElectrumOnchainProvider, EmulatorPacket, EsploraProvider, Estimator, Extension, ExtensionNotFoundError, ForeignDescriptorError, HDDescriptorProvider, INTENT_TERMINAL_STATES, InMemoryContractRepository, InMemoryIntentRepository, InMemoryVirtualTxRepository, InMemoryWalletRepository, IndexedDBContractRepository, IndexedDBIntentRepository, IndexedDBVirtualTxRepository, IndexedDBWalletRepository, MAX_USED_SIGNING_DESCRIPTORS_LOOK_AHEAD, MESSAGE_BUS_INITIALIZING, MESSAGE_BUS_NOT_INITIALIZED, MIGRATION_KEY, MUTINYNET_MIN_CHECKPOINT_EXIT_DELAY_SECONDS, MessageBus, MessageBusInitializingError, MessageBusNotInitializedError, MissingSigningDescriptorError, MnemonicIdentity, OnchainWallet, OversizedChangeError, P2A, PaymentRouter, PrevTxUnavailableError, REGTEST_MIN_BATCH_EXPIRY_SECONDS, REGTEST_MIN_CHECKPOINT_EXIT_DELAY_SECONDS, Ramps, ReadonlyAssetManager, ReadonlyDescriptorIdentity, ReadonlySingleKey, ReadonlyWallet, ReadonlyWalletError, RestDelegateProvider, RestDelegatorProvider, RestEmulatorProvider, SIGNET_MIN_CHECKPOINT_EXIT_DELAY_SECONDS, SeedIdentity, ServiceWorkerReadonlyWallet, ServiceWorkerTimeoutError, ServiceWorkerWallet, SingleKey, TxTree, TxType, TxWeightEstimator, UnannotatableInputError, UnilateralExit, UnknownPacket, Unroll, VtxoManager, Wallet2 as Wallet, WalletCannotAllocateAddressError, WalletCannotSignError, WalletMessageHandler, WalletNotInitializedError, WalletRepositoryImpl, WsElectrumChainSource, adoptContractDescriptor, arkAssetRail, arkRail, arkTarget, arkade_exports, assertFinalCommitmentMatchesValidated, assertIntentIdUnique, assertNoAssets, assertNoSelection, assertSendableAmount, assertSubmittedArkTxid, assertValidBatchExpiry, assertValidServerUnrollScript, assetMintResolver, assetsOf, attachPrevArkTxs, attachPrevoutTxs, awaitTransaction, boardingResolver, btcTarget, buildForfeitTx, buildOffchainTx, buildPreimageMessage, buildSaltedPreimageMessage, claimWithPreimageIdentity, classifyAgainstSignerSet, classifyContractSigner, closeDatabase, collabExitResolver, combineTapscriptSigs, contractFromArkContract, contractFromArkContractWithAddress, contractPreimage, contractSigner, createAssetPacket, createDefaultActivityRegistry, createDefaultPaymentRouter, createExitChainResolver, createManagedConnection, decodeArkContract, defaultBatchExpiryPolicy, defaultCheckpointExitDelayPolicy, deleteByIndex, deserializeAssets, deserializeExitPackage, deserializeUtxo, deserializeVtxo, encodeArkContract, exitObserverFor, extendVirtualCoinForContract, extractArkProviderUrl, gateExclusion, gatedContracts, getAllByIndexValues, getMigrationStatus, getRandomId, hasBoardingTxExpired, hasCandidates, intentMatchesFilter, intentPageBounds, invoiceTarget, isAddressAllocationCapable, isArkContract, isBatchSignable, isBtcAddress, isContractGenericallySpendable, isContractVtxoEvent, isCooperativelyMigratable, isDeterministicSigner, isDiscoverable, isHDAllocationCapable, isHDDeterministicSignCapable, isHDWalletCapable, isLightningInvoice, isLnurl, isMessageBusInitializingError, isMessageBusNotInitializedError, isPerArtifactDescriptor, isSigningIdentity, isSubdust, isTerminalIntentState, isValidArkAddress, isVtxoExpiringSoon, isVtxoForScript, isWatchedContract, logExcludedVtxos, makeHandle, matchServerCheckpoints, mergeChainedTxType, migrateWalletRepository, notifyExitObserved, onchainRail, openDatabase, outpointExclusion, outpointReasons, promisifyRequest, provisionClaimSecret, provisionRefundKey, registerWalletRestoreHook, requiresMigration, resolveAssetAmount, resolveBatchExpiryPolicy, resolveCheckpointExitDelayPolicy, resolveDescriptorSigner, resolvePrevTxs, resolveSendAmount, rollbackMigration, saveVtxosForContract, scriptFromArkAddress, selectCoinsWithAsset, selectVirtualCoins, selectionOf, serializeAssets, serializeExitPackage, serializeUtxo, serializeVtxo, setupServiceWorker, signAndSubmitOffchainTx, signerSetFromInfo, signingDescriptorIndex, strictSigningDescriptorIndex, toXOnlySignerHex, tryResolveSendAmount, validateBatchRecipients, validateConnectorsTxGraph, validateVtxoTxGraph, verifyTapscriptSignatures, waitForIncomingFunds, walletFeeSource, warnAndFilterVtxosForScript, withPrevTxs };
24698
- //# sourceMappingURL=chunk-WMHELOHH.js.map
24699
- //# sourceMappingURL=chunk-WMHELOHH.js.map
25163
+ export { ARKADE_MAGIC, ARKADE_SALTED_PREIMAGE_TAG, ARKADE_SWAP_PREIMAGE_TAG, ASSET_CARRIER_SATS, AbortError, ActivityRegistry, AmbiguousRouteError, ArkNote, ArkadeCash, ArkadeCashCreateError, AssetManager, BIP21, BIP21Error, BIP322, BLOCKED_UPGRADE_TIMEOUT_MS, Batch, CachingArkProvider, ChainedTxType, ConnectionDisposedError, ContractManager, ContractRepositoryImpl, ContractWatcher, DEFAULT_MESSAGE_TIMEOUTS, DEFAULT_MIN_BATCH_EXPIRY_SECONDS, DEFAULT_MIN_CHECKPOINT_EXIT_DELAY_SECONDS, DatabaseUpgradeBlockedError, DelegateManagerImpl, DelegateNotConfiguredError, DelegatorManagerImpl, DelegatorNotConfiguredError, DescriptorIdentity, DescriptorSigningProviderMissingError, DustChangeError, ELECTRUM_TCP_HOST, ELECTRUM_WS_URL, ESPLORA_URL, ElectrumOnchainProvider, EmulatorPacket, EsploraProvider, Estimator, Extension, ExtensionNotFoundError, ForeignDescriptorError, HDDescriptorProvider, INTENT_TERMINAL_STATES, InMemoryContractRepository, InMemoryIntentRepository, InMemoryVirtualTxRepository, InMemoryWalletRepository, IndexedDBContractRepository, IndexedDBIntentRepository, IndexedDBVirtualTxRepository, IndexedDBWalletRepository, MAX_USED_SIGNING_DESCRIPTORS_LOOK_AHEAD, MESSAGE_BUS_INITIALIZING, MESSAGE_BUS_NOT_INITIALIZED, MIGRATION_KEY, MUTINYNET_MIN_CHECKPOINT_EXIT_DELAY_SECONDS, MessageBus, MessageBusInitializingError, MessageBusNotInitializedError, MissingSigningDescriptorError, MnemonicIdentity, OnchainWallet, OversizedChangeError, P2A, PaymentRouter, PrevTxUnavailableError, REGTEST_MIN_BATCH_EXPIRY_SECONDS, REGTEST_MIN_CHECKPOINT_EXIT_DELAY_SECONDS, Ramps, ReadonlyAssetManager, ReadonlyDescriptorIdentity, ReadonlySingleKey, ReadonlyWallet, ReadonlyWalletError, RestDelegateProvider, RestDelegatorProvider, RestEmulatorProvider, SIGNET_MIN_CHECKPOINT_EXIT_DELAY_SECONDS, SeedIdentity, ServiceWorkerReadonlyWallet, ServiceWorkerTimeoutError, ServiceWorkerWallet, SingleKey, TxTree, TxType, TxWeightEstimator, UnannotatableInputError, UnilateralExit, UnknownPacket, Unroll, VtxoManager, Wallet2 as Wallet, WalletCannotAllocateAddressError, WalletCannotSignError, WalletMessageHandler, WalletNotInitializedError, WalletRepositoryImpl, WsElectrumChainSource, adoptContractDescriptor, arkAssetRail, arkRail, arkTarget, arkade_exports, assertFinalCommitmentMatchesValidated, assertIntentIdUnique, assertNoAssets, assertNoSelection, assertSendableAmount, assertSubmittedArkTxid, assertValidBatchExpiry, assertValidServerUnrollScript, assetMintResolver, assetsOf, attachPrevArkTxs, attachPrevoutTxs, awaitTransaction, boardingResolver, btcTarget, buildForfeitTx, buildOffchainTx, buildPreimageMessage, buildSaltedPreimageMessage, claimWithPreimageIdentity, classifyAgainstSignerSet, classifyContractSigner, closeDatabase, collabExitResolver, combineTapscriptSigs, contractFromArkContract, contractFromArkContractWithAddress, contractPreimage, contractSigner, createAssetPacket, createDefaultActivityRegistry, createDefaultPaymentRouter, createExitChainResolver, createManagedConnection, decodeArkContract, defaultBatchExpiryPolicy, defaultCheckpointExitDelayPolicy, deleteByIndex, deserializeAssets, deserializeExitPackage, deserializeUtxo, deserializeVtxo, encodeArkContract, exitObserverFor, extendVirtualCoinForContract, extractArkProviderUrl, gateExclusion, gatedContracts, getAllByIndexValues, getMigrationStatus, getRandomId, hasBoardingTxExpired, hasCandidates, intentMatchesFilter, intentPageBounds, invoiceTarget, isAddressAllocationCapable, isArkContract, isBatchSignable, isBtcAddress, isContractGenericallySpendable, isContractVtxoEvent, isCooperativelyMigratable, isDeterministicSigner, isDiscoverable, isHDAllocationCapable, isHDDeterministicSignCapable, isHDWalletCapable, isLightningInvoice, isLnurl, isMessageBusInitializingError, isMessageBusNotInitializedError, isPerArtifactDescriptor, isSigningIdentity, isSubdust, isTerminalIntentState, isValidArkAddress, isVtxoExpiringSoon, isVtxoForScript, isWatchedContract, logExcludedVtxos, makeHandle, matchServerCheckpoints, mergeChainedTxType, migrateWalletRepository, notifyExitObserved, onchainRail, openDatabase, outpointExclusion, outpointReasons, promisifyRequest, provisionClaimSecret, provisionRefundKey, registerWalletRestoreHook, requiresMigration, resolveAssetAmount, resolveBatchExpiryPolicy, resolveCheckpointExitDelayPolicy, resolveDescriptorSigner, resolvePrevTxs, resolveSendAmount, rollbackMigration, saveVtxosForContract, scriptFromArkAddress, selectCoinsWithAsset, selectVirtualCoins, selectionOf, serializeAssets, serializeExitPackage, serializeUtxo, serializeVtxo, setupServiceWorker, signAndSubmitOffchainTx, signerSetFromInfo, signingDescriptorIndex, strictSigningDescriptorIndex, toXOnlySignerHex, tryResolveSendAmount, validateBatchRecipients, validateConnectorsTxGraph, validateVtxoTxGraph, verifyTapscriptSignatures, waitForIncomingFunds, walletFeeSource, warnAndFilterVtxosForScript, withPrevTxs };
25164
+ //# sourceMappingURL=chunk-LKAASQTL.js.map
25165
+ //# sourceMappingURL=chunk-LKAASQTL.js.map