@arkade-os/sdk 0.4.75 → 0.4.77

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-DjwCbb07.d.ts → asyncStorageTaskQueue-DTl0WM7E.d.ts} +1 -1
  12. package/dist/{asyncStorageTaskQueue-5k0H_cDM.d.cts → asyncStorageTaskQueue-MmJyAnu-.d.cts} +1 -1
  13. package/dist/{chunk-YA3L3WL4.cjs → chunk-4LP6XOMW.cjs} +17 -17
  14. package/dist/{chunk-YA3L3WL4.cjs.map → chunk-4LP6XOMW.cjs.map} +1 -1
  15. package/dist/{chunk-UKFA7NVE.js → chunk-52OA3UIC.js} +25 -12
  16. package/dist/chunk-52OA3UIC.js.map +1 -0
  17. package/dist/{chunk-4GLSOP6L.js → chunk-ACPANSOX.js} +43 -11
  18. package/dist/chunk-ACPANSOX.js.map +1 -0
  19. package/dist/{chunk-J5EX3OMM.cjs → chunk-CJN4CUEU.cjs} +25 -11
  20. package/dist/chunk-CJN4CUEU.cjs.map +1 -0
  21. package/dist/{chunk-SH23SRZE.cjs → chunk-H62OOAS4.cjs} +17 -17
  22. package/dist/{chunk-SH23SRZE.cjs.map → chunk-H62OOAS4.cjs.map} +1 -1
  23. package/dist/{chunk-PS564TAU.js → chunk-HKOFYHZP.js} +3 -3
  24. package/dist/{chunk-PS564TAU.js.map → chunk-HKOFYHZP.js.map} +1 -1
  25. package/dist/{chunk-YYWEBYFJ.cjs → chunk-LUWSY2QO.cjs} +89 -57
  26. package/dist/chunk-LUWSY2QO.cjs.map +1 -0
  27. package/dist/{chunk-FTLPTF3R.js → chunk-MLOZHTBN.js} +570 -114
  28. package/dist/chunk-MLOZHTBN.js.map +1 -0
  29. package/dist/{chunk-SKAL4MCW.js → chunk-T6MGH4V4.js} +4 -4
  30. package/dist/{chunk-SKAL4MCW.js.map → chunk-T6MGH4V4.js.map} +1 -1
  31. package/dist/{chunk-2Q2PYU3Y.cjs → chunk-Y4YK44IM.cjs} +10 -10
  32. package/dist/{chunk-2Q2PYU3Y.cjs.map → chunk-Y4YK44IM.cjs.map} +1 -1
  33. package/dist/{chunk-EHJYR4E5.cjs → chunk-YJEWWLOQ.cjs} +1005 -550
  34. package/dist/chunk-YJEWWLOQ.cjs.map +1 -0
  35. package/dist/{chunk-2ULUCL6A.js → chunk-YKNTUOUG.js} +4 -4
  36. package/dist/{chunk-2ULUCL6A.js.map → chunk-YKNTUOUG.js.map} +1 -1
  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-D_sW61rj.d.ts → delegate-CjcysR7s.d.ts} +1 -1
  42. package/dist/{delegate-D0v8yT-a.d.cts → delegate-QPCGaD_z.d.cts} +1 -1
  43. package/dist/{hdWalletCapable-66hG5KQP.d.cts → hdWalletCapable--pMncWbC.d.ts} +6 -4
  44. package/dist/{hdWalletCapable-DHCNbAgl.d.ts → hdWalletCapable-Bp_p0WLj.d.cts} +6 -4
  45. package/dist/{index-B2JSxDi1.d.cts → index-BMY272ns.d.cts} +26 -8
  46. package/dist/{index-TZBbMNqA.d.ts → index-Ie_ak2zY.d.cts} +39 -9
  47. package/dist/{index-TZBbMNqA.d.cts → index-Ie_ak2zY.d.ts} +39 -9
  48. package/dist/{index-vUeTpifq.d.ts → index-m-EM-e8D.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-CAAPpdyi.d.cts → taskRunner-BgRowCqB.d.cts} +1 -1
  66. package/dist/{taskRunner-CairkunF.d.ts → taskRunner-CqrlrJEt.d.ts} +1 -1
  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-4GLSOP6L.js.map +0 -1
  83. package/dist/chunk-EHJYR4E5.cjs.map +0 -1
  84. package/dist/chunk-FTLPTF3R.js.map +0 -1
  85. package/dist/chunk-J5EX3OMM.cjs.map +0 -1
  86. package/dist/chunk-UKFA7NVE.js.map +0 -1
  87. package/dist/chunk-YYWEBYFJ.cjs.map +0 -1
