@arkade-os/sdk 0.4.42 → 0.4.44

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 (70) hide show
  1. package/README.md +65 -0
  2. package/dist/adapters/expo.cjs +4 -4
  3. package/dist/adapters/expo.d.cts +2 -2
  4. package/dist/adapters/expo.d.ts +2 -2
  5. package/dist/adapters/expo.js +2 -2
  6. package/dist/adapters/indexedDB.cjs +3 -3
  7. package/dist/adapters/indexedDB.js +2 -2
  8. package/dist/{ark-CKDWpuGL.d.cts → ark-I8jfe5VE.d.cts} +157 -2
  9. package/dist/{ark-CKDWpuGL.d.ts → ark-I8jfe5VE.d.ts} +157 -2
  10. package/dist/{asyncStorageTaskQueue-BB2MffwG.d.ts → asyncStorageTaskQueue-BtSOVZFn.d.ts} +2 -2
  11. package/dist/{asyncStorageTaskQueue-BpN8RRHR.d.cts → asyncStorageTaskQueue-Dap7qUlF.d.cts} +2 -2
  12. package/dist/{chunk-V3FCNVJE.js → chunk-FMI2GKYO.js} +2158 -502
  13. package/dist/chunk-FMI2GKYO.js.map +1 -0
  14. package/dist/{chunk-FLPPSRYH.js → chunk-LCQQPOIT.js} +3 -3
  15. package/dist/{chunk-FLPPSRYH.js.map → chunk-LCQQPOIT.js.map} +1 -1
  16. package/dist/{chunk-5X5YMZHB.js → chunk-LJ35HPJW.js} +39 -5
  17. package/dist/chunk-LJ35HPJW.js.map +1 -0
  18. package/dist/{chunk-HHPUCCB3.cjs → chunk-RJ6XVCX4.cjs} +7 -7
  19. package/dist/{chunk-HHPUCCB3.cjs.map → chunk-RJ6XVCX4.cjs.map} +1 -1
  20. package/dist/{chunk-YEF5FL6E.cjs → chunk-UBYY2TPK.cjs} +39 -4
  21. package/dist/chunk-UBYY2TPK.cjs.map +1 -0
  22. package/dist/{chunk-2V3BU2X6.js → chunk-V7WIZR2B.js} +3 -3
  23. package/dist/{chunk-2V3BU2X6.js.map → chunk-V7WIZR2B.js.map} +1 -1
  24. package/dist/{chunk-T2ACJD22.cjs → chunk-X37EKDIH.cjs} +11 -11
  25. package/dist/{chunk-T2ACJD22.cjs.map → chunk-X37EKDIH.cjs.map} +1 -1
  26. package/dist/{chunk-ED7JDFX4.cjs → chunk-XIVCG6EP.cjs} +2248 -578
  27. package/dist/chunk-XIVCG6EP.cjs.map +1 -0
  28. package/dist/contracts/handlers/index.d.cts +3 -3
  29. package/dist/contracts/handlers/index.d.ts +3 -3
  30. package/dist/{delegate-yjhcR9n4.d.cts → delegate-BRqbh2fW.d.cts} +1 -1
  31. package/dist/{delegate-DllAQ3Eu.d.ts → delegate-CgjVjYOP.d.ts} +1 -1
  32. package/dist/{index-D1te-ZR6.d.cts → index-DHuXlDEg.d.cts} +2 -2
  33. package/dist/{index-DVdCGyyH.d.ts → index-DvWNB_rl.d.ts} +2 -2
  34. package/dist/index.cjs +162 -118
  35. package/dist/index.d.cts +344 -9
  36. package/dist/index.d.ts +344 -9
  37. package/dist/index.js +2 -2
  38. package/dist/repositories/realm/index.cjs +268 -12
  39. package/dist/repositories/realm/index.cjs.map +1 -1
  40. package/dist/repositories/realm/index.d.cts +101 -3
  41. package/dist/repositories/realm/index.d.ts +101 -3
  42. package/dist/repositories/realm/index.js +254 -4
  43. package/dist/repositories/realm/index.js.map +1 -1
  44. package/dist/repositories/sqlite/index.cjs +366 -40
  45. package/dist/repositories/sqlite/index.cjs.map +1 -1
  46. package/dist/repositories/sqlite/index.d.cts +52 -6
  47. package/dist/repositories/sqlite/index.d.ts +52 -6
  48. package/dist/repositories/sqlite/index.js +353 -32
  49. package/dist/repositories/sqlite/index.js.map +1 -1
  50. package/dist/{taskRunner-BdiRy4iI.d.ts → taskRunner-Bptw9UMa.d.ts} +2 -2
  51. package/dist/{taskRunner-CuTk8RYg.d.cts → taskRunner-CeFxKhSH.d.cts} +2 -2
  52. package/dist/wallet/expo/background.cjs +12 -12
  53. package/dist/wallet/expo/background.d.cts +3 -3
  54. package/dist/wallet/expo/background.d.ts +3 -3
  55. package/dist/wallet/expo/background.js +4 -4
  56. package/dist/wallet/expo/index.cjs +12 -12
  57. package/dist/wallet/expo/index.d.cts +4 -4
  58. package/dist/wallet/expo/index.d.ts +4 -4
  59. package/dist/wallet/expo/index.js +3 -3
  60. package/dist/{wallet-CyPOQ0oM.d.cts → wallet-BsqRU8KM.d.cts} +29 -5
  61. package/dist/{wallet-CrIVUQMb.d.ts → wallet-D9z7wssl.d.ts} +29 -5
  62. package/dist/worker/expo/index.cjs +7 -7
  63. package/dist/worker/expo/index.d.cts +4 -4
  64. package/dist/worker/expo/index.d.ts +4 -4
  65. package/dist/worker/expo/index.js +3 -3
  66. package/package.json +2 -2
  67. package/dist/chunk-5X5YMZHB.js.map +0 -1
  68. package/dist/chunk-ED7JDFX4.cjs.map +0 -1
  69. package/dist/chunk-V3FCNVJE.js.map +0 -1
  70. package/dist/chunk-YEF5FL6E.cjs.map +0 -1
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkYEF5FL6E_cjs = require('./chunk-YEF5FL6E.cjs');
3
+ var chunkUBYY2TPK_cjs = require('./chunk-UBYY2TPK.cjs');
4
4
  var chunk32YKLQPS_cjs = require('./chunk-32YKLQPS.cjs');
5
5
  var chunkHXDZ2QTW_cjs = require('./chunk-HXDZ2QTW.cjs');
6
6
  var utils_js = require('@scure/btc-signer/utils.js');
