@arkade-os/sdk 0.4.67 → 0.5.0-rc.0

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 (80) hide show
  1. package/README.md +40 -5
  2. package/dist/adapters/expo.cjs +5 -5
  3. package/dist/adapters/expo.d.cts +2 -2
  4. package/dist/adapters/expo.d.ts +2 -2
  5. package/dist/adapters/expo.js +3 -3
  6. package/dist/adapters/indexedDB.cjs +5 -5
  7. package/dist/adapters/indexedDB.js +4 -4
  8. package/dist/{asyncStorageTaskQueue-Za-e_zMk.d.ts → asyncStorageTaskQueue-BCzN3Y45.d.ts} +2 -2
  9. package/dist/{asyncStorageTaskQueue-LTpeXYaD.d.cts → asyncStorageTaskQueue-CdF_ELX8.d.cts} +2 -2
  10. package/dist/{chunk-MJUTO3PY.cjs → chunk-5MJHZHG7.cjs} +771 -477
  11. package/dist/chunk-5MJHZHG7.cjs.map +1 -0
  12. package/dist/{chunk-2JV4VQ7C.cjs → chunk-6AFUNKK7.cjs} +33 -7
  13. package/dist/chunk-6AFUNKK7.cjs.map +1 -0
  14. package/dist/{chunk-AEWJU6NZ.js → chunk-6F2Q32KA.js} +352 -59
  15. package/dist/chunk-6F2Q32KA.js.map +1 -0
  16. package/dist/{chunk-EALWOVHK.js → chunk-ABJPM4JF.js} +4 -4
  17. package/dist/{chunk-EALWOVHK.js.map → chunk-ABJPM4JF.js.map} +1 -1
  18. package/dist/{chunk-CO3KKSWM.cjs → chunk-CZ32XADN.cjs} +17 -17
  19. package/dist/{chunk-CO3KKSWM.cjs.map → chunk-CZ32XADN.cjs.map} +1 -1
  20. package/dist/{chunk-X3VEMUVV.js → chunk-FVTJJQNM.js} +3 -3
  21. package/dist/{chunk-X3VEMUVV.js.map → chunk-FVTJJQNM.js.map} +1 -1
  22. package/dist/{chunk-OIZ2V2VD.cjs → chunk-HL7EC2Z5.cjs} +121 -170
  23. package/dist/chunk-HL7EC2Z5.cjs.map +1 -0
  24. package/dist/{chunk-MZFUZUTQ.js → chunk-JYP7YXT2.js} +4 -4
  25. package/dist/{chunk-MZFUZUTQ.js.map → chunk-JYP7YXT2.js.map} +1 -1
  26. package/dist/{chunk-6TAQF6EV.js → chunk-KTU44AEH.js} +75 -124
  27. package/dist/chunk-KTU44AEH.js.map +1 -0
  28. package/dist/{chunk-YDRZDZPP.cjs → chunk-PB5KJI4U.cjs} +10 -10
  29. package/dist/{chunk-YDRZDZPP.cjs.map → chunk-PB5KJI4U.cjs.map} +1 -1
  30. package/dist/{chunk-3WIWNE4P.js → chunk-PPG6W34P.js} +30 -8
  31. package/dist/chunk-PPG6W34P.js.map +1 -0
  32. package/dist/{chunk-R2YAMONM.cjs → chunk-RXWKCEZP.cjs} +17 -17
  33. package/dist/{chunk-R2YAMONM.cjs.map → chunk-RXWKCEZP.cjs.map} +1 -1
  34. package/dist/contracts/handlers/index.cjs +9 -9
  35. package/dist/contracts/handlers/index.d.cts +3 -3
  36. package/dist/contracts/handlers/index.d.ts +3 -3
  37. package/dist/contracts/handlers/index.js +2 -2
  38. package/dist/{delegate-Bz2e2zXY.d.cts → delegate-DLIOBQ5x.d.cts} +1 -1
  39. package/dist/{delegate-cdmzb2f9.d.ts → delegate-Dyn5L80J.d.ts} +1 -1
  40. package/dist/{hdWalletCapable-BSjuUYOO.d.cts → hdWalletCapable-D9tooZ6F.d.ts} +102 -27
  41. package/dist/{hdWalletCapable-Hi12xjCJ.d.ts → hdWalletCapable-Dt3MUiGE.d.cts} +102 -27
  42. package/dist/{index-BiACuWaf.d.ts → index-D3hmsuKs.d.cts} +100 -123
  43. package/dist/{index-D2GjZxBr.d.cts → index-D9tXd-tN.d.ts} +100 -123
  44. package/dist/{index-DU5o_hz7.d.ts → index-Udm5V2xW.d.cts} +184 -19
  45. package/dist/{index-DU5o_hz7.d.cts → index-Udm5V2xW.d.ts} +184 -19
  46. package/dist/index.cjs +309 -289
  47. package/dist/index.d.cts +125 -29
  48. package/dist/index.d.ts +125 -29
  49. package/dist/index.js +4 -4
  50. package/dist/repositories/realm/index.cjs +19 -19
  51. package/dist/repositories/realm/index.d.cts +2 -2
  52. package/dist/repositories/realm/index.d.ts +2 -2
  53. package/dist/repositories/realm/index.js +5 -5
  54. package/dist/repositories/sqlite/index.cjs +18 -18
  55. package/dist/repositories/sqlite/index.d.cts +2 -2
  56. package/dist/repositories/sqlite/index.d.ts +2 -2
  57. package/dist/repositories/sqlite/index.js +5 -5
  58. package/dist/{taskRunner-BZJMLQ6g.d.cts → taskRunner-DxZciNaa.d.cts} +2 -2
  59. package/dist/{taskRunner-DATaRQus.d.ts → taskRunner-ZdS16Ypm.d.ts} +2 -2
  60. package/dist/wallet/expo/background.cjs +14 -14
  61. package/dist/wallet/expo/background.d.cts +3 -3
  62. package/dist/wallet/expo/background.d.ts +3 -3
  63. package/dist/wallet/expo/background.js +6 -6
  64. package/dist/wallet/expo/index.cjs +23 -14
  65. package/dist/wallet/expo/index.cjs.map +1 -1
  66. package/dist/wallet/expo/index.d.cts +7 -4
  67. package/dist/wallet/expo/index.d.ts +7 -4
  68. package/dist/wallet/expo/index.js +14 -5
  69. package/dist/wallet/expo/index.js.map +1 -1
  70. package/dist/worker/expo/index.cjs +9 -9
  71. package/dist/worker/expo/index.d.cts +4 -4
  72. package/dist/worker/expo/index.d.ts +4 -4
  73. package/dist/worker/expo/index.js +5 -5
  74. package/package.json +3 -2
  75. package/dist/chunk-2JV4VQ7C.cjs.map +0 -1
  76. package/dist/chunk-3WIWNE4P.js.map +0 -1
  77. package/dist/chunk-6TAQF6EV.js.map +0 -1
  78. package/dist/chunk-AEWJU6NZ.js.map +0 -1
  79. package/dist/chunk-MJUTO3PY.cjs.map +0 -1
  80. package/dist/chunk-OIZ2V2VD.cjs.map +0 -1