@@ -1,6 +1,6 @@
1
- import { AssetId, AssetGroup, AssetInput, AssetOutput, Metadata, Packet, BufferReader, BufferWriter, AssetRef, RestIndexerProvider } from './chunk-PS564TAU.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-4GLSOP6L.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-UKFA7NVE.js';
1
+ import { AssetId, AssetGroup, AssetInput, AssetOutput, Metadata, Packet, BufferReader, BufferWriter, AssetRef, RestIndexerProvider } from './chunk-HKOFYHZP.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-ACPANSOX.js';
3
+ import { VtxoScript, Transaction, isRecoverable, CSVMultisigTapscript, networks, getNetwork, DEFAULT_NETWORK, resolveEmulatorPubkey, isVtxoSpent, 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-52OA3UIC.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,
@@ -3918,7 +3918,7 @@ function parseProgram(text, env) {
3918
3918
  var DUST_AMOUNT = 546;
3919
3919
  var FALLBACK_WALLET_DUST_AMOUNT = 330n;
3920
3920
  function getDustAmount(wallet) {
3921
- return "dustAmount" in wallet ? wallet.dustAmount : FALLBACK_WALLET_DUST_AMOUNT;
3921
+ return wallet && "dustAmount" in wallet ? wallet.dustAmount : FALLBACK_WALLET_DUST_AMOUNT;
3922
3922
  }
3923
3923
  function extendCoinWithTapscript(boardingTapscript, utxo) {
3924
3924
  return {
@@ -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;
@@ -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(
@@ -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
@@ -8776,15 +8796,21 @@ var InMemoryWalletRepository = class {
8776
8796
  this.vtxosByAddress.delete(address);
8777
8797
  }
8778
8798
  async getVtxosForScript(script) {
8779
- const allMatches = [];
8799
+ return this.getVtxosForScripts([script]);
8800
+ }
8801
+ async getVtxosForScripts(scripts, options) {
8802
+ if (scripts.length === 0) return [];
8803
+ const selected = new Set(scripts);
8804
+ const byOutpoint = /* @__PURE__ */ new Map();
8780
8805
  for (const bucket of this.vtxosByAddress.values()) {
8781
8806
  for (const vtxo of bucket) {
8782
- if (isVtxoForScript(vtxo, script)) {
8783
- allMatches.push(vtxo);
8807
+ if (vtxo.script && selected.has(vtxo.script)) {
8808
+ byOutpoint.set(`${vtxo.script}:${vtxo.txid}:${vtxo.vout}`, vtxo);
8784
8809
  }
8785
8810
  }
8786
8811
  }
8787
- return mergeByKey([], allMatches, (item) => `${item.txid}:${item.vout}`);
8812
+ const rows = [...byOutpoint.values()];
8813
+ return options?.unspentOnly ? rows.filter((vtxo) => !isVtxoSpent(vtxo)) : rows;
8788
8814
  }
8789
8815
  async saveVtxosForScript(key, vtxos) {
8790
8816
  if (!key.address) {
@@ -9565,7 +9591,7 @@ var ContractWatcher = class {
9565
9591
  }
9566
9592
  if (target === byWatchedScript && eventType === "vtxo_received") {
9567
9593
  const n = normalizeVtxo(vtxo);
9568
- if (hasTerminalSpend(n) || n.isSwept || isPastExpiry(n, now)) continue;
9594
+ if (isVtxoSpent(n) || n.isSwept || isPastExpiry(n, now)) continue;
9569
9595
  }
9570
9596
  let bucket = target.get(vtxo.script);
9571
9597
  if (!bucket) {
@@ -9664,7 +9690,7 @@ var NON_TERMINAL_INTENT_STATES = ALL_INTENT_STATES.filter(
9664
9690
  (s) => !isTerminalIntentState(s)
9665
9691
  );
9666
9692
  function isConsumed(vtxo) {
9667
- return vtxo !== void 0 && hasTerminalSpend(vtxo);
9693
+ return vtxo !== void 0 && isVtxoSpent(vtxo);
9668
9694
  }
9669
9695
  async function classifyIntent(intent, vtxosByOutpoint, now) {
9670
9696
  if (isTerminalIntentState(intent.state)) return void 0;
@@ -9754,7 +9780,8 @@ function areCoalescibleContractTypes(a, b) {
9754
9780
  function toWatchOnlyContract(params) {
9755
9781
  return { ...params, state: params.state ?? "active", createdAt: Date.now() };
9756
9782
  }
9757
- function annotatableIn(scriptToContract, vtxos) {
9783
+ var TAPSCRIPT_MEMO_MAX_ENTRIES = 1024;
9784
+ function annotatableIn(scriptToContract, vtxos, memo) {
9758
9785
  const scripts = /* @__PURE__ */ new Set();
9759
9786
  const cache = /* @__PURE__ */ new Map();
9760
9787
  const failures = /* @__PURE__ */ new Map();
@@ -9762,7 +9789,25 @@ function annotatableIn(scriptToContract, vtxos) {
9762
9789
  const contract = scriptToContract.get(script);
9763
9790
  if (!contract) continue;
9764
9791
  try {
9765
- cache.set(script, deriveContractTapscripts(contract));
9792
+ const key = `${contract.type}\0${script}\0${JSON.stringify(contract.params)}`;
9793
+ const handler = contractHandlers.get(contract.type);
9794
+ const hit = memo?.get(script);
9795
+ let tapscripts;
9796
+ if (hit && hit.key === key && hit.handler === handler) {
9797
+ tapscripts = hit.tapscripts;
9798
+ memo.delete(script);
9799
+ memo.set(script, hit);
9800
+ } else {
9801
+ memo?.delete(script);
9802
+ tapscripts = deriveContractTapscripts(contract);
9803
+ if (handler && memo) {
9804
+ memo.set(script, { key, handler, tapscripts });
9805
+ if (memo.size > TAPSCRIPT_MEMO_MAX_ENTRIES) {
9806
+ memo.delete(memo.keys().next().value);
9807
+ }
9808
+ }
9809
+ }
9810
+ cache.set(script, tapscripts);
9766
9811
  scripts.add(script);
9767
9812
  } catch (err) {
9768
9813
  failures.set(
@@ -9780,6 +9825,8 @@ var CHAIN_TIP_TIMEOUT_MS = 5e3;
9780
9825
  var ContractManager = class _ContractManager {
9781
9826
  config;
9782
9827
  watcher;
9828
+ /** Per-sync tapscript caches start empty; this outlives them. Consumers only ever see clones. */
9829
+ tapscriptMemo = /* @__PURE__ */ new Map();
9783
9830
  initialized = false;
9784
9831
  eventCallbacks = /* @__PURE__ */ new Set();
9785
9832
  stopWatcherFn;
@@ -10462,9 +10509,15 @@ var ContractManager = class _ContractManager {
10462
10509
  const dbFilter = this.buildContractsDbFilter(filter ?? {});
10463
10510
  return await this.config.contractRepository.getContracts(dbFilter);
10464
10511
  }
10465
- async getContractsWithVtxos(filter, pageSize) {
10512
+ async getContractsWithVtxos(filter, pageSize, options) {
10513
+ if (options?.maxSyncAgeMs !== void 0 && (!Number.isSafeInteger(options.maxSyncAgeMs) || options.maxSyncAgeMs < 0)) {
10514
+ throw new Error("maxSyncAgeMs must be a non-negative safe integer");
10515
+ }
10466
10516
  const contracts = await this.getContracts(filter);
10467
- if (this.syncedWithin(contracts, this.config.vtxoSyncMaxAgeMs ?? 0)) {
10517
+ if (this.syncedWithin(
10518
+ contracts,
10519
+ options?.maxSyncAgeMs ?? (options?.requireSynced ? 0 : this.config.vtxoSyncMaxAgeMs ?? 0)
10520
+ )) {
10468
10521
  await this.demoteFundedAwaitingContracts(contracts);
10469
10522
  } else {
10470
10523
  try {
@@ -10473,12 +10526,23 @@ var ContractManager = class _ContractManager {
10473
10526
  } catch (err) {
10474
10527
  if (!isRetryableProviderError(err)) throw err;
10475
10528
  this.markSyncDegraded(err);
10529
+ if (options?.requireSynced) {
10530
+ throw new Error("Spendable VTXO read requires an online contract sync", {
10531
+ cause: err
10532
+ });
10533
+ }
10476
10534
  }
10477
10535
  }
10478
- const vtxos = await this.getVtxosForContracts(contracts);
10536
+ const vtxos = await this.getVtxosForContracts(contracts, options);
10537
+ const vtxosByScript = /* @__PURE__ */ new Map();
10538
+ for (const vtxo of vtxos) {
10539
+ const group = vtxosByScript.get(vtxo.contractScript) ?? [];
10540
+ group.push(vtxo);
10541
+ vtxosByScript.set(vtxo.contractScript, group);
10542
+ }
10479
10543
  return contracts.map((contract) => ({
10480
10544
  contract,
10481
- vtxos: vtxos.filter((vtxo) => vtxo.contractScript === contract.script)
10545
+ vtxos: vtxosByScript.get(contract.script)?.slice() ?? []
10482
10546
  }));
10483
10547
  }
10484
10548
  async annotateVtxos(vtxos, tapscripts) {
@@ -10504,7 +10568,8 @@ var ContractManager = class _ContractManager {
10504
10568
  });
10505
10569
  const { scripts, failures } = annotatableIn(
10506
10570
  new Map(contracts.map((contract) => [contract.script, contract])),
10507
- vtxos
10571
+ vtxos,
10572
+ this.tapscriptMemo
10508
10573
  );
10509
10574
  const orphans = vtxos.filter(
10510
10575
  (vtxo) => !scripts.has(vtxo.script) && !failures.has(vtxo.script)
@@ -10655,6 +10720,7 @@ var ContractManager = class _ContractManager {
10655
10720
  */
10656
10721
  async deleteContract(script) {
10657
10722
  await this.config.contractRepository.deleteContract(script);
10723
+ this.tapscriptMemo.delete(script);
10658
10724
  await this.watcher.removeContract(script);
10659
10725
  }
10660
10726
  /**
@@ -10906,20 +10972,28 @@ var ContractManager = class _ContractManager {
10906
10972
  }
10907
10973
  this.emitEvent(event);
10908
10974
  }
10909
- async getVtxosForContracts(contracts) {
10910
- const res = await Promise.all(
10911
- contracts.map(
10912
- (contract) => getVtxosForContract(this.config.walletRepository, contract).then(
10913
- (vtxos) => vtxos.map(
10914
- (vtxo) => ({
10915
- ...vtxo,
10916
- contractScript: contract.script
10917
- })
10975
+ async getVtxosForContracts(contracts, options) {
10976
+ if (contracts.length === 0) return [];
10977
+ let rows;
10978
+ if (this.config.walletRepository.getVtxosForScripts) {
10979
+ const byScript = new Set(contracts.map((contract) => contract.script));
10980
+ 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 }));
10981
+ } else {
10982
+ const res = await Promise.all(
10983
+ contracts.map(
10984
+ (contract) => getVtxosForContract(this.config.walletRepository, contract).then(
10985
+ (vtxos) => vtxos.map(
10986
+ (vtxo) => ({
10987
+ ...vtxo,
10988
+ contractScript: contract.script
10989
+ })
10990
+ )
10918
10991
  )
10919
10992
  )
10920
- )
10921
- );
10922
- return res.flat();
10993
+ );
10994
+ rows = res.flat();
10995
+ }
10996
+ return options?.unspentOnly ? rows.filter((vtxo) => !isVtxoSpent(vtxo)) : rows;
10923
10997
  }
10924
10998
  /**
10925
10999
  * Sync virtual outputs for the given contracts against the indexer.
@@ -11000,7 +11074,11 @@ var ContractManager = class _ContractManager {
11000
11074
  for (const [script, contract] of await this.promoteLookAheadHits(vtxos)) {
11001
11075
  scriptToContract.set(script, contract);
11002
11076
  }
11003
- const { scripts: annotatable, cache, failures } = annotatableIn(scriptToContract, vtxos);
11077
+ const {
11078
+ scripts: annotatable,
11079
+ cache,
11080
+ failures
11081
+ } = annotatableIn(scriptToContract, vtxos, this.tapscriptMemo);
11004
11082
  this.recordAnnotationFailures(annotatable, failures);
11005
11083
  const owned = vtxos.filter((v) => annotatable.has(v.script));
11006
11084
  const annotated = await this.annotateVtxos(owned, cache);
@@ -11100,7 +11178,11 @@ var ContractManager = class _ContractManager {
11100
11178
  for (const [script, contract] of promoted) {
11101
11179
  scriptToContract.set(script, contract);
11102
11180
  }
11103
- const { scripts: annotatable, cache, failures } = annotatableIn(scriptToContract, vtxos);
11181
+ const {
11182
+ scripts: annotatable,
11183
+ cache,
11184
+ failures
11185
+ } = annotatableIn(scriptToContract, vtxos, this.tapscriptMemo);
11104
11186
  this.recordAnnotationFailures(annotatable, failures);
11105
11187
  const owned = vtxos.filter((v) => annotatable.has(v.script));
11106
11188
  const annotated = await this.annotateVtxos(owned, cache);
@@ -11412,7 +11494,7 @@ var IndexedDBWalletRepository = class {
11412
11494
  const store = transaction.objectStore(STORE_VTXOS);
11413
11495
  for (const vtxo of vtxos) {
11414
11496
  const serialized = serializeVtxo(vtxo);
11415
- store.put({ address, ...serialized });
11497
+ store.put({ address, ...serialized, ...unspentFlag(vtxo) });
11416
11498
  }
11417
11499
  await awaitTransaction(transaction);
11418
11500
  } catch (error) {
@@ -11432,31 +11514,52 @@ var IndexedDBWalletRepository = class {
11432
11514
  }
11433
11515
  }
11434
11516
  async getVtxosForScript(script) {
11517
+ return this.getVtxosForScripts([script]);
11518
+ }
11519
+ async getVtxosForScripts(scripts, options) {
11520
+ const unique = [...new Set(scripts)];
11521
+ if (unique.length === 0) return [];
11435
11522
  try {
11436
11523
  const db = await this.getDB();
11437
- const store = db.transaction([STORE_VTXOS], "readonly").objectStore(STORE_VTXOS);
11438
- const results = await promisifyRequest(
11439
- store.index("script").getAll(script)
11440
- );
11441
- const matching = (results || []).filter((r) => r.script === script);
11524
+ const rows = options?.unspentOnly ? await getAllByIndexValues(
11525
+ this.vtxoStore(db),
11526
+ "scriptUnspent",
11527
+ unique.map((script) => [script, 1])
11528
+ ) : await getAllByIndexValues(this.vtxoStore(db), "script", unique);
11529
+ const selected = new Set(unique);
11442
11530
  const byOutpoint = /* @__PURE__ */ new Map();
11443
- for (const row of matching) {
11444
- const outpoint = `${row.txid}:${row.vout}`;
11445
- const existing = byOutpoint.get(outpoint);
11446
- if (!existing) {
11447
- byOutpoint.set(outpoint, row);
11531
+ for (const row of rows) {
11532
+ if (!selected.has(row.script)) continue;
11533
+ const key = `${row.script}:${row.txid}:${row.vout}`;
11534
+ const existing = byOutpoint.get(key);
11535
+ if (!existing || shouldReplaceVtxo(existing, row)) byOutpoint.set(key, row);
11536
+ }
11537
+ if (options?.unspentOnly && byOutpoint.size > 0) {
11538
+ const txids = [...new Set([...byOutpoint.values()].map((row) => row.txid))];
11539
+ const store = this.vtxoStore(db);
11540
+ for (const row of await getAllByIndexValues(store, "txid", txids)) {
11541
+ const key = `${row.script}:${row.txid}:${row.vout}`;
11542
+ const existing = byOutpoint.get(key);
11543
+ if (existing && shouldReplaceVtxo(existing, row)) byOutpoint.set(key, row);
11544
+ }
11545
+ }
11546
+ const result = [];
11547
+ for (const row of byOutpoint.values()) {
11548
+ if (options?.unspentOnly && (row.isSpent || row.spentBy || row.settledBy)) {
11448
11549
  continue;
11449
11550
  }
11450
- if (shouldReplaceVtxo(existing, row)) {
11451
- byOutpoint.set(outpoint, row);
11452
- }
11551
+ const vtxo = deserializeVtxoWithBackfill(row);
11552
+ if (!options?.unspentOnly || !isVtxoSpent(vtxo)) result.push(vtxo);
11453
11553
  }
11454
- return Array.from(byOutpoint.values()).map(deserializeVtxoWithBackfill);
11554
+ return result;
11455
11555
  } catch (error) {
11456
- console.error(`Failed to get VTXOs for script ${script}:`, error);
11556
+ console.error("Failed to get VTXOs for scripts:", error);
11457
11557
  throw error;
11458
11558
  }
11459
11559
  }
11560
+ vtxoStore(db) {
11561
+ return db.transaction([STORE_VTXOS], "readonly").objectStore(STORE_VTXOS);
11562
+ }
11460
11563
  async saveVtxosForScript(key, vtxos) {
11461
11564
  if (!key.address) {
11462
11565
  throw new Error("IndexedDBWalletRepository requires an address");
@@ -11587,7 +11690,7 @@ var IndexedDBWalletRepository = class {
11587
11690
  return this.connection.get();
11588
11691
  }
11589
11692
  };
11590
- function deserializeVtxoWithBackfill(o) {
11693
+ function deserializeVtxoWithBackfill({ unspent: _unspent, ...o }) {
11591
11694
  if (!o.script) {
11592
11695
  o = { ...o, script: scriptFromArkAddress(o.address) };
11593
11696
  }
@@ -12507,7 +12610,7 @@ async function resolveDescriptorProvider(config, walletRepository) {
12507
12610
 
12508
12611
  // src/wallet/balance.ts
12509
12612
  function computeOffchainBalance(vtxos, caps) {
12510
- const { now, isPendingRecovery, isGenericallySpendable, isUnlocked } = caps;
12613
+ const { now, isPendingRecovery, isGenericallySpendable, isUnlocked, dustCarrier } = caps;
12511
12614
  let settled = 0;
12512
12615
  let preconfirmed = 0;
12513
12616
  let available = 0;
@@ -12524,7 +12627,7 @@ function computeOffchainBalance(vtxos, caps) {
12524
12627
  }
12525
12628
  };
12526
12629
  for (const vtxo of vtxos) {
12527
- if (hasTerminalSpend(vtxo)) continue;
12630
+ if (isVtxoSpent(vtxo)) continue;
12528
12631
  addAssets(owned, vtxo);
12529
12632
  if (vtxo.isUnrolled) {
12530
12633
  unrolled += vtxo.value;
@@ -12553,6 +12656,9 @@ function computeOffchainBalance(vtxos, caps) {
12553
12656
  addAssets(spendable, vtxo);
12554
12657
  }
12555
12658
  }
12659
+ if (available > 0 && spendable.size > 0) {
12660
+ available = Math.max(0, available - Number(dustCarrier));
12661
+ }
12556
12662
  const toAssets = (from) => Array.from(from.entries()).map(([assetId, amount]) => ({ assetId, amount }));
12557
12663
  return {
12558
12664
  settled,
@@ -12793,7 +12899,7 @@ function filterSnapshotVtxos(snapshot, filter, pendingSpendOutpoints) {
12793
12899
  if (vtxo.isUnrolled) {
12794
12900
  return !!f.withUnrolled;
12795
12901
  }
12796
- if (hasTerminalSpend(vtxo)) {
12902
+ if (isVtxoSpent(vtxo)) {
12797
12903
  return false;
12798
12904
  }
12799
12905
  if (!f.withRecoverable && canRecoverOnchain(vtxo, now)) {
@@ -13361,7 +13467,9 @@ var ReadonlyWallet = class _ReadonlyWallet {
13361
13467
  }
13362
13468
  /** @inheritdoc */
13363
13469
  async getSpendableVtxos(filter) {
13364
- const snapshot = await this.contractSnapshot();
13470
+ const snapshot = await this.contractSnapshot(filter, {
13471
+ unspentOnly: !filter?.withUnrolled
13472
+ });
13365
13473
  const vtxos = filterSnapshotVtxos(snapshot, filter, this._pendingSpendOutpoints);
13366
13474
  const { gated, pendingRecovery } = this.spendabilityView(snapshot);
13367
13475
  const selectable = vtxos.filter(
@@ -13379,7 +13487,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
13379
13487
  if (!filter?.withUnrolled) {
13380
13488
  logExcludedVtxos(
13381
13489
  "getSpendableVtxos",
13382
- snapshot.flatMap((contract) => contract.vtxos).filter((vtxo) => vtxo.isUnrolled && !hasTerminalSpend(vtxo)),
13490
+ snapshot.flatMap((contract) => contract.vtxos).filter((vtxo) => vtxo.isUnrolled && !isVtxoSpent(vtxo)),
13383
13491
  [() => UNROLLED_REASON]
13384
13492
  );
13385
13493
  }
@@ -13453,9 +13561,25 @@ var ReadonlyWallet = class _ReadonlyWallet {
13453
13561
  * otherwise the gate and the pending-recovery set answer about two different
13454
13562
  * points in time, and each read costs another sync.
13455
13563
  */
13456
- async contractSnapshot() {
13564
+ async contractSnapshot(filter, options) {
13457
13565
  const contractManager = await this.getContractManager();
13458
- return contractManager.getContractsWithVtxos();
13566
+ const scope = filter?.watchedOnly ? { watch: ["watched", "awaiting-funds"] } : void 0;
13567
+ let query = scope;
13568
+ if (filter?.genericallySpendableOnly) {
13569
+ const scripts = (await contractManager.getContracts(scope)).filter(isContractGenericallySpendable).map((contract) => contract.script);
13570
+ if (scripts.length === 0) {
13571
+ if (filter.requireSynced) {
13572
+ throw new Error("No generically spendable contracts to sync");
13573
+ }
13574
+ return [];
13575
+ }
13576
+ query = { ...scope, script: scripts };
13577
+ }
13578
+ return contractManager.getContractsWithVtxos(query, void 0, {
13579
+ maxSyncAgeMs: filter?.maxSyncAgeMs,
13580
+ unspentOnly: options?.unspentOnly,
13581
+ requireSynced: filter?.requireSynced
13582
+ });
13459
13583
  }
13460
13584
  /**
13461
13585
  * The two exclusion sets the gate is made of, derived from one snapshot so
@@ -13479,7 +13603,8 @@ var ReadonlyWallet = class _ReadonlyWallet {
13479
13603
  now: { timestamp: /* @__PURE__ */ new Date() },
13480
13604
  isPendingRecovery: (vtxo) => pendingRecovery.has(`${vtxo.txid}:${vtxo.vout}`),
13481
13605
  isGenericallySpendable: (vtxo) => !isGatedVtxo(vtxo, gated),
13482
- isUnlocked: (vtxo) => unlocked.has(`${vtxo.txid}:${vtxo.vout}`)
13606
+ isUnlocked: (vtxo) => unlocked.has(`${vtxo.txid}:${vtxo.vout}`),
13607
+ dustCarrier: getDustAmount(this)
13483
13608
  };
13484
13609
  }
13485
13610
  /**
@@ -15283,7 +15408,10 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
15283
15408
  filteredBoardingUtxos.push(utxo);
15284
15409
  amount += utxo.value - inputFee.satoshis;
15285
15410
  }
15286
- const vtxos = await this.getSpendableVtxos({ withRecoverable: true });
15411
+ const vtxos = await this.getSpendableVtxos({
15412
+ withRecoverable: true,
15413
+ genericallySpendableOnly: true
15414
+ });
15287
15415
  const filteredVtxos = [];
15288
15416
  for (const vtxo of byValueDescending(vtxos)) {
15289
15417
  if (filteredVtxos.length >= MAX_VTXOS_PER_SETTLEMENT) {
@@ -16196,7 +16324,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
16196
16324
  const spendable = [];
16197
16325
  const unclaimed = [];
16198
16326
  for (const vtxo of vtxos) {
16199
- if (hasTerminalSpend(vtxo)) {
16327
+ if (isVtxoSpent(vtxo)) {
16200
16328
  unclaimed.push(cashReport(vtxo, "already-spent"));
16201
16329
  } else if (vtxo.isUnrolled) {
16202
16330
  unclaimed.push(cashReport(vtxo, "exited"));
@@ -16387,7 +16515,8 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
16387
16515
  let virtualCoins = [];
16388
16516
  if (!selectedVtxos) {
16389
16517
  virtualCoins = await this.getSpendableVtxos({
16390
- withRecoverable: false
16518
+ withRecoverable: false,
16519
+ genericallySpendableOnly: true
16391
16520
  });
16392
16521
  }
16393
16522
  const assetChanges = /* @__PURE__ */ new Map();
@@ -16419,6 +16548,15 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
16419
16548
  }
16420
16549
  }
16421
16550
  } else {
16551
+ const coinsByAsset = /* @__PURE__ */ new Map();
16552
+ for (const coin of virtualCoins) {
16553
+ for (const assetId of new Set(coin.assets?.map((asset) => asset.assetId))) {
16554
+ const coins = coinsByAsset.get(assetId) ?? [];
16555
+ coins.push(coin);
16556
+ coinsByAsset.set(assetId, coins);
16557
+ }
16558
+ }
16559
+ const selectedOutpoints2 = /* @__PURE__ */ new Set();
16422
16560
  for (const recipient of recipients) {
16423
16561
  if (!recipient.assets) {
16424
16562
  continue;
@@ -16437,8 +16575,8 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
16437
16575
  amountToSelect -= existingChange;
16438
16576
  assetChanges.delete(receiverAsset.assetId);
16439
16577
  }
16440
- const availableCoins = virtualCoins.filter(
16441
- (c) => !selectedCoins.find((sc) => sc.txid === c.txid && sc.vout === c.vout)
16578
+ const availableCoins = (coinsByAsset.get(receiverAsset.assetId) ?? []).filter(
16579
+ (coin) => !selectedOutpoints2.has(vtxoOutpoint(coin))
16442
16580
  );
16443
16581
  const { selected, totalAssetAmount } = selectCoinsWithAsset(
16444
16582
  availableCoins,
@@ -16447,6 +16585,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
16447
16585
  );
16448
16586
  for (const coin of selected) {
16449
16587
  selectedCoins.push(coin);
16588
+ selectedOutpoints2.add(vtxoOutpoint(coin));
16450
16589
  btcAmountToSelect -= coin.value;
16451
16590
  if (coin.assets) {
16452
16591
  for (const a of coin.assets) {
@@ -16467,7 +16606,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
16467
16606
  }
16468
16607
  if (btcAmountToSelect > 0) {
16469
16608
  const availableCoins = virtualCoins.filter(
16470
- (c) => !selectedCoins.find((sc) => sc.txid === c.txid && sc.vout === c.vout)
16609
+ (coin) => !selectedOutpoints2.has(vtxoOutpoint(coin))
16471
16610
  );
16472
16611
  const { inputs: btcCoins } = selectVirtualCoins(availableCoins, btcAmountToSelect);
16473
16612
  for (const coin of btcCoins) {
@@ -16479,6 +16618,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
16479
16618
  }
16480
16619
  }
16481
16620
  selectedCoins = [...selectedCoins, ...btcCoins];
16621
+ for (const coin of btcCoins) selectedOutpoints2.add(vtxoOutpoint(coin));
16482
16622
  }
16483
16623
  }
16484
16624
  let totalBtcSelected = selectedCoins.reduce((sum, c) => sum + c.value, 0);
@@ -16495,19 +16635,58 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
16495
16635
  `send({ selectedVtxos }): inputs total ${totalBtcSelected} sats, outputs need ${totalBtcOutput}`
16496
16636
  );
16497
16637
  }
16498
- if (assetChanges.size > 0 && changeAmount < Number(this.dustAmount)) {
16499
- if (selectedVtxos) {
16500
- throw new Error(
16501
- `send({ selectedVtxos }): ${changeAmount} sats of change cannot carry ${assetChanges.size} asset change(s), needs ${this.dustAmount}`
16502
- );
16503
- }
16504
- const availableCoins = virtualCoins.filter(
16505
- (c) => !selectedCoins.find((sc) => sc.txid === c.txid && sc.vout === c.vout)
16638
+ if (selectedVtxos && assetChanges.size > 0 && changeAmount < Number(this.dustAmount)) {
16639
+ throw new Error(
16640
+ `send({ selectedVtxos }): ${changeAmount} sats of change cannot carry ${assetChanges.size} asset change(s), needs ${this.dustAmount}`
16641
+ );
16642
+ }
16643
+ const vtxoMinAmount = changeAmount > 0 || assetChanges.size > 0 ? (await this.arkProvider.getInfo()).vtxoMinAmount ?? 0n : 0n;
16644
+ if (selectedVtxos && changeAmount > 0 && BigInt(changeAmount) < vtxoMinAmount) {
16645
+ throw new Error(
16646
+ `send({ selectedVtxos }): ${changeAmount} sats of change is below the operator minimum of ${vtxoMinAmount} sats`
16506
16647
  );
16507
- const { inputs: extraCoins } = selectVirtualCoins(
16508
- availableCoins,
16509
- Number(this.dustAmount) - changeAmount
16648
+ }
16649
+ const selectedOutpoints = new Set(selectedCoins.map(vtxoOutpoint));
16650
+ while (!selectedVtxos && (changeAmount > 0 && BigInt(changeAmount) < vtxoMinAmount || assetChanges.size > 0 && BigInt(changeAmount) < this.dustAmount)) {
16651
+ const minimumChange = assetChanges.size > 0 && this.dustAmount > vtxoMinAmount ? this.dustAmount : vtxoMinAmount;
16652
+ const availableCoins = virtualCoins.filter(
16653
+ (coin) => !selectedOutpoints.has(vtxoOutpoint(coin))
16510
16654
  );
16655
+ let extraCoins;
16656
+ try {
16657
+ ({ inputs: extraCoins } = selectVirtualCoins(
16658
+ availableCoins,
16659
+ Number(minimumChange) - changeAmount
16660
+ ));
16661
+ } catch (error) {
16662
+ if (!(error instanceof Error) || error.message !== "Insufficient funds") {
16663
+ throw error;
16664
+ }
16665
+ if (recipients.every((r) => r.assets.length === 0)) {
16666
+ const plainCoins = virtualCoins.filter((coin) => !coin.assets?.length);
16667
+ const exact = plainCoins.find((coin) => coin.value === totalBtcOutput);
16668
+ let exactCoins = exact ? [exact] : void 0;
16669
+ if (!exactCoins) {
16670
+ const byAmount = /* @__PURE__ */ new Map();
16671
+ for (const coin of plainCoins) {
16672
+ const partner = byAmount.get(totalBtcOutput - coin.value);
16673
+ if (partner) {
16674
+ exactCoins = [partner, coin];
16675
+ break;
16676
+ }
16677
+ byAmount.set(coin.value, coin);
16678
+ }
16679
+ }
16680
+ if (exactCoins) {
16681
+ selectedCoins = exactCoins;
16682
+ assetChanges.clear();
16683
+ totalBtcSelected = totalBtcOutput;
16684
+ changeAmount = 0;
16685
+ break;
16686
+ }
16687
+ }
16688
+ throw new Error(`Cannot form minimum change amount of ${minimumChange} sats`);
16689
+ }
16511
16690
  for (const coin of extraCoins) {
16512
16691
  if (coin.assets) {
16513
16692
  for (const asset of coin.assets) {
@@ -16517,6 +16696,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
16517
16696
  }
16518
16697
  }
16519
16698
  selectedCoins = [...selectedCoins, ...extraCoins];
16699
+ for (const coin of extraCoins) selectedOutpoints.add(vtxoOutpoint(coin));
16520
16700
  totalBtcSelected += extraCoins.reduce((sum, c) => sum + c.value, 0);
16521
16701
  changeAmount = totalBtcSelected - totalBtcOutput;
16522
16702
  }
@@ -17903,7 +18083,8 @@ var Ramps = class {
17903
18083
  if (vtxos) reportUngatedInputs(this.wallet, vtxos);
17904
18084
  const spendable = vtxos ?? await this.wallet.getSpendableVtxos({
17905
18085
  withRecoverable: true,
17906
- withUnrolled: false
18086
+ withUnrolled: false,
18087
+ genericallySpendableOnly: true
17907
18088
  });
17908
18089
  const estimator = new Estimator(feeInfo?.intentFee ?? {});
17909
18090
  const { inputs, subtotal } = filterOffboardInputs(spendable, feeInfo, named);
@@ -17945,7 +18126,8 @@ var Ramps = class {
17945
18126
  if (vtxos) reportUngatedInputs(this.wallet, vtxos);
17946
18127
  const spendable = vtxos ?? await this.wallet.getSpendableVtxos({
17947
18128
  withRecoverable: true,
17948
- withUnrolled: false
18129
+ withUnrolled: false,
18130
+ genericallySpendableOnly: true
17949
18131
  });
17950
18132
  const estimator = new Estimator(feeInfo?.intentFee ?? {});
17951
18133
  const { inputs, subtotal } = filterOffboardInputs(spendable, feeInfo, named);
@@ -18332,7 +18514,7 @@ var WalletMessageHandler = class {
18332
18514
  return this.tagged({
18333
18515
  id,
18334
18516
  type: "SPENDABLE_VTXOS",
18335
- payload: { vtxos }
18517
+ payload: { vtxos, filterApplied: true }
18336
18518
  });
18337
18519
  }
18338
18520
  case "GET_BOARDING_UTXOS": {
@@ -18413,11 +18595,15 @@ var WalletMessageHandler = class {
18413
18595
  }
18414
18596
  case "GET_CONTRACTS_WITH_VTXOS": {
18415
18597
  const manager = await this.readonlyWallet.getContractManager();
18416
- const contracts = await manager.getContractsWithVtxos(message.payload.filter);
18598
+ const contracts = await manager.getContractsWithVtxos(
18599
+ message.payload.filter,
18600
+ void 0,
18601
+ message.payload.options
18602
+ );
18417
18603
  return this.tagged({
18418
18604
  id,
18419
18605
  type: "CONTRACTS_WITH_VTXOS",
18420
- payload: { contracts }
18606
+ payload: { contracts, filterApplied: true }
18421
18607
  });
18422
18608
  }
18423
18609
  case "WATCH_SCRIPT": {
@@ -18835,7 +19021,8 @@ var WalletMessageHandler = class {
18835
19021
  now: { timestamp: /* @__PURE__ */ new Date() },
18836
19022
  isPendingRecovery: (vtxo) => pendingOutpoints.has(`${vtxo.txid}:${vtxo.vout}`),
18837
19023
  isGenericallySpendable: (vtxo) => !isGatedVtxo(vtxo, gated),
18838
- isUnlocked: (vtxo) => unlocked.has(`${vtxo.txid}:${vtxo.vout}`)
19024
+ isUnlocked: (vtxo) => unlocked.has(`${vtxo.txid}:${vtxo.vout}`),
19025
+ dustCarrier: getDustAmount(this.readonlyWallet)
18839
19026
  });
18840
19027
  return {
18841
19028
  boarding: {
@@ -19088,7 +19275,7 @@ var WalletMessageHandler = class {
19088
19275
  if (v.isUnrolled) {
19089
19276
  return withUnrolled;
19090
19277
  }
19091
- if (hasTerminalSpend(v)) {
19278
+ if (isVtxoSpent(v)) {
19092
19279
  return false;
19093
19280
  }
19094
19281
  if (includeRecoverable) {
@@ -19941,7 +20128,13 @@ var ServiceWorkerReadonlyWallet = class _ServiceWorkerReadonlyWallet {
19941
20128
  };
19942
20129
  try {
19943
20130
  const response = await this.sendMessage(message);
19944
- return response.payload.vtxos.map(normalizeVtxo);
20131
+ const payload = response.payload;
20132
+ if ((filter?.watchedOnly || filter?.genericallySpendableOnly || filter?.requireSynced) && payload.filterApplied !== true) {
20133
+ throw new Error(
20134
+ "Service worker does not support the requested contract scope or freshness check"
20135
+ );
20136
+ }
20137
+ return payload.vtxos.map(normalizeVtxo);
19945
20138
  } catch (error) {
19946
20139
  throw new Error(`Failed to get spendable vtxos: ${error}`);
19947
20140
  }
@@ -20026,15 +20219,20 @@ var ServiceWorkerReadonlyWallet = class _ServiceWorkerReadonlyWallet {
20026
20219
  throw new Error("Failed to get contracts");
20027
20220
  }
20028
20221
  },
20029
- async getContractsWithVtxos(filter) {
20222
+ async getContractsWithVtxos(filter, _pageSize, options) {
20030
20223
  const message = {
20031
20224
  type: "GET_CONTRACTS_WITH_VTXOS",
20032
20225
  id: getRandomId(),
20033
20226
  tag: messageTag,
20034
- payload: { filter }
20227
+ payload: { filter, options }
20035
20228
  };
20036
20229
  try {
20037
20230
  const response = await sendContractMessage(message);
20231
+ if (options?.requireSynced && response.payload.filterApplied !== true) {
20232
+ throw new Error(
20233
+ "Service worker does not support the requested freshness check"
20234
+ );
20235
+ }
20038
20236
  await refreshSyncState();
20039
20237
  return response.payload.contracts;
20040
20238
  } catch (e) {
@@ -23512,7 +23710,9 @@ __export(arkade_exports, {
23512
23710
  fromASM: () => fromASM,
23513
23711
  getOpcodeName: () => getOpcodeName,
23514
23712
  getOpcodeValue: () => getOpcodeValue,
23713
+ isContractArtifact: () => isContractArtifact,
23515
23714
  parseArtifact: () => parseArtifact,
23715
+ programFromArtifact: () => programFromArtifact,
23516
23716
  resolveAsm: () => resolveAsm,
23517
23717
  serializeArkadeContractParams: () => serializeArkadeContractParams,
23518
23718
  stringifyArtifact: () => stringifyArtifact,
@@ -23710,6 +23910,263 @@ function createArkadeBatchHandler(intentId, inputs, signer, signedProof, message
23710
23910
  }
23711
23911
  };
23712
23912
  }
23913
+ var NATIVE_STRUCTS = {
23914
+ AssetId: [
23915
+ ["txid", "bytes32"],
23916
+ ["gidx", "int"]
23917
+ ],
23918
+ Outpoint: [
23919
+ ["txid", "bytes32"],
23920
+ ["vout", "int"]
23921
+ ],
23922
+ ECPoint: [
23923
+ ["x", "int"],
23924
+ ["y", "int"]
23925
+ ]
23926
+ };
23927
+ var HASH_OPCODES = /* @__PURE__ */ new Set(["OP_SHA256", "OP_HASH160", "OP_HASH256", "OP_RIPEMD160"]);
23928
+ function isRecord(value) {
23929
+ return typeof value === "object" && value !== null && !Array.isArray(value);
23930
+ }
23931
+ function isNamed(value) {
23932
+ return isRecord(value) && typeof value.name === "string" && typeof value.type === "string" && value.type.length > 0;
23933
+ }
23934
+ function isContractArtifact(value) {
23935
+ if (!isRecord(value) || typeof value.contractName !== "string") return false;
23936
+ if (!Array.isArray(value.constructorInputs)) return false;
23937
+ if (value.structs !== void 0 && !Array.isArray(value.structs)) return false;
23938
+ if (!Array.isArray(value.functions) || value.functions.length === 0) return false;
23939
+ return value.functions.every(
23940
+ (group) => isRecord(group) && typeof group.name === "string" && Array.isArray(group.leaves) && group.leaves.length > 0
23941
+ );
23942
+ }
23943
+ function fail(detail) {
23944
+ throw new Error(`programFromArtifact: ${detail}`);
23945
+ }
23946
+ function claim(seen, name, kind) {
23947
+ if (seen.has(name)) fail(`duplicate ${kind} '${name}'`);
23948
+ seen.add(name);
23949
+ }
23950
+ var SCALAR_TYPES = {
23951
+ pubkey: "pubkey",
23952
+ signature: "sig",
23953
+ bytes: "bytes",
23954
+ bytes20: "hash",
23955
+ bytes32: "hash",
23956
+ asset: "hash",
23957
+ int: "int",
23958
+ bool: "int"
23959
+ };
23960
+ function argType(arkType) {
23961
+ if (!Object.hasOwn(SCALAR_TYPES, arkType)) fail(`unknown type '${arkType}'`);
23962
+ return SCALAR_TYPES[arkType];
23963
+ }
23964
+ function arrayParts(type) {
23965
+ const open = type.indexOf("[");
23966
+ if (open < 0 || !type.endsWith("]")) return [type, 0];
23967
+ const length = Number(type.slice(open + 1, -1));
23968
+ return Number.isInteger(length) && length > 0 ? [type.slice(0, open), length] : [type, 0];
23969
+ }
23970
+ function flatten(param, structs, stack = []) {
23971
+ if (!isNamed(param)) fail("parameter needs a name and a type");
23972
+ if (Object.hasOwn(NATIVE_STRUCTS, param.type)) {
23973
+ return NATIVE_STRUCTS[param.type].map(([field, type]) => ({
23974
+ name: `${param.name}.${field}`,
23975
+ type: argType(type)
23976
+ }));
23977
+ }
23978
+ const declared = structs.find((struct) => struct?.name === param.type);
23979
+ if (declared) {
23980
+ if (stack.includes(param.type)) fail(`recursive struct layout '${param.type}'`);
23981
+ return declared.fields.flatMap(
23982
+ (field) => flatten({ name: `${param.name}.${field.name}`, type: field.type }, structs, [
23983
+ ...stack,
23984
+ param.type
23985
+ ])
23986
+ );
23987
+ }
23988
+ const [element, length] = arrayParts(param.type);
23989
+ if (length === 0) {
23990
+ if (param.type.includes("[") || param.type.includes("]")) {
23991
+ fail(`invalid array type '${param.type}'`);
23992
+ }
23993
+ return [{ name: param.name, type: argType(param.type) }];
23994
+ }
23995
+ if (Object.hasOwn(NATIVE_STRUCTS, element) || structs.some((struct) => struct.name === element)) {
23996
+ fail(`arrays of structs are not supported: '${param.type}'`);
23997
+ }
23998
+ return Array.from({ length }, (_, index) => ({
23999
+ name: `${param.name}.${index}`,
24000
+ type: argType(element)
24001
+ }));
24002
+ }
24003
+ function opcodeToken(token) {
24004
+ const base = token.slice(3);
24005
+ const name = base === "0" || /^([1-9]|1[0-6])$/.test(base) ? token : base;
24006
+ if (!Object.hasOwn(ARKADE_OPS, name)) {
24007
+ fail(`opcode '${token}' is not in this SDK's table`);
24008
+ }
24009
+ return name;
24010
+ }
24011
+ function instantiationParam(body) {
24012
+ return `contract_${body.replace(/[^A-Za-z0-9]+/g, "_")}`.replace(/_+$/, "");
24013
+ }
24014
+ function asmToken(token, instantiations) {
24015
+ if (token.startsWith("OP_")) return opcodeToken(token);
24016
+ if (token.startsWith("<") && token.endsWith(">")) {
24017
+ const inner = token.slice(1, -1);
24018
+ if (inner.startsWith("CONTRACT:")) {
24019
+ const body = inner.slice("CONTRACT:".length);
24020
+ const name = instantiationParam(body);
24021
+ const seen = instantiations.get(name);
24022
+ if (seen !== void 0 && seen !== body) {
24023
+ fail(`instantiations '${seen}' and '${body}' both map to parameter '${name}'`);
24024
+ }
24025
+ instantiations.set(name, body);
24026
+ return `$${name}`;
24027
+ }
24028
+ if (inner.startsWith("VTXO:")) {
24029
+ fail(`'${token}' is not a placeholder; the compiler emits <CONTRACT:\u2026>`);
24030
+ }
24031
+ if (inner === "SERVER_KEY" || inner.startsWith("EMULATOR_KEY:")) {
24032
+ fail(`${token} is a signer role and cannot appear in a covenant`);
24033
+ }
24034
+ return `$${inner}`;
24035
+ }
24036
+ if (token.startsWith("0x")) return hex.decode(token.slice(2));
24037
+ try {
24038
+ return BigInt(token);
24039
+ } catch {
24040
+ fail(`unrecognized assembly token '${token}'`);
24041
+ }
24042
+ }
24043
+ function providedWitness(witness) {
24044
+ if (witness === void 0) return [];
24045
+ if (!Array.isArray(witness)) fail("leaf witness must be an array");
24046
+ return witness.filter((item) => {
24047
+ if (!isNamed(item)) fail("witness item needs a name and a type");
24048
+ return !item.injected && item.type !== "signature";
24049
+ });
24050
+ }
24051
+ function parseLeaf(leaf, hasCovenant, instantiations, extras) {
24052
+ const asm = leaf.asm;
24053
+ let index = 0;
24054
+ let condition;
24055
+ if (asm.length >= 4 && HASH_OPCODES.has(asm[0]) && asm[2] === "OP_EQUAL") {
24056
+ if (asm[3] !== "OP_VERIFY")
24057
+ fail(`leaf '${leaf.name}': hash condition must end in OP_VERIFY`);
24058
+ condition = asm.slice(0, 3).map((token) => asmToken(token, instantiations));
24059
+ index = 4;
24060
+ }
24061
+ let csv;
24062
+ let cltv;
24063
+ if (asm.length >= index + 3 && asm[index + 2] === "OP_DROP") {
24064
+ const operand = asm[index].startsWith("<") && asm[index].endsWith(">") ? `$${asm[index].slice(1, -1)}` : BigInt(asm[index]);
24065
+ if (asm[index + 1] === "OP_CHECKSEQUENCEVERIFY") csv = { type: "blocks", value: operand };
24066
+ else if (asm[index + 1] === "OP_CHECKLOCKTIMEVERIFY") cltv = operand;
24067
+ else fail(`leaf '${leaf.name}': unexpected timelock opcode ${asm[index + 1]}`);
24068
+ index += 3;
24069
+ }
24070
+ const signers = [];
24071
+ let emulator;
24072
+ while (index < asm.length) {
24073
+ const key = asm[index];
24074
+ const terminator = asm[index + 1];
24075
+ const last = terminator === "OP_CHECKSIG";
24076
+ if (!last && terminator !== "OP_CHECKSIGVERIFY") {
24077
+ fail(`leaf '${leaf.name}': expected CHECKSIG after '${key}', found '${terminator}'`);
24078
+ }
24079
+ if (!key.startsWith("<") || !key.endsWith(">")) {
24080
+ fail(`leaf '${leaf.name}': unsupported key operand '${key}'`);
24081
+ }
24082
+ const inner = key.slice(1, -1);
24083
+ if (inner === "SERVER_KEY") signers.push("$server");
24084
+ else if (inner.startsWith("TWEAK:")) {
24085
+ const [, base, fn] = inner.match(/^TWEAK:([^:]+):([^:]+)$/) ?? [];
24086
+ if (!base || !fn) fail(`leaf '${leaf.name}': malformed tweak '${key}'`);
24087
+ signers.push({ tweak: `$${base}`, fn });
24088
+ } else if (inner.startsWith("EMULATOR_KEY:")) {
24089
+ if (!hasCovenant) fail(`leaf '${leaf.name}': ${key} needs a covenant`);
24090
+ if (!last) fail(`leaf '${leaf.name}': ${key} must be the last signer`);
24091
+ emulator = inner.slice("EMULATOR_KEY:".length);
24092
+ } else signers.push(`$${inner}`);
24093
+ index += 2;
24094
+ }
24095
+ const constructorTweak = signers.some((signer) => typeof signer !== "string");
24096
+ if (hasCovenant && emulator === void 0 && !constructorTweak) {
24097
+ fail(
24098
+ `leaf '${leaf.name}': covenant leaf must sign with the emulator or a tweaked constructor key`
24099
+ );
24100
+ }
24101
+ if (signers.length === 0) fail(`leaf '${leaf.name}': at least one named signer is required`);
24102
+ return {
24103
+ signers,
24104
+ ...emulator !== void 0 ? { emulator } : hasCovenant ? { emulator: null } : {},
24105
+ ...condition ? { asm: condition } : {},
24106
+ ...csv ? { csv } : {},
24107
+ ...cltv !== void 0 ? { cltv } : {},
24108
+ ...extras.length > 0 ? { witness: extras.map((item) => item.name) } : {}
24109
+ };
24110
+ }
24111
+ function programFromArtifact(artifact) {
24112
+ if (!isContractArtifact(artifact)) {
24113
+ fail(
24114
+ "expected a complete arkadec artifact with contractName, constructorInputs, and non-empty functions"
24115
+ );
24116
+ }
24117
+ const structs = artifact.structs ?? [];
24118
+ const structNames = /* @__PURE__ */ new Set();
24119
+ for (const struct of structs) {
24120
+ if (!isRecord(struct) || typeof struct.name !== "string") continue;
24121
+ if (Object.hasOwn(SCALAR_TYPES, struct.name) || Object.hasOwn(NATIVE_STRUCTS, struct.name)) {
24122
+ fail(`struct name '${struct.name}' shadows a built-in type`);
24123
+ }
24124
+ claim(structNames, struct.name, "struct");
24125
+ }
24126
+ const instantiations = /* @__PURE__ */ new Map();
24127
+ const functions = {};
24128
+ const groups = /* @__PURE__ */ new Set();
24129
+ const leafNames = /* @__PURE__ */ new Set();
24130
+ for (const group of artifact.functions) {
24131
+ claim(groups, group.name, "spend group");
24132
+ const covenantInputs = (group.arkade?.inputs ?? []).flatMap(
24133
+ (input) => flatten(input, structs)
24134
+ );
24135
+ const arkadeScript = group.arkade ? {
24136
+ asm: group.arkade.asm.map((token) => asmToken(token, instantiations)),
24137
+ // Covenant witness is reverse declaration order.
24138
+ witness: covenantInputs.map((field) => field.name).reverse()
24139
+ } : void 0;
24140
+ for (const [index, leaf] of group.leaves.entries()) {
24141
+ const name = index === 0 ? group.name : `${group.name}/${index}:${leaf.name}`;
24142
+ claim(leafNames, name, "function name");
24143
+ const extras = providedWitness(leaf.witness);
24144
+ const tapscript = parseLeaf(leaf, group.arkade !== void 0, instantiations, extras);
24145
+ const inputs = [
24146
+ ...covenantInputs,
24147
+ ...extras.map((item) => ({ name: item.name, type: argType(item.type) }))
24148
+ ];
24149
+ functions[name] = {
24150
+ ...inputs.length > 0 ? { inputs } : {},
24151
+ tapscript,
24152
+ ...arkadeScript ? { arkadeScript } : {}
24153
+ };
24154
+ }
24155
+ }
24156
+ const params = [
24157
+ ...artifact.constructorInputs.flatMap((input) => flatten(input, structs)),
24158
+ { name: "server", type: "pubkey" },
24159
+ ...[...instantiations.keys()].map((name) => ({ name, type: "hash" }))
24160
+ ];
24161
+ const paramNames = /* @__PURE__ */ new Set();
24162
+ for (const param of params) claim(paramNames, param.name, "program parameter");
24163
+ return {
24164
+ version: SUPPORTED_PROGRAM_VERSION,
24165
+ name: artifact.contractName,
24166
+ params,
24167
+ functions
24168
+ };
24169
+ }
23713
24170
  var Arkade = class _Arkade {
23714
24171
  arkProvider;
23715
24172
  /** The co-signing service, or undefined for emulator-less (pure tapscript) usage. */
@@ -23922,9 +24379,7 @@ var ArkadeContract = class _ArkadeContract {
23922
24379
  const [registered] = await manager.getContracts({ script: scriptHex });
23923
24380
  if (registered) {
23924
24381
  const [withVtxos] = await manager.getContractsWithVtxos({ script: scriptHex });
23925
- return (withVtxos?.vtxos ?? []).filter(
23926
- (v) => !hasTerminalSpend(v) && !v.isUnrolled
23927
- );
24382
+ return (withVtxos?.vtxos ?? []).filter((v) => !isVtxoSpent(v) && !v.isUnrolled);
23928
24383
  }
23929
24384
  }
23930
24385
  if (!this.client.indexer) {
@@ -23934,7 +24389,7 @@ var ArkadeContract = class _ArkadeContract {
23934
24389
  scripts: [scriptHex],
23935
24390
  spendableOnly: true
23936
24391
  });
23937
- return vtxos.filter((v) => !hasTerminalSpend(v) && !v.isUnrolled);
24392
+ return vtxos.filter((v) => !isVtxoSpent(v) && !v.isUnrolled);
23938
24393
  }
23939
24394
  /** Total spendable balance (requires an indexer). */
23940
24395
  async getBalance() {
@@ -24199,6 +24654,7 @@ var BIP21Error = /* @__PURE__ */ ((BIP21Error2) => {
24199
24654
  BIP21Error2["INVALID_ADDRESS"] = "Invalid address";
24200
24655
  return BIP21Error2;
24201
24656
  })(BIP21Error || {});
24657
+ var formatAmount = (btc) => btc.toFixed(8).replace(/\.?0+$/, "");
24202
24658
  var BIP21 = class _BIP21 {
24203
24659
  /**
24204
24660
  * Create a BIP21 URI from the provided parameters.
@@ -24220,7 +24676,7 @@ var BIP21 = class _BIP21 {
24220
24676
  if (amount < 0) {
24221
24677
  continue;
24222
24678
  }
24223
- queryParams[key] = value;
24679
+ queryParams[key] = formatAmount(amount);
24224
24680
  } else if (key === "ark") {
24225
24681
  if (typeof value === "string" && (value.startsWith("ark") || value.startsWith("tark"))) {
24226
24682
  queryParams[key] = value;
@@ -24657,6 +25113,6 @@ function createDefaultPaymentRouter(wallet) {
24657
25113
  (*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
24658
25114
  */
24659
25115
 
24660
- 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 };
24661
- //# sourceMappingURL=chunk-FTLPTF3R.js.map
24662
- //# sourceMappingURL=chunk-FTLPTF3R.js.map
25116
+ 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 };
25117
+ //# sourceMappingURL=chunk-MLOZHTBN.js.map
25118
+ //# sourceMappingURL=chunk-MLOZHTBN.js.map