@@ -181,7 +181,7 @@ var TreeSignerSession = class _TreeSignerSession {
181
181
  noncesByPubkey.set(base.hex.encode(myPublicKey.subarray(1)), myNonce);
182
182
  const tx = this.graph.find(txid);
183
183
  if (!tx) throw new Error(`missing tx for txid ${txid}`);
184
- const cosigners = chunkYEF5FL6E_cjs.getArkPsbtFields(tx.root, 0, chunkYEF5FL6E_cjs.CosignerPublicKey).map(
184
+ const cosigners = chunkUBYY2TPK_cjs.getArkPsbtFields(tx.root, 0, chunkUBYY2TPK_cjs.CosignerPublicKey).map(
185
185
  (c) => base.hex.encode(c.key.subarray(1))
186
186
  // xonly pubkey
187
187
  );
@@ -233,7 +233,7 @@ var TreeSignerSession = class _TreeSignerSession {
233
233
  if (!aggNonce) throw new Error("missing aggregate nonce");
234
234
  const prevoutAmounts = [];
235
235
  const prevoutScripts = [];
236
- const cosigners = chunkYEF5FL6E_cjs.getArkPsbtFields(g.root, 0, chunkYEF5FL6E_cjs.CosignerPublicKey).map((c) => c.key);
236
+ const cosigners = chunkUBYY2TPK_cjs.getArkPsbtFields(g.root, 0, chunkUBYY2TPK_cjs.CosignerPublicKey).map((c) => c.key);
237
237
  const { finalKey } = aggregateKeys(cosigners, true, {
238
238
  taprootTweak: this.scriptRoot
239
239
  });
@@ -980,14 +980,14 @@ var RestDelegateProvider = class {
980
980
  */
981
981
  async delegate(intent, forfeitTxs, options) {
982
982
  const url = `${this.url}/v1/delegate`;
983
- const response = await chunkYEF5FL6E_cjs.baseFetch(url, {
983
+ const response = await chunkUBYY2TPK_cjs.baseFetch(url, {
984
984
  method: "POST",
985
985
  headers: {
986
986
  "Content-Type": "application/json"
987
987
  },
988
988
  body: JSON.stringify({
989
989
  intent: {
990
- message: chunkYEF5FL6E_cjs.Intent.encodeMessage(intent.message),
990
+ message: chunkUBYY2TPK_cjs.Intent.encodeMessage(intent.message),
991
991
  proof: intent.proof
992
992
  },
993
993
  forfeit_txs: forfeitTxs,
@@ -1007,7 +1007,7 @@ var RestDelegateProvider = class {
1007
1007
  */
1008
1008
  async getDelegateInfo() {
1009
1009
  const url = `${this.url}/v1/delegator/info`;
1010
- const response = await chunkYEF5FL6E_cjs.baseFetch(url);
1010
+ const response = await chunkUBYY2TPK_cjs.baseFetch(url);
1011
1011
  if (!response.ok) {
1012
1012
  const errorText2 = await response.text();
1013
1013
  throw new Error(`Failed to get delegate info: ${errorText2}`);
@@ -1042,14 +1042,14 @@ var EsploraProvider = class {
1042
1042
  pollingInterval;
1043
1043
  forcePolling;
1044
1044
  async getCoins(address) {
1045
- const response = await chunkYEF5FL6E_cjs.baseFetch(`${this.baseUrl}/address/${address}/utxo`);
1045
+ const response = await chunkUBYY2TPK_cjs.baseFetch(`${this.baseUrl}/address/${address}/utxo`);
1046
1046
  if (!response.ok) {
1047
1047
  throw new Error(`Failed to fetch UTXOs: ${response.statusText}`);
1048
1048
  }
1049
1049
  return response.json();
1050
1050
  }
1051
1051
  async getFeeRate() {
1052
- const response = await chunkYEF5FL6E_cjs.baseFetch(`${this.baseUrl}/fee-estimates`);
1052
+ const response = await chunkUBYY2TPK_cjs.baseFetch(`${this.baseUrl}/fee-estimates`);
1053
1053
  if (response.status === 404) {
1054
1054
  return void 0;
1055
1055
  }
@@ -1070,7 +1070,7 @@ var EsploraProvider = class {
1070
1070
  }
1071
1071
  }
1072
1072
  async getTxOutspends(txid) {
1073
- const response = await chunkYEF5FL6E_cjs.baseFetch(`${this.baseUrl}/tx/${txid}/outspends`);
1073
+ const response = await chunkUBYY2TPK_cjs.baseFetch(`${this.baseUrl}/tx/${txid}/outspends`);
1074
1074
  if (!response.ok) {
1075
1075
  const error = await response.text();
1076
1076
  throw new Error(`Failed to get transaction outspends: ${error}`);
@@ -1078,7 +1078,7 @@ var EsploraProvider = class {
1078
1078
  return response.json();
1079
1079
  }
1080
1080
  async getTransactions(address) {
1081
- const response = await chunkYEF5FL6E_cjs.baseFetch(`${this.baseUrl}/address/${address}/txs`);
1081
+ const response = await chunkUBYY2TPK_cjs.baseFetch(`${this.baseUrl}/address/${address}/txs`);
1082
1082
  if (!response.ok) {
1083
1083
  const error = await response.text();
1084
1084
  throw new Error(`Failed to get transactions: ${error}`);
@@ -1086,7 +1086,7 @@ var EsploraProvider = class {
1086
1086
  return response.json();
1087
1087
  }
1088
1088
  async getTxStatus(txid) {
1089
- const txresponse = await chunkYEF5FL6E_cjs.baseFetch(`${this.baseUrl}/tx/${txid}`);
1089
+ const txresponse = await chunkUBYY2TPK_cjs.baseFetch(`${this.baseUrl}/tx/${txid}`);
1090
1090
  if (!txresponse.ok) {
1091
1091
  throw new Error(txresponse.statusText);
1092
1092
  }
@@ -1094,7 +1094,7 @@ var EsploraProvider = class {
1094
1094
  if (!tx.status.confirmed) {
1095
1095
  return { confirmed: false };
1096
1096
  }
1097
- const response = await chunkYEF5FL6E_cjs.baseFetch(`${this.baseUrl}/tx/${txid}/status`);
1097
+ const response = await chunkUBYY2TPK_cjs.baseFetch(`${this.baseUrl}/tx/${txid}/status`);
1098
1098
  if (!response.ok) {
1099
1099
  throw new Error(`Failed to get transaction status: ${response.statusText}`);
1100
1100
  }
@@ -1178,7 +1178,7 @@ var EsploraProvider = class {
1178
1178
  return stopFunc;
1179
1179
  }
1180
1180
  async getChainTip() {
1181
- const tipBlocks = await chunkYEF5FL6E_cjs.baseFetch(`${this.baseUrl}/blocks`);
1181
+ const tipBlocks = await chunkUBYY2TPK_cjs.baseFetch(`${this.baseUrl}/blocks`);
1182
1182
  if (!tipBlocks.ok) {
1183
1183
  throw new Error(`Failed to get chain tip: ${tipBlocks.statusText}`);
1184
1184
  }
@@ -1197,7 +1197,7 @@ var EsploraProvider = class {
1197
1197
  };
1198
1198
  }
1199
1199
  async broadcastPackage(parent, child) {
1200
- const response = await chunkYEF5FL6E_cjs.baseFetch(`${this.baseUrl}/txs/package`, {
1200
+ const response = await chunkUBYY2TPK_cjs.baseFetch(`${this.baseUrl}/txs/package`, {
1201
1201
  method: "POST",
1202
1202
  headers: {
1203
1203
  "Content-Type": "application/json"
@@ -1211,7 +1211,7 @@ var EsploraProvider = class {
1211
1211
  return response.json();
1212
1212
  }
1213
1213
  async broadcastTx(tx) {
1214
- const response = await chunkYEF5FL6E_cjs.baseFetch(`${this.baseUrl}/tx`, {
1214
+ const response = await chunkUBYY2TPK_cjs.baseFetch(`${this.baseUrl}/tx`, {
1215
1215
  method: "POST",
1216
1216
  headers: {
1217
1217
  "Content-Type": "text/plain"
@@ -1237,6 +1237,114 @@ var isExplorerTransaction = (tx) => {
1237
1237
  (vout) => typeof vout.scriptpubkey_address === "string" && typeof vout.value === "number"
1238
1238
  ) && typeof tx.status === "object" && typeof tx.status.confirmed === "boolean";
1239
1239
  };
1240
+ var getVarIntSize = (n) => {
1241
+ if (n < 253) return 1;
1242
+ if (n <= 65535) return 3;
1243
+ if (n <= 4294967295) return 5;
1244
+ return 9;
1245
+ };
1246
+ var TxWeightEstimator = class _TxWeightEstimator {
1247
+ static P2PKH_SCRIPT_SIG_SIZE = 1 + 73 + 1 + 33;
1248
+ static INPUT_SIZE = 32 + 4 + 1 + 4;
1249
+ static BASE_CONTROL_BLOCK_SIZE = 1 + 32;
1250
+ static OUTPUT_SIZE = 8 + 1;
1251
+ static P2WPKH_OUTPUT_SIZE = 1 + 1 + 20;
1252
+ static BASE_TX_SIZE = 8 + 2;
1253
+ // Version + LockTime
1254
+ static WITNESS_HEADER_SIZE = 2;
1255
+ // Flag + Marker
1256
+ static WITNESS_SCALE_FACTOR = 4;
1257
+ static P2TR_OUTPUT_SIZE = 1 + 1 + 32;
1258
+ hasWitness;
1259
+ inputCount;
1260
+ outputCount;
1261
+ inputSize;
1262
+ inputWitnessSize;
1263
+ outputSize;
1264
+ constructor(hasWitness, inputCount, outputCount, inputSize, inputWitnessSize, outputSize) {
1265
+ this.hasWitness = hasWitness;
1266
+ this.inputCount = inputCount;
1267
+ this.outputCount = outputCount;
1268
+ this.inputSize = inputSize;
1269
+ this.inputWitnessSize = inputWitnessSize;
1270
+ this.outputSize = outputSize;
1271
+ }
1272
+ static create() {
1273
+ return new _TxWeightEstimator(false, 0, 0, 0, 0, 0);
1274
+ }
1275
+ addP2AInput() {
1276
+ this.inputCount++;
1277
+ this.inputSize += _TxWeightEstimator.INPUT_SIZE;
1278
+ return this;
1279
+ }
1280
+ addKeySpendInput(isDefault = true) {
1281
+ this.inputCount++;
1282
+ this.inputWitnessSize += 64 + 1 + (isDefault ? 0 : 1);
1283
+ this.inputSize += _TxWeightEstimator.INPUT_SIZE;
1284
+ this.hasWitness = true;
1285
+ return this;
1286
+ }
1287
+ addP2PKHInput() {
1288
+ this.inputCount++;
1289
+ this.inputWitnessSize++;
1290
+ this.inputSize += _TxWeightEstimator.INPUT_SIZE + _TxWeightEstimator.P2PKH_SCRIPT_SIG_SIZE;
1291
+ return this;
1292
+ }
1293
+ addTapscriptInput(leafWitnessSize, leafScriptSize, leafControlBlockSize) {
1294
+ const controlBlockWitnessSize = 1 + _TxWeightEstimator.BASE_CONTROL_BLOCK_SIZE + 1 + leafScriptSize + 1 + leafControlBlockSize;
1295
+ this.inputCount++;
1296
+ this.inputWitnessSize += leafWitnessSize + 1 + controlBlockWitnessSize;
1297
+ this.inputSize += _TxWeightEstimator.INPUT_SIZE;
1298
+ this.hasWitness = true;
1299
+ return this;
1300
+ }
1301
+ addP2WPKHOutput() {
1302
+ this.outputCount++;
1303
+ this.outputSize += _TxWeightEstimator.OUTPUT_SIZE + _TxWeightEstimator.P2WPKH_OUTPUT_SIZE;
1304
+ return this;
1305
+ }
1306
+ addP2TROutput() {
1307
+ this.outputCount++;
1308
+ this.outputSize += _TxWeightEstimator.OUTPUT_SIZE + _TxWeightEstimator.P2TR_OUTPUT_SIZE;
1309
+ return this;
1310
+ }
1311
+ /**
1312
+ * Adds an output given a raw script.
1313
+ * Cost = 8 bytes (amount) + varint(scriptLen) + scriptLen
1314
+ */
1315
+ addOutputScript(script) {
1316
+ this.outputCount++;
1317
+ this.outputSize += 8 + getVarIntSize(script.length) + script.length;
1318
+ return this;
1319
+ }
1320
+ /**
1321
+ * Adds an output by decoding the address to get the exact script size.
1322
+ */
1323
+ addOutputAddress(address, network) {
1324
+ const payment = btcSigner.Address(network).decode(address);
1325
+ const script = btcSigner.OutScript.encode(payment);
1326
+ return this.addOutputScript(script);
1327
+ }
1328
+ vsize() {
1329
+ const inputCount = getVarIntSize(this.inputCount);
1330
+ const outputCount = getVarIntSize(this.outputCount);
1331
+ const txSizeStripped = _TxWeightEstimator.BASE_TX_SIZE + inputCount + this.inputSize + outputCount + this.outputSize;
1332
+ let weight = txSizeStripped * _TxWeightEstimator.WITNESS_SCALE_FACTOR;
1333
+ if (this.hasWitness) {
1334
+ weight += _TxWeightEstimator.WITNESS_HEADER_SIZE + this.inputWitnessSize;
1335
+ }
1336
+ return vsize(weight);
1337
+ }
1338
+ };
1339
+ var vsize = (weight) => {
1340
+ const value = BigInt(Math.ceil(weight / TxWeightEstimator.WITNESS_SCALE_FACTOR));
1341
+ return {
1342
+ value,
1343
+ fee: (feeRate) => feeRate * value
1344
+ };
1345
+ };
1346
+
1347
+ // src/utils/anchor.ts
1240
1348
  var ANCHOR_VALUE = 0n;
1241
1349
  var ANCHOR_PKSCRIPT = new Uint8Array([81, 2, 78, 115]);
1242
1350
  var P2A = {
@@ -1262,6 +1370,37 @@ function findP2AOutput(tx) {
1262
1370
  }
1263
1371
  throw new Error("P2A output not found");
1264
1372
  }
1373
+ var CHILD_DUST_AMOUNT = 546;
1374
+ function buildAnchorChild(params) {
1375
+ const { parent, feeRate, fundingCoins, changeAddress, tapInternalKey, network } = params;
1376
+ const child = new chunkUBYY2TPK_cjs.Transaction({ version: 3, allowLegacyWitnessUtxo: true });
1377
+ child.addInput(findP2AOutput(parent));
1378
+ const estimator = TxWeightEstimator.create().addP2AInput();
1379
+ for (const _ of fundingCoins) {
1380
+ estimator.addKeySpendInput(true);
1381
+ }
1382
+ estimator.addOutputAddress(changeAddress, network);
1383
+ const childVsize = Number(estimator.vsize().value);
1384
+ const fee = Math.ceil(feeRate * (parent.vsize + childVsize));
1385
+ let total = 0n;
1386
+ for (const coin of fundingCoins) {
1387
+ total += BigInt(coin.value);
1388
+ child.addInput({
1389
+ txid: coin.txid,
1390
+ index: coin.vout,
1391
+ witnessUtxo: { script: params.changeScript, amount: BigInt(coin.value) },
1392
+ tapInternalKey
1393
+ });
1394
+ }
1395
+ const change = total + P2A.amount - BigInt(fee);
1396
+ if (change < BigInt(CHILD_DUST_AMOUNT)) {
1397
+ throw new Error(
1398
+ `insufficient funding for anchor child: need change >= ${CHILD_DUST_AMOUNT}, got ${change}`
1399
+ );
1400
+ }
1401
+ child.addOutputAddress(changeAddress, change, network);
1402
+ return { child, fee };
1403
+ }
1265
1404
 
1266
1405
  // src/forfeit.ts
1267
1406
  function buildForfeitTx(inputs, forfeitPkScript, txLocktime) {
@@ -1282,7 +1421,7 @@ function buildForfeitTx(inputs, forfeitPkScript, txLocktime) {
1282
1421
  );
1283
1422
  }
1284
1423
  function buildForfeitTxWithOutput(inputs, output, txLocktime) {
1285
- const tx = new chunkYEF5FL6E_cjs.Transaction({
1424
+ const tx = new chunkUBYY2TPK_cjs.Transaction({
1286
1425
  version: 3,
1287
1426
  lockTime: txLocktime
1288
1427
  });
@@ -1358,7 +1497,7 @@ function validateVtxoTxGraph(graph, roundTransaction, sweepTapTreeRoot) {
1358
1497
  if (previousScriptKey.length !== 32) {
1359
1498
  throw new Error(`parent output ${childIndex} has invalid script`);
1360
1499
  }
1361
- const cosigners = chunkYEF5FL6E_cjs.getArkPsbtFields(child.root, 0, chunkYEF5FL6E_cjs.CosignerPublicKey);
1500
+ const cosigners = chunkUBYY2TPK_cjs.getArkPsbtFields(child.root, 0, chunkUBYY2TPK_cjs.CosignerPublicKey);
1362
1501
  if (cosigners.length === 0) {
1363
1502
  throw ErrMissingCosignersPublicKeys;
1364
1503
  }
@@ -1458,7 +1597,7 @@ var Extension = class _Extension {
1458
1597
  `expected magic prefix ${base.hex.encode(ARKADE_MAGIC)}, got ${base.hex.encode(payload.slice(0, Math.min(payload.length, ARKADE_MAGIC.length)))}`
1459
1598
  );
1460
1599
  }
1461
- const reader = new chunkYEF5FL6E_cjs.BufferReader(payload.slice(ARKADE_MAGIC.length));
1600
+ const reader = new chunkUBYY2TPK_cjs.BufferReader(payload.slice(ARKADE_MAGIC.length));
1462
1601
  const packets = [];
1463
1602
  while (reader.remaining() > 0) {
1464
1603
  const packetType = reader.readByte();
@@ -1532,7 +1671,7 @@ var Extension = class _Extension {
1532
1671
  */
1533
1672
  getAssetPacket() {
1534
1673
  for (const p of this.packets) {
1535
- if (p instanceof chunkYEF5FL6E_cjs.Packet) {
1674
+ if (p instanceof chunkUBYY2TPK_cjs.Packet) {
1536
1675
  return p;
1537
1676
  }
1538
1677
  }
@@ -1540,8 +1679,8 @@ var Extension = class _Extension {
1540
1679
  }
1541
1680
  };
1542
1681
  function parsePacket(packetType, data) {
1543
- if (packetType === chunkYEF5FL6E_cjs.Packet.PACKET_TYPE) {
1544
- return chunkYEF5FL6E_cjs.Packet.fromBytes(data);
1682
+ if (packetType === chunkUBYY2TPK_cjs.Packet.PACKET_TYPE) {
1683
+ return chunkUBYY2TPK_cjs.Packet.fromBytes(data);
1545
1684
  }
1546
1685
  return new UnknownPacket(packetType, data);
1547
1686
  }
@@ -1848,7 +1987,7 @@ function createAssetPacket(assetInputs, receivers, changeReceiver) {
1848
1987
  const existing = inputsByAssetId.get(asset.assetId);
1849
1988
  inputsByAssetId.set(asset.assetId, [
1850
1989
  ...existing ?? [],
1851
- chunkYEF5FL6E_cjs.AssetInput.create(inputIndex, asset.amount)
1990
+ chunkUBYY2TPK_cjs.AssetInput.create(inputIndex, asset.amount)
1852
1991
  ]);
1853
1992
  }
1854
1993
  }
@@ -1860,7 +1999,7 @@ function createAssetPacket(assetInputs, receivers, changeReceiver) {
1860
1999
  const existing = outputsByAssetId.get(asset.assetId);
1861
2000
  outputsByAssetId.set(asset.assetId, [
1862
2001
  ...existing ?? [],
1863
- chunkYEF5FL6E_cjs.AssetOutput.create(outputIndex, asset.amount)
2002
+ chunkUBYY2TPK_cjs.AssetOutput.create(outputIndex, asset.amount)
1864
2003
  ]);
1865
2004
  }
1866
2005
  }
@@ -1871,7 +2010,7 @@ function createAssetPacket(assetInputs, receivers, changeReceiver) {
1871
2010
  const existing = outputsByAssetId.get(asset.assetId);
1872
2011
  outputsByAssetId.set(asset.assetId, [
1873
2012
  ...existing ?? [],
1874
- chunkYEF5FL6E_cjs.AssetOutput.create(outputIndex, asset.amount)
2013
+ chunkUBYY2TPK_cjs.AssetOutput.create(outputIndex, asset.amount)
1875
2014
  ]);
1876
2015
  }
1877
2016
  }
@@ -1880,11 +2019,11 @@ function createAssetPacket(assetInputs, receivers, changeReceiver) {
1880
2019
  for (const assetIdStr of allAssetIds) {
1881
2020
  const inputs = inputsByAssetId.get(assetIdStr);
1882
2021
  const outputs = outputsByAssetId.get(assetIdStr);
1883
- const assetId = chunkYEF5FL6E_cjs.AssetId.fromString(assetIdStr);
1884
- const group = chunkYEF5FL6E_cjs.AssetGroup.create(assetId, null, inputs ?? [], outputs ?? [], []);
2022
+ const assetId = chunkUBYY2TPK_cjs.AssetId.fromString(assetIdStr);
2023
+ const group = chunkUBYY2TPK_cjs.AssetGroup.create(assetId, null, inputs ?? [], outputs ?? [], []);
1885
2024
  groups.push(group);
1886
2025
  }
1887
- return chunkYEF5FL6E_cjs.Packet.create(groups);
2026
+ return chunkUBYY2TPK_cjs.Packet.create(groups);
1888
2027
  }
1889
2028
  function selectCoinsWithAsset(coins, assetId, requiredAmount) {
1890
2029
  const coinsWithAsset = coins.filter((coin) => coin.assets?.some((a) => a.assetId === assetId));
@@ -2001,7 +2140,7 @@ function buildVirtualTx(inputs, outputs) {
2001
2140
  }
2002
2141
  }
2003
2142
  }
2004
- const tx = new chunkYEF5FL6E_cjs.Transaction({
2143
+ const tx = new chunkUBYY2TPK_cjs.Transaction({
2005
2144
  version: 3,
2006
2145
  lockTime: Number(lockTime)
2007
2146
  });
@@ -2016,7 +2155,7 @@ function buildVirtualTx(inputs, outputs) {
2016
2155
  },
2017
2156
  tapLeafScript: [input.tapLeafScript]
2018
2157
  });
2019
- chunkYEF5FL6E_cjs.setArkPsbtField(tx, i, chunkYEF5FL6E_cjs.VtxoTaprootTree, input.tapTree);
2158
+ chunkUBYY2TPK_cjs.setArkPsbtField(tx, i, chunkUBYY2TPK_cjs.VtxoTaprootTree, input.tapTree);
2020
2159
  }
2021
2160
  for (const output of outputs) {
2022
2161
  tx.addOutput(output);
@@ -2176,112 +2315,6 @@ function isValidArkAddress(address) {
2176
2315
  return false;
2177
2316
  }
2178
2317
  }
2179
- var getVarIntSize = (n) => {
2180
- if (n < 253) return 1;
2181
- if (n <= 65535) return 3;
2182
- if (n <= 4294967295) return 5;
2183
- return 9;
2184
- };
2185
- var TxWeightEstimator = class _TxWeightEstimator {
2186
- static P2PKH_SCRIPT_SIG_SIZE = 1 + 73 + 1 + 33;
2187
- static INPUT_SIZE = 32 + 4 + 1 + 4;
2188
- static BASE_CONTROL_BLOCK_SIZE = 1 + 32;
2189
- static OUTPUT_SIZE = 8 + 1;
2190
- static P2WPKH_OUTPUT_SIZE = 1 + 1 + 20;
2191
- static BASE_TX_SIZE = 8 + 2;
2192
- // Version + LockTime
2193
- static WITNESS_HEADER_SIZE = 2;
2194
- // Flag + Marker
2195
- static WITNESS_SCALE_FACTOR = 4;
2196
- static P2TR_OUTPUT_SIZE = 1 + 1 + 32;
2197
- hasWitness;
2198
- inputCount;
2199
- outputCount;
2200
- inputSize;
2201
- inputWitnessSize;
2202
- outputSize;
2203
- constructor(hasWitness, inputCount, outputCount, inputSize, inputWitnessSize, outputSize) {
2204
- this.hasWitness = hasWitness;
2205
- this.inputCount = inputCount;
2206
- this.outputCount = outputCount;
2207
- this.inputSize = inputSize;
2208
- this.inputWitnessSize = inputWitnessSize;
2209
- this.outputSize = outputSize;
2210
- }
2211
- static create() {
2212
- return new _TxWeightEstimator(false, 0, 0, 0, 0, 0);
2213
- }
2214
- addP2AInput() {
2215
- this.inputCount++;
2216
- this.inputSize += _TxWeightEstimator.INPUT_SIZE;
2217
- return this;
2218
- }
2219
- addKeySpendInput(isDefault = true) {
2220
- this.inputCount++;
2221
- this.inputWitnessSize += 64 + 1 + (isDefault ? 0 : 1);
2222
- this.inputSize += _TxWeightEstimator.INPUT_SIZE;
2223
- this.hasWitness = true;
2224
- return this;
2225
- }
2226
- addP2PKHInput() {
2227
- this.inputCount++;
2228
- this.inputWitnessSize++;
2229
- this.inputSize += _TxWeightEstimator.INPUT_SIZE + _TxWeightEstimator.P2PKH_SCRIPT_SIG_SIZE;
2230
- return this;
2231
- }
2232
- addTapscriptInput(leafWitnessSize, leafScriptSize, leafControlBlockSize) {
2233
- const controlBlockWitnessSize = 1 + _TxWeightEstimator.BASE_CONTROL_BLOCK_SIZE + 1 + leafScriptSize + 1 + leafControlBlockSize;
2234
- this.inputCount++;
2235
- this.inputWitnessSize += leafWitnessSize + 1 + controlBlockWitnessSize;
2236
- this.inputSize += _TxWeightEstimator.INPUT_SIZE;
2237
- this.hasWitness = true;
2238
- return this;
2239
- }
2240
- addP2WPKHOutput() {
2241
- this.outputCount++;
2242
- this.outputSize += _TxWeightEstimator.OUTPUT_SIZE + _TxWeightEstimator.P2WPKH_OUTPUT_SIZE;
2243
- return this;
2244
- }
2245
- addP2TROutput() {
2246
- this.outputCount++;
2247
- this.outputSize += _TxWeightEstimator.OUTPUT_SIZE + _TxWeightEstimator.P2TR_OUTPUT_SIZE;
2248
- return this;
2249
- }
2250
- /**
2251
- * Adds an output given a raw script.
2252
- * Cost = 8 bytes (amount) + varint(scriptLen) + scriptLen
2253
- */
2254
- addOutputScript(script) {
2255
- this.outputCount++;
2256
- this.outputSize += 8 + getVarIntSize(script.length) + script.length;
2257
- return this;
2258
- }
2259
- /**
2260
- * Adds an output by decoding the address to get the exact script size.
2261
- */
2262
- addOutputAddress(address, network) {
2263
- const payment = btcSigner.Address(network).decode(address);
2264
- const script = btcSigner.OutScript.encode(payment);
2265
- return this.addOutputScript(script);
2266
- }
2267
- vsize() {
2268
- const inputCount = getVarIntSize(this.inputCount);
2269
- const outputCount = getVarIntSize(this.outputCount);
2270
- const txSizeStripped = _TxWeightEstimator.BASE_TX_SIZE + inputCount + this.inputSize + outputCount + this.outputSize;
2271
- let weight = txSizeStripped * _TxWeightEstimator.WITNESS_SCALE_FACTOR;
2272
- if (this.hasWitness) {
2273
- weight += _TxWeightEstimator.WITNESS_HEADER_SIZE + this.inputWitnessSize;
2274
- }
2275
- return vsize(weight);
2276
- }
2277
- };
2278
- var vsize = (weight) => {
2279
- const value = BigInt(Math.ceil(weight / TxWeightEstimator.WITNESS_SCALE_FACTOR));
2280
- return {
2281
- value,
2282
- fee: (feeRate) => feeRate * value
2283
- };
2284
- };
2285
2318
  var AmountVariableName = "amount";
2286
2319
  var ExpiryVariableName = "expiry";
2287
2320
  var BirthVariableName = "birth";
@@ -2666,7 +2699,7 @@ function getExpiringAndRecoverableVtxos(vtxos, thresholdMs, dustAmount) {
2666
2699
  );
2667
2700
  }
2668
2701
  function isMigrationCapable(wallet) {
2669
- return "arkProvider" in wallet && "arkServerPublicKey" in wallet && "onchainProvider" in wallet && typeof wallet.rotateServerSigner === "function" && typeof wallet.sendSelectedVtxosToSelf === "function" && typeof wallet.getBoardingUtxosForSigners === "function";
2702
+ return "arkProvider" in wallet && "arkServerPublicKey" in wallet && "onchainProvider" in wallet && typeof wallet.rotateServerSigner === "function" && typeof wallet.refreshDeprecatedSigners === "function" && typeof wallet.sendSelectedVtxosToSelf === "function" && typeof wallet.getBoardingUtxosForSigners === "function";
2670
2703
  }
2671
2704
  function classifiedToRef(c) {
2672
2705
  return {
@@ -3124,7 +3157,7 @@ var VtxoManager = class _VtxoManager {
3124
3157
  `Sweep not economical: output ${outputAmount} sats after ${fee} sats fee is below dust (${dustAmount} sats)`
3125
3158
  );
3126
3159
  }
3127
- const tx = new chunkYEF5FL6E_cjs.Transaction();
3160
+ const tx = new chunkUBYY2TPK_cjs.Transaction();
3128
3161
  for (const utxo of expiredUtxos) {
3129
3162
  const utxoScript = chunkHXDZ2QTW_cjs.VtxoScript.decode(utxo.tapTree);
3130
3163
  const utxoExitLeaf = utxoScript.leaves.find(
@@ -3190,6 +3223,7 @@ var VtxoManager = class _VtxoManager {
3190
3223
  async migrateCore(options) {
3191
3224
  const wallet = this.requireMigrationCapableWallet();
3192
3225
  const info = await wallet.arkProvider.getInfo();
3226
+ wallet.refreshDeprecatedSigners(info);
3193
3227
  const signerSet = signerSetFromInfo(info);
3194
3228
  const nowSeconds = Math.floor(Date.now() / 1e3);
3195
3229
  const walletSignerHex = base.hex.encode(wallet.arkServerPublicKey);
@@ -3762,7 +3796,7 @@ var VtxoManager = class _VtxoManager {
3762
3796
  * or doesn't carry the metadata.
3763
3797
  */
3764
3798
  extractSpentOutpoint(error) {
3765
- const ark = chunkYEF5FL6E_cjs.maybeArkError(error);
3799
+ const ark = chunkUBYY2TPK_cjs.maybeArkError(error);
3766
3800
  if (!ark || ark.name !== "VTXO_ALREADY_SPENT") return void 0;
3767
3801
  const raw = ark.metadata?.vtxo_outpoint;
3768
3802
  if (typeof raw !== "string") return void 0;
@@ -4158,6 +4192,123 @@ function readUInt32BE(array, offset) {
4158
4192
  function noteTapscript(preimageHash) {
4159
4193
  return btcSigner.Script.encode(["SHA256", preimageHash, "EQUAL"]);
4160
4194
  }
4195
+
4196
+ // src/repositories/intentRepository.ts
4197
+ var ALL_INTENT_STATES = [
4198
+ "waiting_to_submit",
4199
+ "waiting_for_batch",
4200
+ "batch_in_progress",
4201
+ "batch_failed",
4202
+ "batch_succeeded",
4203
+ "cancelled"
4204
+ ];
4205
+ var INTENT_TERMINAL_STATES = /* @__PURE__ */ new Set([
4206
+ "batch_failed",
4207
+ "batch_succeeded",
4208
+ "cancelled"
4209
+ ]);
4210
+ function isTerminalIntentState(s) {
4211
+ return INTENT_TERMINAL_STATES.has(s);
4212
+ }
4213
+ function assertIntentIdUnique(intent, existing) {
4214
+ if (intent.intentId === void 0) return;
4215
+ for (const e of existing) {
4216
+ if (e.intentTxId !== intent.intentTxId && e.intentId === intent.intentId) {
4217
+ throw new Error(
4218
+ `intentId "${intent.intentId}" is already used by intent ${e.intentTxId}`
4219
+ );
4220
+ }
4221
+ }
4222
+ }
4223
+ function intentMatchesFilter(i, f) {
4224
+ if (f.intentTxIds && !f.intentTxIds.includes(i.intentTxId)) return false;
4225
+ if (f.intentIds && (!i.intentId || !f.intentIds.includes(i.intentId))) return false;
4226
+ if (f.states && !f.states.includes(i.state)) return false;
4227
+ if (f.containingInputs) {
4228
+ const keys = new Set(i.intentVtxos.map((o) => `${o.txid}:${o.vout}`));
4229
+ if (!f.containingInputs.some((o) => keys.has(`${o.txid}:${o.vout}`))) return false;
4230
+ }
4231
+ if (f.validAt !== void 0) {
4232
+ if (i.validFrom !== void 0 && f.validAt < i.validFrom) return false;
4233
+ if (i.validUntil !== void 0 && f.validAt > i.validUntil) return false;
4234
+ }
4235
+ if (f.searchText) {
4236
+ const hay = [i.intentId, i.batchId, i.commitmentTransactionId].filter(Boolean).join(" ");
4237
+ if (!hay.includes(f.searchText)) return false;
4238
+ }
4239
+ return true;
4240
+ }
4241
+ function intentPageBounds(filter, total) {
4242
+ const skip = Math.max(0, filter?.skip ?? 0);
4243
+ const take = filter?.take === void 0 ? total : Math.max(0, filter.take);
4244
+ return { skip, end: skip + take };
4245
+ }
4246
+
4247
+ // src/wallet/intentStateReducer.ts
4248
+ function reduceIntentState(current, event) {
4249
+ if (isTerminalIntentState(current)) return current;
4250
+ switch (event.type) {
4251
+ case "batch_started" /* BatchStarted */:
4252
+ return "batch_in_progress";
4253
+ case "batch_finalized" /* BatchFinalized */:
4254
+ return "batch_succeeded";
4255
+ case "batch_failed" /* BatchFailed */:
4256
+ return "batch_failed";
4257
+ default:
4258
+ return current;
4259
+ }
4260
+ }
4261
+ function applySettlementEventToIntent(intent, event) {
4262
+ const next = reduceIntentState(intent.state, event);
4263
+ if (next === intent.state) return void 0;
4264
+ const updated = { ...intent, state: next };
4265
+ if ("id" in event && typeof event.id === "string") {
4266
+ updated.batchId = event.id;
4267
+ }
4268
+ if (event.type === "batch_finalized" /* BatchFinalized */) {
4269
+ updated.commitmentTransactionId = event.commitmentTxid;
4270
+ }
4271
+ if (event.type === "batch_failed" /* BatchFailed */) {
4272
+ updated.cancellationReason = event.reason;
4273
+ }
4274
+ return updated;
4275
+ }
4276
+
4277
+ // src/wallet/intentPersistenceHandler.ts
4278
+ async function persistTransition(deps, event) {
4279
+ const repo = deps.intentRepository;
4280
+ if (!repo) return;
4281
+ try {
4282
+ const cur = (await repo.getIntents({ intentTxIds: [deps.intentTxId] }))[0];
4283
+ if (!cur) return;
4284
+ const next = applySettlementEventToIntent(cur, event);
4285
+ if (next) await repo.saveIntent(next);
4286
+ } catch (e) {
4287
+ console.error(`Failed to apply settlement event to intent ${deps.intentTxId}`, e);
4288
+ }
4289
+ }
4290
+ function wrapHandlerWithIntentPersistence(base, deps) {
4291
+ return {
4292
+ ...base,
4293
+ onBatchStarted: async (event) => {
4294
+ const result = await base.onBatchStarted(event);
4295
+ if (!result.skip) await persistTransition(deps, event);
4296
+ return result;
4297
+ },
4298
+ onBatchFinalized: async (event) => {
4299
+ if (base.onBatchFinalized) await base.onBatchFinalized(event);
4300
+ await persistTransition(deps, event);
4301
+ },
4302
+ onBatchFailed: async (event) => {
4303
+ await persistTransition(deps, event);
4304
+ if (base.onBatchFailed) {
4305
+ await base.onBatchFailed(event);
4306
+ return;
4307
+ }
4308
+ throw new Error(event.reason);
4309
+ }
4310
+ };
4311
+ }
4161
4312
  var TxTree = class {
4162
4313
  constructor(root, children = /* @__PURE__ */ new Map()) {
4163
4314
  this.root = root;
@@ -4673,7 +4824,7 @@ var AssetManager = class extends ReadonlyAssetManager {
4673
4824
  const virtualCoins = await this.wallet.getVtxos({
4674
4825
  withRecoverable: false
4675
4826
  });
4676
- const controlAssetRef = params.controlAssetId ? chunkYEF5FL6E_cjs.AssetRef.fromId(chunkYEF5FL6E_cjs.AssetId.fromString(params.controlAssetId)) : null;
4827
+ const controlAssetRef = params.controlAssetId ? chunkUBYY2TPK_cjs.AssetRef.fromId(chunkUBYY2TPK_cjs.AssetId.fromString(params.controlAssetId)) : null;
4677
4828
  const coinSelection = selectVirtualCoins(virtualCoins, Number(this.wallet.dustAmount));
4678
4829
  let totalBtcSelected = 0n;
4679
4830
  const assetChanges = /* @__PURE__ */ new Map();
@@ -4686,8 +4837,8 @@ var AssetManager = class extends ReadonlyAssetManager {
4686
4837
  }
4687
4838
  }
4688
4839
  const groups = [];
4689
- const issuedAssetOutput = chunkYEF5FL6E_cjs.AssetOutput.create(0, params.amount);
4690
- const issuedAssetGroup = chunkYEF5FL6E_cjs.AssetGroup.create(
4840
+ const issuedAssetOutput = chunkUBYY2TPK_cjs.AssetOutput.create(0, params.amount);
4841
+ const issuedAssetGroup = chunkUBYY2TPK_cjs.AssetGroup.create(
4691
4842
  null,
4692
4843
  controlAssetRef,
4693
4844
  [],
@@ -4702,15 +4853,15 @@ var AssetManager = class extends ReadonlyAssetManager {
4702
4853
  for (const [inputIndex, assets] of assetInputs) {
4703
4854
  for (const asset of assets) {
4704
4855
  if (asset.assetId !== assetId) continue;
4705
- changeInputs.push(chunkYEF5FL6E_cjs.AssetInput.create(inputIndex, asset.amount));
4856
+ changeInputs.push(chunkUBYY2TPK_cjs.AssetInput.create(inputIndex, asset.amount));
4706
4857
  }
4707
4858
  }
4708
4859
  groups.push(
4709
- chunkYEF5FL6E_cjs.AssetGroup.create(
4710
- chunkYEF5FL6E_cjs.AssetId.fromString(assetId),
4860
+ chunkUBYY2TPK_cjs.AssetGroup.create(
4861
+ chunkUBYY2TPK_cjs.AssetId.fromString(assetId),
4711
4862
  null,
4712
4863
  changeInputs,
4713
- [chunkYEF5FL6E_cjs.AssetOutput.create(0, amount)],
4864
+ [chunkUBYY2TPK_cjs.AssetOutput.create(0, amount)],
4714
4865
  []
4715
4866
  )
4716
4867
  );
@@ -4723,7 +4874,7 @@ var AssetManager = class extends ReadonlyAssetManager {
4723
4874
  script: outputAddress.pkScript,
4724
4875
  amount: BigInt(totalBtcSelected)
4725
4876
  },
4726
- Extension.create([chunkYEF5FL6E_cjs.Packet.create(groups)]).txOut()
4877
+ Extension.create([chunkUBYY2TPK_cjs.Packet.create(groups)]).txOut()
4727
4878
  ];
4728
4879
  const { arkTxid } = await this.wallet.buildAndSubmitOffchainTx(
4729
4880
  coinSelection.inputs,
@@ -4731,7 +4882,7 @@ var AssetManager = class extends ReadonlyAssetManager {
4731
4882
  );
4732
4883
  return {
4733
4884
  arkTxId: arkTxid,
4734
- assetId: chunkYEF5FL6E_cjs.AssetId.create(arkTxid, 0).toString()
4885
+ assetId: chunkUBYY2TPK_cjs.AssetId.create(arkTxid, 0).toString()
4735
4886
  };
4736
4887
  }
4737
4888
  /**
@@ -4803,16 +4954,16 @@ var AssetManager = class extends ReadonlyAssetManager {
4803
4954
  for (const [inputIndex, assets] of assetInputs) {
4804
4955
  for (const asset of assets) {
4805
4956
  if (asset.assetId !== params.assetId) continue;
4806
- reissueInputs.push(chunkYEF5FL6E_cjs.AssetInput.create(inputIndex, asset.amount));
4957
+ reissueInputs.push(chunkUBYY2TPK_cjs.AssetInput.create(inputIndex, asset.amount));
4807
4958
  }
4808
4959
  }
4809
4960
  const totalAssetAmount = assetToReissueAmount + params.amount;
4810
- const reissueAssetIdObj = chunkYEF5FL6E_cjs.AssetId.fromString(params.assetId);
4811
- const reissueAssetGroup = chunkYEF5FL6E_cjs.AssetGroup.create(
4961
+ const reissueAssetIdObj = chunkUBYY2TPK_cjs.AssetId.fromString(params.assetId);
4962
+ const reissueAssetGroup = chunkUBYY2TPK_cjs.AssetGroup.create(
4812
4963
  reissueAssetIdObj,
4813
4964
  null,
4814
4965
  reissueInputs,
4815
- [chunkYEF5FL6E_cjs.AssetOutput.create(0, totalAssetAmount)],
4966
+ [chunkUBYY2TPK_cjs.AssetOutput.create(0, totalAssetAmount)],
4816
4967
  []
4817
4968
  );
4818
4969
  const groups = [reissueAssetGroup];
@@ -4821,15 +4972,15 @@ var AssetManager = class extends ReadonlyAssetManager {
4821
4972
  for (const [inputIndex, assets] of assetInputs) {
4822
4973
  for (const asset of assets) {
4823
4974
  if (asset.assetId !== assetId) continue;
4824
- changeInputs.push(chunkYEF5FL6E_cjs.AssetInput.create(inputIndex, asset.amount));
4975
+ changeInputs.push(chunkUBYY2TPK_cjs.AssetInput.create(inputIndex, asset.amount));
4825
4976
  }
4826
4977
  }
4827
4978
  groups.push(
4828
- chunkYEF5FL6E_cjs.AssetGroup.create(
4829
- chunkYEF5FL6E_cjs.AssetId.fromString(assetId),
4979
+ chunkUBYY2TPK_cjs.AssetGroup.create(
4980
+ chunkUBYY2TPK_cjs.AssetId.fromString(assetId),
4830
4981
  null,
4831
4982
  changeInputs,
4832
- [chunkYEF5FL6E_cjs.AssetOutput.create(0, amount)],
4983
+ [chunkUBYY2TPK_cjs.AssetOutput.create(0, amount)],
4833
4984
  []
4834
4985
  )
4835
4986
  );
@@ -4841,7 +4992,7 @@ var AssetManager = class extends ReadonlyAssetManager {
4841
4992
  script: outputAddress.pkScript,
4842
4993
  amount: BigInt(totalBtcSelected)
4843
4994
  },
4844
- Extension.create([chunkYEF5FL6E_cjs.Packet.create(groups)]).txOut()
4995
+ Extension.create([chunkUBYY2TPK_cjs.Packet.create(groups)]).txOut()
4845
4996
  ];
4846
4997
  const { arkTxid } = await this.wallet.buildAndSubmitOffchainTx(selectedCoins, outputs);
4847
4998
  return arkTxid;
@@ -4908,15 +5059,15 @@ var AssetManager = class extends ReadonlyAssetManager {
4908
5059
  for (const [inputIndex, assets] of assetInputs) {
4909
5060
  for (const asset of assets) {
4910
5061
  if (asset.assetId !== assetId) continue;
4911
- changeInputs.push(chunkYEF5FL6E_cjs.AssetInput.create(inputIndex, asset.amount));
5062
+ changeInputs.push(chunkUBYY2TPK_cjs.AssetInput.create(inputIndex, asset.amount));
4912
5063
  }
4913
5064
  }
4914
5065
  groups.push(
4915
- chunkYEF5FL6E_cjs.AssetGroup.create(
4916
- chunkYEF5FL6E_cjs.AssetId.fromString(assetId),
5066
+ chunkUBYY2TPK_cjs.AssetGroup.create(
5067
+ chunkUBYY2TPK_cjs.AssetId.fromString(assetId),
4917
5068
  null,
4918
5069
  changeInputs,
4919
- amount > 0n ? [chunkYEF5FL6E_cjs.AssetOutput.create(0, amount)] : [],
5070
+ amount > 0n ? [chunkUBYY2TPK_cjs.AssetOutput.create(0, amount)] : [],
4920
5071
  []
4921
5072
  )
4922
5073
  );
@@ -4928,7 +5079,7 @@ var AssetManager = class extends ReadonlyAssetManager {
4928
5079
  script: outputAddress.pkScript,
4929
5080
  amount: BigInt(totalBtcSelected)
4930
5081
  },
4931
- Extension.create([chunkYEF5FL6E_cjs.Packet.create(groups)]).txOut()
5082
+ Extension.create([chunkUBYY2TPK_cjs.Packet.create(groups)]).txOut()
4932
5083
  ];
4933
5084
  const { arkTxid } = await this.wallet.buildAndSubmitOffchainTx(selectedCoins, outputs);
4934
5085
  return arkTxid;
@@ -4953,7 +5104,7 @@ function castMetadata(metadata) {
4953
5104
  } else {
4954
5105
  throw new Error("Invalid metadata value type");
4955
5106
  }
4956
- md.push(chunkYEF5FL6E_cjs.Metadata.create(textEncoder.encode(key), valueBytes));
5107
+ md.push(chunkUBYY2TPK_cjs.Metadata.create(textEncoder.encode(key), valueBytes));
4957
5108
  }
4958
5109
  return md;
4959
5110
  }
@@ -5238,7 +5389,7 @@ async function makeSignedDelegateIntent(identity, coins, outputs, onchainOutputs
5238
5389
  expire_at: 0,
5239
5390
  cosigners_public_keys: cosignerPubKeys
5240
5391
  };
5241
- const proof = chunkYEF5FL6E_cjs.Intent.create(message, coins, outputs);
5392
+ const proof = chunkUBYY2TPK_cjs.Intent.create(message, coins, outputs);
5242
5393
  const signedProof = await identity.sign(proof);
5243
5394
  return {
5244
5395
  proof: base.base64.encode(signedProof.toPSBT()),
@@ -5267,9 +5418,661 @@ function isAnnotated(v) {
5267
5418
  return v.tapTree !== void 0 && v.forfeitTapLeafScript !== void 0 && v.intentTapLeafScript !== void 0;
5268
5419
  }
5269
5420
 
5270
- // src/contracts/vtxoOwnership.ts
5271
- function vtxoOutpoint(vtxo) {
5272
- return `${vtxo.txid}:${vtxo.vout}`;
5421
+ // src/repositories/virtualTxRepository.ts
5422
+ var ChainedTxType = /* @__PURE__ */ ((ChainedTxType2) => {
5423
+ ChainedTxType2[ChainedTxType2["Unspecified"] = 0] = "Unspecified";
5424
+ ChainedTxType2[ChainedTxType2["Commitment"] = 1] = "Commitment";
5425
+ ChainedTxType2[ChainedTxType2["Ark"] = 2] = "Ark";
5426
+ ChainedTxType2[ChainedTxType2["Tree"] = 3] = "Tree";
5427
+ ChainedTxType2[ChainedTxType2["Checkpoint"] = 4] = "Checkpoint";
5428
+ return ChainedTxType2;
5429
+ })(ChainedTxType || {});
5430
+ function mergeChainedTxType(incoming, prev) {
5431
+ if (incoming !== 0 /* Unspecified */) return incoming;
5432
+ return prev ?? 0 /* Unspecified */;
5433
+ }
5434
+
5435
+ // src/repositories/inMemory/intentRepository.ts
5436
+ var InMemoryIntentRepository = class {
5437
+ version = 1;
5438
+ byId = /* @__PURE__ */ new Map();
5439
+ async clear() {
5440
+ this.byId.clear();
5441
+ }
5442
+ async saveIntent(intent) {
5443
+ assertIntentIdUnique(intent, this.byId.values());
5444
+ this.byId.set(intent.intentTxId, {
5445
+ ...intent,
5446
+ intentVtxos: [...intent.intentVtxos],
5447
+ partialForfeits: [...intent.partialForfeits],
5448
+ updatedAt: Date.now()
5449
+ });
5450
+ }
5451
+ async getIntents(filter) {
5452
+ let out = [...this.byId.values()];
5453
+ if (filter) out = out.filter((i) => intentMatchesFilter(i, filter));
5454
+ out.sort((a, b) => a.createdAt - b.createdAt || a.intentTxId.localeCompare(b.intentTxId));
5455
+ const { skip, end } = intentPageBounds(filter, out.length);
5456
+ return out.slice(skip, end).map(clone);
5457
+ }
5458
+ async getLockedVtxoOutpoints() {
5459
+ const out = [];
5460
+ for (const i of this.byId.values())
5461
+ if (!isTerminalIntentState(i.state)) for (const o of i.intentVtxos) out.push({ ...o });
5462
+ return out;
5463
+ }
5464
+ async [Symbol.asyncDispose]() {
5465
+ }
5466
+ };
5467
+ var clone = (i) => ({
5468
+ ...i,
5469
+ intentVtxos: i.intentVtxos.map((o) => ({ ...o })),
5470
+ partialForfeits: [...i.partialForfeits]
5471
+ });
5472
+
5473
+ // src/repositories/inMemory/virtualTxRepository.ts
5474
+ var opKey = (o) => `${o.txid}:${o.vout}`;
5475
+ var InMemoryVirtualTxRepository = class {
5476
+ version = 1;
5477
+ txs = /* @__PURE__ */ new Map();
5478
+ branches = /* @__PURE__ */ new Map();
5479
+ async clear() {
5480
+ this.txs.clear();
5481
+ this.branches.clear();
5482
+ }
5483
+ async upsertVirtualTxs(txs) {
5484
+ for (const t of txs) {
5485
+ const prev = this.txs.get(t.txid);
5486
+ if (!prev) {
5487
+ this.txs.set(t.txid, { ...t });
5488
+ continue;
5489
+ }
5490
+ this.txs.set(t.txid, {
5491
+ txid: t.txid,
5492
+ psbt: t.psbt ?? prev.psbt,
5493
+ expiresAt: t.expiresAt ?? prev.expiresAt,
5494
+ type: mergeChainedTxType(t.type, prev.type)
5495
+ });
5496
+ }
5497
+ }
5498
+ async getVirtualTx(txid) {
5499
+ const t = this.txs.get(txid);
5500
+ return t ? { ...t } : null;
5501
+ }
5502
+ async setBranch(vtxo, branch) {
5503
+ this.branches.set(
5504
+ opKey(vtxo),
5505
+ [...branch].sort((a, b) => a.position - b.position)
5506
+ );
5507
+ }
5508
+ async getBranch(vtxo) {
5509
+ const b = this.branches.get(opKey(vtxo)) ?? [];
5510
+ return b.map((x) => this.txs.get(x.virtualTxid)).filter((t) => !!t).map((t) => ({ ...t }));
5511
+ }
5512
+ async hasBranch(vtxo) {
5513
+ return (this.branches.get(opKey(vtxo)) ?? []).length > 0;
5514
+ }
5515
+ async pruneForSpentVtxo(vtxo) {
5516
+ const removed = this.branches.get(opKey(vtxo)) ?? [];
5517
+ this.branches.delete(opKey(vtxo));
5518
+ const stillReferenced = /* @__PURE__ */ new Set();
5519
+ for (const b of this.branches.values())
5520
+ for (const e of b) stillReferenced.add(e.virtualTxid);
5521
+ for (const e of removed)
5522
+ if (!stillReferenced.has(e.virtualTxid)) this.txs.delete(e.virtualTxid);
5523
+ }
5524
+ async [Symbol.asyncDispose]() {
5525
+ }
5526
+ };
5527
+
5528
+ // src/repositories/indexedDB/idbUtils.ts
5529
+ var promisifyRequest = (request) => new Promise((resolve, reject) => {
5530
+ request.onsuccess = () => resolve(request.result);
5531
+ request.onerror = () => reject(request.error);
5532
+ });
5533
+ var awaitTransaction = (transaction) => new Promise((resolve, reject) => {
5534
+ transaction.oncomplete = () => resolve();
5535
+ transaction.onerror = () => reject(transaction.error);
5536
+ transaction.onabort = () => reject(transaction.error ?? new Error("transaction aborted"));
5537
+ });
5538
+
5539
+ // src/repositories/indexedDB/manager.ts
5540
+ function getGlobalObject() {
5541
+ if (typeof globalThis !== "undefined") {
5542
+ if (typeof globalThis.self === "object" && globalThis.self !== null) {
5543
+ return { globalObject: globalThis.self };
5544
+ }
5545
+ if (typeof globalThis.window === "object" && globalThis.window !== null) {
5546
+ return { globalObject: globalThis.window };
5547
+ }
5548
+ return { globalObject: globalThis };
5549
+ }
5550
+ throw new Error("Global object not found");
5551
+ }
5552
+ var dbCache = /* @__PURE__ */ new Map();
5553
+ var refCounts = /* @__PURE__ */ new Map();
5554
+ async function openDatabase(dbName, dbVersion, initDatabase2) {
5555
+ const { globalObject } = getGlobalObject();
5556
+ if (!globalObject.indexedDB) {
5557
+ throw new Error("IndexedDB is not available in this environment");
5558
+ }
5559
+ const cached = dbCache.get(dbName);
5560
+ if (cached) {
5561
+ if (cached.version !== dbVersion) {
5562
+ throw new Error(
5563
+ `Database "${dbName}" already opened with version ${cached.version}; requested ${dbVersion}`
5564
+ );
5565
+ }
5566
+ refCounts.set(dbName, (refCounts.get(dbName) ?? 0) + 1);
5567
+ return cached.promise;
5568
+ }
5569
+ const dbPromise = new Promise((resolve, reject) => {
5570
+ const request = globalObject.indexedDB.open(dbName, dbVersion);
5571
+ request.onerror = () => {
5572
+ dbCache.delete(dbName);
5573
+ refCounts.delete(dbName);
5574
+ reject(request.error);
5575
+ };
5576
+ request.onsuccess = () => {
5577
+ const db = request.result;
5578
+ db.onversionchange = () => {
5579
+ db.close();
5580
+ dbCache.delete(dbName);
5581
+ refCounts.delete(dbName);
5582
+ };
5583
+ resolve(db);
5584
+ };
5585
+ request.onupgradeneeded = (event) => {
5586
+ const db = request.result;
5587
+ initDatabase2(db, event.oldVersion, request.transaction);
5588
+ };
5589
+ request.onblocked = () => {
5590
+ console.warn("Database upgrade blocked - close other tabs/connections");
5591
+ };
5592
+ });
5593
+ dbCache.set(dbName, { version: dbVersion, promise: dbPromise });
5594
+ refCounts.set(dbName, 1);
5595
+ return dbPromise;
5596
+ }
5597
+ async function closeDatabase(dbName) {
5598
+ const cachedEntry = dbCache.get(dbName);
5599
+ if (!cachedEntry) return false;
5600
+ const count = (refCounts.get(dbName) ?? 1) - 1;
5601
+ if (count > 0) {
5602
+ refCounts.set(dbName, count);
5603
+ return false;
5604
+ }
5605
+ refCounts.delete(dbName);
5606
+ dbCache.delete(dbName);
5607
+ try {
5608
+ const db = await cachedEntry.promise;
5609
+ db.close();
5610
+ } catch {
5611
+ }
5612
+ return true;
5613
+ }
5614
+ function scriptFromArkAddress(address) {
5615
+ return base.hex.encode(chunkHXDZ2QTW_cjs.ArkAddress.decode(address).pkScript);
5616
+ }
5617
+
5618
+ // src/repositories/indexedDB/schema.ts
5619
+ var STORE_VTXOS = "vtxos";
5620
+ var STORE_UTXOS = "utxos";
5621
+ var STORE_TRANSACTIONS = "transactions";
5622
+ var STORE_WALLET_STATE = "walletState";
5623
+ var STORE_CONTRACTS = "contracts";
5624
+ var STORE_INTENTS = "intents";
5625
+ var STORE_VIRTUAL_TXS = "virtualTxs";
5626
+ var STORE_VTXO_BRANCHES = "vtxoBranches";
5627
+ var LEGACY_STORE_CONTRACT_COLLECTIONS = "contractsCollections";
5628
+ var DB_VERSION = 3;
5629
+ var INTENT_DB_VERSION = 5;
5630
+ function initDatabase(db, oldVersion, transaction) {
5631
+ if (!db.objectStoreNames.contains(STORE_VTXOS)) {
5632
+ const vtxosStore = db.createObjectStore(STORE_VTXOS, {
5633
+ keyPath: ["address", "txid", "vout"]
5634
+ });
5635
+ if (!vtxosStore.indexNames.contains("address")) {
5636
+ vtxosStore.createIndex("address", "address", {
5637
+ unique: false
5638
+ });
5639
+ }
5640
+ if (!vtxosStore.indexNames.contains("txid")) {
5641
+ vtxosStore.createIndex("txid", "txid", { unique: false });
5642
+ }
5643
+ if (!vtxosStore.indexNames.contains("value")) {
5644
+ vtxosStore.createIndex("value", "value", { unique: false });
5645
+ }
5646
+ if (!vtxosStore.indexNames.contains("status")) {
5647
+ vtxosStore.createIndex("status", "status", {
5648
+ unique: false
5649
+ });
5650
+ }
5651
+ if (!vtxosStore.indexNames.contains("virtualStatus")) {
5652
+ vtxosStore.createIndex("virtualStatus", "virtualStatus", {
5653
+ unique: false
5654
+ });
5655
+ }
5656
+ if (!vtxosStore.indexNames.contains("createdAt")) {
5657
+ vtxosStore.createIndex("createdAt", "createdAt", {
5658
+ unique: false
5659
+ });
5660
+ }
5661
+ if (!vtxosStore.indexNames.contains("isSpent")) {
5662
+ vtxosStore.createIndex("isSpent", "isSpent", {
5663
+ unique: false
5664
+ });
5665
+ }
5666
+ if (!vtxosStore.indexNames.contains("isUnrolled")) {
5667
+ vtxosStore.createIndex("isUnrolled", "isUnrolled", {
5668
+ unique: false
5669
+ });
5670
+ }
5671
+ if (!vtxosStore.indexNames.contains("spentBy")) {
5672
+ vtxosStore.createIndex("spentBy", "spentBy", {
5673
+ unique: false
5674
+ });
5675
+ }
5676
+ if (!vtxosStore.indexNames.contains("settledBy")) {
5677
+ vtxosStore.createIndex("settledBy", "settledBy", {
5678
+ unique: false
5679
+ });
5680
+ }
5681
+ if (!vtxosStore.indexNames.contains("arkTxId")) {
5682
+ vtxosStore.createIndex("arkTxId", "arkTxId", {
5683
+ unique: false
5684
+ });
5685
+ }
5686
+ if (!vtxosStore.indexNames.contains("script")) {
5687
+ vtxosStore.createIndex("script", "script", {
5688
+ unique: false
5689
+ });
5690
+ }
5691
+ }
5692
+ if (!db.objectStoreNames.contains(STORE_UTXOS)) {
5693
+ const utxosStore = db.createObjectStore(STORE_UTXOS, {
5694
+ keyPath: ["address", "txid", "vout"]
5695
+ });
5696
+ if (!utxosStore.indexNames.contains("address")) {
5697
+ utxosStore.createIndex("address", "address", {
5698
+ unique: false
5699
+ });
5700
+ }
5701
+ if (!utxosStore.indexNames.contains("txid")) {
5702
+ utxosStore.createIndex("txid", "txid", { unique: false });
5703
+ }
5704
+ if (!utxosStore.indexNames.contains("value")) {
5705
+ utxosStore.createIndex("value", "value", { unique: false });
5706
+ }
5707
+ if (!utxosStore.indexNames.contains("status")) {
5708
+ utxosStore.createIndex("status", "status", {
5709
+ unique: false
5710
+ });
5711
+ }
5712
+ }
5713
+ if (!db.objectStoreNames.contains(STORE_TRANSACTIONS)) {
5714
+ const transactionsStore = db.createObjectStore(STORE_TRANSACTIONS, {
5715
+ keyPath: ["address", "keyBoardingTxid", "keyCommitmentTxid", "keyArkTxid"]
5716
+ });
5717
+ if (!transactionsStore.indexNames.contains("address")) {
5718
+ transactionsStore.createIndex("address", "address", {
5719
+ unique: false
5720
+ });
5721
+ }
5722
+ if (!transactionsStore.indexNames.contains("type")) {
5723
+ transactionsStore.createIndex("type", "type", {
5724
+ unique: false
5725
+ });
5726
+ }
5727
+ if (!transactionsStore.indexNames.contains("amount")) {
5728
+ transactionsStore.createIndex("amount", "amount", {
5729
+ unique: false
5730
+ });
5731
+ }
5732
+ if (!transactionsStore.indexNames.contains("settled")) {
5733
+ transactionsStore.createIndex("settled", "settled", {
5734
+ unique: false
5735
+ });
5736
+ }
5737
+ if (!transactionsStore.indexNames.contains("createdAt")) {
5738
+ transactionsStore.createIndex("createdAt", "createdAt", {
5739
+ unique: false
5740
+ });
5741
+ }
5742
+ if (!transactionsStore.indexNames.contains("arkTxid")) {
5743
+ transactionsStore.createIndex("arkTxid", "key.arkTxid", {
5744
+ unique: false
5745
+ });
5746
+ }
5747
+ }
5748
+ if (!db.objectStoreNames.contains(STORE_WALLET_STATE)) {
5749
+ db.createObjectStore(STORE_WALLET_STATE, {
5750
+ keyPath: "key"
5751
+ });
5752
+ }
5753
+ if (!db.objectStoreNames.contains(STORE_CONTRACTS)) {
5754
+ const contractsStore = db.createObjectStore(STORE_CONTRACTS, {
5755
+ keyPath: "script"
5756
+ });
5757
+ if (!contractsStore.indexNames.contains("type")) {
5758
+ contractsStore.createIndex("type", "type", {
5759
+ unique: false
5760
+ });
5761
+ }
5762
+ if (!contractsStore.indexNames.contains("state")) {
5763
+ contractsStore.createIndex("state", "state", {
5764
+ unique: false
5765
+ });
5766
+ }
5767
+ }
5768
+ if (!db.objectStoreNames.contains(LEGACY_STORE_CONTRACT_COLLECTIONS)) {
5769
+ db.createObjectStore(LEGACY_STORE_CONTRACT_COLLECTIONS, {
5770
+ keyPath: "key"
5771
+ });
5772
+ }
5773
+ if (oldVersion >= 1 && oldVersion < 3 && transaction) {
5774
+ const vtxosStore = transaction.objectStore(STORE_VTXOS);
5775
+ if (!vtxosStore.indexNames.contains("script")) {
5776
+ vtxosStore.createIndex("script", "script", { unique: false });
5777
+ }
5778
+ backfillVtxoScripts(transaction);
5779
+ }
5780
+ }
5781
+ function initDatabaseWithIntents(db, oldVersion, transaction) {
5782
+ initDatabase(db, oldVersion, transaction);
5783
+ if (!db.objectStoreNames.contains(STORE_INTENTS)) {
5784
+ const intentsStore = db.createObjectStore(STORE_INTENTS, {
5785
+ keyPath: "intentTxId"
5786
+ });
5787
+ intentsStore.createIndex("intentId", "intentId", { unique: true });
5788
+ intentsStore.createIndex("state", "state", { unique: false });
5789
+ }
5790
+ if (!db.objectStoreNames.contains(STORE_VIRTUAL_TXS)) {
5791
+ db.createObjectStore(STORE_VIRTUAL_TXS, { keyPath: "txid" });
5792
+ }
5793
+ if (!db.objectStoreNames.contains(STORE_VTXO_BRANCHES)) {
5794
+ const branchesStore = db.createObjectStore(STORE_VTXO_BRANCHES, {
5795
+ keyPath: ["vtxoTxid", "vtxoVout", "position"]
5796
+ });
5797
+ branchesStore.createIndex("vtxo", ["vtxoTxid", "vtxoVout"], { unique: false });
5798
+ branchesStore.createIndex("virtualTxid", "virtualTxid", { unique: false });
5799
+ }
5800
+ if (oldVersion === 4 && transaction) {
5801
+ const intentsStore = transaction.objectStore(STORE_INTENTS);
5802
+ if (intentsStore.indexNames.contains("intentId")) {
5803
+ intentsStore.deleteIndex("intentId");
5804
+ }
5805
+ dedupeIntentIds(intentsStore, () => {
5806
+ intentsStore.createIndex("intentId", "intentId", { unique: true });
5807
+ });
5808
+ }
5809
+ }
5810
+ function dedupeIntentIds(store, onComplete) {
5811
+ const seen = /* @__PURE__ */ new Set();
5812
+ const cursorRequest = store.openCursor();
5813
+ cursorRequest.onsuccess = () => {
5814
+ const cursor = cursorRequest.result;
5815
+ if (!cursor) {
5816
+ onComplete();
5817
+ return;
5818
+ }
5819
+ const id = cursor.value.intentId;
5820
+ if (id != null) {
5821
+ if (seen.has(id)) {
5822
+ cursor.delete();
5823
+ } else {
5824
+ seen.add(id);
5825
+ }
5826
+ }
5827
+ cursor.continue();
5828
+ };
5829
+ }
5830
+ function backfillVtxoScripts(transaction) {
5831
+ const store = transaction.objectStore(STORE_VTXOS);
5832
+ const cursorRequest = store.openCursor();
5833
+ cursorRequest.onsuccess = () => {
5834
+ const cursor = cursorRequest.result;
5835
+ if (!cursor) return;
5836
+ const value = cursor.value;
5837
+ if (!value.script) {
5838
+ value.script = scriptFromArkAddress(value.address);
5839
+ cursor.update(value);
5840
+ }
5841
+ cursor.continue();
5842
+ };
5843
+ }
5844
+
5845
+ // src/worker/browser/utils.ts
5846
+ var DEFAULT_DB_NAME = "arkade-service-worker";
5847
+ var DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS = 1e4;
5848
+ function normalizeOptions2(pathOrOptions) {
5849
+ if (typeof pathOrOptions === "string") {
5850
+ return {
5851
+ path: pathOrOptions,
5852
+ activationTimeoutMs: DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS
5853
+ };
5854
+ }
5855
+ return {
5856
+ path: pathOrOptions.path,
5857
+ activationTimeoutMs: pathOrOptions.activationTimeoutMs ?? DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS
5858
+ };
5859
+ }
5860
+ function waitForServiceWorkerReady(timeoutMs) {
5861
+ return new Promise((resolve, reject) => {
5862
+ const timeoutId = setTimeout(() => {
5863
+ reject(new Error(`Service worker activation timed out after ${timeoutMs}ms`));
5864
+ }, timeoutMs);
5865
+ navigator.serviceWorker.ready.then((registration) => {
5866
+ clearTimeout(timeoutId);
5867
+ resolve(registration);
5868
+ }).catch((error) => {
5869
+ clearTimeout(timeoutId);
5870
+ reject(error);
5871
+ });
5872
+ });
5873
+ }
5874
+ async function setupServiceWorker(pathOrOptions) {
5875
+ if (!("serviceWorker" in navigator)) {
5876
+ throw new Error("Service workers are not supported in this browser");
5877
+ }
5878
+ const { path, activationTimeoutMs } = normalizeOptions2(pathOrOptions);
5879
+ const registration = await navigator.serviceWorker.register(path);
5880
+ await registration.update();
5881
+ const serviceWorker = registration.active || registration.waiting || registration.installing;
5882
+ if (!serviceWorker) {
5883
+ throw new Error("Failed to get service worker instance");
5884
+ }
5885
+ if (serviceWorker.state === "activated") {
5886
+ return serviceWorker;
5887
+ }
5888
+ const readyRegistration = await waitForServiceWorkerReady(activationTimeoutMs);
5889
+ if (!readyRegistration.active) {
5890
+ throw new Error("Service worker registration is ready but has no active worker");
5891
+ }
5892
+ return readyRegistration.active;
5893
+ }
5894
+
5895
+ // src/repositories/indexedDB/virtualTxRepository.ts
5896
+ var IndexedDBVirtualTxRepository = class {
5897
+ constructor(dbName = DEFAULT_DB_NAME) {
5898
+ this.dbName = dbName;
5899
+ }
5900
+ version = 1;
5901
+ db = null;
5902
+ async getDB() {
5903
+ if (!this.db)
5904
+ this.db = await openDatabase(this.dbName, INTENT_DB_VERSION, initDatabaseWithIntents);
5905
+ return this.db;
5906
+ }
5907
+ async clear() {
5908
+ const db = await this.getDB();
5909
+ const transaction = db.transaction([STORE_VIRTUAL_TXS, STORE_VTXO_BRANCHES], "readwrite");
5910
+ transaction.objectStore(STORE_VIRTUAL_TXS).clear();
5911
+ transaction.objectStore(STORE_VTXO_BRANCHES).clear();
5912
+ await awaitTransaction(transaction);
5913
+ }
5914
+ async upsertVirtualTxs(txs) {
5915
+ if (txs.length === 0) return;
5916
+ const db = await this.getDB();
5917
+ const merged = /* @__PURE__ */ new Map();
5918
+ for (const tx of txs) {
5919
+ const acc = merged.get(tx.txid);
5920
+ merged.set(
5921
+ tx.txid,
5922
+ acc ? {
5923
+ txid: tx.txid,
5924
+ psbt: tx.psbt ?? acc.psbt,
5925
+ expiresAt: tx.expiresAt ?? acc.expiresAt,
5926
+ type: mergeChainedTxType(tx.type, acc.type)
5927
+ } : tx
5928
+ );
5929
+ }
5930
+ const transaction = db.transaction([STORE_VIRTUAL_TXS], "readwrite");
5931
+ const store = transaction.objectStore(STORE_VIRTUAL_TXS);
5932
+ for (const tx of merged.values()) {
5933
+ const getReq = store.get(tx.txid);
5934
+ getReq.onsuccess = () => {
5935
+ const prev = getReq.result;
5936
+ store.put({
5937
+ txid: tx.txid,
5938
+ psbt: tx.psbt ?? prev?.psbt ?? null,
5939
+ expiresAt: tx.expiresAt ?? prev?.expiresAt ?? null,
5940
+ type: mergeChainedTxType(tx.type, prev?.type)
5941
+ });
5942
+ };
5943
+ }
5944
+ await awaitTransaction(transaction);
5945
+ }
5946
+ async getVirtualTx(txid) {
5947
+ const db = await this.getDB();
5948
+ const store = db.transaction([STORE_VIRTUAL_TXS], "readonly").objectStore(STORE_VIRTUAL_TXS);
5949
+ const r = await promisifyRequest(store.get(txid));
5950
+ return r ?? null;
5951
+ }
5952
+ async setBranch(vtxo, branch) {
5953
+ const db = await this.getDB();
5954
+ const transaction = db.transaction([STORE_VTXO_BRANCHES], "readwrite");
5955
+ const store = transaction.objectStore(STORE_VTXO_BRANCHES);
5956
+ const getAllReq = store.index("vtxo").getAll(IDBKeyRange.only([vtxo.txid, vtxo.vout]));
5957
+ getAllReq.onsuccess = () => {
5958
+ const existing = getAllReq.result;
5959
+ for (const e of existing) store.delete([e.vtxoTxid, e.vtxoVout, e.position]);
5960
+ for (const b of branch) store.put(b);
5961
+ };
5962
+ await awaitTransaction(transaction);
5963
+ }
5964
+ async getBranch(vtxo) {
5965
+ const db = await this.getDB();
5966
+ const transaction = db.transaction([STORE_VTXO_BRANCHES, STORE_VIRTUAL_TXS], "readonly");
5967
+ const branchStore = transaction.objectStore(STORE_VTXO_BRANCHES);
5968
+ const txStore = transaction.objectStore(STORE_VIRTUAL_TXS);
5969
+ const getAllReq = branchStore.index("vtxo").getAll(IDBKeyRange.only([vtxo.txid, vtxo.vout]));
5970
+ const byTxid = /* @__PURE__ */ new Map();
5971
+ let ordered = [];
5972
+ getAllReq.onsuccess = () => {
5973
+ ordered = getAllReq.result.slice().sort((a, b) => a.position - b.position);
5974
+ for (const b of ordered) {
5975
+ const txReq = txStore.get(b.virtualTxid);
5976
+ txReq.onsuccess = () => {
5977
+ const r = txReq.result;
5978
+ if (r) byTxid.set(b.virtualTxid, r);
5979
+ };
5980
+ }
5981
+ };
5982
+ await awaitTransaction(transaction);
5983
+ const out = [];
5984
+ for (const b of ordered) {
5985
+ const tx = byTxid.get(b.virtualTxid);
5986
+ if (tx) out.push(tx);
5987
+ }
5988
+ return out;
5989
+ }
5990
+ async hasBranch(vtxo) {
5991
+ const db = await this.getDB();
5992
+ const vtxoIndex = db.transaction([STORE_VTXO_BRANCHES], "readonly").objectStore(STORE_VTXO_BRANCHES).index("vtxo");
5993
+ const count = await promisifyRequest(
5994
+ vtxoIndex.count(IDBKeyRange.only([vtxo.txid, vtxo.vout]))
5995
+ );
5996
+ return count > 0;
5997
+ }
5998
+ async pruneForSpentVtxo(vtxo) {
5999
+ const db = await this.getDB();
6000
+ const transaction = db.transaction([STORE_VTXO_BRANCHES, STORE_VIRTUAL_TXS], "readwrite");
6001
+ const branches = transaction.objectStore(STORE_VTXO_BRANCHES);
6002
+ const txStore = transaction.objectStore(STORE_VIRTUAL_TXS);
6003
+ const getAllReq = branches.index("vtxo").getAll(IDBKeyRange.only([vtxo.txid, vtxo.vout]));
6004
+ getAllReq.onsuccess = () => {
6005
+ const removed = getAllReq.result;
6006
+ if (removed.length === 0) return;
6007
+ for (const e of removed) branches.delete([e.vtxoTxid, e.vtxoVout, e.position]);
6008
+ for (const e of removed) {
6009
+ const countReq = branches.index("virtualTxid").count(IDBKeyRange.only(e.virtualTxid));
6010
+ countReq.onsuccess = () => {
6011
+ if (countReq.result === 0) txStore.delete(e.virtualTxid);
6012
+ };
6013
+ }
6014
+ };
6015
+ await awaitTransaction(transaction);
6016
+ }
6017
+ async [Symbol.asyncDispose]() {
6018
+ if (!this.db) return;
6019
+ await closeDatabase(this.dbName);
6020
+ this.db = null;
6021
+ }
6022
+ };
6023
+
6024
+ // src/repositories/indexedDB/intentRepository.ts
6025
+ var IndexedDBIntentRepository = class {
6026
+ constructor(dbName = DEFAULT_DB_NAME) {
6027
+ this.dbName = dbName;
6028
+ }
6029
+ version = 1;
6030
+ db = null;
6031
+ async getDB() {
6032
+ if (!this.db)
6033
+ this.db = await openDatabase(this.dbName, INTENT_DB_VERSION, initDatabaseWithIntents);
6034
+ return this.db;
6035
+ }
6036
+ async clear() {
6037
+ const db = await this.getDB();
6038
+ const transaction = db.transaction([STORE_INTENTS], "readwrite");
6039
+ transaction.objectStore(STORE_INTENTS).clear();
6040
+ await awaitTransaction(transaction);
6041
+ }
6042
+ async saveIntent(intent) {
6043
+ const db = await this.getDB();
6044
+ const transaction = db.transaction([STORE_INTENTS], "readwrite");
6045
+ transaction.objectStore(STORE_INTENTS).put({ ...intent, updatedAt: Date.now() });
6046
+ await awaitTransaction(transaction);
6047
+ }
6048
+ async getIntents(filter) {
6049
+ const db = await this.getDB();
6050
+ const store = db.transaction([STORE_INTENTS], "readonly").objectStore(STORE_INTENTS);
6051
+ const all = await promisifyRequest(store.getAll());
6052
+ all.sort((a, b) => a.createdAt - b.createdAt || a.intentTxId.localeCompare(b.intentTxId));
6053
+ const out = filter ? all.filter((i) => intentMatchesFilter(i, filter)) : all;
6054
+ const { skip, end } = intentPageBounds(filter, out.length);
6055
+ return out.slice(skip, end);
6056
+ }
6057
+ async getLockedVtxoOutpoints() {
6058
+ const db = await this.getDB();
6059
+ const store = db.transaction([STORE_INTENTS], "readonly").objectStore(STORE_INTENTS);
6060
+ const all = await promisifyRequest(store.getAll());
6061
+ const out = [];
6062
+ for (const i of all)
6063
+ if (!isTerminalIntentState(i.state)) for (const o of i.intentVtxos) out.push(o);
6064
+ return out;
6065
+ }
6066
+ async [Symbol.asyncDispose]() {
6067
+ if (!this.db) return;
6068
+ await closeDatabase(this.dbName);
6069
+ this.db = null;
6070
+ }
6071
+ };
6072
+
6073
+ // src/contracts/vtxoOwnership.ts
6074
+ function vtxoOutpoint(vtxo) {
6075
+ return `${vtxo.txid}:${vtxo.vout}`;
5273
6076
  }
5274
6077
  function isVtxoForScript(vtxo, script) {
5275
6078
  return !!vtxo.script && vtxo.script === script;
@@ -5455,190 +6258,13 @@ var InMemoryContractRepository = class {
5455
6258
  async saveContract(contract) {
5456
6259
  this.contractsByScript.set(contract.script, contract);
5457
6260
  }
5458
- async deleteContract(script) {
5459
- this.contractsByScript.delete(script);
5460
- }
5461
- async [Symbol.asyncDispose]() {
5462
- return;
5463
- }
5464
- };
5465
- function scriptFromArkAddress(address) {
5466
- return base.hex.encode(chunkHXDZ2QTW_cjs.ArkAddress.decode(address).pkScript);
5467
- }
5468
-
5469
- // src/repositories/indexedDB/schema.ts
5470
- var STORE_VTXOS = "vtxos";
5471
- var STORE_UTXOS = "utxos";
5472
- var STORE_TRANSACTIONS = "transactions";
5473
- var STORE_WALLET_STATE = "walletState";
5474
- var STORE_CONTRACTS = "contracts";
5475
- var LEGACY_STORE_CONTRACT_COLLECTIONS = "contractsCollections";
5476
- var DB_VERSION = 3;
5477
- function initDatabase(db, oldVersion, transaction) {
5478
- if (!db.objectStoreNames.contains(STORE_VTXOS)) {
5479
- const vtxosStore = db.createObjectStore(STORE_VTXOS, {
5480
- keyPath: ["address", "txid", "vout"]
5481
- });
5482
- if (!vtxosStore.indexNames.contains("address")) {
5483
- vtxosStore.createIndex("address", "address", {
5484
- unique: false
5485
- });
5486
- }
5487
- if (!vtxosStore.indexNames.contains("txid")) {
5488
- vtxosStore.createIndex("txid", "txid", { unique: false });
5489
- }
5490
- if (!vtxosStore.indexNames.contains("value")) {
5491
- vtxosStore.createIndex("value", "value", { unique: false });
5492
- }
5493
- if (!vtxosStore.indexNames.contains("status")) {
5494
- vtxosStore.createIndex("status", "status", {
5495
- unique: false
5496
- });
5497
- }
5498
- if (!vtxosStore.indexNames.contains("virtualStatus")) {
5499
- vtxosStore.createIndex("virtualStatus", "virtualStatus", {
5500
- unique: false
5501
- });
5502
- }
5503
- if (!vtxosStore.indexNames.contains("createdAt")) {
5504
- vtxosStore.createIndex("createdAt", "createdAt", {
5505
- unique: false
5506
- });
5507
- }
5508
- if (!vtxosStore.indexNames.contains("isSpent")) {
5509
- vtxosStore.createIndex("isSpent", "isSpent", {
5510
- unique: false
5511
- });
5512
- }
5513
- if (!vtxosStore.indexNames.contains("isUnrolled")) {
5514
- vtxosStore.createIndex("isUnrolled", "isUnrolled", {
5515
- unique: false
5516
- });
5517
- }
5518
- if (!vtxosStore.indexNames.contains("spentBy")) {
5519
- vtxosStore.createIndex("spentBy", "spentBy", {
5520
- unique: false
5521
- });
5522
- }
5523
- if (!vtxosStore.indexNames.contains("settledBy")) {
5524
- vtxosStore.createIndex("settledBy", "settledBy", {
5525
- unique: false
5526
- });
5527
- }
5528
- if (!vtxosStore.indexNames.contains("arkTxId")) {
5529
- vtxosStore.createIndex("arkTxId", "arkTxId", {
5530
- unique: false
5531
- });
5532
- }
5533
- if (!vtxosStore.indexNames.contains("script")) {
5534
- vtxosStore.createIndex("script", "script", {
5535
- unique: false
5536
- });
5537
- }
5538
- }
5539
- if (!db.objectStoreNames.contains(STORE_UTXOS)) {
5540
- const utxosStore = db.createObjectStore(STORE_UTXOS, {
5541
- keyPath: ["address", "txid", "vout"]
5542
- });
5543
- if (!utxosStore.indexNames.contains("address")) {
5544
- utxosStore.createIndex("address", "address", {
5545
- unique: false
5546
- });
5547
- }
5548
- if (!utxosStore.indexNames.contains("txid")) {
5549
- utxosStore.createIndex("txid", "txid", { unique: false });
5550
- }
5551
- if (!utxosStore.indexNames.contains("value")) {
5552
- utxosStore.createIndex("value", "value", { unique: false });
5553
- }
5554
- if (!utxosStore.indexNames.contains("status")) {
5555
- utxosStore.createIndex("status", "status", {
5556
- unique: false
5557
- });
5558
- }
5559
- }
5560
- if (!db.objectStoreNames.contains(STORE_TRANSACTIONS)) {
5561
- const transactionsStore = db.createObjectStore(STORE_TRANSACTIONS, {
5562
- keyPath: ["address", "keyBoardingTxid", "keyCommitmentTxid", "keyArkTxid"]
5563
- });
5564
- if (!transactionsStore.indexNames.contains("address")) {
5565
- transactionsStore.createIndex("address", "address", {
5566
- unique: false
5567
- });
5568
- }
5569
- if (!transactionsStore.indexNames.contains("type")) {
5570
- transactionsStore.createIndex("type", "type", {
5571
- unique: false
5572
- });
5573
- }
5574
- if (!transactionsStore.indexNames.contains("amount")) {
5575
- transactionsStore.createIndex("amount", "amount", {
5576
- unique: false
5577
- });
5578
- }
5579
- if (!transactionsStore.indexNames.contains("settled")) {
5580
- transactionsStore.createIndex("settled", "settled", {
5581
- unique: false
5582
- });
5583
- }
5584
- if (!transactionsStore.indexNames.contains("createdAt")) {
5585
- transactionsStore.createIndex("createdAt", "createdAt", {
5586
- unique: false
5587
- });
5588
- }
5589
- if (!transactionsStore.indexNames.contains("arkTxid")) {
5590
- transactionsStore.createIndex("arkTxid", "key.arkTxid", {
5591
- unique: false
5592
- });
5593
- }
5594
- }
5595
- if (!db.objectStoreNames.contains(STORE_WALLET_STATE)) {
5596
- db.createObjectStore(STORE_WALLET_STATE, {
5597
- keyPath: "key"
5598
- });
5599
- }
5600
- if (!db.objectStoreNames.contains(STORE_CONTRACTS)) {
5601
- const contractsStore = db.createObjectStore(STORE_CONTRACTS, {
5602
- keyPath: "script"
5603
- });
5604
- if (!contractsStore.indexNames.contains("type")) {
5605
- contractsStore.createIndex("type", "type", {
5606
- unique: false
5607
- });
5608
- }
5609
- if (!contractsStore.indexNames.contains("state")) {
5610
- contractsStore.createIndex("state", "state", {
5611
- unique: false
5612
- });
5613
- }
5614
- }
5615
- if (!db.objectStoreNames.contains(LEGACY_STORE_CONTRACT_COLLECTIONS)) {
5616
- db.createObjectStore(LEGACY_STORE_CONTRACT_COLLECTIONS, {
5617
- keyPath: "key"
5618
- });
6261
+ async deleteContract(script) {
6262
+ this.contractsByScript.delete(script);
5619
6263
  }
5620
- if (oldVersion >= 1 && oldVersion < 3 && transaction) {
5621
- const vtxosStore = transaction.objectStore(STORE_VTXOS);
5622
- if (!vtxosStore.indexNames.contains("script")) {
5623
- vtxosStore.createIndex("script", "script", { unique: false });
5624
- }
5625
- backfillVtxoScripts(transaction);
6264
+ async [Symbol.asyncDispose]() {
6265
+ return;
5626
6266
  }
5627
- }
5628
- function backfillVtxoScripts(transaction) {
5629
- const store = transaction.objectStore(STORE_VTXOS);
5630
- const cursorRequest = store.openCursor();
5631
- cursorRequest.onsuccess = () => {
5632
- const cursor = cursorRequest.result;
5633
- if (!cursor) return;
5634
- const value = cursor.value;
5635
- if (!value.script) {
5636
- value.script = scriptFromArkAddress(value.address);
5637
- cursor.update(value);
5638
- }
5639
- cursor.continue();
5640
- };
5641
- }
6267
+ };
5642
6268
  var serializeTapLeaf = ([cb, s]) => ({
5643
6269
  cb: base.hex.encode(btcSigner.TaprootControlBlock.encode(cb)),
5644
6270
  s: base.hex.encode(s)
@@ -5705,132 +6331,6 @@ var deserializeTransaction = (o) => ({
5705
6331
  assets: deserializeAssets(o.assets)
5706
6332
  });
5707
6333
 
5708
- // src/repositories/indexedDB/manager.ts
5709
- function getGlobalObject() {
5710
- if (typeof globalThis !== "undefined") {
5711
- if (typeof globalThis.self === "object" && globalThis.self !== null) {
5712
- return { globalObject: globalThis.self };
5713
- }
5714
- if (typeof globalThis.window === "object" && globalThis.window !== null) {
5715
- return { globalObject: globalThis.window };
5716
- }
5717
- return { globalObject: globalThis };
5718
- }
5719
- throw new Error("Global object not found");
5720
- }
5721
- var dbCache = /* @__PURE__ */ new Map();
5722
- var refCounts = /* @__PURE__ */ new Map();
5723
- async function openDatabase(dbName, dbVersion, initDatabase2) {
5724
- const { globalObject } = getGlobalObject();
5725
- if (!globalObject.indexedDB) {
5726
- throw new Error("IndexedDB is not available in this environment");
5727
- }
5728
- const cached = dbCache.get(dbName);
5729
- if (cached) {
5730
- if (cached.version !== dbVersion) {
5731
- throw new Error(
5732
- `Database "${dbName}" already opened with version ${cached.version}; requested ${dbVersion}`
5733
- );
5734
- }
5735
- refCounts.set(dbName, (refCounts.get(dbName) ?? 0) + 1);
5736
- return cached.promise;
5737
- }
5738
- const dbPromise = new Promise((resolve, reject) => {
5739
- const request = globalObject.indexedDB.open(dbName, dbVersion);
5740
- request.onerror = () => {
5741
- dbCache.delete(dbName);
5742
- refCounts.delete(dbName);
5743
- reject(request.error);
5744
- };
5745
- request.onsuccess = () => {
5746
- const db = request.result;
5747
- db.onversionchange = () => {
5748
- db.close();
5749
- dbCache.delete(dbName);
5750
- refCounts.delete(dbName);
5751
- };
5752
- resolve(db);
5753
- };
5754
- request.onupgradeneeded = (event) => {
5755
- const db = request.result;
5756
- initDatabase2(db, event.oldVersion, request.transaction);
5757
- };
5758
- request.onblocked = () => {
5759
- console.warn("Database upgrade blocked - close other tabs/connections");
5760
- };
5761
- });
5762
- dbCache.set(dbName, { version: dbVersion, promise: dbPromise });
5763
- refCounts.set(dbName, 1);
5764
- return dbPromise;
5765
- }
5766
- async function closeDatabase(dbName) {
5767
- const cachedEntry = dbCache.get(dbName);
5768
- if (!cachedEntry) return false;
5769
- const count = (refCounts.get(dbName) ?? 1) - 1;
5770
- if (count > 0) {
5771
- refCounts.set(dbName, count);
5772
- return false;
5773
- }
5774
- refCounts.delete(dbName);
5775
- dbCache.delete(dbName);
5776
- try {
5777
- const db = await cachedEntry.promise;
5778
- db.close();
5779
- } catch {
5780
- }
5781
- return true;
5782
- }
5783
-
5784
- // src/worker/browser/utils.ts
5785
- var DEFAULT_DB_NAME = "arkade-service-worker";
5786
- var DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS = 1e4;
5787
- function normalizeOptions2(pathOrOptions) {
5788
- if (typeof pathOrOptions === "string") {
5789
- return {
5790
- path: pathOrOptions,
5791
- activationTimeoutMs: DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS
5792
- };
5793
- }
5794
- return {
5795
- path: pathOrOptions.path,
5796
- activationTimeoutMs: pathOrOptions.activationTimeoutMs ?? DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS
5797
- };
5798
- }
5799
- function waitForServiceWorkerReady(timeoutMs) {
5800
- return new Promise((resolve, reject) => {
5801
- const timeoutId = setTimeout(() => {
5802
- reject(new Error(`Service worker activation timed out after ${timeoutMs}ms`));
5803
- }, timeoutMs);
5804
- navigator.serviceWorker.ready.then((registration) => {
5805
- clearTimeout(timeoutId);
5806
- resolve(registration);
5807
- }).catch((error) => {
5808
- clearTimeout(timeoutId);
5809
- reject(error);
5810
- });
5811
- });
5812
- }
5813
- async function setupServiceWorker(pathOrOptions) {
5814
- if (!("serviceWorker" in navigator)) {
5815
- throw new Error("Service workers are not supported in this browser");
5816
- }
5817
- const { path, activationTimeoutMs } = normalizeOptions2(pathOrOptions);
5818
- const registration = await navigator.serviceWorker.register(path);
5819
- await registration.update();
5820
- const serviceWorker = registration.active || registration.waiting || registration.installing;
5821
- if (!serviceWorker) {
5822
- throw new Error("Failed to get service worker instance");
5823
- }
5824
- if (serviceWorker.state === "activated") {
5825
- return serviceWorker;
5826
- }
5827
- const readyRegistration = await waitForServiceWorkerReady(activationTimeoutMs);
5828
- if (!readyRegistration.active) {
5829
- throw new Error("Service worker registration is ready but has no active worker");
5830
- }
5831
- return readyRegistration.active;
5832
- }
5833
-
5834
6334
  // src/repositories/indexedDB/contractRepository.ts
5835
6335
  var IndexedDBContractRepository = class {
5836
6336
  constructor(dbName = DEFAULT_DB_NAME) {
@@ -6987,7 +7487,7 @@ var ContractWatcher = class {
6987
7487
  this.connectionState = "connected";
6988
7488
  this.reconnectAttempts = 0;
6989
7489
  this.listenLoop().catch((e) => {
6990
- if (chunkYEF5FL6E_cjs.isEventSourceError(e)) {
7490
+ if (chunkUBYY2TPK_cjs.isEventSourceError(e)) {
6991
7491
  console.debug("ContractWatcher subscription disconnected; reconnecting");
6992
7492
  } else {
6993
7493
  console.error(e);
@@ -7268,6 +7768,89 @@ var ContractWatcher = class {
7268
7768
  }
7269
7769
  };
7270
7770
 
7771
+ // src/wallet/intentReconciliation.ts
7772
+ var NON_TERMINAL_INTENT_STATES = ALL_INTENT_STATES.filter(
7773
+ (s) => !isTerminalIntentState(s)
7774
+ );
7775
+ function isConsumed(vtxo) {
7776
+ return vtxo !== void 0 && (vtxo.isSpent === true || vtxo.settledBy !== void 0);
7777
+ }
7778
+ async function classifyIntent(intent, vtxosByOutpoint, now) {
7779
+ if (isTerminalIntentState(intent.state)) return void 0;
7780
+ const inputs = intent.intentVtxos;
7781
+ const consumed = inputs.map((o) => vtxosByOutpoint.get(outpointKey(o)));
7782
+ if (inputs.length > 0 && consumed.every(isConsumed)) {
7783
+ const commitmentTransactionId = intent.commitmentTransactionId ?? consumed.map((v) => v?.settledBy).find((s) => !!s);
7784
+ return terminal(intent, "batch_succeeded", now, { commitmentTransactionId });
7785
+ }
7786
+ if (intent.state === "waiting_to_submit") {
7787
+ return terminal(intent, "cancelled", now, {
7788
+ cancellationReason: "reconciliation: intent was never submitted before restart"
7789
+ });
7790
+ }
7791
+ if (intent.validUntil !== void 0 && intent.validUntil < now) {
7792
+ return terminal(intent, "cancelled", now, {
7793
+ cancellationReason: "reconciliation: intent expired"
7794
+ });
7795
+ }
7796
+ return void 0;
7797
+ }
7798
+ async function reconcileIntents(deps) {
7799
+ const now = deps.now ?? Date.now;
7800
+ let intents;
7801
+ try {
7802
+ intents = await deps.intentRepository.getIntents({
7803
+ states: [...NON_TERMINAL_INTENT_STATES]
7804
+ });
7805
+ } catch (e) {
7806
+ console.error("reconcileIntents: failed to read non-terminal intents", e);
7807
+ return;
7808
+ }
7809
+ if (intents.length === 0) return;
7810
+ const outpoints = dedupeOutpoints(intents.flatMap((i) => i.intentVtxos));
7811
+ let vtxosByOutpoint = /* @__PURE__ */ new Map();
7812
+ if (outpoints.length > 0) {
7813
+ try {
7814
+ const { vtxos } = await deps.indexerProvider.getVtxos({ outpoints });
7815
+ vtxosByOutpoint = new Map(vtxos.map((v) => [outpointKey(v), v]));
7816
+ } catch (e) {
7817
+ console.error("reconcileIntents: failed to fetch input VTXO state", e);
7818
+ return;
7819
+ }
7820
+ }
7821
+ const nowMs = now();
7822
+ for (const intent of intents) {
7823
+ try {
7824
+ const next = await classifyIntent(intent, vtxosByOutpoint, nowMs);
7825
+ if (!next) continue;
7826
+ const [fresh] = await deps.intentRepository.getIntents({
7827
+ intentTxIds: [intent.intentTxId]
7828
+ });
7829
+ if (!fresh || fresh.state !== intent.state) continue;
7830
+ await deps.intentRepository.saveIntent(next);
7831
+ } catch (e) {
7832
+ console.error(`reconcileIntents: failed to reconcile intent ${intent.intentTxId}`, e);
7833
+ }
7834
+ }
7835
+ }
7836
+ function terminal(intent, state, now, patch) {
7837
+ return { ...intent, ...patch, state, updatedAt: now };
7838
+ }
7839
+ function outpointKey(o) {
7840
+ return `${o.txid}:${o.vout}`;
7841
+ }
7842
+ function dedupeOutpoints(outpoints) {
7843
+ const seen = /* @__PURE__ */ new Set();
7844
+ const out = [];
7845
+ for (const o of outpoints) {
7846
+ const k = outpointKey(o);
7847
+ if (seen.has(k)) continue;
7848
+ seen.add(k);
7849
+ out.push(o);
7850
+ }
7851
+ return out;
7852
+ }
7853
+
7271
7854
  // src/utils/syncCursors.ts
7272
7855
  var SAFETY_LAG_MS = 3e4;
7273
7856
  var OVERLAP_MS = 24 * 60 * 60 * 1e3;
@@ -7391,6 +7974,26 @@ var ContractManager = class _ContractManager {
7391
7974
  if (watched.length > 0) {
7392
7975
  await this.reconcilePendingFrontier(watched);
7393
7976
  }
7977
+ await this.reconcileStaleIntents();
7978
+ }
7979
+ /**
7980
+ * Crash-recovery for persisted settlement intents: reconcile any
7981
+ * non-terminal intent left behind by a mid-settle crash against
7982
+ * authoritative indexer state (see {@link reconcileIntents}). Runs on the
7983
+ * online sync path only — boot and subscription reconnect — never from a
7984
+ * wallet read API. Best-effort: intent recovery is not a sync invariant, so
7985
+ * a failure is logged and sync continues. No-op without an intent store.
7986
+ */
7987
+ async reconcileStaleIntents() {
7988
+ if (!this.config.intentRepository) return;
7989
+ try {
7990
+ await reconcileIntents({
7991
+ intentRepository: this.config.intentRepository,
7992
+ indexerProvider: this.config.indexerProvider
7993
+ });
7994
+ } catch (e) {
7995
+ console.error("ContractManager: intent reconciliation failed", e);
7996
+ }
7394
7997
  }
7395
7998
  /**
7396
7999
  * Create and register a new contract.
@@ -8761,6 +9364,22 @@ async function resolveBoardingBootTapscript(contractRepository, serverPubKey, ba
8761
9364
  function hasToReadonly(identity) {
8762
9365
  return typeof identity === "object" && identity !== null && "toReadonly" in identity && typeof identity.toReadonly === "function";
8763
9366
  }
9367
+ function excludeLockedOutpoints(vtxos, locked) {
9368
+ if (locked.length === 0) return vtxos;
9369
+ const lockedKeys = new Set(locked.map((o) => `${o.txid}:${o.vout}`));
9370
+ return vtxos.filter((v) => !lockedKeys.has(`${v.txid}:${v.vout}`));
9371
+ }
9372
+ async function spendableVtxosExcludingLocked(vtxos, intentRepository) {
9373
+ if (!intentRepository) return vtxos;
9374
+ let locked;
9375
+ try {
9376
+ locked = await intentRepository.getLockedVtxoOutpoints();
9377
+ } catch (e) {
9378
+ console.error("getLockedVtxoOutpoints failed; reporting unfiltered balance", e);
9379
+ return vtxos;
9380
+ }
9381
+ return excludeLockedOutpoints(vtxos, locked);
9382
+ }
8764
9383
  var ReadonlyWallet = class _ReadonlyWallet {
8765
9384
  constructor(identity, network, onchainProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, dustAmount, walletRepository, contractRepository, delegateProvider, watcherConfig, walletContractTimelocks) {
8766
9385
  this.identity = identity;
@@ -8791,6 +9410,20 @@ var ReadonlyWallet = class _ReadonlyWallet {
8791
9410
  _contractManager;
8792
9411
  _contractManagerInitializing;
8793
9412
  watcherConfig;
9413
+ /**
9414
+ * Opt-in intent-lifecycle repository. Assigned by the `create()`
9415
+ * factories from `config.storage.intentRepository`; `undefined` ⇒ all
9416
+ * intent-persistence code paths are no-ops (default behaviour unchanged).
9417
+ */
9418
+ intentRepository;
9419
+ /**
9420
+ * **Experimental / inert.** Opt-in virtual-tx / exit-branch repository,
9421
+ * exposed so callers can pass it to {@link Unroll.Session.create} as a
9422
+ * best-effort raw-tx cache. Assigned by `create()` from
9423
+ * `config.storage.virtualTxRepository`. Normal sync never writes it
9424
+ * (ContractManager isn't given it); `undefined` ⇒ no-op.
9425
+ */
9426
+ virtualTxRepository;
8794
9427
  _assetManager;
8795
9428
  walletContractTimelocks;
8796
9429
  // Outpoints ("txid:vout") committed to an in-flight settle/send. Filtered
@@ -8848,9 +9481,12 @@ var ReadonlyWallet = class _ReadonlyWallet {
8848
9481
  _deprecatedSigners = /* @__PURE__ */ new Map();
8849
9482
  /**
8850
9483
  * Refresh the cached deprecated-signer set from a fresh server-info
8851
- * snapshot. Called by the create() factories at construction and by the
8852
- * server-info-change handler mid-session. Lenient: a malformed deprecated
8853
- * entry is skipped, never fatal to wallet creation.
9484
+ * snapshot. Called by the create() factories at construction, by the
9485
+ * server-info-change handler mid-session, and by the deprecated-signer
9486
+ * migration pass (`VtxoManager.migrateDeprecatedSignerVtxos`). This set feeds
9487
+ * {@link pendingRecoveryOutpoints}, which drops EXPIRED (past-cutoff)
9488
+ * deprecated-signer VTXOs from the wallet's own coin selection. Lenient: a
9489
+ * malformed deprecated entry is skipped, never fatal to wallet creation.
8854
9490
  */
8855
9491
  refreshDeprecatedSigners(info) {
8856
9492
  const next = /* @__PURE__ */ new Map();
@@ -8945,7 +9581,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
8945
9581
  */
8946
9582
  static async setupWalletConfig(config, pubKey) {
8947
9583
  const arkadeServerUrl = getArkadeServerUrl(config);
8948
- const arkProvider = config.arkProvider || new chunkYEF5FL6E_cjs.RestArkProvider(arkadeServerUrl);
9584
+ const arkProvider = config.arkProvider || new chunkUBYY2TPK_cjs.RestArkProvider(arkadeServerUrl);
8949
9585
  let indexerProvider = config.indexerProvider;
8950
9586
  if (!indexerProvider) {
8951
9587
  let indexerUrl = config.indexerUrl;
@@ -8962,7 +9598,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
8962
9598
  indexerUrl = arkadeServerUrl;
8963
9599
  }
8964
9600
  }
8965
- indexerProvider = new chunkYEF5FL6E_cjs.RestIndexerProvider(indexerUrl);
9601
+ indexerProvider = new chunkUBYY2TPK_cjs.RestIndexerProvider(indexerUrl);
8966
9602
  }
8967
9603
  const info = await arkProvider.getInfo();
8968
9604
  const network = chunkHXDZ2QTW_cjs.getNetwork(info.network);
@@ -9066,6 +9702,8 @@ var ReadonlyWallet = class _ReadonlyWallet {
9066
9702
  config.watcherConfig,
9067
9703
  setup.walletContractTimelocks
9068
9704
  );
9705
+ wallet.intentRepository = config.storage?.intentRepository;
9706
+ wallet.virtualTxRepository = config.storage?.virtualTxRepository;
9069
9707
  wallet.refreshDeprecatedSigners(setup.info);
9070
9708
  return wallet;
9071
9709
  }
@@ -9097,6 +9735,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
9097
9735
  this.pendingRecoveryOutpoints()
9098
9736
  ]);
9099
9737
  const isPendingRecovery = (coin) => pendingOutpoints.has(`${coin.txid}:${coin.vout}`);
9738
+ const unlockedVtxos = await spendableVtxosExcludingLocked(vtxos, this.intentRepository);
9100
9739
  let confirmed = 0;
9101
9740
  let unconfirmed = 0;
9102
9741
  for (const utxo of boardingUtxos) {
@@ -9106,14 +9745,15 @@ var ReadonlyWallet = class _ReadonlyWallet {
9106
9745
  unconfirmed += utxo.value;
9107
9746
  }
9108
9747
  }
9109
- let settled = 0;
9110
- let preconfirmed = 0;
9111
9748
  let recoverable = 0;
9112
9749
  let pendingRecovery = 0;
9113
- settled = vtxos.filter((coin) => coin.virtualStatus.state === "settled" && !isPendingRecovery(coin)).reduce((sum, coin) => sum + coin.value, 0);
9114
- preconfirmed = vtxos.filter(
9750
+ const settled = vtxos.filter((coin) => coin.virtualStatus.state === "settled" && !isPendingRecovery(coin)).reduce((sum, coin) => sum + coin.value, 0);
9751
+ const preconfirmed = vtxos.filter(
9115
9752
  (coin) => coin.virtualStatus.state === "preconfirmed" && !isPendingRecovery(coin)
9116
9753
  ).reduce((sum, coin) => sum + coin.value, 0);
9754
+ const available = unlockedVtxos.filter(
9755
+ (coin) => (coin.virtualStatus.state === "settled" || coin.virtualStatus.state === "preconfirmed") && !isPendingRecovery(coin)
9756
+ ).reduce((sum, coin) => sum + coin.value, 0);
9117
9757
  recoverable = vtxos.filter((coin) => isSpendable(coin) && coin.virtualStatus.state === "swept").reduce((sum, coin) => sum + coin.value, 0);
9118
9758
  pendingRecovery = vtxos.filter(isPendingRecovery).reduce((sum, coin) => sum + coin.value, 0);
9119
9759
  const totalBoarding = confirmed + unconfirmed;
@@ -9140,7 +9780,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
9140
9780
  },
9141
9781
  settled,
9142
9782
  preconfirmed,
9143
- available: settled + preconfirmed,
9783
+ available,
9144
9784
  recoverable,
9145
9785
  pendingRecovery,
9146
9786
  total: totalBoarding + totalOffchain,
@@ -9617,6 +10257,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
9617
10257
  indexerProvider: this.indexerProvider,
9618
10258
  contractRepository: this.contractRepository,
9619
10259
  walletRepository: this.walletRepository,
10260
+ intentRepository: this.intentRepository,
9620
10261
  watcherConfig: this.watcherConfig
9621
10262
  });
9622
10263
  const baselinePubkey = await this.identity.xOnlyPublicKey();
@@ -10310,6 +10951,8 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
10310
10951
  wallet.setBoardingTapscriptForRotation(resolvedBoarding);
10311
10952
  }
10312
10953
  }
10954
+ wallet.intentRepository = config.storage?.intentRepository;
10955
+ wallet.virtualTxRepository = config.storage?.virtualTxRepository;
10313
10956
  await wallet.getVtxoManager();
10314
10957
  return wallet;
10315
10958
  }
@@ -10589,12 +11232,26 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
10589
11232
  ),
10590
11233
  this.makeDeleteIntentSignature(params.inputs)
10591
11234
  ]);
11235
+ let intentTxId;
11236
+ try {
11237
+ intentTxId = btcSigner.Transaction.fromPSBT(base.base64.decode(intent.proof)).id;
11238
+ } catch {
11239
+ intentTxId = params.inputs.map((i) => `${i.txid}:${i.vout}`).sort().join("|");
11240
+ }
11241
+ await this.persistIntentSnapshot(
11242
+ intentTxId,
11243
+ "waiting_to_submit",
11244
+ intent,
11245
+ deleteIntent,
11246
+ params.inputs
11247
+ );
10592
11248
  const topics = [
10593
11249
  ...signingPublicKeys,
10594
11250
  ...params.inputs.map((input) => `${input.txid}:${input.vout}`)
10595
11251
  ];
10596
11252
  const abortController = new AbortController();
10597
11253
  let stream;
11254
+ let committedTxid;
10598
11255
  this._addPendingSpends(params.inputs);
10599
11256
  try {
10600
11257
  stream = this.arkProvider.getEventStream(abortController.signal, topics);
@@ -10609,16 +11266,43 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
10609
11266
  yield* stream;
10610
11267
  })();
10611
11268
  const intentId = await this.safeRegisterIntent(intent, params.inputs);
10612
- const handler = this.createBatchHandler(intentId, params.inputs, recipients, session);
11269
+ await this.persistIntentSnapshot(
11270
+ intentTxId,
11271
+ "waiting_for_batch",
11272
+ intent,
11273
+ deleteIntent,
11274
+ params.inputs,
11275
+ { intentId }
11276
+ );
11277
+ const handler = wrapHandlerWithIntentPersistence(
11278
+ this.createBatchHandler(intentId, params.inputs, recipients, session),
11279
+ { intentRepository: this.intentRepository, intentTxId }
11280
+ );
10613
11281
  const commitmentTxid = await exports.Batch.join(primedStream, handler, {
10614
11282
  abortController,
10615
11283
  skipVtxoTreeSigning: !hasOffchainOutputs,
10616
- eventCallback: eventCallback ? (event) => Promise.resolve(eventCallback(event)) : void 0
11284
+ // async so a synchronous throw becomes a rejection Batch.join
11285
+ // swallows, keeping the observational callback off the stream path.
11286
+ eventCallback: eventCallback && (async (event) => {
11287
+ await eventCallback(event);
11288
+ })
10617
11289
  });
11290
+ committedTxid = commitmentTxid;
10618
11291
  await this.updateDbAfterSettle(params.inputs, commitmentTxid);
10619
11292
  await this.maybeRotateBoardingAfterBoard(params.inputs);
10620
11293
  return commitmentTxid;
10621
11294
  } catch (error) {
11295
+ if (committedTxid !== void 0) {
11296
+ await this.persistIntentSnapshot(
11297
+ intentTxId,
11298
+ "batch_succeeded",
11299
+ intent,
11300
+ deleteIntent,
11301
+ params.inputs,
11302
+ { commitmentTransactionId: committedTxid }
11303
+ );
11304
+ throw error;
11305
+ }
10622
11306
  const inputIds = params.inputs.map((i) => `${i.txid}:${i.vout}`).join(",");
10623
11307
  await this.arkProvider.deleteIntent(deleteIntent).catch((e) => {
10624
11308
  console.warn(
@@ -10626,6 +11310,16 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
10626
11310
  e
10627
11311
  );
10628
11312
  });
11313
+ await this.persistIntentSnapshot(
11314
+ intentTxId,
11315
+ "cancelled",
11316
+ intent,
11317
+ deleteIntent,
11318
+ params.inputs,
11319
+ {
11320
+ cancellationReason: error instanceof Error ? error.message : String(error)
11321
+ }
11322
+ );
10629
11323
  throw error;
10630
11324
  } finally {
10631
11325
  this._removePendingSpends(params.inputs);
@@ -10900,6 +11594,42 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
10900
11594
  }
10901
11595
  return jobs;
10902
11596
  }
11597
+ /**
11598
+ * Best-effort upsert of the current settlement intent into the optional
11599
+ * {@link intentRepository}. Never throws into the settle path — intent
11600
+ * persistence is observational and must not break money flow. Preserves
11601
+ * fields already written by the event reducer (state-event ordering).
11602
+ */
11603
+ async persistIntentSnapshot(intentTxId, state, intent, deleteIntent, inputs, patch) {
11604
+ const repo = this.intentRepository;
11605
+ if (!repo) return;
11606
+ try {
11607
+ const now = Date.now();
11608
+ const existing = (await repo.getIntents({ intentTxIds: [intentTxId] }))[0];
11609
+ if (existing && isTerminalIntentState(existing.state)) {
11610
+ return;
11611
+ }
11612
+ await repo.saveIntent({
11613
+ ...existing ?? {},
11614
+ intentTxId,
11615
+ registerProof: intent.proof,
11616
+ registerProofMessage: chunkUBYY2TPK_cjs.Intent.encodeMessage(intent.message),
11617
+ deleteProof: deleteIntent.proof,
11618
+ deleteProofMessage: chunkUBYY2TPK_cjs.Intent.encodeMessage(deleteIntent.message),
11619
+ intentVtxos: inputs.map((i) => ({
11620
+ txid: i.txid,
11621
+ vout: i.vout
11622
+ })),
11623
+ partialForfeits: existing?.partialForfeits ?? [],
11624
+ createdAt: existing?.createdAt ?? now,
11625
+ updatedAt: now,
11626
+ state,
11627
+ ...patch
11628
+ });
11629
+ } catch (e) {
11630
+ console.error(`Failed to persist intent ${intentTxId} (state=${state})`, e);
11631
+ }
11632
+ }
10903
11633
  /**
10904
11634
  * @internal Sign an on-chain boarding exit / sweep transaction, routing
10905
11635
  * each input to the correct key by its `witnessUtxo.script`: the identity
@@ -10917,7 +11647,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
10917
11647
  try {
10918
11648
  return await this.arkProvider.registerIntent(intent);
10919
11649
  } catch (error) {
10920
- if (error instanceof chunkYEF5FL6E_cjs.ArkError && error.code === 0 && error.message.includes("duplicated input")) {
11650
+ if (error instanceof chunkUBYY2TPK_cjs.ArkError && error.code === 0 && error.message.includes("duplicated input")) {
10921
11651
  const deleteIntent = await this.makeDeleteIntentSignature(inputs);
10922
11652
  await this.arkProvider.deleteIntent(deleteIntent);
10923
11653
  return this.arkProvider.registerIntent(intent);
@@ -10933,7 +11663,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
10933
11663
  expire_at: 0,
10934
11664
  cosigners_public_keys: cosignerPubKeys
10935
11665
  };
10936
- const proof = chunkYEF5FL6E_cjs.Intent.create(message, coins, outputs);
11666
+ const proof = chunkUBYY2TPK_cjs.Intent.create(message, coins, outputs);
10937
11667
  const signedProof = await this._signerRouter.sign(proof, intentProofJobs(coins));
10938
11668
  return {
10939
11669
  proof: base.base64.encode(signedProof.toPSBT()),
@@ -10945,7 +11675,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
10945
11675
  type: "delete",
10946
11676
  expire_at: 0
10947
11677
  };
10948
- const proof = chunkYEF5FL6E_cjs.Intent.create(message, coins, []);
11678
+ const proof = chunkUBYY2TPK_cjs.Intent.create(message, coins, []);
10949
11679
  const signedProof = await this._signerRouter.sign(proof, intentProofJobs(coins));
10950
11680
  return {
10951
11681
  proof: base.base64.encode(signedProof.toPSBT()),
@@ -10957,7 +11687,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
10957
11687
  type: "get-pending-tx",
10958
11688
  expire_at: 0
10959
11689
  };
10960
- const proof = chunkYEF5FL6E_cjs.Intent.create(message, coins, []);
11690
+ const proof = chunkUBYY2TPK_cjs.Intent.create(message, coins, []);
10961
11691
  const signedProof = await this._signerRouter.sign(proof, intentProofJobs(coins));
10962
11692
  return {
10963
11693
  proof: base.base64.encode(signedProof.toPSBT()),
@@ -11915,7 +12645,7 @@ var MessageBus = class {
11915
12645
  this.initialized = true;
11916
12646
  }
11917
12647
  async buildServices(config) {
11918
- const arkProvider = new chunkYEF5FL6E_cjs.RestArkProvider(config.arkServer.url);
12648
+ const arkProvider = new chunkUBYY2TPK_cjs.RestArkProvider(config.arkServer.url);
11919
12649
  const storage = {
11920
12650
  walletRepository: this.walletRepository,
11921
12651
  contractRepository: this.contractRepository
@@ -13072,7 +13802,7 @@ var WalletMessageHandler = class {
13072
13802
  // Wallet methods
13073
13803
  async handleInitWallet({ payload }) {
13074
13804
  const { arkServerUrl } = payload;
13075
- this.indexerProvider = new chunkYEF5FL6E_cjs.RestIndexerProvider(arkServerUrl);
13805
+ this.indexerProvider = new chunkUBYY2TPK_cjs.RestIndexerProvider(arkServerUrl);
13076
13806
  await this.onWalletInitialized();
13077
13807
  }
13078
13808
  async handleGetBalance() {
@@ -14879,7 +15609,7 @@ var OnchainWallet = class _OnchainWallet {
14879
15609
  if (!inputs) {
14880
15610
  throw new Error("Fee estimation failed");
14881
15611
  }
14882
- let tx = new chunkYEF5FL6E_cjs.Transaction();
15612
+ let tx = new chunkUBYY2TPK_cjs.Transaction();
14883
15613
  for (const input of inputs) {
14884
15614
  tx.addInput({
14885
15615
  txid: input.txid,
@@ -14909,42 +15639,11 @@ var OnchainWallet = class _OnchainWallet {
14909
15639
  * @see send
14910
15640
  */
14911
15641
  async bumpP2A(parent) {
14912
- const parentVsize = parent.vsize;
14913
- let child = new chunkYEF5FL6E_cjs.Transaction({
14914
- version: 3,
14915
- allowLegacyWitnessUtxo: true
14916
- });
14917
- child.addInput(findP2AOutput(parent));
14918
- const childVsize = TxWeightEstimator.create().addKeySpendInput(true).addP2AInput().addOutputAddress(this.address, this.network).vsize().value;
14919
- const packageVSize = parentVsize + Number(childVsize);
14920
15642
  let feeRate = await this.provider.getFeeRate();
14921
15643
  if (!feeRate || feeRate < _OnchainWallet.MIN_FEE_RATE) {
14922
15644
  feeRate = _OnchainWallet.MIN_FEE_RATE;
14923
15645
  }
14924
- const fee = Math.ceil(feeRate * packageVSize);
14925
- if (!fee) {
14926
- throw new Error(
14927
- `invalid fee, got ${fee} with vsize ${packageVSize}, feeRate ${feeRate}`
14928
- );
14929
- }
14930
- const coins = await this.getCoins();
14931
- const selected = selectCoins(coins, fee, true);
14932
- for (const input of selected.inputs) {
14933
- child.addInput({
14934
- txid: input.txid,
14935
- index: input.vout,
14936
- witnessUtxo: {
14937
- script: this.onchainP2TR.script,
14938
- amount: BigInt(input.value)
14939
- },
14940
- tapInternalKey: this.onchainP2TR.tapInternalKey
14941
- });
14942
- }
14943
- child.addOutputAddress(this.address, P2A.amount + selected.changeAmount, this.network);
14944
- child = await this.identity.sign(child);
14945
- for (let i = 1; i < child.inputsLength; i++) {
14946
- child.finalizeIdx(i);
14947
- }
15646
+ const child = await this.buildBumpPackage(parent, feeRate, await this.getCoins());
14948
15647
  try {
14949
15648
  await this.provider.broadcastTransaction(parent.hex, child.hex);
14950
15649
  } catch (error) {
@@ -14953,6 +15652,59 @@ var OnchainWallet = class _OnchainWallet {
14953
15652
  return [parent.hex, child.hex];
14954
15653
  }
14955
15654
  }
15655
+ /**
15656
+ * Build and sign a CPFP fee child for a parent tx (given as raw hex)
15657
+ * carrying a P2A anchor, funding it from this wallet's **confirmed**
15658
+ * coins, and return the 1P1C package hexes WITHOUT broadcasting.
15659
+ *
15660
+ * This is the graph-mode fee source ({@link ExitFeeWallet}): the exit
15661
+ * executor calls it to bump each transported virtual tx at execution
15662
+ * time, so funding can be deferred rather than pre-signed.
15663
+ *
15664
+ * @param parentHex - Finalized parent transaction, raw network hex
15665
+ * @param feeRate - sat/vB floor for the package (raised to MIN_FEE_RATE)
15666
+ * @returns Tuple of parent hex (unchanged) and signed child hex
15667
+ * @throws If the parent has no anchor, or funding cannot be selected/signed
15668
+ */
15669
+ async bumpAnchor(parentHex, feeRate) {
15670
+ const parent = chunkUBYY2TPK_cjs.Transaction.fromRaw(base.hex.decode(parentHex));
15671
+ const coins = (await this.getCoins()).filter((c) => c.status.confirmed);
15672
+ const child = await this.buildBumpPackage(parent, feeRate, coins);
15673
+ return [parent.hex, child.hex];
15674
+ }
15675
+ /**
15676
+ * Shared core of {@link bumpP2A} and {@link bumpAnchor}: probe the package
15677
+ * fee with a single-input child, select coins for it, then build and sign
15678
+ * with the actual selection (the fee grows per extra input).
15679
+ */
15680
+ async buildBumpPackage(parent, feeRate, coins) {
15681
+ let rate = feeRate;
15682
+ if (!rate || rate < _OnchainWallet.MIN_FEE_RATE) {
15683
+ rate = _OnchainWallet.MIN_FEE_RATE;
15684
+ }
15685
+ const probeVsize = TxWeightEstimator.create().addKeySpendInput(true).addP2AInput().addOutputAddress(this.address, this.network).vsize().value;
15686
+ const probeFee = Math.ceil(rate * (parent.vsize + Number(probeVsize)));
15687
+ if (!probeFee) {
15688
+ throw new Error(
15689
+ `invalid fee, got ${probeFee} with vsize ${parent.vsize + Number(probeVsize)}, feeRate ${rate}`
15690
+ );
15691
+ }
15692
+ const selected = selectCoins(coins, probeFee, true);
15693
+ const { child } = buildAnchorChild({
15694
+ parent,
15695
+ feeRate: rate,
15696
+ fundingCoins: selected.inputs,
15697
+ changeAddress: this.address,
15698
+ changeScript: this.onchainP2TR.script,
15699
+ tapInternalKey: this.onchainP2TR.tapInternalKey,
15700
+ network: this.network
15701
+ });
15702
+ const signed = await this.identity.sign(child);
15703
+ for (let i = 1; i < signed.inputsLength; i++) {
15704
+ signed.finalizeIdx(i);
15705
+ }
15706
+ return signed;
15707
+ }
14956
15708
  };
14957
15709
  function selectCoins(coins, targetAmount, forceChange = false) {
14958
15710
  if (isNaN(targetAmount)) {
@@ -15676,7 +16428,7 @@ exports.BIP322 = void 0;
15676
16428
  async function sign2(message, identity, network) {
15677
16429
  const xOnlyPubKey = await identity.xOnlyPublicKey();
15678
16430
  const payment = btcSigner.p2tr(xOnlyPubKey, void 0, network);
15679
- const toSpend = chunkYEF5FL6E_cjs.craftToSpendTx(message, payment.script, TAG_BIP322);
16431
+ const toSpend = chunkUBYY2TPK_cjs.craftToSpendTx(message, payment.script, TAG_BIP322);
15680
16432
  const toSign = craftBIP322ToSignP2TR(toSpend, payment.script, xOnlyPubKey);
15681
16433
  const signed = await identity.sign(toSign, [0]);
15682
16434
  signed.finalizeIdx(0);
@@ -15734,7 +16486,7 @@ function verifyP2TR(message, witnessItems, pkScript, pubkey) {
15734
16486
  if (sighashType !== btcSigner.SigHash.DEFAULT && sighashType !== btcSigner.SigHash.ALL) {
15735
16487
  return false;
15736
16488
  }
15737
- const toSpend = chunkYEF5FL6E_cjs.craftToSpendTx(message, pkScript, TAG_BIP322);
16489
+ const toSpend = chunkUBYY2TPK_cjs.craftToSpendTx(message, pkScript, TAG_BIP322);
15738
16490
  const toSign = craftBIP322ToSignP2TR(toSpend, pkScript, pubkey);
15739
16491
  const sighash = toSign.preimageWitnessV1(0, [pkScript], sighashType, [0n]);
15740
16492
  const rawSig = sig.length === 65 ? sig.subarray(0, 64) : sig;
@@ -15755,7 +16507,7 @@ function verifyP2WPKH(message, witnessItems, pkScript, addressHash) {
15755
16507
  }
15756
16508
  const sighashType = sigWithHash[sigWithHash.length - 1];
15757
16509
  const derSig = sigWithHash.subarray(0, sigWithHash.length - 1);
15758
- const toSpend = chunkYEF5FL6E_cjs.craftToSpendTx(message, pkScript, TAG_BIP322);
16510
+ const toSpend = chunkUBYY2TPK_cjs.craftToSpendTx(message, pkScript, TAG_BIP322);
15759
16511
  const toSign = craftBIP322ToSignSimple(toSpend, pkScript);
15760
16512
  const scriptCode = btcSigner.OutScript.encode({ type: "pkh", hash: addressHash });
15761
16513
  const sighash = toSign.preimageWitnessV0(0, scriptCode, sighashType, 0n);
@@ -15808,7 +16560,7 @@ function encodeCompactSize(n) {
15808
16560
  return buf;
15809
16561
  }
15810
16562
  function craftBIP322ToSignP2TR(toSpend, pkScript, tapInternalKey) {
15811
- const tx = new chunkYEF5FL6E_cjs.Transaction({ version: 0 });
16563
+ const tx = new chunkUBYY2TPK_cjs.Transaction({ version: 0 });
15812
16564
  tx.addInput({
15813
16565
  txid: toSpend.id,
15814
16566
  index: 0,
@@ -15822,12 +16574,12 @@ function craftBIP322ToSignP2TR(toSpend, pkScript, tapInternalKey) {
15822
16574
  });
15823
16575
  tx.addOutput({
15824
16576
  amount: 0n,
15825
- script: chunkYEF5FL6E_cjs.OP_RETURN_EMPTY_PKSCRIPT
16577
+ script: chunkUBYY2TPK_cjs.OP_RETURN_EMPTY_PKSCRIPT
15826
16578
  });
15827
16579
  return tx;
15828
16580
  }
15829
16581
  function craftBIP322ToSignSimple(toSpend, pkScript) {
15830
- const tx = new chunkYEF5FL6E_cjs.Transaction({ version: 0 });
16582
+ const tx = new chunkUBYY2TPK_cjs.Transaction({ version: 0 });
15831
16583
  tx.addInput({
15832
16584
  txid: toSpend.id,
15833
16585
  index: 0,
@@ -15839,10 +16591,102 @@ function craftBIP322ToSignSimple(toSpend, pkScript) {
15839
16591
  });
15840
16592
  tx.addOutput({
15841
16593
  amount: 0n,
15842
- script: chunkYEF5FL6E_cjs.OP_RETURN_EMPTY_PKSCRIPT
16594
+ script: chunkUBYY2TPK_cjs.OP_RETURN_EMPTY_PKSCRIPT
15843
16595
  });
15844
16596
  return tx;
15845
16597
  }
16598
+ function finalizeVirtualTx(type, psbtBase64) {
16599
+ const tx = chunkUBYY2TPK_cjs.Transaction.fromPSBT(base.base64.decode(psbtBase64));
16600
+ if (type === "INDEXER_CHAINED_TX_TYPE_TREE" /* TREE */) {
16601
+ const input = tx.getInput(0);
16602
+ if (!input) {
16603
+ throw new Error("Input not found");
16604
+ }
16605
+ const tapKeySig = input.tapKeySig;
16606
+ if (!tapKeySig) {
16607
+ throw new Error("Tap key sig not found");
16608
+ }
16609
+ tx.updateInput(0, {
16610
+ finalScriptWitness: [tapKeySig]
16611
+ });
16612
+ return tx;
16613
+ }
16614
+ tx.finalize();
16615
+ return tx;
16616
+ }
16617
+ var ExitPathError = class extends Error {
16618
+ constructor(reason, message) {
16619
+ super(message);
16620
+ this.reason = reason;
16621
+ this.name = "ExitPathError";
16622
+ }
16623
+ };
16624
+ function delayWeight(sequence) {
16625
+ const t = chunkHXDZ2QTW_cjs.sequenceToTimelock(sequence);
16626
+ return t.type === "blocks" ? Number(t.value) : Number(t.value) / 600;
16627
+ }
16628
+ async function resolveUnilateralPath(params) {
16629
+ const { vtxo, scriptHex, contractRepository } = params;
16630
+ const contract = contractRepository ? (await contractRepository.getContracts({ script: scriptHex }))[0] : void 0;
16631
+ if (contract) {
16632
+ const handler = chunk32YKLQPS_cjs.contractHandlers.get(contract.type);
16633
+ if (!handler) {
16634
+ throw new ExitPathError(
16635
+ "no-handler",
16636
+ `no contract handler registered for type '${contract.type}'`
16637
+ );
16638
+ }
16639
+ const script = handler.createScript(contract.params);
16640
+ const paths = handler.getAllSpendingPaths(script, contract, {
16641
+ collaborative: false,
16642
+ currentTime: params.currentTime,
16643
+ walletDescriptor: params.walletDescriptor,
16644
+ walletPubKey: params.walletPubKeyHex
16645
+ }).filter((p) => p.sequence !== void 0);
16646
+ if (paths.length === 0) {
16647
+ throw new ExitPathError(
16648
+ "no-unilateral-path",
16649
+ `no unilateral path for vtxo ${vtxo.txid}:${vtxo.vout} (type '${contract.type}')`
16650
+ );
16651
+ }
16652
+ paths.sort((a, b) => delayWeight(a.sequence) - delayWeight(b.sequence));
16653
+ return { selection: paths[0], label: `${contract.type}:unilateral` };
16654
+ }
16655
+ const decoded = chunkHXDZ2QTW_cjs.VtxoScript.decode(vtxo.tapTree);
16656
+ let best;
16657
+ for (const exit of decoded.exitPaths()) {
16658
+ const leaf = decoded.findLeaf(base.hex.encode(exit.script));
16659
+ if (!leaf) continue;
16660
+ const sequence = chunkHXDZ2QTW_cjs.timelockToSequence(exit.params.timelock);
16661
+ const weight = delayWeight(sequence);
16662
+ if (!best || weight < best.weight) {
16663
+ best = { selection: { leaf, sequence }, weight };
16664
+ }
16665
+ }
16666
+ if (!best) {
16667
+ throw new ExitPathError(
16668
+ "no-unilateral-path",
16669
+ `no exit path found for vtxo ${vtxo.txid}:${vtxo.vout}`
16670
+ );
16671
+ }
16672
+ return { selection: best.selection, label: "default:exit" };
16673
+ }
16674
+
16675
+ // src/wallet/unroll.ts
16676
+ function chainTxTypeToChainedExit(t) {
16677
+ switch (t) {
16678
+ case "INDEXER_CHAINED_TX_TYPE_COMMITMENT" /* COMMITMENT */:
16679
+ return 1 /* Commitment */;
16680
+ case "INDEXER_CHAINED_TX_TYPE_ARK" /* ARK */:
16681
+ return 2 /* Ark */;
16682
+ case "INDEXER_CHAINED_TX_TYPE_TREE" /* TREE */:
16683
+ return 3 /* Tree */;
16684
+ case "INDEXER_CHAINED_TX_TYPE_CHECKPOINT" /* CHECKPOINT */:
16685
+ return 4 /* Checkpoint */;
16686
+ default:
16687
+ return 0 /* Unspecified */;
16688
+ }
16689
+ }
15846
16690
  exports.Unroll = void 0;
15847
16691
  ((Unroll2) => {
15848
16692
  ((StepType2) => {
@@ -15852,16 +16696,54 @@ exports.Unroll = void 0;
15852
16696
  })(Unroll2.StepType || (Unroll2.StepType = {}));
15853
16697
  class Session2 {
15854
16698
  /** Create an unroll session from a virtual output outpoint and its dependency chain. */
15855
- constructor(toUnroll, bumper, explorer, indexer) {
16699
+ constructor(toUnroll, bumper, explorer, indexer, virtualTxRepository) {
15856
16700
  this.toUnroll = toUnroll;
15857
16701
  this.bumper = bumper;
15858
16702
  this.explorer = explorer;
15859
16703
  this.indexer = indexer;
16704
+ this.virtualTxRepository = virtualTxRepository;
15860
16705
  }
15861
16706
  /** Create an unroll session by loading the virtual output chain from the indexer. */
15862
- static async create(toUnroll, bumper, explorer, indexer) {
16707
+ static async create(toUnroll, bumper, explorer, indexer, virtualTxRepository) {
15863
16708
  const { chain } = await indexer.getVtxoChain(toUnroll);
15864
- return new Session2({ ...toUnroll, chain }, bumper, explorer, indexer);
16709
+ return new Session2(
16710
+ { ...toUnroll, chain },
16711
+ bumper,
16712
+ explorer,
16713
+ indexer,
16714
+ virtualTxRepository
16715
+ );
16716
+ }
16717
+ /**
16718
+ * Resolve a chain tx's raw PSBT (base64). Repo first when configured;
16719
+ * indexer on a miss, then best-effort cached so a later exit doesn't
16720
+ * re-fetch. Never throws from the cache
16721
+ * write — exit correctness must not depend on persistence.
16722
+ */
16723
+ async resolveVirtualTxBase64(next) {
16724
+ const repo = this.virtualTxRepository;
16725
+ if (repo) {
16726
+ try {
16727
+ const stored = await repo.getVirtualTx(next.txid);
16728
+ if (stored?.psbt) return stored.psbt;
16729
+ } catch {
16730
+ }
16731
+ }
16732
+ const fetched = await this.indexer.getVirtualTxs([next.txid]);
16733
+ const psbt = fetched.txs[0];
16734
+ if (psbt && repo) {
16735
+ const cached = {
16736
+ txid: next.txid,
16737
+ psbt,
16738
+ expiresAt: null,
16739
+ type: chainTxTypeToChainedExit(next.type)
16740
+ };
16741
+ try {
16742
+ await repo.upsertVirtualTxs([cached]);
16743
+ } catch {
16744
+ }
16745
+ }
16746
+ return psbt;
15865
16747
  }
15866
16748
  /**
15867
16749
  * Get the next step to be executed
@@ -15896,26 +16778,11 @@ exports.Unroll = void 0;
15896
16778
  do: () => Promise.resolve()
15897
16779
  };
15898
16780
  }
15899
- const virtualTxs = await this.indexer.getVirtualTxs([nextTxToBroadcast.txid]);
15900
- if (virtualTxs.txs.length === 0) {
16781
+ const virtualTxBase64 = await this.resolveVirtualTxBase64(nextTxToBroadcast);
16782
+ if (!virtualTxBase64) {
15901
16783
  throw new Error(`Tx ${nextTxToBroadcast.txid} not found`);
15902
16784
  }
15903
- const tx = chunkYEF5FL6E_cjs.Transaction.fromPSBT(base.base64.decode(virtualTxs.txs[0]));
15904
- if (nextTxToBroadcast.type === "INDEXER_CHAINED_TX_TYPE_TREE" /* TREE */) {
15905
- const input = tx.getInput(0);
15906
- if (!input) {
15907
- throw new Error("Input not found");
15908
- }
15909
- const tapKeySig = input.tapKeySig;
15910
- if (!tapKeySig) {
15911
- throw new Error("Tap key sig not found");
15912
- }
15913
- tx.updateInput(0, {
15914
- finalScriptWitness: [tapKeySig]
15915
- });
15916
- } else {
15917
- tx.finalize();
15918
- }
16785
+ const tx = finalizeVirtualTx(nextTxToBroadcast.type, virtualTxBase64);
15919
16786
  const pkg = await this.bumper.bumpP2A(tx);
15920
16787
  return {
15921
16788
  type: 0 /* UNROLL */,
@@ -15969,20 +16836,21 @@ async function prepareUnrollTransaction(wallet, vtxoTxIds, outputAddress) {
15969
16836
  if (!txStatus.confirmed) {
15970
16837
  throw new Error(`tx ${vtxo.txid} is not confirmed`);
15971
16838
  }
15972
- const exit = availableExitPath(
15973
- { height: txStatus.blockHeight, time: txStatus.blockTime },
15974
- chainTip,
15975
- vtxo
15976
- );
15977
- if (!exit) {
16839
+ const resolved = await resolveUnilateralPath({
16840
+ vtxo,
16841
+ scriptHex: base.hex.encode(chunkHXDZ2QTW_cjs.VtxoScript.decode(vtxo.tapTree).pkScript),
16842
+ contractRepository: wallet.contractRepository,
16843
+ walletPubKeyHex: base.hex.encode(await wallet.identity.xOnlyPublicKey()),
16844
+ currentTime: Date.now()
16845
+ });
16846
+ const spendingLeaf = resolved.selection.leaf;
16847
+ const sequence = resolved.selection.sequence;
16848
+ const timelock = chunkHXDZ2QTW_cjs.sequenceToTimelock(sequence);
16849
+ const elapsed = timelock.type === "blocks" ? chainTip.height >= txStatus.blockHeight + Number(timelock.value) : chainTip.time >= txStatus.blockTime + Number(timelock.value);
16850
+ if (!elapsed) {
15978
16851
  throw new Error(`no available exit path found for vtxo ${vtxo.txid}:${vtxo.vout}`);
15979
16852
  }
15980
- const spendingLeaf = chunkHXDZ2QTW_cjs.VtxoScript.decode(vtxo.tapTree).findLeaf(base.hex.encode(exit.script));
15981
- if (!spendingLeaf) {
15982
- throw new Error(`spending leaf not found for vtxo ${vtxo.txid}:${vtxo.vout}`);
15983
- }
15984
16853
  totalAmount += BigInt(vtxo.value);
15985
- const sequence = chunkHXDZ2QTW_cjs.timelockToSequence(exit.params.timelock);
15986
16854
  inputs.push({
15987
16855
  txid: vtxo.txid,
15988
16856
  index: vtxo.vout,
@@ -16000,7 +16868,7 @@ async function prepareUnrollTransaction(wallet, vtxoTxIds, outputAddress) {
16000
16868
  btcSigner.TaprootControlBlock.encode(spendingLeaf[0]).length
16001
16869
  );
16002
16870
  }
16003
- const tx = new chunkYEF5FL6E_cjs.Transaction({ version: 2 });
16871
+ const tx = new chunkUBYY2TPK_cjs.Transaction({ version: 2 });
16004
16872
  for (const input of inputs) {
16005
16873
  tx.addInput(input);
16006
16874
  }
@@ -16046,21 +16914,809 @@ function doWait(onchainProvider, txid) {
16046
16914
  });
16047
16915
  };
16048
16916
  }
16049
- function availableExitPath(confirmedAt, current, vtxo) {
16050
- const exits = chunkHXDZ2QTW_cjs.VtxoScript.decode(vtxo.tapTree).exitPaths();
16051
- for (const exit of exits) {
16052
- if (exit.params.timelock.type === "blocks") {
16053
- if (current.height >= confirmedAt.height + Number(exit.params.timelock.value)) {
16054
- return exit;
16917
+
16918
+ // src/wallet/exit/chain.ts
16919
+ var isCommitment = (t) => t === "INDEXER_CHAINED_TX_TYPE_COMMITMENT" /* COMMITMENT */ || t === "INDEXER_CHAINED_TX_TYPE_UNSPECIFIED" /* UNSPECIFIED */;
16920
+ async function buildExitDag(params) {
16921
+ const byTxid = /* @__PURE__ */ new Map();
16922
+ for (const vtxo of params.vtxos) {
16923
+ const outpoint = `${vtxo.txid}:${vtxo.vout}`;
16924
+ let pageIndex = 0;
16925
+ let hasMore = true;
16926
+ while (hasMore) {
16927
+ const { chain, page } = await params.indexer.getVtxoChain(
16928
+ { txid: vtxo.txid, vout: vtxo.vout },
16929
+ { pageIndex, pageSize: chunk32YKLQPS_cjs.DEFAULT_PAGE_SIZE }
16930
+ );
16931
+ for (const chainTx of chain) {
16932
+ const existing = byTxid.get(chainTx.txid);
16933
+ if (existing) {
16934
+ existing.forVtxos.add(outpoint);
16935
+ } else {
16936
+ byTxid.set(chainTx.txid, { ...chainTx, forVtxos: /* @__PURE__ */ new Set([outpoint]) });
16937
+ }
16055
16938
  }
16056
- } else {
16057
- if (current.time >= confirmedAt.time + Number(exit.params.timelock.value)) {
16058
- return exit;
16939
+ hasMore = page ? chain.length === chunk32YKLQPS_cjs.DEFAULT_PAGE_SIZE : false;
16940
+ pageIndex++;
16941
+ }
16942
+ }
16943
+ const pending = new Map(
16944
+ [...byTxid.values()].filter((tx) => !isCommitment(tx.type)).map((tx) => [tx.txid, tx])
16945
+ );
16946
+ const inDegree = /* @__PURE__ */ new Map();
16947
+ for (const tx of pending.values()) {
16948
+ const parents = tx.spends.filter((p) => pending.has(p));
16949
+ inDegree.set(tx.txid, parents.length);
16950
+ }
16951
+ const order = [];
16952
+ const queue = [...pending.values()].filter((tx) => inDegree.get(tx.txid) === 0).sort((a, b) => a.txid.localeCompare(b.txid));
16953
+ while (queue.length > 0) {
16954
+ const tx = queue.shift();
16955
+ order.push(tx);
16956
+ const children = [...pending.values()].filter((c) => c.spends.includes(tx.txid)).sort((a, b) => a.txid.localeCompare(b.txid));
16957
+ for (const child of children) {
16958
+ const deg = inDegree.get(child.txid) - 1;
16959
+ inDegree.set(child.txid, deg);
16960
+ if (deg === 0) queue.push(child);
16961
+ }
16962
+ }
16963
+ if (order.length !== pending.size) {
16964
+ throw new Error("inconsistent vtxo chain from indexer (cycle detected)");
16965
+ }
16966
+ const nodes = [];
16967
+ for (const tx of order) {
16968
+ let confirmed = false;
16969
+ try {
16970
+ const status = await params.onchain.getTxStatus(tx.txid);
16971
+ confirmed = status.confirmed;
16972
+ } catch {
16973
+ confirmed = false;
16974
+ }
16975
+ const expires = Number(tx.expiresAt);
16976
+ nodes.push({
16977
+ txid: tx.txid,
16978
+ type: tx.type,
16979
+ expiresAt: Number.isFinite(expires) && expires > 0 ? expires : void 0,
16980
+ forVtxos: [...tx.forVtxos].sort(),
16981
+ confirmed
16982
+ });
16983
+ }
16984
+ return nodes;
16985
+ }
16986
+ function topoSortByDeps(items, idOf, depsOf) {
16987
+ const produced = new Set(items.map(idOf));
16988
+ const emitted = /* @__PURE__ */ new Set();
16989
+ const ordered = [];
16990
+ let remaining = items;
16991
+ while (remaining.length > 0) {
16992
+ const next = [];
16993
+ let progress = false;
16994
+ for (const item of remaining) {
16995
+ if (depsOf(item).every((d) => !produced.has(d) || emitted.has(d))) {
16996
+ ordered.push(item);
16997
+ emitted.add(idOf(item));
16998
+ progress = true;
16999
+ } else {
17000
+ next.push(item);
16059
17001
  }
16060
17002
  }
17003
+ if (!progress) {
17004
+ throw new Error("topological sort: cycle or unsatisfiable dependency");
17005
+ }
17006
+ remaining = next;
16061
17007
  }
16062
- return void 0;
17008
+ return ordered;
17009
+ }
17010
+ function extraWitnessSize(path) {
17011
+ return (path.extraWitness ?? []).reduce((sum, item) => sum + 1 + item.length, 0);
17012
+ }
17013
+ function sweepFeeFor(path, outputAddress, network, feeRate) {
17014
+ const [controlBlock, scriptWithVersion] = path.leaf;
17015
+ const estimator = TxWeightEstimator.create().addTapscriptInput(
17016
+ 64 + extraWitnessSize(path),
17017
+ scriptWithVersion.length,
17018
+ btcSigner.TaprootControlBlock.encode(controlBlock).length
17019
+ ).addOutputAddress(outputAddress, network);
17020
+ return Number(estimator.vsize().fee(BigInt(Math.ceil(feeRate))));
17021
+ }
17022
+ async function buildSignedSweep(params) {
17023
+ const { vtxo, path, outputAddress, feeRate, network, identity } = params;
17024
+ const fee = sweepFeeFor(path, outputAddress, network, feeRate);
17025
+ const sendAmount = BigInt(vtxo.value) - BigInt(fee);
17026
+ if (sendAmount < BigInt(DUST_AMOUNT)) {
17027
+ throw new Error(
17028
+ `uneconomic vtxo ${vtxo.txid}:${vtxo.vout}: value ${vtxo.value} - fee ${fee} < dust`
17029
+ );
17030
+ }
17031
+ const tx = new chunkUBYY2TPK_cjs.Transaction({ version: 2 });
17032
+ tx.addInput({
17033
+ txid: vtxo.txid,
17034
+ index: vtxo.vout,
17035
+ tapLeafScript: [path.leaf],
17036
+ sequence: path.sequence,
17037
+ witnessUtxo: { amount: BigInt(vtxo.value), script: vtxo.pkScript },
17038
+ sighashType: btcSigner.SigHash.DEFAULT
17039
+ });
17040
+ tx.addOutputAddress(outputAddress, sendAmount, network);
17041
+ const signed = await identity.sign(tx);
17042
+ if (!path.extraWitness || path.extraWitness.length === 0) {
17043
+ signed.finalize();
17044
+ return { tx: signed, fee };
17045
+ }
17046
+ const input = signed.getInput(0);
17047
+ const tapScriptSig = input.tapScriptSig;
17048
+ if (!tapScriptSig || tapScriptSig.length === 0) {
17049
+ throw new Error("exit path requires additional signers");
17050
+ }
17051
+ const [controlBlock] = path.leaf;
17052
+ signed.updateInput(0, {
17053
+ finalScriptWitness: [
17054
+ tapScriptSig[0][1],
17055
+ ...path.extraWitness,
17056
+ chunkHXDZ2QTW_cjs.scriptFromTapLeafScript(path.leaf),
17057
+ btcSigner.TaprootControlBlock.encode(controlBlock)
17058
+ ]
17059
+ });
17060
+ return { tx: signed, fee };
17061
+ }
17062
+
17063
+ // src/wallet/exit/estimate.ts
17064
+ var CHILD_OUTPUT_DUST = 546;
17065
+ function stepFundingAmount(stepFee) {
17066
+ return stepFee + CHILD_OUTPUT_DUST;
17067
+ }
17068
+ function resolveMode(opts) {
17069
+ return opts.mode ?? "funded";
17070
+ }
17071
+ function resolveNetworkName(opts) {
17072
+ if (opts.networkName) return opts.networkName;
17073
+ const { bech32 } = opts.wallet.network;
17074
+ if (bech32 === "bc") return "bitcoin";
17075
+ if (bech32 === "bcrt") return "regtest";
17076
+ return "testnet";
17077
+ }
17078
+ async function resolveFeeRate(opts) {
17079
+ if (opts.feeRate) return Math.ceil(opts.feeRate);
17080
+ const feeRate = await opts.wallet.onchainProvider.getFeeRate();
17081
+ if (!feeRate || feeRate < OnchainWallet.MIN_FEE_RATE) return OnchainWallet.MIN_FEE_RATE;
17082
+ return Math.ceil(feeRate);
17083
+ }
17084
+ async function selectExitVtxos(opts) {
17085
+ if (!opts.vtxos) return opts.wallet.getVtxos();
17086
+ const res = await opts.wallet.indexerProvider.getVtxos({ outpoints: opts.vtxos });
17087
+ const tapTrees = /* @__PURE__ */ new Map();
17088
+ const out = [];
17089
+ for (const vtxo of res.vtxos) {
17090
+ if (vtxo.isSpent) continue;
17091
+ let tapTree = tapTrees.get(vtxo.script);
17092
+ if (!tapTree) {
17093
+ const [contract] = await opts.wallet.contractRepository.getContracts({
17094
+ script: vtxo.script
17095
+ });
17096
+ if (!contract) {
17097
+ throw new Error(
17098
+ `no contract registered for vtxo script ${vtxo.script} \u2014 register the contract before exiting`
17099
+ );
17100
+ }
17101
+ const handler = chunk32YKLQPS_cjs.contractHandlers.get(contract.type);
17102
+ if (!handler) {
17103
+ throw new Error(`no contract handler registered for type '${contract.type}'`);
17104
+ }
17105
+ tapTree = handler.createScript(contract.params).encode();
17106
+ tapTrees.set(vtxo.script, tapTree);
17107
+ }
17108
+ out.push({ txid: vtxo.txid, vout: vtxo.vout, value: vtxo.value, tapTree });
17109
+ }
17110
+ return out;
17111
+ }
17112
+ async function computeExitLayout(opts, feeRate) {
17113
+ const { wallet, onchainWallet } = opts;
17114
+ const vtxos = await selectExitVtxos(opts);
17115
+ if (vtxos.length === 0) throw new Error("no vtxos to exit");
17116
+ const dag = await buildExitDag({
17117
+ vtxos,
17118
+ indexer: wallet.indexerProvider,
17119
+ onchain: wallet.onchainProvider
17120
+ });
17121
+ const pendingNodes = dag.filter((n) => !n.confirmed);
17122
+ const psbts = /* @__PURE__ */ new Map();
17123
+ if (pendingNodes.length > 0) {
17124
+ const txids = pendingNodes.map((n) => n.txid);
17125
+ let pageIndex = 0;
17126
+ let hasMore = true;
17127
+ while (hasMore) {
17128
+ const { txs, page } = await wallet.indexerProvider.getVirtualTxs(txids, {
17129
+ pageIndex,
17130
+ pageSize: chunk32YKLQPS_cjs.DEFAULT_PAGE_SIZE
17131
+ });
17132
+ for (const psbt of txs) {
17133
+ psbts.set(chunkUBYY2TPK_cjs.Transaction.fromPSBT(base.base64.decode(psbt)).id, psbt);
17134
+ }
17135
+ hasMore = page ? txs.length === chunk32YKLQPS_cjs.DEFAULT_PAGE_SIZE : false;
17136
+ pageIndex++;
17137
+ }
17138
+ }
17139
+ const childVsize = Number(
17140
+ TxWeightEstimator.create().addP2AInput().addKeySpendInput(true).addOutputAddress(onchainWallet.address, wallet.network).vsize().value
17141
+ );
17142
+ const rawSteps = pendingNodes.map((node) => {
17143
+ const psbt = psbts.get(node.txid);
17144
+ if (!psbt) {
17145
+ throw new Error(`indexer did not return virtual tx ${node.txid}`);
17146
+ }
17147
+ const parent = finalizeVirtualTx(node.type, psbt);
17148
+ const stepFee = Math.ceil(feeRate * (parent.vsize + childVsize));
17149
+ return { node, parent, stepFee, funding: stepFundingAmount(stepFee) };
17150
+ });
17151
+ const parentInputTxids = (tx) => {
17152
+ const ids = [];
17153
+ for (let i = 0; i < tx.inputsLength; i++) {
17154
+ const txid = tx.getInput(i).txid;
17155
+ if (txid) ids.push(base.hex.encode(txid));
17156
+ }
17157
+ return ids;
17158
+ };
17159
+ const steps = topoSortByDeps(
17160
+ rawSteps,
17161
+ (s) => s.parent.id,
17162
+ (s) => parentInputTxids(s.parent)
17163
+ );
17164
+ const walletPubKeyHex = base.hex.encode(await wallet.identity.xOnlyPublicKey());
17165
+ const infos = [];
17166
+ const sweeps = [];
17167
+ for (const vtxo of vtxos) {
17168
+ const outpoint = `${vtxo.txid}:${vtxo.vout}`;
17169
+ try {
17170
+ const resolved = await resolveUnilateralPath({
17171
+ vtxo,
17172
+ scriptHex: base.hex.encode(chunkHXDZ2QTW_cjs.VtxoScript.decode(vtxo.tapTree).pkScript),
17173
+ contractRepository: wallet.contractRepository,
17174
+ walletPubKeyHex,
17175
+ currentTime: Date.now()
17176
+ });
17177
+ const sweepFee = sweepFeeFor(
17178
+ resolved.selection,
17179
+ opts.sweepAddress,
17180
+ wallet.network,
17181
+ feeRate
17182
+ );
17183
+ if (vtxo.value - sweepFee < CHILD_OUTPUT_DUST) {
17184
+ infos.push({
17185
+ outpoint,
17186
+ value: vtxo.value,
17187
+ skipped: `uneconomic: value ${vtxo.value} <= sweep fee + dust`
17188
+ });
17189
+ continue;
17190
+ }
17191
+ const t = chunkHXDZ2QTW_cjs.sequenceToTimelock(resolved.selection.sequence);
17192
+ const delay = { type: t.type, value: Number(t.value) };
17193
+ infos.push({ outpoint, value: vtxo.value, sweepFee, path: resolved.label, delay });
17194
+ sweeps.push({ vtxo, resolved, sweepFee, delay });
17195
+ } catch (e) {
17196
+ if (e instanceof ExitPathError) {
17197
+ infos.push({ outpoint, value: vtxo.value, skipped: e.message });
17198
+ continue;
17199
+ }
17200
+ throw e;
17201
+ }
17202
+ }
17203
+ const coins = (await onchainWallet.getCoins()).filter((c) => c.status.confirmed);
17204
+ const balance = coins.reduce((sum, c) => sum + c.value, 0);
17205
+ const fundingTotal = steps.reduce((sum, s) => sum + s.funding, 0);
17206
+ const splitterFeeFor = (inputCount) => {
17207
+ if (steps.length === 0) return 0;
17208
+ const est = TxWeightEstimator.create();
17209
+ for (let i = 0; i < inputCount; i++) est.addKeySpendInput(true);
17210
+ for (let i = 0; i < steps.length + 1; i++) {
17211
+ est.addOutputAddress(onchainWallet.address, wallet.network);
17212
+ }
17213
+ return Number(est.vsize().fee(BigInt(feeRate)));
17214
+ };
17215
+ const graph = resolveMode(opts) === "graph";
17216
+ let splitterFee = 0;
17217
+ if (!graph) {
17218
+ splitterFee = splitterFeeFor(Math.max(1, coins.length));
17219
+ if (steps.length > 0 && balance < fundingTotal + splitterFee) {
17220
+ splitterFee = splitterFeeFor(coins.length + 1);
17221
+ }
17222
+ }
17223
+ const stepFees = steps.reduce((s, x) => s + x.stepFee, 0);
17224
+ const sweepFees = sweeps.reduce((s, x) => s + x.sweepFee, 0);
17225
+ const totals = {
17226
+ // graph: each unroll step is parent + a live-built child (2 txs), no
17227
+ // splitter. funded: + 1 splitter tx and pre-signed children.
17228
+ txCount: (graph ? 0 : steps.length > 0 ? 1 : 0) + steps.length * 2 + sweeps.length,
17229
+ totalFeeSats: splitterFee + stepFees + sweepFees,
17230
+ // graph funding is what the executor sends to its own fee address:
17231
+ // just the CPFP fees (change recycles); funded also locks the
17232
+ // per-child dust into the splitter.
17233
+ fundingRequiredSats: graph ? stepFees : splitterFee + fundingTotal,
17234
+ recoveredSats: sweeps.reduce((s, x) => s + (x.vtxo.value - x.sweepFee), 0)
17235
+ };
17236
+ const expiries = pendingNodes.map((n) => n.expiresAt).filter((x) => x !== void 0);
17237
+ const validUntil = expiries.length > 0 ? Math.min(...expiries) : void 0;
17238
+ return {
17239
+ vtxos,
17240
+ dag,
17241
+ steps,
17242
+ sweeps,
17243
+ infos,
17244
+ totals,
17245
+ splitterFee,
17246
+ validUntil,
17247
+ balance,
17248
+ coins
17249
+ };
17250
+ }
17251
+ async function estimate(opts) {
17252
+ const feeRate = await resolveFeeRate(opts);
17253
+ const layout = await computeExitLayout(opts, feeRate);
17254
+ if (resolveMode(opts) === "graph") {
17255
+ return {
17256
+ feeRate,
17257
+ fundingAddress: "",
17258
+ currentBalanceSats: 0,
17259
+ shortfallSats: layout.totals.fundingRequiredSats,
17260
+ validUntil: layout.validUntil,
17261
+ totals: layout.totals,
17262
+ vtxos: layout.infos
17263
+ };
17264
+ }
17265
+ return {
17266
+ feeRate,
17267
+ fundingAddress: opts.onchainWallet.address,
17268
+ currentBalanceSats: layout.balance,
17269
+ shortfallSats: Math.max(0, layout.totals.fundingRequiredSats - layout.balance),
17270
+ validUntil: layout.validUntil,
17271
+ totals: layout.totals,
17272
+ vtxos: layout.infos
17273
+ };
17274
+ }
17275
+
17276
+ // src/wallet/exit/executor.ts
17277
+ var Executor = class {
17278
+ constructor(pkg, provider, opts) {
17279
+ this.pkg = pkg;
17280
+ this.provider = provider;
17281
+ this.pollIntervalMs = opts?.pollIntervalMs ?? 5e3;
17282
+ this.feeWallet = opts?.feeWallet;
17283
+ }
17284
+ pollIntervalMs;
17285
+ feeWallet;
17286
+ sleep() {
17287
+ return new Promise((r) => setTimeout(r, this.pollIntervalMs));
17288
+ }
17289
+ async status(txid) {
17290
+ try {
17291
+ return await this.provider.getTxStatus(txid);
17292
+ } catch {
17293
+ return void 0;
17294
+ }
17295
+ }
17296
+ async waitConfirmed(txid) {
17297
+ for (; ; ) {
17298
+ const s = await this.status(txid);
17299
+ if (s?.confirmed) return s;
17300
+ await this.sleep();
17301
+ }
17302
+ }
17303
+ async *[Symbol.asyncIterator]() {
17304
+ const dead = /* @__PURE__ */ new Set();
17305
+ if (this.pkg.validUntil && Date.now() / 1e3 > this.pkg.validUntil) {
17306
+ yield {
17307
+ stepIndex: -1,
17308
+ kind: "broadcast",
17309
+ status: "warning",
17310
+ reason: `package validUntil ${this.pkg.validUntil} has passed; the operator may have swept \u2014 attempting anyway`
17311
+ };
17312
+ }
17313
+ const sweeps = [];
17314
+ for (let i = 0; i < this.pkg.steps.length; i++) {
17315
+ const step = this.pkg.steps[i];
17316
+ if (step.kind === "sweep") {
17317
+ sweeps.push({ index: i, step });
17318
+ continue;
17319
+ }
17320
+ const forVtxos = step.kind === "package" || step.kind === "bump" ? step.forVtxos : void 0;
17321
+ if (forVtxos && forVtxos.every((v) => dead.has(v))) {
17322
+ yield {
17323
+ stepIndex: i,
17324
+ kind: step.kind,
17325
+ status: "skipped",
17326
+ forVtxos,
17327
+ reason: "branch failed earlier"
17328
+ };
17329
+ continue;
17330
+ }
17331
+ const anchorTxid = step.kind === "package" || step.kind === "bump" ? step.parentTxid : step.txid;
17332
+ const existing = await this.status(anchorTxid);
17333
+ if (existing?.confirmed) {
17334
+ yield {
17335
+ stepIndex: i,
17336
+ kind: step.kind,
17337
+ status: "skipped",
17338
+ txid: anchorTxid,
17339
+ forVtxos
17340
+ };
17341
+ continue;
17342
+ }
17343
+ if (!existing) {
17344
+ try {
17345
+ if (step.kind === "package") {
17346
+ await this.provider.broadcastTransaction(step.parentHex, step.childHex);
17347
+ } else if (step.kind === "bump") {
17348
+ if (!this.feeWallet) {
17349
+ throw new Error(
17350
+ "graph package requires a fee wallet (opts.feeWallet) to fund CPFP bumps"
17351
+ );
17352
+ }
17353
+ const [parentHex, childHex] = await this.feeWallet.bumpAnchor(
17354
+ step.parentHex,
17355
+ this.pkg.feeRate
17356
+ );
17357
+ await this.provider.broadcastTransaction(parentHex, childHex);
17358
+ } else {
17359
+ await this.provider.broadcastTransaction(step.hex);
17360
+ }
17361
+ yield {
17362
+ stepIndex: i,
17363
+ kind: step.kind,
17364
+ status: "broadcast",
17365
+ txid: anchorTxid,
17366
+ forVtxos
17367
+ };
17368
+ } catch (e) {
17369
+ const reason = e instanceof Error ? e.message : String(e);
17370
+ if (step.kind === "broadcast") {
17371
+ for (const s of this.pkg.steps) {
17372
+ if (s.kind === "package") s.forVtxos.forEach((v) => dead.add(v));
17373
+ }
17374
+ } else if (step.kind === "package" || step.kind === "bump") {
17375
+ step.forVtxos.forEach((v) => dead.add(v));
17376
+ }
17377
+ yield {
17378
+ stepIndex: i,
17379
+ kind: step.kind,
17380
+ status: "failed",
17381
+ txid: anchorTxid,
17382
+ forVtxos,
17383
+ reason
17384
+ };
17385
+ continue;
17386
+ }
17387
+ }
17388
+ await this.waitConfirmed(anchorTxid);
17389
+ yield {
17390
+ stepIndex: i,
17391
+ kind: step.kind,
17392
+ status: "confirmed",
17393
+ txid: anchorTxid,
17394
+ forVtxos
17395
+ };
17396
+ }
17397
+ for (const { index, step } of sweeps) {
17398
+ if (dead.has(step.vtxo)) {
17399
+ yield {
17400
+ stepIndex: index,
17401
+ kind: "sweep",
17402
+ status: "failed",
17403
+ txid: step.txid,
17404
+ forVtxos: [step.vtxo],
17405
+ reason: "branch failed earlier"
17406
+ };
17407
+ }
17408
+ }
17409
+ const pending = sweeps.filter(({ step }) => !dead.has(step.vtxo));
17410
+ const waitingAnnounced = /* @__PURE__ */ new Set();
17411
+ const done = /* @__PURE__ */ new Set();
17412
+ while (done.size < pending.length) {
17413
+ for (const { index, step } of pending) {
17414
+ if (done.has(index)) continue;
17415
+ const swept = await this.status(step.txid);
17416
+ if (swept?.confirmed) {
17417
+ done.add(index);
17418
+ yield {
17419
+ stepIndex: index,
17420
+ kind: "sweep",
17421
+ status: "confirmed",
17422
+ txid: step.txid,
17423
+ forVtxos: [step.vtxo]
17424
+ };
17425
+ continue;
17426
+ }
17427
+ if (swept) continue;
17428
+ const dep = await this.status(step.dependsOnTxid);
17429
+ if (!dep?.confirmed) continue;
17430
+ const tip = await this.provider.getChainTip();
17431
+ const mature = step.delay.type === "blocks" ? tip.height >= (dep.blockHeight ?? 0) + step.delay.value : tip.time >= (dep.blockTime ?? 0) + step.delay.value;
17432
+ if (!mature) {
17433
+ if (!waitingAnnounced.has(index)) {
17434
+ waitingAnnounced.add(index);
17435
+ yield {
17436
+ stepIndex: index,
17437
+ kind: "sweep",
17438
+ status: "waiting_csv",
17439
+ txid: step.txid,
17440
+ forVtxos: [step.vtxo],
17441
+ ...step.delay.type === "blocks" ? { maturesAtHeight: (dep.blockHeight ?? 0) + step.delay.value } : { maturesAtTime: (dep.blockTime ?? 0) + step.delay.value }
17442
+ };
17443
+ }
17444
+ continue;
17445
+ }
17446
+ try {
17447
+ await this.provider.broadcastTransaction(step.hex);
17448
+ yield {
17449
+ stepIndex: index,
17450
+ kind: "sweep",
17451
+ status: "broadcast",
17452
+ txid: step.txid,
17453
+ forVtxos: [step.vtxo]
17454
+ };
17455
+ } catch (e) {
17456
+ const reason = e instanceof Error ? e.message : String(e);
17457
+ if (/non-?bip68|non-?final|premature/i.test(reason)) {
17458
+ continue;
17459
+ }
17460
+ done.add(index);
17461
+ yield {
17462
+ stepIndex: index,
17463
+ kind: "sweep",
17464
+ status: "failed",
17465
+ txid: step.txid,
17466
+ forVtxos: [step.vtxo],
17467
+ reason
17468
+ };
17469
+ }
17470
+ }
17471
+ if (done.size < pending.length) await this.sleep();
17472
+ }
17473
+ }
17474
+ };
17475
+ async function prepare(opts) {
17476
+ const { wallet, onchainWallet } = opts;
17477
+ const feeRate = await resolveFeeRate(opts);
17478
+ const mode = resolveMode(opts);
17479
+ if (mode === "funded") {
17480
+ const walletKey = await wallet.identity.xOnlyPublicKey();
17481
+ if (base.hex.encode(onchainWallet.onchainP2TR.tapInternalKey) !== base.hex.encode(walletKey)) {
17482
+ throw new Error("onchainWallet must share the wallet identity");
17483
+ }
17484
+ }
17485
+ const layout = await computeExitLayout(opts, feeRate);
17486
+ const sweepSteps = [];
17487
+ const activeOutpoints = /* @__PURE__ */ new Set();
17488
+ for (const sweep of layout.sweeps) {
17489
+ const outpoint = `${sweep.vtxo.txid}:${sweep.vtxo.vout}`;
17490
+ try {
17491
+ const { tx } = await buildSignedSweep({
17492
+ vtxo: {
17493
+ txid: sweep.vtxo.txid,
17494
+ vout: sweep.vtxo.vout,
17495
+ value: sweep.vtxo.value,
17496
+ pkScript: chunkHXDZ2QTW_cjs.VtxoScript.decode(sweep.vtxo.tapTree).pkScript
17497
+ },
17498
+ path: sweep.resolved.selection,
17499
+ outputAddress: opts.sweepAddress,
17500
+ feeRate,
17501
+ network: wallet.network,
17502
+ identity: wallet.identity
17503
+ });
17504
+ activeOutpoints.add(outpoint);
17505
+ sweepSteps.push({
17506
+ kind: "sweep",
17507
+ vtxo: outpoint,
17508
+ txid: tx.id,
17509
+ hex: tx.hex,
17510
+ dependsOnTxid: sweep.vtxo.txid,
17511
+ delay: sweep.delay
17512
+ });
17513
+ } catch (e) {
17514
+ const info = layout.infos.find((i) => i.outpoint === outpoint);
17515
+ if (info) {
17516
+ info.skipped = e instanceof Error ? e.message : String(e);
17517
+ delete info.sweepFee;
17518
+ delete info.path;
17519
+ delete info.delay;
17520
+ }
17521
+ }
17522
+ }
17523
+ const steps = layout.steps.filter((s) => s.node.forVtxos.some((v) => activeOutpoints.has(v)));
17524
+ if (steps.length === 0 && sweepSteps.length === 0) {
17525
+ throw new Error("no exitable vtxos (all skipped)");
17526
+ }
17527
+ if (mode === "graph") {
17528
+ const bumpSteps = steps.map((step) => ({
17529
+ kind: "bump",
17530
+ parentTxid: step.parent.id,
17531
+ parentHex: step.parent.hex,
17532
+ forVtxos: step.node.forVtxos.filter((v) => activeOutpoints.has(v))
17533
+ }));
17534
+ const stepFees2 = steps.reduce((s, x) => s + x.stepFee, 0);
17535
+ const activeInfos2 = layout.infos.filter((i) => !i.skipped);
17536
+ const sweepFees2 = activeInfos2.reduce((s, i) => s + (i.sweepFee ?? 0), 0);
17537
+ const recovered2 = activeInfos2.reduce((s, i) => s + (i.value ?? 0) - (i.sweepFee ?? 0), 0);
17538
+ return {
17539
+ version: 1,
17540
+ mode: "graph",
17541
+ network: resolveNetworkName(opts),
17542
+ createdAt: Math.floor(Date.now() / 1e3),
17543
+ validUntil: layout.validUntil,
17544
+ feeRate,
17545
+ sweepAddress: opts.sweepAddress,
17546
+ totals: {
17547
+ txCount: bumpSteps.length * 2 + sweepSteps.length,
17548
+ totalFeeSats: stepFees2 + sweepFees2,
17549
+ fundingRequiredSats: stepFees2,
17550
+ recoveredSats: recovered2
17551
+ },
17552
+ vtxos: layout.infos,
17553
+ steps: [...bumpSteps, ...sweepSteps]
17554
+ };
17555
+ }
17556
+ const packageSteps = [];
17557
+ let broadcastStep;
17558
+ let splitterFee = 0;
17559
+ if (steps.length > 0) {
17560
+ const fundingTotal = steps.reduce((sum, s) => sum + s.funding, 0);
17561
+ let selected = { inputs: [], changeAmount: 0n };
17562
+ for (let i = 0; i < 10; i++) {
17563
+ const target = fundingTotal + splitterFee;
17564
+ try {
17565
+ selected = selectCoins(layout.coins, target);
17566
+ } catch {
17567
+ throw new Error(
17568
+ `insufficient confirmed onchain funds: need ${target} sats, have ${layout.balance} (deposit the shortfall to ${onchainWallet.address})`
17569
+ );
17570
+ }
17571
+ const est = TxWeightEstimator.create();
17572
+ for (const _ of selected.inputs) est.addKeySpendInput(true);
17573
+ for (const _ of steps) est.addOutputAddress(onchainWallet.address, wallet.network);
17574
+ if (selected.changeAmount >= BigInt(DUST_AMOUNT)) {
17575
+ est.addOutputAddress(onchainWallet.address, wallet.network);
17576
+ }
17577
+ const newFee = Number(est.vsize().fee(BigInt(feeRate)));
17578
+ if (newFee <= splitterFee) break;
17579
+ splitterFee = newFee;
17580
+ }
17581
+ const splitter = new chunkUBYY2TPK_cjs.Transaction({ version: 2 });
17582
+ for (const coin of selected.inputs) {
17583
+ splitter.addInput({
17584
+ txid: coin.txid,
17585
+ index: coin.vout,
17586
+ witnessUtxo: {
17587
+ script: onchainWallet.onchainP2TR.script,
17588
+ amount: BigInt(coin.value)
17589
+ },
17590
+ tapInternalKey: onchainWallet.onchainP2TR.tapInternalKey
17591
+ });
17592
+ }
17593
+ for (const step of steps) {
17594
+ splitter.addOutputAddress(onchainWallet.address, BigInt(step.funding), wallet.network);
17595
+ }
17596
+ const inputSum = selected.inputs.reduce((s, c) => s + c.value, 0);
17597
+ const change = BigInt(inputSum - fundingTotal - splitterFee);
17598
+ if (change < 0n) {
17599
+ throw new Error(
17600
+ `insufficient confirmed onchain funds for the splitter fee: need ${fundingTotal + splitterFee} sats, have ${inputSum} (deposit the shortfall to ${onchainWallet.address})`
17601
+ );
17602
+ }
17603
+ if (change >= BigInt(DUST_AMOUNT)) {
17604
+ splitter.addOutputAddress(onchainWallet.address, change, wallet.network);
17605
+ } else if (change > 0n) {
17606
+ splitterFee += Number(change);
17607
+ }
17608
+ const signedSplitter = await wallet.identity.sign(splitter);
17609
+ signedSplitter.finalize();
17610
+ for (let k = 0; k < steps.length; k++) {
17611
+ const step = steps[k];
17612
+ const { child } = buildAnchorChild({
17613
+ parent: step.parent,
17614
+ feeRate,
17615
+ fundingCoins: [{ txid: signedSplitter.id, vout: k, value: step.funding }],
17616
+ changeAddress: onchainWallet.address,
17617
+ changeScript: onchainWallet.onchainP2TR.script,
17618
+ tapInternalKey: onchainWallet.onchainP2TR.tapInternalKey,
17619
+ network: wallet.network
17620
+ });
17621
+ const signedChild = await wallet.identity.sign(child);
17622
+ for (let i = 1; i < signedChild.inputsLength; i++) {
17623
+ signedChild.finalizeIdx(i);
17624
+ }
17625
+ packageSteps.push({
17626
+ kind: "package",
17627
+ parentTxid: step.parent.id,
17628
+ parentHex: step.parent.hex,
17629
+ childTxid: signedChild.id,
17630
+ childHex: signedChild.hex,
17631
+ forVtxos: step.node.forVtxos.filter((v) => activeOutpoints.has(v))
17632
+ });
17633
+ }
17634
+ broadcastStep = { kind: "broadcast", txid: signedSplitter.id, hex: signedSplitter.hex };
17635
+ await onchainWallet.provider.broadcastTransaction(signedSplitter.hex);
17636
+ }
17637
+ const stepFees = steps.reduce((s, x) => s + x.stepFee, 0);
17638
+ const activeInfos = layout.infos.filter((i) => !i.skipped);
17639
+ const sweepFees = activeInfos.reduce((s, i) => s + (i.sweepFee ?? 0), 0);
17640
+ const recovered = activeInfos.reduce((s, i) => s + (i.value ?? 0) - (i.sweepFee ?? 0), 0);
17641
+ const fundingRequired = splitterFee + steps.reduce((s, x) => s + x.funding, 0);
17642
+ return {
17643
+ version: 1,
17644
+ mode: "funded",
17645
+ network: resolveNetworkName(opts),
17646
+ createdAt: Math.floor(Date.now() / 1e3),
17647
+ validUntil: layout.validUntil,
17648
+ feeRate,
17649
+ sweepAddress: opts.sweepAddress,
17650
+ totals: {
17651
+ txCount: (broadcastStep ? 1 : 0) + packageSteps.length * 2 + sweepSteps.length,
17652
+ totalFeeSats: splitterFee + stepFees + sweepFees,
17653
+ fundingRequiredSats: fundingRequired,
17654
+ recoveredSats: recovered
17655
+ },
17656
+ vtxos: layout.infos,
17657
+ steps: [...broadcastStep ? [broadcastStep] : [], ...packageSteps, ...sweepSteps]
17658
+ };
17659
+ }
17660
+
17661
+ // src/wallet/exit/types.ts
17662
+ function serializeExitPackage(pkg) {
17663
+ return JSON.stringify(pkg);
16063
17664
  }
17665
+ function deserializeExitPackage(json) {
17666
+ const data = JSON.parse(json);
17667
+ if (data?.version !== 1) {
17668
+ throw new Error(`unsupported exit package version: ${data?.version}`);
17669
+ }
17670
+ if (typeof data.network !== "string" || typeof data.sweepAddress !== "string") {
17671
+ throw new Error("invalid exit package: missing network or sweepAddress");
17672
+ }
17673
+ if (typeof data.feeRate !== "number" || typeof data.createdAt !== "number") {
17674
+ throw new Error("invalid exit package: missing feeRate or createdAt");
17675
+ }
17676
+ if (!Array.isArray(data.steps) || !Array.isArray(data.vtxos)) {
17677
+ throw new Error("invalid exit package: steps and vtxos must be arrays");
17678
+ }
17679
+ if (!isValidTotals(data.totals)) {
17680
+ throw new Error("invalid exit package: malformed totals");
17681
+ }
17682
+ for (const step of data.steps) {
17683
+ if (!isValidStep(step)) {
17684
+ throw new Error(`invalid step: ${JSON.stringify(step)}`);
17685
+ }
17686
+ }
17687
+ return data;
17688
+ }
17689
+ function isValidDelay(d) {
17690
+ const delay = d;
17691
+ return !!delay && (delay.type === "blocks" || delay.type === "seconds") && typeof delay.value === "number";
17692
+ }
17693
+ function isValidTotals(t) {
17694
+ const totals = t;
17695
+ return !!totals && typeof totals === "object" && typeof totals.txCount === "number" && typeof totals.totalFeeSats === "number" && typeof totals.fundingRequiredSats === "number" && typeof totals.recoveredSats === "number";
17696
+ }
17697
+ function isValidStep(step) {
17698
+ const s = step;
17699
+ if (!s || typeof s !== "object") return false;
17700
+ switch (s.kind) {
17701
+ case "broadcast":
17702
+ return typeof s.txid === "string" && typeof s.hex === "string";
17703
+ case "package":
17704
+ return typeof s.parentTxid === "string" && typeof s.parentHex === "string" && typeof s.childTxid === "string" && typeof s.childHex === "string" && Array.isArray(s.forVtxos);
17705
+ case "bump":
17706
+ return typeof s.parentTxid === "string" && typeof s.parentHex === "string" && Array.isArray(s.forVtxos);
17707
+ case "sweep":
17708
+ return typeof s.vtxo === "string" && typeof s.txid === "string" && typeof s.hex === "string" && typeof s.dependsOnTxid === "string" && isValidDelay(s.delay);
17709
+ default:
17710
+ return false;
17711
+ }
17712
+ }
17713
+
17714
+ // src/wallet/exit/index.ts
17715
+ var UnilateralExit = {
17716
+ estimate,
17717
+ prepare,
17718
+ Executor
17719
+ };
16064
17720
  var ARKCONTRACT_PREFIX = "arkcontract";
16065
17721
  function encodeArkContract(contract) {
16066
17722
  const params = new URLSearchParams();
@@ -16123,6 +17779,7 @@ function isArkContract(str) {
16123
17779
  exports.ActivityRegistry = ActivityRegistry;
16124
17780
  exports.ArkNote = ArkNote;
16125
17781
  exports.AssetManager = AssetManager;
17782
+ exports.ChainedTxType = ChainedTxType;
16126
17783
  exports.ContractManager = ContractManager;
16127
17784
  exports.ContractRepositoryImpl = ContractRepositoryImpl;
16128
17785
  exports.ContractWatcher = ContractWatcher;
@@ -16141,9 +17798,14 @@ exports.ElectrumOnchainProvider = ElectrumOnchainProvider;
16141
17798
  exports.EsploraProvider = EsploraProvider;
16142
17799
  exports.Estimator = Estimator;
16143
17800
  exports.HDDescriptorProvider = HDDescriptorProvider;
17801
+ exports.INTENT_TERMINAL_STATES = INTENT_TERMINAL_STATES;
16144
17802
  exports.InMemoryContractRepository = InMemoryContractRepository;
17803
+ exports.InMemoryIntentRepository = InMemoryIntentRepository;
17804
+ exports.InMemoryVirtualTxRepository = InMemoryVirtualTxRepository;
16145
17805
  exports.InMemoryWalletRepository = InMemoryWalletRepository;
16146
17806
  exports.IndexedDBContractRepository = IndexedDBContractRepository;
17807
+ exports.IndexedDBIntentRepository = IndexedDBIntentRepository;
17808
+ exports.IndexedDBVirtualTxRepository = IndexedDBVirtualTxRepository;
16147
17809
  exports.IndexedDBWalletRepository = IndexedDBWalletRepository;
16148
17810
  exports.MESSAGE_BUS_INITIALIZING = MESSAGE_BUS_INITIALIZING;
16149
17811
  exports.MESSAGE_BUS_NOT_INITIALIZED = MESSAGE_BUS_NOT_INITIALIZED;
@@ -16171,12 +17833,14 @@ exports.SingleKey = SingleKey;
16171
17833
  exports.TxTree = TxTree;
16172
17834
  exports.TxType = TxType;
16173
17835
  exports.TxWeightEstimator = TxWeightEstimator;
17836
+ exports.UnilateralExit = UnilateralExit;
16174
17837
  exports.VtxoManager = VtxoManager;
16175
17838
  exports.Wallet = Wallet2;
16176
17839
  exports.WalletMessageHandler = WalletMessageHandler;
16177
17840
  exports.WalletNotInitializedError = WalletNotInitializedError;
16178
17841
  exports.WalletRepositoryImpl = WalletRepositoryImpl;
16179
17842
  exports.WsElectrumChainSource = WsElectrumChainSource;
17843
+ exports.assertIntentIdUnique = assertIntentIdUnique;
16180
17844
  exports.boardingResolver = boardingResolver;
16181
17845
  exports.buildForfeitTx = buildForfeitTx;
16182
17846
  exports.buildOffchainTx = buildOffchainTx;
@@ -16189,6 +17853,7 @@ exports.contractFromArkContractWithAddress = contractFromArkContractWithAddress;
16189
17853
  exports.createDefaultActivityRegistry = createDefaultActivityRegistry;
16190
17854
  exports.decodeArkContract = decodeArkContract;
16191
17855
  exports.deserializeAssets = deserializeAssets;
17856
+ exports.deserializeExitPackage = deserializeExitPackage;
16192
17857
  exports.deserializeUtxo = deserializeUtxo;
16193
17858
  exports.deserializeVtxo = deserializeVtxo;
16194
17859
  exports.encodeArkContract = encodeArkContract;
@@ -16196,6 +17861,8 @@ exports.extendVirtualCoinForContract = extendVirtualCoinForContract;
16196
17861
  exports.getMigrationStatus = getMigrationStatus;
16197
17862
  exports.getRandomId = getRandomId;
16198
17863
  exports.hasBoardingTxExpired = hasBoardingTxExpired;
17864
+ exports.intentMatchesFilter = intentMatchesFilter;
17865
+ exports.intentPageBounds = intentPageBounds;
16199
17866
  exports.isArkContract = isArkContract;
16200
17867
  exports.isBatchSignable = isBatchSignable;
16201
17868
  exports.isCooperativelyMigratable = isCooperativelyMigratable;
@@ -16204,9 +17871,11 @@ exports.isExpired = isExpired;
16204
17871
  exports.isRecoverable = isRecoverable;
16205
17872
  exports.isSpendable = isSpendable;
16206
17873
  exports.isSubdust = isSubdust;
17874
+ exports.isTerminalIntentState = isTerminalIntentState;
16207
17875
  exports.isValidArkAddress = isValidArkAddress;
16208
17876
  exports.isVtxoExpiringSoon = isVtxoExpiringSoon;
16209
17877
  exports.isVtxoForScript = isVtxoForScript;
17878
+ exports.mergeChainedTxType = mergeChainedTxType;
16210
17879
  exports.migrateWalletRepository = migrateWalletRepository;
16211
17880
  exports.openDatabase = openDatabase;
16212
17881
  exports.requiresMigration = requiresMigration;
@@ -16214,6 +17883,7 @@ exports.rollbackMigration = rollbackMigration;
16214
17883
  exports.saveVtxosForContract = saveVtxosForContract;
16215
17884
  exports.scriptFromArkAddress = scriptFromArkAddress;
16216
17885
  exports.serializeAssets = serializeAssets;
17886
+ exports.serializeExitPackage = serializeExitPackage;
16217
17887
  exports.serializeUtxo = serializeUtxo;
16218
17888
  exports.serializeVtxo = serializeVtxo;
16219
17889
  exports.setupServiceWorker = setupServiceWorker;
@@ -16224,5 +17894,5 @@ exports.validateVtxoTxGraph = validateVtxoTxGraph;
16224
17894
  exports.verifyTapscriptSignatures = verifyTapscriptSignatures;
16225
17895
  exports.waitForIncomingFunds = waitForIncomingFunds;
16226
17896
  exports.warnAndFilterVtxosForScript = warnAndFilterVtxosForScript;
16227
- //# sourceMappingURL=chunk-ED7JDFX4.cjs.map
16228
- //# sourceMappingURL=chunk-ED7JDFX4.cjs.map
17897
+ //# sourceMappingURL=chunk-XIVCG6EP.cjs.map
17898
+ //# sourceMappingURL=chunk-XIVCG6EP.cjs.map