@@ -1,6 +1,6 @@
1
- import { AssetId, AssetGroup, AssetInput, AssetOutput, Metadata, Packet, BufferReader, BufferWriter, AssetRef, RestIndexerProvider } from './chunk-X3VEMUVV.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-6TAQF6EV.js';
3
- import { VtxoScript, Transaction, isRecoverable, CSVMultisigTapscript, 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, baseFetch, Intent, rateGate, DEFAULT_NETWORK_NAME, normalizeVtxo, getSequence, toBatchExpiry, maybeArkError, isArkError, ArkErrorName, toOffchainInputFeeParams, canRecoverOnchain, getNetwork, MultisigTapscript, isEventSourceError, isEventSourceUnavailableError, isRetryableProviderError, getAllNormalizedVtxos, DEFAULT_PAGE_SIZE, RestArkProvider, toVirtualStatus, ArkError, TapTreeCoder, resolveTimeHeight, canSpendOffchain, parseLegacyExpiry, isExpired, PrevoutTxField, DEFAULT_ARKADE_SERVER_URL, ProviderUnavailableError, networks as networks$1, isPastExpiry, fetchVtxoCreatedAtByTxid } from './chunk-3WIWNE4P.js';
1
+ import { AssetId, AssetGroup, AssetInput, AssetOutput, Metadata, Packet, BufferReader, BufferWriter, AssetRef, RestIndexerProvider } from './chunk-FVTJJQNM.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-KTU44AEH.js';
3
+ import { VtxoScript, Transaction, isRecoverable, CSVMultisigTapscript, 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, getNetwork, MultisigTapscript, isEventSourceError, isEventSourceUnavailableError, isRetryableProviderError, getAllNormalizedVtxos, DEFAULT_PAGE_SIZE, RestArkProvider, networkFromArkadeInfo, toVirtualStatus, ArkError, TapTreeCoder, resolveTimeHeight, canSpendOffchain, parseLegacyExpiry, isExpired, INFO_FETCH_TIMEOUT_MS, PrevoutTxField, ProviderUnavailableError, DEFAULT_ARKADE_SERVER_URL, networks as networks$1, isPastExpiry, fetchVtxoCreatedAtByTxid } from './chunk-PPG6W34P.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';
@@ -272,7 +272,7 @@ function getPrevOutput(finalKey, graph, sharedOutputAmount, tx) {
272
272
  script: pkScript
273
273
  };
274
274
  }
275
- var ALL_SIGHASH = Object.values(SigHash).filter((x) => typeof x === "number");
275
+ var ALLOWED_SIGHASH = [SigHash.DEFAULT, SigHash.ALL, SigHash.ALL_ANYONECANPAY];
276
276
  var SingleKey = class _SingleKey {
277
277
  key;
278
278
  constructor(key) {
@@ -301,8 +301,9 @@ var SingleKey = class _SingleKey {
301
301
  async sign(tx, inputIndexes) {
302
302
  const txCpy = tx.clone();
303
303
  if (!inputIndexes) {
304
+ assertAllowedSighashTypes(txCpy, ALLOWED_SIGHASH);
304
305
  try {
305
- if (!txCpy.sign(this.key, ALL_SIGHASH)) {
306
+ if (!txCpy.sign(this.key, ALLOWED_SIGHASH)) {
306
307
  throw new Error("Failed to sign transaction");
307
308
  }
308
309
  } catch (e) {
@@ -313,7 +314,7 @@ var SingleKey = class _SingleKey {
313
314
  return txCpy;
314
315
  }
315
316
  for (const inputIndex of inputIndexes) {
316
- if (!txCpy.signIdx(this.key, inputIndex, ALL_SIGHASH)) {
317
+ if (!txCpy.signIdx(this.key, inputIndex, ALLOWED_SIGHASH)) {
317
318
  throw new Error(`Failed to sign input #${inputIndex}`);
318
319
  }
319
320
  }
@@ -378,7 +379,7 @@ var ReadonlySingleKey = class _ReadonlySingleKey {
378
379
  return Promise.resolve(this.publicKey);
379
380
  }
380
381
  };
381
- var ALL_SIGHASH2 = Object.values(SigHash).filter((x) => typeof x === "number");
382
+ var ALLOWED_SIGHASH2 = [SigHash.DEFAULT, SigHash.ALL, SigHash.ALL_ANYONECANPAY];
382
383
  var seedBytes = /* @__PURE__ */ new WeakMap();
383
384
  var mnemonicMeta = /* @__PURE__ */ new WeakMap();
384
385
  var SeedIdentity = class _SeedIdentity {
@@ -581,8 +582,9 @@ var SeedIdentity = class _SeedIdentity {
581
582
  signTxWithKey(tx, key, inputIndexes) {
582
583
  const txCpy = tx.clone();
583
584
  if (!inputIndexes) {
585
+ assertAllowedSighashTypes(txCpy, ALLOWED_SIGHASH2);
584
586
  try {
585
- if (!txCpy.sign(key, ALL_SIGHASH2)) {
587
+ if (!txCpy.sign(key, ALLOWED_SIGHASH2)) {
586
588
  throw new Error("Failed to sign transaction");
587
589
  }
588
590
  } catch (e) {
@@ -592,7 +594,7 @@ var SeedIdentity = class _SeedIdentity {
592
594
  }
593
595
  } else {
594
596
  for (const idx of inputIndexes) {
595
- if (!txCpy.signIdx(key, idx, ALL_SIGHASH2)) {
597
+ if (!txCpy.signIdx(key, idx, ALLOWED_SIGHASH2)) {
596
598
  throw new Error(`Failed to sign input #${idx}`);
597
599
  }
598
600
  }
@@ -3085,9 +3087,6 @@ function hasBoardingTxExpired(coin, boardingTimelock, chainTipHeight) {
3085
3087
  const blockTime = BigInt(Math.floor(coin.status.block_time));
3086
3088
  return blockTime + boardingTimelock.value <= now;
3087
3089
  }
3088
- function formatSighash(type) {
3089
- return `0x${type.toString(16).padStart(2, "0")}`;
3090
- }
3091
3090
  function verifyTapscriptSignatures(tx, inputIndex, requiredSigners, excludePubkeys = [], allowedSighashTypes = [SigHash.DEFAULT], expectedLeafHash) {
3092
3091
  const input = tx.getInput(inputIndex);
3093
3092
  const prevoutScripts = [];
@@ -3305,6 +3304,15 @@ async function submitOffchainTx(provider, offchainTx, signer, hooks, options) {
3305
3304
  assertSubmittedArkTxid(response, signedArkTx, "submitTx");
3306
3305
  const { arkTxid, signedCheckpointTxs } = response;
3307
3306
  const matched = matchServerCheckpoints(signedCheckpointTxs, offchainTx.checkpoints, "submitTx");
3307
+ matched.forEach(({ server }, index) => {
3308
+ try {
3309
+ assertAllowedSighashTypes(server);
3310
+ } catch (error) {
3311
+ throw new ServerResponseMismatchError(
3312
+ `submitTx checkpoint ${index}: ${error instanceof Error ? error.message : String(error)}`
3313
+ );
3314
+ }
3315
+ });
3308
3316
  const verify = options?.verifyServerSignatures;
3309
3317
  if (verify) {
3310
3318
  if (response.finalArkTx === void 0) {
@@ -3643,7 +3651,7 @@ function isValidArkAddress2(address) {
3643
3651
  return false;
3644
3652
  }
3645
3653
  }
3646
- function assertRecipientArkAddress(encoded, address, context) {
3654
+ function assertRecipientArkadeAddress(encoded, address, context) {
3647
3655
  if (address.hrp !== context.hrp) {
3648
3656
  throw new Error(
3649
3657
  `Invalid Arkade address ${encoded}: expected prefix "${context.hrp}", got "${address.hrp}"`
@@ -3686,7 +3694,7 @@ function validateRecipients(recipients, dustAmount, context) {
3686
3694
  } catch (e) {
3687
3695
  throw new Error(`Invalid Arkade address: ${recipient.address}`);
3688
3696
  }
3689
- assertRecipientArkAddress(recipient.address, address, context);
3697
+ assertRecipientArkadeAddress(recipient.address, address, context);
3690
3698
  const amount = recipient.amount || dustAmount;
3691
3699
  if (amount <= 0) {
3692
3700
  throw new Error("Amount must be positive");
@@ -3906,6 +3914,7 @@ function getRecoverableWithSubdust(vtxos, dustAmount, now) {
3906
3914
  };
3907
3915
  }
3908
3916
  function isVtxoExpiringSoon(vtxo, thresholdMs) {
3917
+ if (vtxo.isUnrolled) return false;
3909
3918
  const realThresholdMs = thresholdMs <= 100 ? DEFAULT_THRESHOLD_MS : thresholdMs;
3910
3919
  const expiresAt = normalizeVtxo(vtxo).expiresAt;
3911
3920
  if (expiresAt === void 0) return false;
@@ -3916,7 +3925,9 @@ function isVtxoExpiringSoon(vtxo, thresholdMs) {
3916
3925
  }
3917
3926
  function getExpiringAndRecoverableVtxos(vtxos, thresholdMs, dustAmount, now) {
3918
3927
  return vtxos.filter(
3919
- (vtxo) => isVtxoExpiringSoon(vtxo, thresholdMs) || canRecoverOnchain(vtxo, now) || isSubdust(vtxo, dustAmount)
3928
+ // The leading exclusion covers the `isSubdust` arm too, which is a value
3929
+ // property and would otherwise re-admit an exited coin on its own.
3930
+ (vtxo) => !vtxo.isUnrolled && (isVtxoExpiringSoon(vtxo, thresholdMs) || canRecoverOnchain(vtxo, now) || isSubdust(vtxo, dustAmount))
3920
3931
  );
3921
3932
  }
3922
3933
  function isMigrationCapable(wallet) {
@@ -4574,7 +4585,7 @@ var VtxoManager = class _VtxoManager {
4574
4585
  }
4575
4586
  /**
4576
4587
  * Core migration routine shared by the manual API and the automatic poll
4577
- * pass. Fetches a fresh {@link ArkInfo}, applies a mid-session signer
4588
+ * pass. Fetches a fresh {@link ArkadeInfo}, applies a mid-session signer
4578
4589
  * rotation when the wallet's own snapshot signer has been deprecated,
4579
4590
  * selects spendable VTXOs under deprecated-signer contracts (cutoff-first),
4580
4591
  * and settles them to the active-signer Ark address.
@@ -6357,12 +6368,12 @@ async function saveArkInfoSnapshot(repo, info, savedAt) {
6357
6368
  }
6358
6369
  }));
6359
6370
  }
6360
- async function resolveArkInfo(arkProvider, walletRepository) {
6371
+ async function resolveArkInfo(arkProvider, walletRepository, opts) {
6361
6372
  let info;
6362
6373
  try {
6363
6374
  info = await arkProvider.getInfo();
6364
6375
  } catch (err) {
6365
- if (!isRetryableProviderError(err)) throw err;
6376
+ if (opts?.requireLive || !isRetryableProviderError(err)) throw err;
6366
6377
  const snapshot = await loadArkInfoSnapshot(walletRepository);
6367
6378
  if (!snapshot) {
6368
6379
  throw new ProviderUnavailableError(
@@ -6448,6 +6459,13 @@ var TxTree = class {
6448
6459
  if (nbOfInputs !== 1) {
6449
6460
  throw new Error(`unexpected number of inputs: ${nbOfInputs}, expected 1`);
6450
6461
  }
6462
+ if (this.root.lockTime !== 0) {
6463
+ throw new Error(`unexpected locktime: ${this.root.lockTime}, expected 0`);
6464
+ }
6465
+ const sequence = this.root.getInput(0).sequence;
6466
+ if (sequence !== void 0 && sequence !== DEFAULT_SEQUENCE) {
6467
+ throw new Error(`unexpected sequence: ${sequence}, expected ${DEFAULT_SEQUENCE}`);
6468
+ }
6451
6469
  if (this.children.size > nbOfOutputs - 1) {
6452
6470
  throw new Error(
6453
6471
  `unexpected number of children: ${this.children.size}, expected maximum ${nbOfOutputs - 1}`
@@ -12079,6 +12097,7 @@ function filterSnapshotVtxos(snapshot, filter, pendingSpendOutpoints) {
12079
12097
  });
12080
12098
  }
12081
12099
  var PENDING_RECOVERY_REASON = "is past its operator signer's rotation cutoff \u2014 the operator will not co-sign it until it recovers";
12100
+ var UNROLLED_REASON = "was unilaterally exited and lives onchain \u2014 `Unroll.completeUnroll` is the only spend that reaches it";
12082
12101
  function omittedOutpoints(before, after) {
12083
12102
  if (before.length === after.length) return /* @__PURE__ */ new Set();
12084
12103
  const kept = new Set(after.map((vtxo) => `${vtxo.txid}:${vtxo.vout}`));
@@ -12121,10 +12140,11 @@ var ArkadeCashCreateError = class extends Error {
12121
12140
  cause;
12122
12141
  };
12123
12142
  var ReadonlyWallet = class _ReadonlyWallet {
12124
- constructor(identity, network, onchainProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, dustAmount, walletRepository, contractRepository, delegateProvider, watcherConfig, walletContractTimelocks) {
12143
+ constructor(identity, network, onchainProvider, arkProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, dustAmount, walletRepository, contractRepository, delegateProvider, watcherConfig, walletContractTimelocks) {
12125
12144
  this.identity = identity;
12126
12145
  this.network = network;
12127
12146
  this.onchainProvider = onchainProvider;
12147
+ this.arkProvider = arkProvider;
12128
12148
  this.indexerProvider = indexerProvider;
12129
12149
  this.dustAmount = dustAmount;
12130
12150
  this.walletRepository = walletRepository;
@@ -12150,6 +12170,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
12150
12170
  identity;
12151
12171
  network;
12152
12172
  onchainProvider;
12173
+ arkProvider;
12153
12174
  indexerProvider;
12154
12175
  dustAmount;
12155
12176
  walletRepository;
@@ -12217,14 +12238,19 @@ var ReadonlyWallet = class _ReadonlyWallet {
12217
12238
  */
12218
12239
  _arkServerPublicKey;
12219
12240
  /**
12220
- * Whether this wallet was constructed from live operator server-info
12221
- * (`"live"`) or from a cached snapshot because the operator was unreachable
12222
- * (`"cache"`). Freshness signal for {@link getProviderConnectionState}.
12241
+ * Whether the LATEST server-info resolution answered live (`"live"`) or
12242
+ * from the cached snapshot because the operator was unreachable
12243
+ * (`"cache"`). Seeded at construction and updated by every
12244
+ * {@link getArkadeInfo} read, so a wallet that booted offline and
12245
+ * recovered stops reporting degraded — and one that just fell back stops
12246
+ * claiming online. Freshness signal for
12247
+ * {@link getProviderConnectionState}.
12223
12248
  */
12224
12249
  _serverInfoSource = "live";
12225
12250
  /**
12226
- * Epoch-ms of the last known live operator contact: construction time on the
12227
- * `live` boot path, the cached snapshot's `savedAt` on the `cache` path.
12251
+ * Epoch-ms of the last known live operator contact: the most recent live
12252
+ * resolution (construction or a later {@link getArkadeInfo}), or the
12253
+ * cached snapshot's `savedAt` when the latest resolution fell back.
12228
12254
  */
12229
12255
  _serverInfoLastOnlineAt;
12230
12256
  /** @see {@link _serverInfoSource} */
@@ -12232,13 +12258,15 @@ var ReadonlyWallet = class _ReadonlyWallet {
12232
12258
  return this._serverInfoSource;
12233
12259
  }
12234
12260
  /**
12235
- * Composed provider-connection freshness: the boot server-info source
12236
- * (Arkade) combined with the contract-manager's indexer-sync health, if the
12237
- * manager has been initialized. Reads no live provider state — it never
12238
- * forces a `ContractManager` to constructso it is safe for readonly
12239
- * callers that only use address/balance APIs.
12261
+ * Composed provider-connection freshness: the LATEST server-info
12262
+ * resolution (boot, or any later {@link getArkadeInfo} read) combined with
12263
+ * the contract-manager's indexer-sync health, if the manager has been
12264
+ * initialized. Reads no live provider state — it never forces a
12265
+ * `ContractManager` to construct so it is safe for readonly callers
12266
+ * that only use address/balance APIs.
12240
12267
  *
12241
- * - Boot fell back to a cached snapshot → degraded on `arkade` (`cache`).
12268
+ * - Latest resolution fell back to the cached snapshot → degraded on
12269
+ * `arkade` (`cache`).
12242
12270
  * - Otherwise, if the contract manager has degraded to repository data →
12243
12271
  * degraded on `indexer` (`repository`).
12244
12272
  * - Otherwise online.
@@ -12284,7 +12312,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
12284
12312
  }
12285
12313
  /**
12286
12314
  * x-only hex of the operator's deprecated signer keys (from
12287
- * `ArkInfo.deprecatedSigners`), cached for the OFFLINE read/watch paths.
12315
+ * `ArkadeInfo.deprecatedSigners`), cached for the OFFLINE read/watch paths.
12288
12316
  * The boarding watch/history surfaces ({@link getBoardingAddresses},
12289
12317
  * {@link getBoardingTxs}) fan out over {current} ∪ this set so a deposit at
12290
12318
  * a boarding address minted under a now-rotated operator signer keeps being
@@ -12359,6 +12387,64 @@ var ReadonlyWallet = class _ReadonlyWallet {
12359
12387
  get arkServerPublicKey() {
12360
12388
  return this._arkServerPublicKey;
12361
12389
  }
12390
+ /**
12391
+ * Server info for the Arkade server this wallet is connected to, resolved
12392
+ * exactly as construction resolves it: live wins, a retryable failure
12393
+ * falls back to the snapshot persisted at boot, a terminal one propagates.
12394
+ *
12395
+ * Live rather than the pinned boot snapshot because the fields callers
12396
+ * come here for — `signerPubkey`, `checkpointTapscript`, `fees` — are the
12397
+ * ones a mid-session rotation moves, and a covenant built against a
12398
+ * superseded signer is unspendable.
12399
+ *
12400
+ * One rotation caveat: reading does NOT re-pin the wallet —
12401
+ * {@link arkServerPublicKey}, {@link dustAmount} and the tapscripts move
12402
+ * only through `handleServerInfoChanged`/`rotateServerSigner` — so inside
12403
+ * a rotation window this can report epoch N+1 while the wallet still
12404
+ * spends on N. The window closes on its own: a read that observes a moved
12405
+ * digest makes the provider emit `onServerInfoChanged`, which is what
12406
+ * drives that rotation. A caller about to bind the answer into a covenant
12407
+ * passes `{ requireLive: true }` and fails closed instead of receiving
12408
+ * the boot snapshot.
12409
+ *
12410
+ * @returns The Arkade server's info
12411
+ * @see ArkadeInfo
12412
+ */
12413
+ async getArkadeInfo(opts) {
12414
+ const { info, source, lastOnlineAt } = await resolveArkInfo(
12415
+ this.arkProvider,
12416
+ this.walletRepository,
12417
+ opts
12418
+ );
12419
+ this._serverInfoSource = source;
12420
+ if (source === "live") {
12421
+ this._serverInfoLastOnlineAt = Date.now();
12422
+ } else if (lastOnlineAt !== void 0) {
12423
+ this._serverInfoLastOnlineAt = lastOnlineAt;
12424
+ }
12425
+ return info;
12426
+ }
12427
+ /**
12428
+ * Chain reads against this wallet's server for scripts it does not own.
12429
+ *
12430
+ * Binds the wallet's own `indexerProvider` — which may be an Expo or
12431
+ * injected one that a caller's hand-built `RestIndexerProvider` would
12432
+ * silently bypass. `getVtxos` goes through
12433
+ * {@link getNormalizedVtxos}, which is what makes every VTXO leaving the
12434
+ * seam carry its canonical facts.
12435
+ *
12436
+ * A bound object rather than the provider itself, so an `IReadonlyWallet`
12437
+ * holder gets `getVtxos`/`getVirtualTxs` and nothing else at runtime.
12438
+ * (`indexerProvider` is still public on the concrete classes, unlike
12439
+ * `arkProvider`, so this narrows the interface rather than the field.)
12440
+ */
12441
+ async getArkadeReader() {
12442
+ const indexer = this.indexerProvider;
12443
+ return {
12444
+ getVtxos: (opts) => getNormalizedVtxos(indexer, opts),
12445
+ getVirtualTxs: (txids, opts) => indexer.getVirtualTxs(txids, opts)
12446
+ };
12447
+ }
12362
12448
  /**
12363
12449
  * The wallet's current boarding tapscript (the on-chain onboarding
12364
12450
  * target). Read-only from the outside; mutated only via
@@ -12437,7 +12523,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
12437
12523
  source: serverInfoSource,
12438
12524
  lastOnlineAt: serverInfoLastOnlineAt
12439
12525
  } = await resolveArkInfo(arkProvider, walletRepository);
12440
- const network = getNetwork(info.network);
12526
+ const network = networkFromArkadeInfo(info);
12441
12527
  if ("descriptor" in config.identity) {
12442
12528
  const descriptor = config.identity.descriptor;
12443
12529
  const identityIsMainnet = !descriptor.includes("tpub");
@@ -12527,6 +12613,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
12527
12613
  config.identity,
12528
12614
  setup.network,
12529
12615
  setup.onchainProvider,
12616
+ setup.arkProvider,
12530
12617
  setup.indexerProvider,
12531
12618
  setup.serverPubKey,
12532
12619
  setup.offchainTapscript,
@@ -12651,6 +12738,13 @@ var ReadonlyWallet = class _ReadonlyWallet {
12651
12738
  "is locked by an in-flight settlement intent"
12652
12739
  )
12653
12740
  ]);
12741
+ if (!filter?.withUnrolled) {
12742
+ logExcludedVtxos(
12743
+ "getSpendableVtxos",
12744
+ snapshot.flatMap((contract) => contract.vtxos).filter((vtxo) => vtxo.isUnrolled && !hasTerminalSpend(vtxo)),
12745
+ [() => UNROLLED_REASON]
12746
+ );
12747
+ }
12654
12748
  return unlocked;
12655
12749
  }
12656
12750
  /**
@@ -13367,6 +13461,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
13367
13461
  identity,
13368
13462
  network,
13369
13463
  onchainProvider,
13464
+ arkProvider,
13370
13465
  indexerProvider,
13371
13466
  arkServerPublicKey,
13372
13467
  offchainTapscript,
@@ -13378,7 +13473,6 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
13378
13473
  watcherConfig,
13379
13474
  walletContractTimelocks
13380
13475
  );
13381
- this.arkProvider = arkProvider;
13382
13476
  this.forfeitOutputScript = forfeitOutputScript;
13383
13477
  this.forfeitPubkey = forfeitPubkey;
13384
13478
  this.batchExpiryPolicy = batchExpiryPolicy;
@@ -13412,7 +13506,6 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
13412
13506
  boardingPkScript: boardingTapscript.pkScript
13413
13507
  });
13414
13508
  }
13415
- arkProvider;
13416
13509
  forfeitOutputScript;
13417
13510
  forfeitPubkey;
13418
13511
  batchExpiryPolicy;
@@ -13575,7 +13668,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
13575
13668
  /**
13576
13669
  * Output script for checkpoint transactions, decoded from the server's
13577
13670
  * `checkpointTapscript`. Server-controlled state: pinned at construction
13578
- * and re-sourced from a fresh `ArkInfo` on server-signer rotation. Read it
13671
+ * and re-sourced from a fresh `ArkadeInfo` on server-signer rotation. Read it
13579
13672
  * through {@link serverUnrollScript}; write it only through
13580
13673
  * {@link setServerUnrollScriptForRotation}.
13581
13674
  */
@@ -13586,7 +13679,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
13586
13679
  /**
13587
13680
  * @internal Sole write path for `serverUnrollScript` after construction.
13588
13681
  * Called by {@link Wallet._doRotateServerSigner} with the checkpoint script
13589
- * sourced from the fresh `ArkInfo` that triggered the rotation, so the send
13682
+ * sourced from the fresh `ArkadeInfo` that triggered the rotation, so the send
13590
13683
  * path builds checkpoints against the new server epoch. External code must
13591
13684
  * treat `serverUnrollScript` as read-only.
13592
13685
  */
@@ -14218,6 +14311,21 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
14218
14311
  /** @deprecated Use settlementConfig instead */
14219
14312
  renewalConfig;
14220
14313
  settlementConfig;
14314
+ /**
14315
+ * Broadcast access bound to this wallet's server, so a plugin needs only
14316
+ * the wallet.
14317
+ *
14318
+ * Defined here and not on {@link ReadonlyWallet} for the same reason
14319
+ * `arkProvider` is protected there: a readonly wallet, and every
14320
+ * `toReadonly()` view, must not be able to submit.
14321
+ */
14322
+ async getArkadeBroadcaster() {
14323
+ const ark = this.arkProvider;
14324
+ return {
14325
+ submitTx: (signedArkTx, checkpointTxs) => ark.submitTx(signedArkTx, checkpointTxs),
14326
+ finalizeTx: (arkTxid, finalCheckpointTxs) => ark.finalizeTx(arkTxid, finalCheckpointTxs)
14327
+ };
14328
+ }
14221
14329
  get assetManager() {
14222
14330
  this._walletAssetManager ??= new AssetManager(this);
14223
14331
  return this._walletAssetManager;
@@ -14397,6 +14505,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
14397
14505
  readonlyIdentity,
14398
14506
  this.network,
14399
14507
  this.onchainProvider,
14508
+ this.arkProvider,
14400
14509
  this.indexerProvider,
14401
14510
  this.arkServerPublicKey,
14402
14511
  this.offchainTapscript,
@@ -14449,7 +14558,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
14449
14558
  changeAmount: BigInt(changeAmount)
14450
14559
  };
14451
14560
  const outputAddress = ArkAddress.decode(params.address);
14452
- assertRecipientArkAddress(
14561
+ assertRecipientArkadeAddress(
14453
14562
  params.address,
14454
14563
  outputAddress,
14455
14564
  this.recipientAddressContext(serverPubKey)
@@ -14602,7 +14711,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
14602
14711
  }
14603
14712
  if (arkAddress) {
14604
14713
  recipientContext ??= this.recipientAddressContext();
14605
- assertRecipientArkAddress(output.address, arkAddress, recipientContext);
14714
+ assertRecipientArkadeAddress(output.address, arkAddress, recipientContext);
14606
14715
  script = arkAddress.pkScript;
14607
14716
  hasOffchainOutputs = true;
14608
14717
  } else {
@@ -15203,9 +15312,11 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
15203
15312
  seen.add(pendingTx.arkTxid);
15204
15313
  batchPending.push(pendingTx.arkTxid);
15205
15314
  try {
15206
- const checkpointTxs = pendingTx.signedCheckpointTxs.map(
15207
- (c) => Transaction$2.fromPSBT(base64.decode(c))
15208
- );
15315
+ const checkpointTxs = pendingTx.signedCheckpointTxs.map((c) => {
15316
+ const tx = Transaction$2.fromPSBT(base64.decode(c));
15317
+ assertAllowedSighashTypes(tx);
15318
+ return tx;
15319
+ });
15209
15320
  assertCheckpointsMatchInputs(
15210
15321
  checkpointTxs,
15211
15322
  checkpointInputs,
@@ -15373,7 +15484,9 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
15373
15484
  const myPkScript = ArkAddress.decode(await this.getAddress()).pkScript;
15374
15485
  let { spendable, unclaimed } = this.classifyCashVtxos(vtxos);
15375
15486
  let swept = 0;
15376
- const drainable = vtxos.filter((vtxo) => !isSubdust(vtxo, this.dustAmount));
15487
+ const drainable = vtxos.filter(
15488
+ (vtxo) => !isSubdust(vtxo, this.dustAmount) && !vtxo.isUnrolled
15489
+ );
15377
15490
  if (drainable.length > 0) {
15378
15491
  let drained = { count: 0, swept: 0, claimed: /* @__PURE__ */ new Set() };
15379
15492
  try {
@@ -15533,9 +15646,11 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
15533
15646
  const unrollCandidates = this.checkpointUnrollCandidates(await this.arkProvider.getInfo());
15534
15647
  for (const pendingTx of pendingTxs) {
15535
15648
  try {
15536
- const checkpointTxs = pendingTx.signedCheckpointTxs.map(
15537
- (checkpoint) => Transaction$2.fromPSBT(base64.decode(checkpoint))
15538
- );
15649
+ const checkpointTxs = pendingTx.signedCheckpointTxs.map((checkpoint) => {
15650
+ const tx = Transaction$2.fromPSBT(base64.decode(checkpoint));
15651
+ assertAllowedSighashTypes(tx);
15652
+ return tx;
15653
+ });
15539
15654
  assertCheckpointsMatchInputs(
15540
15655
  checkpointTxs,
15541
15656
  inputs.map((input) => ({
@@ -16800,6 +16915,9 @@ var MessageBus = class {
16800
16915
  });
16801
16916
  return;
16802
16917
  }
16918
+ if (response.error instanceof Error && !response.errorName) {
16919
+ response.errorName = response.error.name;
16920
+ }
16803
16921
  source.postMessage(response);
16804
16922
  }
16805
16923
  /**
@@ -17393,6 +17511,49 @@ var WalletMessageHandler = class {
17393
17511
  payload: { address }
17394
17512
  });
17395
17513
  }
17514
+ case "GET_ARKADE_INFO": {
17515
+ const { payload } = message;
17516
+ const info = await this.readonlyWallet.getArkadeInfo(payload);
17517
+ return this.tagged({
17518
+ id,
17519
+ type: "ARKADE_INFO",
17520
+ payload: { info }
17521
+ });
17522
+ }
17523
+ case "INDEXER_GET_VTXOS": {
17524
+ const { opts } = message.payload;
17525
+ const reader = await this.readonlyWallet.getArkadeReader();
17526
+ return this.tagged({
17527
+ id,
17528
+ type: "INDEXER_VTXOS",
17529
+ payload: await reader.getVtxos(opts)
17530
+ });
17531
+ }
17532
+ case "INDEXER_GET_VIRTUAL_TXS": {
17533
+ const { txids, opts } = message.payload;
17534
+ const reader = await this.readonlyWallet.getArkadeReader();
17535
+ return this.tagged({
17536
+ id,
17537
+ type: "INDEXER_VIRTUAL_TXS",
17538
+ payload: await reader.getVirtualTxs(txids, opts)
17539
+ });
17540
+ }
17541
+ case "SUBMIT_TX": {
17542
+ const { signedArkTx, checkpointTxs } = message.payload;
17543
+ const broadcaster = await this.requireWallet().getArkadeBroadcaster();
17544
+ const result = await broadcaster.submitTx(signedArkTx, checkpointTxs);
17545
+ return this.tagged({
17546
+ id,
17547
+ type: "SUBMIT_TX_SUCCESS",
17548
+ payload: result
17549
+ });
17550
+ }
17551
+ case "FINALIZE_TX": {
17552
+ const { arkTxid, finalCheckpointTxs } = message.payload;
17553
+ const broadcaster = await this.requireWallet().getArkadeBroadcaster();
17554
+ await broadcaster.finalizeTx(arkTxid, finalCheckpointTxs);
17555
+ return this.tagged({ id, type: "FINALIZE_TX_SUCCESS" });
17556
+ }
17396
17557
  case "GET_BALANCE": {
17397
17558
  const balance = await this.handleGetBalance();
17398
17559
  return this.tagged({
@@ -18284,6 +18445,13 @@ var WalletMessageHandler = class {
18284
18445
 
18285
18446
  // src/wallet/serviceWorker/wallet.ts
18286
18447
  var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
18448
+ var restoreErrorName = (response) => {
18449
+ const error = response.error;
18450
+ if (response.errorName && error instanceof Error && error.name !== response.errorName) {
18451
+ error.name = response.errorName;
18452
+ }
18453
+ return error;
18454
+ };
18287
18455
  var INIT_WAIT_BACKOFF_CAP_MS = 2e3;
18288
18456
  var MAX_INIT_WAITS = 8;
18289
18457
  var initWaitBackoffMs = (attempt) => Math.min(100 * 2 ** attempt, INIT_WAIT_BACKOFF_CAP_MS);
@@ -18306,6 +18474,14 @@ var DEFAULT_MESSAGE_TIMEOUTS = {
18306
18474
  GET_USED_SIGNING_DESCRIPTORS: 2e4,
18307
18475
  ADVANCE_SIGNING_DESCRIPTOR_WATERMARK: 1e4,
18308
18476
  // Medium reads — may involve indexer queries
18477
+ // A live `/v1/info` fetch queued behind the same per-origin rate gate as
18478
+ // the indexer, not a repository read. Derived from the fetch's own abort
18479
+ // budget so the worker's snapshot fallback stays reachable before this
18480
+ // page deadline fires — lowering it below the budget would time the page
18481
+ // out while the worker is still seconds from answering from cache.
18482
+ GET_ARKADE_INFO: INFO_FETCH_TIMEOUT_MS + 8e3,
18483
+ INDEXER_GET_VTXOS: 2e4,
18484
+ INDEXER_GET_VIRTUAL_TXS: 2e4,
18309
18485
  GET_VTXOS: 2e4,
18310
18486
  GET_SPENDABLE_VTXOS: 2e4,
18311
18487
  GET_BOARDING_UTXOS: 2e4,
@@ -18328,6 +18504,10 @@ var DEFAULT_MESSAGE_TIMEOUTS = {
18328
18504
  SEND_BITCOIN: 5e4,
18329
18505
  SEND: 5e4,
18330
18506
  SETTLE: 5e4,
18507
+ // Broadcast: the server round-trip a spend ends in, so it belongs
18508
+ // with the writes rather than the reads above.
18509
+ SUBMIT_TX: 5e4,
18510
+ FINALIZE_TX: 5e4,
18331
18511
  ISSUE: 5e4,
18332
18512
  REISSUE: 5e4,
18333
18513
  BURN: 5e4,
@@ -18354,6 +18534,9 @@ var DEFAULT_MESSAGE_TIMEOUTS = {
18354
18534
  };
18355
18535
  var DEDUPABLE_REQUEST_TYPES = /* @__PURE__ */ new Set([
18356
18536
  "GET_ADDRESS",
18537
+ "GET_ARKADE_INFO",
18538
+ "INDEXER_GET_VTXOS",
18539
+ "INDEXER_GET_VIRTUAL_TXS",
18357
18540
  "GET_BALANCE",
18358
18541
  "GET_BOARDING_ADDRESS",
18359
18542
  "GET_BOARDING_UTXOS",
@@ -18445,7 +18628,7 @@ var initializeMessageBus = (serviceWorker, config, timeoutMs = 2e3) => {
18445
18628
  if (response?.id !== initCmd.id) return;
18446
18629
  cleanup();
18447
18630
  if (response.error) {
18448
- reject(response.error);
18631
+ reject(restoreErrorName(response));
18449
18632
  } else {
18450
18633
  resolve();
18451
18634
  }
@@ -18617,7 +18800,7 @@ var ServiceWorkerReadonlyWallet = class _ServiceWorkerReadonlyWallet {
18617
18800
  }
18618
18801
  cleanup();
18619
18802
  if (response.error) {
18620
- reject(response.error);
18803
+ reject(restoreErrorName(response));
18621
18804
  } else {
18622
18805
  resolve(response);
18623
18806
  }
@@ -18643,7 +18826,7 @@ var ServiceWorkerReadonlyWallet = class _ServiceWorkerReadonlyWallet {
18643
18826
  if (request.id !== response.id) return;
18644
18827
  if (response.error) {
18645
18828
  cleanup();
18646
- reject(response.error);
18829
+ reject(restoreErrorName(response));
18647
18830
  return;
18648
18831
  }
18649
18832
  if (isComplete(response)) {
@@ -18869,6 +19052,68 @@ var ServiceWorkerReadonlyWallet = class _ServiceWorkerReadonlyWallet {
18869
19052
  throw new Error(`Failed to get boarding address: ${error}`);
18870
19053
  }
18871
19054
  }
19055
+ /**
19056
+ * Delegated to the worker, which holds the wallet that owns the connection.
19057
+ * The payload crosses raw — see {@link ResponseGetArkadeInfo} for why.
19058
+ */
19059
+ async getArkadeInfo(opts) {
19060
+ const message = {
19061
+ id: getRandomId(),
19062
+ tag: this.messageTag,
19063
+ type: "GET_ARKADE_INFO",
19064
+ // omitted entirely for the default read: wire-shape hygiene
19065
+ // toward workers built before the option existed. (Dedup is
19066
+ // unaffected either way — the key is JSON.stringify, which drops
19067
+ // undefined-valued properties.)
19068
+ ...opts?.requireLive ? { payload: { requireLive: true } } : {}
19069
+ };
19070
+ try {
19071
+ const response = await this.sendMessage(message);
19072
+ return response.payload.info;
19073
+ } catch (error) {
19074
+ throw new Error(`Failed to get arkade info: ${error}`, { cause: error });
19075
+ }
19076
+ }
19077
+ /**
19078
+ * An {@link ArkadeReader} that speaks to the worker rather than to the
19079
+ * server. The page holds no provider, so a caller that built its own from a
19080
+ * URL would open a second connection outside the worker's rate gate and
19081
+ * caches — this proxy is what keeps chain reads in one place.
19082
+ */
19083
+ async getArkadeReader() {
19084
+ return {
19085
+ getVtxos: async (opts) => {
19086
+ const message = {
19087
+ id: getRandomId(),
19088
+ tag: this.messageTag,
19089
+ type: "INDEXER_GET_VTXOS",
19090
+ payload: { opts }
19091
+ };
19092
+ try {
19093
+ const response = await this.sendMessage(message);
19094
+ const { vtxos, page } = response.payload;
19095
+ return { vtxos: vtxos.map(normalizeVtxo), page: page && { ...page } };
19096
+ } catch (error) {
19097
+ throw new Error(`Failed to get vtxos: ${error}`, { cause: error });
19098
+ }
19099
+ },
19100
+ getVirtualTxs: async (txids, opts) => {
19101
+ const message = {
19102
+ id: getRandomId(),
19103
+ tag: this.messageTag,
19104
+ type: "INDEXER_GET_VIRTUAL_TXS",
19105
+ payload: { txids, opts }
19106
+ };
19107
+ try {
19108
+ const response = await this.sendMessage(message);
19109
+ const { txs, page } = response.payload;
19110
+ return { txs: [...txs], page: page && { ...page } };
19111
+ } catch (error) {
19112
+ throw new Error(`Failed to get virtual txs: ${error}`, { cause: error });
19113
+ }
19114
+ }
19115
+ };
19116
+ }
18872
19117
  async getBalance() {
18873
19118
  const message = {
18874
19119
  id: getRandomId(),
@@ -19525,6 +19770,43 @@ var ServiceWorkerWallet = class _ServiceWorkerWallet extends ServiceWorkerReadon
19525
19770
  async signerForDescriptor(descriptor) {
19526
19771
  return resolveDescriptorSigner(descriptor, this.identity);
19527
19772
  }
19773
+ /**
19774
+ * An {@link ArkadeBroadcaster} proxied to the worker, which holds the
19775
+ * provider. Only `ServiceWorkerWallet` offers one: a readonly worker
19776
+ * refuses `SUBMIT_TX`/`FINALIZE_TX` outright, so exposing it on the
19777
+ * readonly class would promise something the boundary rejects.
19778
+ */
19779
+ async getArkadeBroadcaster() {
19780
+ return {
19781
+ submitTx: async (signedArkTx, checkpointTxs) => {
19782
+ const message = {
19783
+ id: getRandomId(),
19784
+ tag: this.messageTag,
19785
+ type: "SUBMIT_TX",
19786
+ payload: { signedArkTx, checkpointTxs }
19787
+ };
19788
+ try {
19789
+ const response = await this.sendMessage(message);
19790
+ return response.payload;
19791
+ } catch (error) {
19792
+ throw new Error(`Failed to submit tx: ${error}`, { cause: error });
19793
+ }
19794
+ },
19795
+ finalizeTx: async (arkTxid, finalCheckpointTxs) => {
19796
+ const message = {
19797
+ id: getRandomId(),
19798
+ tag: this.messageTag,
19799
+ type: "FINALIZE_TX",
19800
+ payload: { arkTxid, finalCheckpointTxs }
19801
+ };
19802
+ try {
19803
+ await this.sendMessage(message);
19804
+ } catch (error) {
19805
+ throw new Error(`Failed to finalize tx: ${error}`, { cause: error });
19806
+ }
19807
+ }
19808
+ };
19809
+ }
19528
19810
  async sendBitcoin(params) {
19529
19811
  const message = {
19530
19812
  id: getRandomId(),
@@ -22834,6 +23116,10 @@ var ArkadeContract = class _ArkadeContract {
22834
23116
  * When a `contractManager` is configured and this contract is registered,
22835
23117
  * reads the repository-backed state (offline-first, kept fresh by the
22836
23118
  * contract watcher). Otherwise falls back to a direct indexer query.
23119
+ *
23120
+ * Both branches refuse a spent or unilaterally exited output. They are not
23121
+ * otherwise interchangeable: the fallback asks `spendableOnly`, which also
23122
+ * drops swept coins — the manager branch keeps those.
22837
23123
  */
22838
23124
  async getUtxos() {
22839
23125
  const manager = this.client.contractManager;
@@ -22854,7 +23140,7 @@ var ArkadeContract = class _ArkadeContract {
22854
23140
  scripts: [scriptHex],
22855
23141
  spendableOnly: true
22856
23142
  });
22857
- return vtxos;
23143
+ return vtxos.filter((v) => !hasTerminalSpend(v) && !v.isUnrolled);
22858
23144
  }
22859
23145
  /** Total spendable balance (requires an indexer). */
22860
23146
  async getBalance() {
@@ -23019,8 +23305,14 @@ var ArkadeTransactionBuilder = class {
23019
23305
  if (!client.identity) {
23020
23306
  throw new Error("a signing identity is required for non-covenant spends");
23021
23307
  }
23308
+ const ark = client.arkProvider;
23309
+ if (!ark.submitTx || !ark.finalizeTx) {
23310
+ throw new Error(
23311
+ "broadcasting requires an `arkade` provider with `submitTx`/`finalizeTx` on the Arkade client"
23312
+ );
23313
+ }
23022
23314
  const signedArk = await this.signArk(arkTx, userInputs);
23023
- const res = await client.arkProvider.submitTx(
23315
+ const res = await ark.submitTx(
23024
23316
  base64.encode(signedArk.toPSBT()),
23025
23317
  checkpoints.map((c) => base64.encode(c.toPSBT()))
23026
23318
  );
@@ -23031,7 +23323,7 @@ var ArkadeTransactionBuilder = class {
23031
23323
  async ({ server }) => base64.encode((await client.identity.sign(server, [0])).toPSBT())
23032
23324
  )
23033
23325
  );
23034
- await client.arkProvider.finalizeTx(res.arkTxid, finalCps);
23326
+ await ark.finalizeTx(res.arkTxid, finalCps);
23035
23327
  return {
23036
23328
  txid: res.arkTxid,
23037
23329
  signedArkTx: res.finalArkTx,
@@ -23119,6 +23411,7 @@ var BIP21Error = /* @__PURE__ */ ((BIP21Error2) => {
23119
23411
  BIP21Error2["INVALID_ADDRESS"] = "Invalid address";
23120
23412
  return BIP21Error2;
23121
23413
  })(BIP21Error || {});
23414
+ var ARK_HRP = /^(?:t?ark|T?ARK)/;
23122
23415
  var BIP21 = class _BIP21 {
23123
23416
  /**
23124
23417
  * Create a BIP21 URI from the provided parameters.
@@ -23142,7 +23435,7 @@ var BIP21 = class _BIP21 {
23142
23435
  }
23143
23436
  queryParams[key] = value;
23144
23437
  } else if (key === "ark") {
23145
- if (typeof value === "string" && (value.startsWith("ark") || value.startsWith("tark"))) {
23438
+ if (typeof value === "string" && ARK_HRP.test(value)) {
23146
23439
  queryParams[key] = value;
23147
23440
  } else {
23148
23441
  console.warn("Invalid ARK address format");
@@ -23162,7 +23455,7 @@ var BIP21 = class _BIP21 {
23162
23455
  Object.entries(queryParams).map(([k, v]) => [k, String(v)])
23163
23456
  )
23164
23457
  ).toString() : "";
23165
- return `bitcoin:${address ? address.toLowerCase() : ""}${query}`;
23458
+ return `bitcoin:${address ?? ""}${query}`;
23166
23459
  }
23167
23460
  /**
23168
23461
  * Integer-sats amount encoded in a BIP21 URI (`amount=` is BTC), or
@@ -23195,7 +23488,7 @@ var BIP21 = class _BIP21 {
23195
23488
  const [address, query] = withoutPrefix.split("?");
23196
23489
  const params = {};
23197
23490
  if (address) {
23198
- params.address = address.toLowerCase();
23491
+ params.address = address;
23199
23492
  }
23200
23493
  if (query) {
23201
23494
  const queryParams = new URLSearchParams(query);
@@ -23211,7 +23504,7 @@ var BIP21 = class _BIP21 {
23211
23504
  }
23212
23505
  params[key] = amount;
23213
23506
  } else if (key === "ark") {
23214
- if (value.startsWith("ark") || value.startsWith("tark")) {
23507
+ if (ARK_HRP.test(value)) {
23215
23508
  params[key] = value;
23216
23509
  } else {
23217
23510
  console.warn("Invalid ARK address format");
@@ -23463,7 +23756,7 @@ function onchainRail() {
23463
23756
  function createDefaultPaymentRouter(wallet) {
23464
23757
  return new PaymentRouter({
23465
23758
  wallet,
23466
- prefs: { priority: ["ark", "lightning", "onchain"] }
23759
+ prefs: { priority: ["ark", "onchain"] }
23467
23760
  }).use(arkRail()).use(onchainRail());
23468
23761
  }
23469
23762
  /*! Bundled license information:
@@ -23472,6 +23765,6 @@ function createDefaultPaymentRouter(wallet) {
23472
23765
  (*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
23473
23766
  */
23474
23767
 
23475
- export { ARKADE_MAGIC, ARKADE_SALTED_PREIMAGE_TAG, ARKADE_SWAP_PREIMAGE_TAG, 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, 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, arkRail, arkTarget, arkade_exports, assertFinalCommitmentMatchesValidated, assertIntentIdUnique, assertSendableAmount, assertSubmittedArkTxid, assertValidBatchExpiry, assertValidServerUnrollScript, assetMintResolver, 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, 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, requiresMigration, resolveBatchExpiryPolicy, resolveCheckpointExitDelayPolicy, resolveDescriptorSigner, resolvePrevTxs, resolveSendAmount, rollbackMigration, saveVtxosForContract, scriptFromArkAddress, selectCoinsWithAsset, selectVirtualCoins, serializeAssets, serializeExitPackage, serializeUtxo, serializeVtxo, setupServiceWorker, signAndSubmitOffchainTx, signerSetFromInfo, signingDescriptorIndex, strictSigningDescriptorIndex, toXOnlySignerHex, tryResolveSendAmount, validateBatchRecipients, validateConnectorsTxGraph, validateVtxoTxGraph, verifyTapscriptSignatures, waitForIncomingFunds, warnAndFilterVtxosForScript, withPrevTxs };
23476
- //# sourceMappingURL=chunk-AEWJU6NZ.js.map
23477
- //# sourceMappingURL=chunk-AEWJU6NZ.js.map
23768
+ export { ARKADE_MAGIC, ARKADE_SALTED_PREIMAGE_TAG, ARKADE_SWAP_PREIMAGE_TAG, 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, 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, arkRail, arkTarget, arkade_exports, assertFinalCommitmentMatchesValidated, assertIntentIdUnique, assertRecipientArkadeAddress, assertSendableAmount, assertSubmittedArkTxid, assertValidBatchExpiry, assertValidServerUnrollScript, assetMintResolver, 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, 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, requiresMigration, resolveBatchExpiryPolicy, resolveCheckpointExitDelayPolicy, resolveDescriptorSigner, resolvePrevTxs, resolveSendAmount, rollbackMigration, saveVtxosForContract, scriptFromArkAddress, selectCoinsWithAsset, selectVirtualCoins, serializeAssets, serializeExitPackage, serializeUtxo, serializeVtxo, setupServiceWorker, signAndSubmitOffchainTx, signerSetFromInfo, signingDescriptorIndex, strictSigningDescriptorIndex, toXOnlySignerHex, tryResolveSendAmount, validateBatchRecipients, validateConnectorsTxGraph, validateVtxoTxGraph, verifyTapscriptSignatures, waitForIncomingFunds, warnAndFilterVtxosForScript, withPrevTxs };
23769
+ //# sourceMappingURL=chunk-6F2Q32KA.js.map
23770
+ //# sourceMappingURL=chunk-6F2Q32KA.js.map