@arkade-os/sdk 0.4.42 → 0.4.43
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.
- package/dist/adapters/expo.cjs +4 -4
- package/dist/adapters/expo.d.cts +2 -2
- package/dist/adapters/expo.d.ts +2 -2
- package/dist/adapters/expo.js +2 -2
- package/dist/adapters/indexedDB.cjs +3 -3
- package/dist/adapters/indexedDB.js +2 -2
- package/dist/{ark-CKDWpuGL.d.cts → ark-I8jfe5VE.d.cts} +157 -2
- package/dist/{ark-CKDWpuGL.d.ts → ark-I8jfe5VE.d.ts} +157 -2
- package/dist/{asyncStorageTaskQueue-BB2MffwG.d.ts → asyncStorageTaskQueue-CjoL-35w.d.ts} +1 -1
- package/dist/{asyncStorageTaskQueue-BpN8RRHR.d.cts → asyncStorageTaskQueue-D1FfHfgK.d.cts} +1 -1
- package/dist/{chunk-YEF5FL6E.cjs → chunk-3AQNLQPM.cjs} +39 -4
- package/dist/chunk-3AQNLQPM.cjs.map +1 -0
- package/dist/{chunk-5X5YMZHB.js → chunk-763XHDAJ.js} +39 -5
- package/dist/chunk-763XHDAJ.js.map +1 -0
- package/dist/{chunk-V3FCNVJE.js → chunk-B4R36PRN.js} +1105 -356
- package/dist/chunk-B4R36PRN.js.map +1 -0
- package/dist/{chunk-HHPUCCB3.cjs → chunk-JIWCE2IK.cjs} +7 -7
- package/dist/{chunk-HHPUCCB3.cjs.map → chunk-JIWCE2IK.cjs.map} +1 -1
- package/dist/{chunk-T2ACJD22.cjs → chunk-PLJZRIFZ.cjs} +11 -11
- package/dist/{chunk-T2ACJD22.cjs.map → chunk-PLJZRIFZ.cjs.map} +1 -1
- package/dist/{chunk-ED7JDFX4.cjs → chunk-QGNTDGMN.cjs} +1189 -429
- package/dist/chunk-QGNTDGMN.cjs.map +1 -0
- package/dist/{chunk-FLPPSRYH.js → chunk-US274KZO.js} +3 -3
- package/dist/{chunk-FLPPSRYH.js.map → chunk-US274KZO.js.map} +1 -1
- package/dist/{chunk-2V3BU2X6.js → chunk-VKKLOT2E.js} +3 -3
- package/dist/{chunk-2V3BU2X6.js.map → chunk-VKKLOT2E.js.map} +1 -1
- package/dist/contracts/handlers/index.d.cts +3 -3
- package/dist/contracts/handlers/index.d.ts +3 -3
- package/dist/{delegate-yjhcR9n4.d.cts → delegate-BRqbh2fW.d.cts} +1 -1
- package/dist/{delegate-DllAQ3Eu.d.ts → delegate-CgjVjYOP.d.ts} +1 -1
- package/dist/{index-D1te-ZR6.d.cts → index-DHuXlDEg.d.cts} +2 -2
- package/dist/{index-DVdCGyyH.d.ts → index-DvWNB_rl.d.ts} +2 -2
- package/dist/index.cjs +150 -118
- package/dist/index.d.cts +113 -9
- package/dist/index.d.ts +113 -9
- package/dist/index.js +2 -2
- package/dist/repositories/realm/index.cjs +268 -12
- package/dist/repositories/realm/index.cjs.map +1 -1
- package/dist/repositories/realm/index.d.cts +101 -3
- package/dist/repositories/realm/index.d.ts +101 -3
- package/dist/repositories/realm/index.js +254 -4
- package/dist/repositories/realm/index.js.map +1 -1
- package/dist/repositories/sqlite/index.cjs +366 -40
- package/dist/repositories/sqlite/index.cjs.map +1 -1
- package/dist/repositories/sqlite/index.d.cts +52 -6
- package/dist/repositories/sqlite/index.d.ts +52 -6
- package/dist/repositories/sqlite/index.js +353 -32
- package/dist/repositories/sqlite/index.js.map +1 -1
- package/dist/{taskRunner-CuTk8RYg.d.cts → taskRunner-BSnslNmq.d.cts} +1 -1
- package/dist/{taskRunner-BdiRy4iI.d.ts → taskRunner-Djlhfgi1.d.ts} +1 -1
- package/dist/wallet/expo/background.cjs +12 -12
- package/dist/wallet/expo/background.d.cts +3 -3
- package/dist/wallet/expo/background.d.ts +3 -3
- package/dist/wallet/expo/background.js +4 -4
- package/dist/wallet/expo/index.cjs +12 -12
- package/dist/wallet/expo/index.d.cts +4 -4
- package/dist/wallet/expo/index.d.ts +4 -4
- package/dist/wallet/expo/index.js +3 -3
- package/dist/{wallet-CyPOQ0oM.d.cts → wallet-BsqRU8KM.d.cts} +29 -5
- package/dist/{wallet-CrIVUQMb.d.ts → wallet-D9z7wssl.d.ts} +29 -5
- package/dist/worker/expo/index.cjs +7 -7
- package/dist/worker/expo/index.d.cts +4 -4
- package/dist/worker/expo/index.d.ts +4 -4
- package/dist/worker/expo/index.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-5X5YMZHB.js.map +0 -1
- package/dist/chunk-ED7JDFX4.cjs.map +0 -1
- package/dist/chunk-V3FCNVJE.js.map +0 -1
- package/dist/chunk-YEF5FL6E.cjs.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { craftToSpendTx, Transaction, OP_RETURN_EMPTY_PKSCRIPT, baseFetch, Intent, getArkPsbtFields, CosignerPublicKey, setArkPsbtField, VtxoTaprootTree, maybeArkError, AssetRef, AssetId, AssetOutput, AssetGroup, AssetInput, Packet, Metadata, isEventSourceError, RestArkProvider, RestIndexerProvider, ArkError, BufferReader } from './chunk-
|
|
1
|
+
import { craftToSpendTx, Transaction, OP_RETURN_EMPTY_PKSCRIPT, baseFetch, Intent, getArkPsbtFields, CosignerPublicKey, setArkPsbtField, VtxoTaprootTree, maybeArkError, AssetRef, AssetId, AssetOutput, AssetGroup, AssetInput, Packet, Metadata, isEventSourceError, RestArkProvider, RestIndexerProvider, ArkError, BufferReader } from './chunk-763XHDAJ.js';
|
|
2
2
|
import { isMainnetDescriptor, descriptorIsOurs, contractHandlers, DEFAULT_PAGE_SIZE, DelegateVtxo, WALLET_RECEIVE_SOURCE, deriveDescriptorLeafPubKey, DefaultVtxo, BoardingContractHandler } from './chunk-YNEDJTX2.js';
|
|
3
3
|
import { VtxoScript, timelockToSequence, DEFAULT_NETWORK, DEFAULT_NETWORK_NAME, decodeTapscript, scriptFromTapLeafScript, CLTVMultisigTapscript, ArkAddress, CSVMultisigTapscript, getSequence, getNetwork, MultisigTapscript, networks as networks$1, DEFAULT_ARKADE_SERVER_URL } from './chunk-BE5MUB7T.js';
|
|
4
4
|
import { sha256, hash160, sha256x2, concatBytes, randomPrivateKeyBytes, pubECDSA, pubSchnorr, equalBytes as equalBytes$1 } from '@scure/btc-signer/utils.js';
|
|
@@ -2644,7 +2644,7 @@ function getExpiringAndRecoverableVtxos(vtxos, thresholdMs, dustAmount) {
|
|
|
2644
2644
|
);
|
|
2645
2645
|
}
|
|
2646
2646
|
function isMigrationCapable(wallet) {
|
|
2647
|
-
return "arkProvider" in wallet && "arkServerPublicKey" in wallet && "onchainProvider" in wallet && typeof wallet.rotateServerSigner === "function" && typeof wallet.sendSelectedVtxosToSelf === "function" && typeof wallet.getBoardingUtxosForSigners === "function";
|
|
2647
|
+
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";
|
|
2648
2648
|
}
|
|
2649
2649
|
function classifiedToRef(c) {
|
|
2650
2650
|
return {
|
|
@@ -3168,6 +3168,7 @@ var VtxoManager = class _VtxoManager {
|
|
|
3168
3168
|
async migrateCore(options) {
|
|
3169
3169
|
const wallet = this.requireMigrationCapableWallet();
|
|
3170
3170
|
const info = await wallet.arkProvider.getInfo();
|
|
3171
|
+
wallet.refreshDeprecatedSigners(info);
|
|
3171
3172
|
const signerSet = signerSetFromInfo(info);
|
|
3172
3173
|
const nowSeconds = Math.floor(Date.now() / 1e3);
|
|
3173
3174
|
const walletSignerHex = hex.encode(wallet.arkServerPublicKey);
|
|
@@ -4136,6 +4137,123 @@ function readUInt32BE(array, offset) {
|
|
|
4136
4137
|
function noteTapscript(preimageHash) {
|
|
4137
4138
|
return Script.encode(["SHA256", preimageHash, "EQUAL"]);
|
|
4138
4139
|
}
|
|
4140
|
+
|
|
4141
|
+
// src/repositories/intentRepository.ts
|
|
4142
|
+
var ALL_INTENT_STATES = [
|
|
4143
|
+
"waiting_to_submit",
|
|
4144
|
+
"waiting_for_batch",
|
|
4145
|
+
"batch_in_progress",
|
|
4146
|
+
"batch_failed",
|
|
4147
|
+
"batch_succeeded",
|
|
4148
|
+
"cancelled"
|
|
4149
|
+
];
|
|
4150
|
+
var INTENT_TERMINAL_STATES = /* @__PURE__ */ new Set([
|
|
4151
|
+
"batch_failed",
|
|
4152
|
+
"batch_succeeded",
|
|
4153
|
+
"cancelled"
|
|
4154
|
+
]);
|
|
4155
|
+
function isTerminalIntentState(s) {
|
|
4156
|
+
return INTENT_TERMINAL_STATES.has(s);
|
|
4157
|
+
}
|
|
4158
|
+
function assertIntentIdUnique(intent, existing) {
|
|
4159
|
+
if (intent.intentId === void 0) return;
|
|
4160
|
+
for (const e of existing) {
|
|
4161
|
+
if (e.intentTxId !== intent.intentTxId && e.intentId === intent.intentId) {
|
|
4162
|
+
throw new Error(
|
|
4163
|
+
`intentId "${intent.intentId}" is already used by intent ${e.intentTxId}`
|
|
4164
|
+
);
|
|
4165
|
+
}
|
|
4166
|
+
}
|
|
4167
|
+
}
|
|
4168
|
+
function intentMatchesFilter(i, f) {
|
|
4169
|
+
if (f.intentTxIds && !f.intentTxIds.includes(i.intentTxId)) return false;
|
|
4170
|
+
if (f.intentIds && (!i.intentId || !f.intentIds.includes(i.intentId))) return false;
|
|
4171
|
+
if (f.states && !f.states.includes(i.state)) return false;
|
|
4172
|
+
if (f.containingInputs) {
|
|
4173
|
+
const keys = new Set(i.intentVtxos.map((o) => `${o.txid}:${o.vout}`));
|
|
4174
|
+
if (!f.containingInputs.some((o) => keys.has(`${o.txid}:${o.vout}`))) return false;
|
|
4175
|
+
}
|
|
4176
|
+
if (f.validAt !== void 0) {
|
|
4177
|
+
if (i.validFrom !== void 0 && f.validAt < i.validFrom) return false;
|
|
4178
|
+
if (i.validUntil !== void 0 && f.validAt > i.validUntil) return false;
|
|
4179
|
+
}
|
|
4180
|
+
if (f.searchText) {
|
|
4181
|
+
const hay = [i.intentId, i.batchId, i.commitmentTransactionId].filter(Boolean).join(" ");
|
|
4182
|
+
if (!hay.includes(f.searchText)) return false;
|
|
4183
|
+
}
|
|
4184
|
+
return true;
|
|
4185
|
+
}
|
|
4186
|
+
function intentPageBounds(filter, total) {
|
|
4187
|
+
const skip = Math.max(0, filter?.skip ?? 0);
|
|
4188
|
+
const take = filter?.take === void 0 ? total : Math.max(0, filter.take);
|
|
4189
|
+
return { skip, end: skip + take };
|
|
4190
|
+
}
|
|
4191
|
+
|
|
4192
|
+
// src/wallet/intentStateReducer.ts
|
|
4193
|
+
function reduceIntentState(current, event) {
|
|
4194
|
+
if (isTerminalIntentState(current)) return current;
|
|
4195
|
+
switch (event.type) {
|
|
4196
|
+
case "batch_started" /* BatchStarted */:
|
|
4197
|
+
return "batch_in_progress";
|
|
4198
|
+
case "batch_finalized" /* BatchFinalized */:
|
|
4199
|
+
return "batch_succeeded";
|
|
4200
|
+
case "batch_failed" /* BatchFailed */:
|
|
4201
|
+
return "batch_failed";
|
|
4202
|
+
default:
|
|
4203
|
+
return current;
|
|
4204
|
+
}
|
|
4205
|
+
}
|
|
4206
|
+
function applySettlementEventToIntent(intent, event) {
|
|
4207
|
+
const next = reduceIntentState(intent.state, event);
|
|
4208
|
+
if (next === intent.state) return void 0;
|
|
4209
|
+
const updated = { ...intent, state: next };
|
|
4210
|
+
if ("id" in event && typeof event.id === "string") {
|
|
4211
|
+
updated.batchId = event.id;
|
|
4212
|
+
}
|
|
4213
|
+
if (event.type === "batch_finalized" /* BatchFinalized */) {
|
|
4214
|
+
updated.commitmentTransactionId = event.commitmentTxid;
|
|
4215
|
+
}
|
|
4216
|
+
if (event.type === "batch_failed" /* BatchFailed */) {
|
|
4217
|
+
updated.cancellationReason = event.reason;
|
|
4218
|
+
}
|
|
4219
|
+
return updated;
|
|
4220
|
+
}
|
|
4221
|
+
|
|
4222
|
+
// src/wallet/intentPersistenceHandler.ts
|
|
4223
|
+
async function persistTransition(deps, event) {
|
|
4224
|
+
const repo = deps.intentRepository;
|
|
4225
|
+
if (!repo) return;
|
|
4226
|
+
try {
|
|
4227
|
+
const cur = (await repo.getIntents({ intentTxIds: [deps.intentTxId] }))[0];
|
|
4228
|
+
if (!cur) return;
|
|
4229
|
+
const next = applySettlementEventToIntent(cur, event);
|
|
4230
|
+
if (next) await repo.saveIntent(next);
|
|
4231
|
+
} catch (e) {
|
|
4232
|
+
console.error(`Failed to apply settlement event to intent ${deps.intentTxId}`, e);
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
function wrapHandlerWithIntentPersistence(base, deps) {
|
|
4236
|
+
return {
|
|
4237
|
+
...base,
|
|
4238
|
+
onBatchStarted: async (event) => {
|
|
4239
|
+
const result = await base.onBatchStarted(event);
|
|
4240
|
+
if (!result.skip) await persistTransition(deps, event);
|
|
4241
|
+
return result;
|
|
4242
|
+
},
|
|
4243
|
+
onBatchFinalized: async (event) => {
|
|
4244
|
+
if (base.onBatchFinalized) await base.onBatchFinalized(event);
|
|
4245
|
+
await persistTransition(deps, event);
|
|
4246
|
+
},
|
|
4247
|
+
onBatchFailed: async (event) => {
|
|
4248
|
+
await persistTransition(deps, event);
|
|
4249
|
+
if (base.onBatchFailed) {
|
|
4250
|
+
await base.onBatchFailed(event);
|
|
4251
|
+
return;
|
|
4252
|
+
}
|
|
4253
|
+
throw new Error(event.reason);
|
|
4254
|
+
}
|
|
4255
|
+
};
|
|
4256
|
+
}
|
|
4139
4257
|
var TxTree = class {
|
|
4140
4258
|
constructor(root, children = /* @__PURE__ */ new Map()) {
|
|
4141
4259
|
this.root = root;
|
|
@@ -5245,214 +5363,216 @@ function isAnnotated(v) {
|
|
|
5245
5363
|
return v.tapTree !== void 0 && v.forfeitTapLeafScript !== void 0 && v.intentTapLeafScript !== void 0;
|
|
5246
5364
|
}
|
|
5247
5365
|
|
|
5248
|
-
// src/
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
const rejected = [];
|
|
5261
|
-
for (const v of vtxos) {
|
|
5262
|
-
if (isVtxoForScript(v, script)) {
|
|
5263
|
-
matches.push(v);
|
|
5264
|
-
} else {
|
|
5265
|
-
rejected.push(`${vtxoOutpoint(v)}(script=${v.script ?? ""})`);
|
|
5266
|
-
}
|
|
5267
|
-
}
|
|
5268
|
-
if (rejected.length > 0) {
|
|
5269
|
-
console.warn(
|
|
5270
|
-
`${context}: dropped ${rejected.length} wrong-script VTXO(s) for script ${script}: ${rejected.join(", ")}`
|
|
5271
|
-
);
|
|
5272
|
-
}
|
|
5273
|
-
return matches;
|
|
5274
|
-
}
|
|
5275
|
-
function validateVtxosForScript(vtxos, script, context) {
|
|
5276
|
-
const mismatches = vtxos.filter((v) => !isVtxoForScript(v, script));
|
|
5277
|
-
if (mismatches.length === 0) return;
|
|
5278
|
-
const detail = mismatches.map((v) => `${vtxoOutpoint(v)}(script=${v.script ?? ""})`).join(", ");
|
|
5279
|
-
throw new Error(
|
|
5280
|
-
`${context}: refusing to persist ${mismatches.length} VTXO(s) whose script does not match ${script}: ${detail}`
|
|
5281
|
-
);
|
|
5282
|
-
}
|
|
5283
|
-
async function getVtxosForContract(repo, contract) {
|
|
5284
|
-
return repo.getVtxosForScript ? repo.getVtxosForScript(contract.script) : filterVtxosForScript(await repo.getVtxos(contract.address), contract.script);
|
|
5285
|
-
}
|
|
5286
|
-
async function saveVtxosForContract(repo, contract, vtxos) {
|
|
5287
|
-
if (repo.saveVtxosForScript) {
|
|
5288
|
-
return repo.saveVtxosForScript(
|
|
5289
|
-
{ script: contract.script, address: contract.address },
|
|
5290
|
-
vtxos
|
|
5291
|
-
);
|
|
5292
|
-
}
|
|
5293
|
-
validateVtxosForScript(vtxos, contract.script, "saveVtxosForContract");
|
|
5294
|
-
return repo.saveVtxos(contract.address, vtxos);
|
|
5366
|
+
// src/repositories/virtualTxRepository.ts
|
|
5367
|
+
var ChainedTxType = /* @__PURE__ */ ((ChainedTxType2) => {
|
|
5368
|
+
ChainedTxType2[ChainedTxType2["Unspecified"] = 0] = "Unspecified";
|
|
5369
|
+
ChainedTxType2[ChainedTxType2["Commitment"] = 1] = "Commitment";
|
|
5370
|
+
ChainedTxType2[ChainedTxType2["Ark"] = 2] = "Ark";
|
|
5371
|
+
ChainedTxType2[ChainedTxType2["Tree"] = 3] = "Tree";
|
|
5372
|
+
ChainedTxType2[ChainedTxType2["Checkpoint"] = 4] = "Checkpoint";
|
|
5373
|
+
return ChainedTxType2;
|
|
5374
|
+
})(ChainedTxType || {});
|
|
5375
|
+
function mergeChainedTxType(incoming, prev) {
|
|
5376
|
+
if (incoming !== 0 /* Unspecified */) return incoming;
|
|
5377
|
+
return prev ?? 0 /* Unspecified */;
|
|
5295
5378
|
}
|
|
5296
5379
|
|
|
5297
|
-
// src/repositories/inMemory/
|
|
5298
|
-
var
|
|
5380
|
+
// src/repositories/inMemory/intentRepository.ts
|
|
5381
|
+
var InMemoryIntentRepository = class {
|
|
5299
5382
|
version = 1;
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
async
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5383
|
+
byId = /* @__PURE__ */ new Map();
|
|
5384
|
+
async clear() {
|
|
5385
|
+
this.byId.clear();
|
|
5386
|
+
}
|
|
5387
|
+
async saveIntent(intent) {
|
|
5388
|
+
assertIntentIdUnique(intent, this.byId.values());
|
|
5389
|
+
this.byId.set(intent.intentTxId, {
|
|
5390
|
+
...intent,
|
|
5391
|
+
intentVtxos: [...intent.intentVtxos],
|
|
5392
|
+
partialForfeits: [...intent.partialForfeits],
|
|
5393
|
+
updatedAt: Date.now()
|
|
5394
|
+
});
|
|
5311
5395
|
}
|
|
5312
|
-
async
|
|
5313
|
-
this.
|
|
5396
|
+
async getIntents(filter) {
|
|
5397
|
+
let out = [...this.byId.values()];
|
|
5398
|
+
if (filter) out = out.filter((i) => intentMatchesFilter(i, filter));
|
|
5399
|
+
out.sort((a, b) => a.createdAt - b.createdAt || a.intentTxId.localeCompare(b.intentTxId));
|
|
5400
|
+
const { skip, end } = intentPageBounds(filter, out.length);
|
|
5401
|
+
return out.slice(skip, end).map(clone);
|
|
5314
5402
|
}
|
|
5315
|
-
async
|
|
5316
|
-
const
|
|
5317
|
-
for (const
|
|
5318
|
-
for (const
|
|
5319
|
-
|
|
5320
|
-
allMatches.push(vtxo);
|
|
5321
|
-
}
|
|
5322
|
-
}
|
|
5323
|
-
}
|
|
5324
|
-
return mergeByKey([], allMatches, (item) => `${item.txid}:${item.vout}`);
|
|
5403
|
+
async getLockedVtxoOutpoints() {
|
|
5404
|
+
const out = [];
|
|
5405
|
+
for (const i of this.byId.values())
|
|
5406
|
+
if (!isTerminalIntentState(i.state)) for (const o of i.intentVtxos) out.push({ ...o });
|
|
5407
|
+
return out;
|
|
5325
5408
|
}
|
|
5326
|
-
async
|
|
5327
|
-
if (!key.address) {
|
|
5328
|
-
throw new Error("InMemoryWalletRepository requires an address");
|
|
5329
|
-
}
|
|
5330
|
-
for (const vtxo of vtxos) {
|
|
5331
|
-
if (!isVtxoForScript(vtxo, key.script)) {
|
|
5332
|
-
throw new Error(
|
|
5333
|
-
`VTXO ${vtxo.txid}:${vtxo.vout} script mismatch: expected ${key.script}, got ${vtxo.script}`
|
|
5334
|
-
);
|
|
5335
|
-
}
|
|
5336
|
-
}
|
|
5337
|
-
return this.saveVtxos(key.address, vtxos);
|
|
5409
|
+
async [Symbol.asyncDispose]() {
|
|
5338
5410
|
}
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5411
|
+
};
|
|
5412
|
+
var clone = (i) => ({
|
|
5413
|
+
...i,
|
|
5414
|
+
intentVtxos: i.intentVtxos.map((o) => ({ ...o })),
|
|
5415
|
+
partialForfeits: [...i.partialForfeits]
|
|
5416
|
+
});
|
|
5417
|
+
|
|
5418
|
+
// src/repositories/inMemory/virtualTxRepository.ts
|
|
5419
|
+
var opKey = (o) => `${o.txid}:${o.vout}`;
|
|
5420
|
+
var InMemoryVirtualTxRepository = class {
|
|
5421
|
+
version = 1;
|
|
5422
|
+
txs = /* @__PURE__ */ new Map();
|
|
5423
|
+
branches = /* @__PURE__ */ new Map();
|
|
5424
|
+
async clear() {
|
|
5425
|
+
this.txs.clear();
|
|
5426
|
+
this.branches.clear();
|
|
5427
|
+
}
|
|
5428
|
+
async upsertVirtualTxs(txs) {
|
|
5429
|
+
for (const t of txs) {
|
|
5430
|
+
const prev = this.txs.get(t.txid);
|
|
5431
|
+
if (!prev) {
|
|
5432
|
+
this.txs.set(t.txid, { ...t });
|
|
5433
|
+
continue;
|
|
5346
5434
|
}
|
|
5435
|
+
this.txs.set(t.txid, {
|
|
5436
|
+
txid: t.txid,
|
|
5437
|
+
psbt: t.psbt ?? prev.psbt,
|
|
5438
|
+
expiresAt: t.expiresAt ?? prev.expiresAt,
|
|
5439
|
+
type: mergeChainedTxType(t.type, prev.type)
|
|
5440
|
+
});
|
|
5347
5441
|
}
|
|
5348
5442
|
}
|
|
5349
|
-
async
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
async saveUtxos(address, utxos) {
|
|
5353
|
-
const existing = this.utxosByAddress.get(address) ?? [];
|
|
5354
|
-
const next = mergeByKey(existing, utxos, (item) => `${item.txid}:${item.vout}`);
|
|
5355
|
-
this.utxosByAddress.set(address, next);
|
|
5356
|
-
}
|
|
5357
|
-
async deleteUtxos(address) {
|
|
5358
|
-
this.utxosByAddress.delete(address);
|
|
5359
|
-
}
|
|
5360
|
-
async getTransactionHistory(address) {
|
|
5361
|
-
return this.txsByAddress.get(address) ?? [];
|
|
5362
|
-
}
|
|
5363
|
-
async saveTransactions(address, txs) {
|
|
5364
|
-
const existing = this.txsByAddress.get(address) ?? [];
|
|
5365
|
-
const next = mergeByKey(existing, txs, serializeTxKey);
|
|
5366
|
-
this.txsByAddress.set(address, next);
|
|
5443
|
+
async getVirtualTx(txid) {
|
|
5444
|
+
const t = this.txs.get(txid);
|
|
5445
|
+
return t ? { ...t } : null;
|
|
5367
5446
|
}
|
|
5368
|
-
async
|
|
5369
|
-
this.
|
|
5447
|
+
async setBranch(vtxo, branch) {
|
|
5448
|
+
this.branches.set(
|
|
5449
|
+
opKey(vtxo),
|
|
5450
|
+
[...branch].sort((a, b) => a.position - b.position)
|
|
5451
|
+
);
|
|
5370
5452
|
}
|
|
5371
|
-
async
|
|
5372
|
-
|
|
5453
|
+
async getBranch(vtxo) {
|
|
5454
|
+
const b = this.branches.get(opKey(vtxo)) ?? [];
|
|
5455
|
+
return b.map((x) => this.txs.get(x.virtualTxid)).filter((t) => !!t).map((t) => ({ ...t }));
|
|
5373
5456
|
}
|
|
5374
|
-
async
|
|
5375
|
-
this.
|
|
5457
|
+
async hasBranch(vtxo) {
|
|
5458
|
+
return (this.branches.get(opKey(vtxo)) ?? []).length > 0;
|
|
5376
5459
|
}
|
|
5377
|
-
async
|
|
5378
|
-
this.
|
|
5379
|
-
this.
|
|
5380
|
-
|
|
5381
|
-
this.
|
|
5460
|
+
async pruneForSpentVtxo(vtxo) {
|
|
5461
|
+
const removed = this.branches.get(opKey(vtxo)) ?? [];
|
|
5462
|
+
this.branches.delete(opKey(vtxo));
|
|
5463
|
+
const stillReferenced = /* @__PURE__ */ new Set();
|
|
5464
|
+
for (const b of this.branches.values())
|
|
5465
|
+
for (const e of b) stillReferenced.add(e.virtualTxid);
|
|
5466
|
+
for (const e of removed)
|
|
5467
|
+
if (!stillReferenced.has(e.virtualTxid)) this.txs.delete(e.virtualTxid);
|
|
5382
5468
|
}
|
|
5383
5469
|
async [Symbol.asyncDispose]() {
|
|
5384
|
-
return;
|
|
5385
5470
|
}
|
|
5386
5471
|
};
|
|
5387
|
-
function serializeTxKey(tx) {
|
|
5388
|
-
const key = tx.key;
|
|
5389
|
-
return `${key.boardingTxid}:${key.commitmentTxid}:${key.arkTxid}`;
|
|
5390
|
-
}
|
|
5391
|
-
function mergeByKey(existing, incoming, toKey) {
|
|
5392
|
-
const next = /* @__PURE__ */ new Map();
|
|
5393
|
-
existing.forEach((item) => {
|
|
5394
|
-
next.set(toKey(item), item);
|
|
5395
|
-
});
|
|
5396
|
-
incoming.forEach((item) => {
|
|
5397
|
-
next.set(toKey(item), item);
|
|
5398
|
-
});
|
|
5399
|
-
return Array.from(next.values());
|
|
5400
|
-
}
|
|
5401
5472
|
|
|
5402
|
-
// src/repositories/
|
|
5403
|
-
var
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
if (
|
|
5417
|
-
return
|
|
5473
|
+
// src/repositories/indexedDB/idbUtils.ts
|
|
5474
|
+
var promisifyRequest = (request) => new Promise((resolve, reject) => {
|
|
5475
|
+
request.onsuccess = () => resolve(request.result);
|
|
5476
|
+
request.onerror = () => reject(request.error);
|
|
5477
|
+
});
|
|
5478
|
+
var awaitTransaction = (transaction) => new Promise((resolve, reject) => {
|
|
5479
|
+
transaction.oncomplete = () => resolve();
|
|
5480
|
+
transaction.onerror = () => reject(transaction.error);
|
|
5481
|
+
transaction.onabort = () => reject(transaction.error ?? new Error("transaction aborted"));
|
|
5482
|
+
});
|
|
5483
|
+
|
|
5484
|
+
// src/repositories/indexedDB/manager.ts
|
|
5485
|
+
function getGlobalObject() {
|
|
5486
|
+
if (typeof globalThis !== "undefined") {
|
|
5487
|
+
if (typeof globalThis.self === "object" && globalThis.self !== null) {
|
|
5488
|
+
return { globalObject: globalThis.self };
|
|
5418
5489
|
}
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
return true;
|
|
5422
|
-
}
|
|
5423
|
-
return Array.isArray(criterion) ? criterion.includes(value) : value === criterion;
|
|
5424
|
-
};
|
|
5425
|
-
const results = [];
|
|
5426
|
-
for (const contract of contracts) {
|
|
5427
|
-
if (matches(contract.script, filter.script) && matches(contract.state, filter.state) && matches(contract.type, filter.type)) {
|
|
5428
|
-
results.push(contract);
|
|
5429
|
-
}
|
|
5490
|
+
if (typeof globalThis.window === "object" && globalThis.window !== null) {
|
|
5491
|
+
return { globalObject: globalThis.window };
|
|
5430
5492
|
}
|
|
5431
|
-
return
|
|
5432
|
-
}
|
|
5433
|
-
async saveContract(contract) {
|
|
5434
|
-
this.contractsByScript.set(contract.script, contract);
|
|
5493
|
+
return { globalObject: globalThis };
|
|
5435
5494
|
}
|
|
5436
|
-
|
|
5437
|
-
|
|
5495
|
+
throw new Error("Global object not found");
|
|
5496
|
+
}
|
|
5497
|
+
var dbCache = /* @__PURE__ */ new Map();
|
|
5498
|
+
var refCounts = /* @__PURE__ */ new Map();
|
|
5499
|
+
async function openDatabase(dbName, dbVersion, initDatabase2) {
|
|
5500
|
+
const { globalObject } = getGlobalObject();
|
|
5501
|
+
if (!globalObject.indexedDB) {
|
|
5502
|
+
throw new Error("IndexedDB is not available in this environment");
|
|
5438
5503
|
}
|
|
5439
|
-
|
|
5440
|
-
|
|
5504
|
+
const cached = dbCache.get(dbName);
|
|
5505
|
+
if (cached) {
|
|
5506
|
+
if (cached.version !== dbVersion) {
|
|
5507
|
+
throw new Error(
|
|
5508
|
+
`Database "${dbName}" already opened with version ${cached.version}; requested ${dbVersion}`
|
|
5509
|
+
);
|
|
5510
|
+
}
|
|
5511
|
+
refCounts.set(dbName, (refCounts.get(dbName) ?? 0) + 1);
|
|
5512
|
+
return cached.promise;
|
|
5441
5513
|
}
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5514
|
+
const dbPromise = new Promise((resolve, reject) => {
|
|
5515
|
+
const request = globalObject.indexedDB.open(dbName, dbVersion);
|
|
5516
|
+
request.onerror = () => {
|
|
5517
|
+
dbCache.delete(dbName);
|
|
5518
|
+
refCounts.delete(dbName);
|
|
5519
|
+
reject(request.error);
|
|
5520
|
+
};
|
|
5521
|
+
request.onsuccess = () => {
|
|
5522
|
+
const db = request.result;
|
|
5523
|
+
db.onversionchange = () => {
|
|
5524
|
+
db.close();
|
|
5525
|
+
dbCache.delete(dbName);
|
|
5526
|
+
refCounts.delete(dbName);
|
|
5527
|
+
};
|
|
5528
|
+
resolve(db);
|
|
5529
|
+
};
|
|
5530
|
+
request.onupgradeneeded = (event) => {
|
|
5531
|
+
const db = request.result;
|
|
5532
|
+
initDatabase2(db, event.oldVersion, request.transaction);
|
|
5533
|
+
};
|
|
5534
|
+
request.onblocked = () => {
|
|
5535
|
+
console.warn("Database upgrade blocked - close other tabs/connections");
|
|
5536
|
+
};
|
|
5537
|
+
});
|
|
5538
|
+
dbCache.set(dbName, { version: dbVersion, promise: dbPromise });
|
|
5539
|
+
refCounts.set(dbName, 1);
|
|
5540
|
+
return dbPromise;
|
|
5541
|
+
}
|
|
5542
|
+
async function closeDatabase(dbName) {
|
|
5543
|
+
const cachedEntry = dbCache.get(dbName);
|
|
5544
|
+
if (!cachedEntry) return false;
|
|
5545
|
+
const count = (refCounts.get(dbName) ?? 1) - 1;
|
|
5546
|
+
if (count > 0) {
|
|
5547
|
+
refCounts.set(dbName, count);
|
|
5548
|
+
return false;
|
|
5549
|
+
}
|
|
5550
|
+
refCounts.delete(dbName);
|
|
5551
|
+
dbCache.delete(dbName);
|
|
5552
|
+
try {
|
|
5553
|
+
const db = await cachedEntry.promise;
|
|
5554
|
+
db.close();
|
|
5555
|
+
} catch {
|
|
5556
|
+
}
|
|
5557
|
+
return true;
|
|
5558
|
+
}
|
|
5559
|
+
function scriptFromArkAddress(address) {
|
|
5560
|
+
return hex.encode(ArkAddress.decode(address).pkScript);
|
|
5561
|
+
}
|
|
5562
|
+
|
|
5563
|
+
// src/repositories/indexedDB/schema.ts
|
|
5564
|
+
var STORE_VTXOS = "vtxos";
|
|
5565
|
+
var STORE_UTXOS = "utxos";
|
|
5566
|
+
var STORE_TRANSACTIONS = "transactions";
|
|
5567
|
+
var STORE_WALLET_STATE = "walletState";
|
|
5568
|
+
var STORE_CONTRACTS = "contracts";
|
|
5569
|
+
var STORE_INTENTS = "intents";
|
|
5570
|
+
var STORE_VIRTUAL_TXS = "virtualTxs";
|
|
5571
|
+
var STORE_VTXO_BRANCHES = "vtxoBranches";
|
|
5572
|
+
var LEGACY_STORE_CONTRACT_COLLECTIONS = "contractsCollections";
|
|
5573
|
+
var DB_VERSION = 3;
|
|
5574
|
+
var INTENT_DB_VERSION = 5;
|
|
5575
|
+
function initDatabase(db, oldVersion, transaction) {
|
|
5456
5576
|
if (!db.objectStoreNames.contains(STORE_VTXOS)) {
|
|
5457
5577
|
const vtxosStore = db.createObjectStore(STORE_VTXOS, {
|
|
5458
5578
|
keyPath: ["address", "txid", "vout"]
|
|
@@ -5590,33 +5710,506 @@ function initDatabase(db, oldVersion, transaction) {
|
|
|
5590
5710
|
});
|
|
5591
5711
|
}
|
|
5592
5712
|
}
|
|
5593
|
-
if (!db.objectStoreNames.contains(LEGACY_STORE_CONTRACT_COLLECTIONS)) {
|
|
5594
|
-
db.createObjectStore(LEGACY_STORE_CONTRACT_COLLECTIONS, {
|
|
5595
|
-
keyPath: "key"
|
|
5596
|
-
});
|
|
5713
|
+
if (!db.objectStoreNames.contains(LEGACY_STORE_CONTRACT_COLLECTIONS)) {
|
|
5714
|
+
db.createObjectStore(LEGACY_STORE_CONTRACT_COLLECTIONS, {
|
|
5715
|
+
keyPath: "key"
|
|
5716
|
+
});
|
|
5717
|
+
}
|
|
5718
|
+
if (oldVersion >= 1 && oldVersion < 3 && transaction) {
|
|
5719
|
+
const vtxosStore = transaction.objectStore(STORE_VTXOS);
|
|
5720
|
+
if (!vtxosStore.indexNames.contains("script")) {
|
|
5721
|
+
vtxosStore.createIndex("script", "script", { unique: false });
|
|
5722
|
+
}
|
|
5723
|
+
backfillVtxoScripts(transaction);
|
|
5724
|
+
}
|
|
5725
|
+
}
|
|
5726
|
+
function initDatabaseWithIntents(db, oldVersion, transaction) {
|
|
5727
|
+
initDatabase(db, oldVersion, transaction);
|
|
5728
|
+
if (!db.objectStoreNames.contains(STORE_INTENTS)) {
|
|
5729
|
+
const intentsStore = db.createObjectStore(STORE_INTENTS, {
|
|
5730
|
+
keyPath: "intentTxId"
|
|
5731
|
+
});
|
|
5732
|
+
intentsStore.createIndex("intentId", "intentId", { unique: true });
|
|
5733
|
+
intentsStore.createIndex("state", "state", { unique: false });
|
|
5734
|
+
}
|
|
5735
|
+
if (!db.objectStoreNames.contains(STORE_VIRTUAL_TXS)) {
|
|
5736
|
+
db.createObjectStore(STORE_VIRTUAL_TXS, { keyPath: "txid" });
|
|
5737
|
+
}
|
|
5738
|
+
if (!db.objectStoreNames.contains(STORE_VTXO_BRANCHES)) {
|
|
5739
|
+
const branchesStore = db.createObjectStore(STORE_VTXO_BRANCHES, {
|
|
5740
|
+
keyPath: ["vtxoTxid", "vtxoVout", "position"]
|
|
5741
|
+
});
|
|
5742
|
+
branchesStore.createIndex("vtxo", ["vtxoTxid", "vtxoVout"], { unique: false });
|
|
5743
|
+
branchesStore.createIndex("virtualTxid", "virtualTxid", { unique: false });
|
|
5744
|
+
}
|
|
5745
|
+
if (oldVersion === 4 && transaction) {
|
|
5746
|
+
const intentsStore = transaction.objectStore(STORE_INTENTS);
|
|
5747
|
+
if (intentsStore.indexNames.contains("intentId")) {
|
|
5748
|
+
intentsStore.deleteIndex("intentId");
|
|
5749
|
+
}
|
|
5750
|
+
dedupeIntentIds(intentsStore, () => {
|
|
5751
|
+
intentsStore.createIndex("intentId", "intentId", { unique: true });
|
|
5752
|
+
});
|
|
5753
|
+
}
|
|
5754
|
+
}
|
|
5755
|
+
function dedupeIntentIds(store, onComplete) {
|
|
5756
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5757
|
+
const cursorRequest = store.openCursor();
|
|
5758
|
+
cursorRequest.onsuccess = () => {
|
|
5759
|
+
const cursor = cursorRequest.result;
|
|
5760
|
+
if (!cursor) {
|
|
5761
|
+
onComplete();
|
|
5762
|
+
return;
|
|
5763
|
+
}
|
|
5764
|
+
const id = cursor.value.intentId;
|
|
5765
|
+
if (id != null) {
|
|
5766
|
+
if (seen.has(id)) {
|
|
5767
|
+
cursor.delete();
|
|
5768
|
+
} else {
|
|
5769
|
+
seen.add(id);
|
|
5770
|
+
}
|
|
5771
|
+
}
|
|
5772
|
+
cursor.continue();
|
|
5773
|
+
};
|
|
5774
|
+
}
|
|
5775
|
+
function backfillVtxoScripts(transaction) {
|
|
5776
|
+
const store = transaction.objectStore(STORE_VTXOS);
|
|
5777
|
+
const cursorRequest = store.openCursor();
|
|
5778
|
+
cursorRequest.onsuccess = () => {
|
|
5779
|
+
const cursor = cursorRequest.result;
|
|
5780
|
+
if (!cursor) return;
|
|
5781
|
+
const value = cursor.value;
|
|
5782
|
+
if (!value.script) {
|
|
5783
|
+
value.script = scriptFromArkAddress(value.address);
|
|
5784
|
+
cursor.update(value);
|
|
5785
|
+
}
|
|
5786
|
+
cursor.continue();
|
|
5787
|
+
};
|
|
5788
|
+
}
|
|
5789
|
+
|
|
5790
|
+
// src/worker/browser/utils.ts
|
|
5791
|
+
var DEFAULT_DB_NAME = "arkade-service-worker";
|
|
5792
|
+
var DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS = 1e4;
|
|
5793
|
+
function normalizeOptions2(pathOrOptions) {
|
|
5794
|
+
if (typeof pathOrOptions === "string") {
|
|
5795
|
+
return {
|
|
5796
|
+
path: pathOrOptions,
|
|
5797
|
+
activationTimeoutMs: DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS
|
|
5798
|
+
};
|
|
5799
|
+
}
|
|
5800
|
+
return {
|
|
5801
|
+
path: pathOrOptions.path,
|
|
5802
|
+
activationTimeoutMs: pathOrOptions.activationTimeoutMs ?? DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS
|
|
5803
|
+
};
|
|
5804
|
+
}
|
|
5805
|
+
function waitForServiceWorkerReady(timeoutMs) {
|
|
5806
|
+
return new Promise((resolve, reject) => {
|
|
5807
|
+
const timeoutId = setTimeout(() => {
|
|
5808
|
+
reject(new Error(`Service worker activation timed out after ${timeoutMs}ms`));
|
|
5809
|
+
}, timeoutMs);
|
|
5810
|
+
navigator.serviceWorker.ready.then((registration) => {
|
|
5811
|
+
clearTimeout(timeoutId);
|
|
5812
|
+
resolve(registration);
|
|
5813
|
+
}).catch((error) => {
|
|
5814
|
+
clearTimeout(timeoutId);
|
|
5815
|
+
reject(error);
|
|
5816
|
+
});
|
|
5817
|
+
});
|
|
5818
|
+
}
|
|
5819
|
+
async function setupServiceWorker(pathOrOptions) {
|
|
5820
|
+
if (!("serviceWorker" in navigator)) {
|
|
5821
|
+
throw new Error("Service workers are not supported in this browser");
|
|
5822
|
+
}
|
|
5823
|
+
const { path, activationTimeoutMs } = normalizeOptions2(pathOrOptions);
|
|
5824
|
+
const registration = await navigator.serviceWorker.register(path);
|
|
5825
|
+
await registration.update();
|
|
5826
|
+
const serviceWorker = registration.active || registration.waiting || registration.installing;
|
|
5827
|
+
if (!serviceWorker) {
|
|
5828
|
+
throw new Error("Failed to get service worker instance");
|
|
5829
|
+
}
|
|
5830
|
+
if (serviceWorker.state === "activated") {
|
|
5831
|
+
return serviceWorker;
|
|
5832
|
+
}
|
|
5833
|
+
const readyRegistration = await waitForServiceWorkerReady(activationTimeoutMs);
|
|
5834
|
+
if (!readyRegistration.active) {
|
|
5835
|
+
throw new Error("Service worker registration is ready but has no active worker");
|
|
5836
|
+
}
|
|
5837
|
+
return readyRegistration.active;
|
|
5838
|
+
}
|
|
5839
|
+
|
|
5840
|
+
// src/repositories/indexedDB/virtualTxRepository.ts
|
|
5841
|
+
var IndexedDBVirtualTxRepository = class {
|
|
5842
|
+
constructor(dbName = DEFAULT_DB_NAME) {
|
|
5843
|
+
this.dbName = dbName;
|
|
5844
|
+
}
|
|
5845
|
+
version = 1;
|
|
5846
|
+
db = null;
|
|
5847
|
+
async getDB() {
|
|
5848
|
+
if (!this.db)
|
|
5849
|
+
this.db = await openDatabase(this.dbName, INTENT_DB_VERSION, initDatabaseWithIntents);
|
|
5850
|
+
return this.db;
|
|
5851
|
+
}
|
|
5852
|
+
async clear() {
|
|
5853
|
+
const db = await this.getDB();
|
|
5854
|
+
const transaction = db.transaction([STORE_VIRTUAL_TXS, STORE_VTXO_BRANCHES], "readwrite");
|
|
5855
|
+
transaction.objectStore(STORE_VIRTUAL_TXS).clear();
|
|
5856
|
+
transaction.objectStore(STORE_VTXO_BRANCHES).clear();
|
|
5857
|
+
await awaitTransaction(transaction);
|
|
5858
|
+
}
|
|
5859
|
+
async upsertVirtualTxs(txs) {
|
|
5860
|
+
if (txs.length === 0) return;
|
|
5861
|
+
const db = await this.getDB();
|
|
5862
|
+
const merged = /* @__PURE__ */ new Map();
|
|
5863
|
+
for (const tx of txs) {
|
|
5864
|
+
const acc = merged.get(tx.txid);
|
|
5865
|
+
merged.set(
|
|
5866
|
+
tx.txid,
|
|
5867
|
+
acc ? {
|
|
5868
|
+
txid: tx.txid,
|
|
5869
|
+
psbt: tx.psbt ?? acc.psbt,
|
|
5870
|
+
expiresAt: tx.expiresAt ?? acc.expiresAt,
|
|
5871
|
+
type: mergeChainedTxType(tx.type, acc.type)
|
|
5872
|
+
} : tx
|
|
5873
|
+
);
|
|
5874
|
+
}
|
|
5875
|
+
const transaction = db.transaction([STORE_VIRTUAL_TXS], "readwrite");
|
|
5876
|
+
const store = transaction.objectStore(STORE_VIRTUAL_TXS);
|
|
5877
|
+
for (const tx of merged.values()) {
|
|
5878
|
+
const getReq = store.get(tx.txid);
|
|
5879
|
+
getReq.onsuccess = () => {
|
|
5880
|
+
const prev = getReq.result;
|
|
5881
|
+
store.put({
|
|
5882
|
+
txid: tx.txid,
|
|
5883
|
+
psbt: tx.psbt ?? prev?.psbt ?? null,
|
|
5884
|
+
expiresAt: tx.expiresAt ?? prev?.expiresAt ?? null,
|
|
5885
|
+
type: mergeChainedTxType(tx.type, prev?.type)
|
|
5886
|
+
});
|
|
5887
|
+
};
|
|
5888
|
+
}
|
|
5889
|
+
await awaitTransaction(transaction);
|
|
5890
|
+
}
|
|
5891
|
+
async getVirtualTx(txid) {
|
|
5892
|
+
const db = await this.getDB();
|
|
5893
|
+
const store = db.transaction([STORE_VIRTUAL_TXS], "readonly").objectStore(STORE_VIRTUAL_TXS);
|
|
5894
|
+
const r = await promisifyRequest(store.get(txid));
|
|
5895
|
+
return r ?? null;
|
|
5896
|
+
}
|
|
5897
|
+
async setBranch(vtxo, branch) {
|
|
5898
|
+
const db = await this.getDB();
|
|
5899
|
+
const transaction = db.transaction([STORE_VTXO_BRANCHES], "readwrite");
|
|
5900
|
+
const store = transaction.objectStore(STORE_VTXO_BRANCHES);
|
|
5901
|
+
const getAllReq = store.index("vtxo").getAll(IDBKeyRange.only([vtxo.txid, vtxo.vout]));
|
|
5902
|
+
getAllReq.onsuccess = () => {
|
|
5903
|
+
const existing = getAllReq.result;
|
|
5904
|
+
for (const e of existing) store.delete([e.vtxoTxid, e.vtxoVout, e.position]);
|
|
5905
|
+
for (const b of branch) store.put(b);
|
|
5906
|
+
};
|
|
5907
|
+
await awaitTransaction(transaction);
|
|
5908
|
+
}
|
|
5909
|
+
async getBranch(vtxo) {
|
|
5910
|
+
const db = await this.getDB();
|
|
5911
|
+
const transaction = db.transaction([STORE_VTXO_BRANCHES, STORE_VIRTUAL_TXS], "readonly");
|
|
5912
|
+
const branchStore = transaction.objectStore(STORE_VTXO_BRANCHES);
|
|
5913
|
+
const txStore = transaction.objectStore(STORE_VIRTUAL_TXS);
|
|
5914
|
+
const getAllReq = branchStore.index("vtxo").getAll(IDBKeyRange.only([vtxo.txid, vtxo.vout]));
|
|
5915
|
+
const byTxid = /* @__PURE__ */ new Map();
|
|
5916
|
+
let ordered = [];
|
|
5917
|
+
getAllReq.onsuccess = () => {
|
|
5918
|
+
ordered = getAllReq.result.slice().sort((a, b) => a.position - b.position);
|
|
5919
|
+
for (const b of ordered) {
|
|
5920
|
+
const txReq = txStore.get(b.virtualTxid);
|
|
5921
|
+
txReq.onsuccess = () => {
|
|
5922
|
+
const r = txReq.result;
|
|
5923
|
+
if (r) byTxid.set(b.virtualTxid, r);
|
|
5924
|
+
};
|
|
5925
|
+
}
|
|
5926
|
+
};
|
|
5927
|
+
await awaitTransaction(transaction);
|
|
5928
|
+
const out = [];
|
|
5929
|
+
for (const b of ordered) {
|
|
5930
|
+
const tx = byTxid.get(b.virtualTxid);
|
|
5931
|
+
if (tx) out.push(tx);
|
|
5932
|
+
}
|
|
5933
|
+
return out;
|
|
5934
|
+
}
|
|
5935
|
+
async hasBranch(vtxo) {
|
|
5936
|
+
const db = await this.getDB();
|
|
5937
|
+
const vtxoIndex = db.transaction([STORE_VTXO_BRANCHES], "readonly").objectStore(STORE_VTXO_BRANCHES).index("vtxo");
|
|
5938
|
+
const count = await promisifyRequest(
|
|
5939
|
+
vtxoIndex.count(IDBKeyRange.only([vtxo.txid, vtxo.vout]))
|
|
5940
|
+
);
|
|
5941
|
+
return count > 0;
|
|
5942
|
+
}
|
|
5943
|
+
async pruneForSpentVtxo(vtxo) {
|
|
5944
|
+
const db = await this.getDB();
|
|
5945
|
+
const transaction = db.transaction([STORE_VTXO_BRANCHES, STORE_VIRTUAL_TXS], "readwrite");
|
|
5946
|
+
const branches = transaction.objectStore(STORE_VTXO_BRANCHES);
|
|
5947
|
+
const txStore = transaction.objectStore(STORE_VIRTUAL_TXS);
|
|
5948
|
+
const getAllReq = branches.index("vtxo").getAll(IDBKeyRange.only([vtxo.txid, vtxo.vout]));
|
|
5949
|
+
getAllReq.onsuccess = () => {
|
|
5950
|
+
const removed = getAllReq.result;
|
|
5951
|
+
if (removed.length === 0) return;
|
|
5952
|
+
for (const e of removed) branches.delete([e.vtxoTxid, e.vtxoVout, e.position]);
|
|
5953
|
+
for (const e of removed) {
|
|
5954
|
+
const countReq = branches.index("virtualTxid").count(IDBKeyRange.only(e.virtualTxid));
|
|
5955
|
+
countReq.onsuccess = () => {
|
|
5956
|
+
if (countReq.result === 0) txStore.delete(e.virtualTxid);
|
|
5957
|
+
};
|
|
5958
|
+
}
|
|
5959
|
+
};
|
|
5960
|
+
await awaitTransaction(transaction);
|
|
5961
|
+
}
|
|
5962
|
+
async [Symbol.asyncDispose]() {
|
|
5963
|
+
if (!this.db) return;
|
|
5964
|
+
await closeDatabase(this.dbName);
|
|
5965
|
+
this.db = null;
|
|
5966
|
+
}
|
|
5967
|
+
};
|
|
5968
|
+
|
|
5969
|
+
// src/repositories/indexedDB/intentRepository.ts
|
|
5970
|
+
var IndexedDBIntentRepository = class {
|
|
5971
|
+
constructor(dbName = DEFAULT_DB_NAME) {
|
|
5972
|
+
this.dbName = dbName;
|
|
5973
|
+
}
|
|
5974
|
+
version = 1;
|
|
5975
|
+
db = null;
|
|
5976
|
+
async getDB() {
|
|
5977
|
+
if (!this.db)
|
|
5978
|
+
this.db = await openDatabase(this.dbName, INTENT_DB_VERSION, initDatabaseWithIntents);
|
|
5979
|
+
return this.db;
|
|
5980
|
+
}
|
|
5981
|
+
async clear() {
|
|
5982
|
+
const db = await this.getDB();
|
|
5983
|
+
const transaction = db.transaction([STORE_INTENTS], "readwrite");
|
|
5984
|
+
transaction.objectStore(STORE_INTENTS).clear();
|
|
5985
|
+
await awaitTransaction(transaction);
|
|
5986
|
+
}
|
|
5987
|
+
async saveIntent(intent) {
|
|
5988
|
+
const db = await this.getDB();
|
|
5989
|
+
const transaction = db.transaction([STORE_INTENTS], "readwrite");
|
|
5990
|
+
transaction.objectStore(STORE_INTENTS).put({ ...intent, updatedAt: Date.now() });
|
|
5991
|
+
await awaitTransaction(transaction);
|
|
5992
|
+
}
|
|
5993
|
+
async getIntents(filter) {
|
|
5994
|
+
const db = await this.getDB();
|
|
5995
|
+
const store = db.transaction([STORE_INTENTS], "readonly").objectStore(STORE_INTENTS);
|
|
5996
|
+
const all = await promisifyRequest(store.getAll());
|
|
5997
|
+
all.sort((a, b) => a.createdAt - b.createdAt || a.intentTxId.localeCompare(b.intentTxId));
|
|
5998
|
+
const out = filter ? all.filter((i) => intentMatchesFilter(i, filter)) : all;
|
|
5999
|
+
const { skip, end } = intentPageBounds(filter, out.length);
|
|
6000
|
+
return out.slice(skip, end);
|
|
6001
|
+
}
|
|
6002
|
+
async getLockedVtxoOutpoints() {
|
|
6003
|
+
const db = await this.getDB();
|
|
6004
|
+
const store = db.transaction([STORE_INTENTS], "readonly").objectStore(STORE_INTENTS);
|
|
6005
|
+
const all = await promisifyRequest(store.getAll());
|
|
6006
|
+
const out = [];
|
|
6007
|
+
for (const i of all)
|
|
6008
|
+
if (!isTerminalIntentState(i.state)) for (const o of i.intentVtxos) out.push(o);
|
|
6009
|
+
return out;
|
|
6010
|
+
}
|
|
6011
|
+
async [Symbol.asyncDispose]() {
|
|
6012
|
+
if (!this.db) return;
|
|
6013
|
+
await closeDatabase(this.dbName);
|
|
6014
|
+
this.db = null;
|
|
6015
|
+
}
|
|
6016
|
+
};
|
|
6017
|
+
|
|
6018
|
+
// src/contracts/vtxoOwnership.ts
|
|
6019
|
+
function vtxoOutpoint(vtxo) {
|
|
6020
|
+
return `${vtxo.txid}:${vtxo.vout}`;
|
|
6021
|
+
}
|
|
6022
|
+
function isVtxoForScript(vtxo, script) {
|
|
6023
|
+
return !!vtxo.script && vtxo.script === script;
|
|
6024
|
+
}
|
|
6025
|
+
function filterVtxosForScript(vtxos, script) {
|
|
6026
|
+
return vtxos.filter((v) => isVtxoForScript(v, script));
|
|
6027
|
+
}
|
|
6028
|
+
function warnAndFilterVtxosForScript(vtxos, script, context) {
|
|
6029
|
+
const matches = [];
|
|
6030
|
+
const rejected = [];
|
|
6031
|
+
for (const v of vtxos) {
|
|
6032
|
+
if (isVtxoForScript(v, script)) {
|
|
6033
|
+
matches.push(v);
|
|
6034
|
+
} else {
|
|
6035
|
+
rejected.push(`${vtxoOutpoint(v)}(script=${v.script ?? ""})`);
|
|
6036
|
+
}
|
|
6037
|
+
}
|
|
6038
|
+
if (rejected.length > 0) {
|
|
6039
|
+
console.warn(
|
|
6040
|
+
`${context}: dropped ${rejected.length} wrong-script VTXO(s) for script ${script}: ${rejected.join(", ")}`
|
|
6041
|
+
);
|
|
6042
|
+
}
|
|
6043
|
+
return matches;
|
|
6044
|
+
}
|
|
6045
|
+
function validateVtxosForScript(vtxos, script, context) {
|
|
6046
|
+
const mismatches = vtxos.filter((v) => !isVtxoForScript(v, script));
|
|
6047
|
+
if (mismatches.length === 0) return;
|
|
6048
|
+
const detail = mismatches.map((v) => `${vtxoOutpoint(v)}(script=${v.script ?? ""})`).join(", ");
|
|
6049
|
+
throw new Error(
|
|
6050
|
+
`${context}: refusing to persist ${mismatches.length} VTXO(s) whose script does not match ${script}: ${detail}`
|
|
6051
|
+
);
|
|
6052
|
+
}
|
|
6053
|
+
async function getVtxosForContract(repo, contract) {
|
|
6054
|
+
return repo.getVtxosForScript ? repo.getVtxosForScript(contract.script) : filterVtxosForScript(await repo.getVtxos(contract.address), contract.script);
|
|
6055
|
+
}
|
|
6056
|
+
async function saveVtxosForContract(repo, contract, vtxos) {
|
|
6057
|
+
if (repo.saveVtxosForScript) {
|
|
6058
|
+
return repo.saveVtxosForScript(
|
|
6059
|
+
{ script: contract.script, address: contract.address },
|
|
6060
|
+
vtxos
|
|
6061
|
+
);
|
|
6062
|
+
}
|
|
6063
|
+
validateVtxosForScript(vtxos, contract.script, "saveVtxosForContract");
|
|
6064
|
+
return repo.saveVtxos(contract.address, vtxos);
|
|
6065
|
+
}
|
|
6066
|
+
|
|
6067
|
+
// src/repositories/inMemory/walletRepository.ts
|
|
6068
|
+
var InMemoryWalletRepository = class {
|
|
6069
|
+
version = 1;
|
|
6070
|
+
vtxosByAddress = /* @__PURE__ */ new Map();
|
|
6071
|
+
utxosByAddress = /* @__PURE__ */ new Map();
|
|
6072
|
+
txsByAddress = /* @__PURE__ */ new Map();
|
|
6073
|
+
walletState = null;
|
|
6074
|
+
async getVtxos(address) {
|
|
6075
|
+
return this.vtxosByAddress.get(address) ?? [];
|
|
6076
|
+
}
|
|
6077
|
+
async saveVtxos(address, vtxos) {
|
|
6078
|
+
const existing = this.vtxosByAddress.get(address) ?? [];
|
|
6079
|
+
const next = mergeByKey(existing, vtxos, (item) => `${item.txid}:${item.vout}`);
|
|
6080
|
+
this.vtxosByAddress.set(address, next);
|
|
6081
|
+
}
|
|
6082
|
+
async deleteVtxos(address) {
|
|
6083
|
+
this.vtxosByAddress.delete(address);
|
|
6084
|
+
}
|
|
6085
|
+
async getVtxosForScript(script) {
|
|
6086
|
+
const allMatches = [];
|
|
6087
|
+
for (const bucket of this.vtxosByAddress.values()) {
|
|
6088
|
+
for (const vtxo of bucket) {
|
|
6089
|
+
if (isVtxoForScript(vtxo, script)) {
|
|
6090
|
+
allMatches.push(vtxo);
|
|
6091
|
+
}
|
|
6092
|
+
}
|
|
6093
|
+
}
|
|
6094
|
+
return mergeByKey([], allMatches, (item) => `${item.txid}:${item.vout}`);
|
|
6095
|
+
}
|
|
6096
|
+
async saveVtxosForScript(key, vtxos) {
|
|
6097
|
+
if (!key.address) {
|
|
6098
|
+
throw new Error("InMemoryWalletRepository requires an address");
|
|
6099
|
+
}
|
|
6100
|
+
for (const vtxo of vtxos) {
|
|
6101
|
+
if (!isVtxoForScript(vtxo, key.script)) {
|
|
6102
|
+
throw new Error(
|
|
6103
|
+
`VTXO ${vtxo.txid}:${vtxo.vout} script mismatch: expected ${key.script}, got ${vtxo.script}`
|
|
6104
|
+
);
|
|
6105
|
+
}
|
|
6106
|
+
}
|
|
6107
|
+
return this.saveVtxos(key.address, vtxos);
|
|
6108
|
+
}
|
|
6109
|
+
async deleteVtxosForScript(script) {
|
|
6110
|
+
for (const [address, bucket] of this.vtxosByAddress.entries()) {
|
|
6111
|
+
const next = bucket.filter((v) => !isVtxoForScript(v, script));
|
|
6112
|
+
if (next.length === 0) {
|
|
6113
|
+
this.vtxosByAddress.delete(address);
|
|
6114
|
+
} else {
|
|
6115
|
+
this.vtxosByAddress.set(address, next);
|
|
6116
|
+
}
|
|
6117
|
+
}
|
|
6118
|
+
}
|
|
6119
|
+
async getUtxos(address) {
|
|
6120
|
+
return this.utxosByAddress.get(address) ?? [];
|
|
6121
|
+
}
|
|
6122
|
+
async saveUtxos(address, utxos) {
|
|
6123
|
+
const existing = this.utxosByAddress.get(address) ?? [];
|
|
6124
|
+
const next = mergeByKey(existing, utxos, (item) => `${item.txid}:${item.vout}`);
|
|
6125
|
+
this.utxosByAddress.set(address, next);
|
|
6126
|
+
}
|
|
6127
|
+
async deleteUtxos(address) {
|
|
6128
|
+
this.utxosByAddress.delete(address);
|
|
6129
|
+
}
|
|
6130
|
+
async getTransactionHistory(address) {
|
|
6131
|
+
return this.txsByAddress.get(address) ?? [];
|
|
6132
|
+
}
|
|
6133
|
+
async saveTransactions(address, txs) {
|
|
6134
|
+
const existing = this.txsByAddress.get(address) ?? [];
|
|
6135
|
+
const next = mergeByKey(existing, txs, serializeTxKey);
|
|
6136
|
+
this.txsByAddress.set(address, next);
|
|
6137
|
+
}
|
|
6138
|
+
async deleteTransactions(address) {
|
|
6139
|
+
this.txsByAddress.delete(address);
|
|
6140
|
+
}
|
|
6141
|
+
async getWalletState() {
|
|
6142
|
+
return this.walletState;
|
|
6143
|
+
}
|
|
6144
|
+
async saveWalletState(state) {
|
|
6145
|
+
this.walletState = state;
|
|
6146
|
+
}
|
|
6147
|
+
async clear() {
|
|
6148
|
+
this.vtxosByAddress.clear();
|
|
6149
|
+
this.utxosByAddress.clear();
|
|
6150
|
+
this.txsByAddress.clear();
|
|
6151
|
+
this.walletState = null;
|
|
6152
|
+
}
|
|
6153
|
+
async [Symbol.asyncDispose]() {
|
|
6154
|
+
return;
|
|
6155
|
+
}
|
|
6156
|
+
};
|
|
6157
|
+
function serializeTxKey(tx) {
|
|
6158
|
+
const key = tx.key;
|
|
6159
|
+
return `${key.boardingTxid}:${key.commitmentTxid}:${key.arkTxid}`;
|
|
6160
|
+
}
|
|
6161
|
+
function mergeByKey(existing, incoming, toKey) {
|
|
6162
|
+
const next = /* @__PURE__ */ new Map();
|
|
6163
|
+
existing.forEach((item) => {
|
|
6164
|
+
next.set(toKey(item), item);
|
|
6165
|
+
});
|
|
6166
|
+
incoming.forEach((item) => {
|
|
6167
|
+
next.set(toKey(item), item);
|
|
6168
|
+
});
|
|
6169
|
+
return Array.from(next.values());
|
|
6170
|
+
}
|
|
6171
|
+
|
|
6172
|
+
// src/repositories/inMemory/contractRepository.ts
|
|
6173
|
+
var InMemoryContractRepository = class {
|
|
6174
|
+
version = 1;
|
|
6175
|
+
contractData = /* @__PURE__ */ new Map();
|
|
6176
|
+
collections = /* @__PURE__ */ new Map();
|
|
6177
|
+
contractsByScript = /* @__PURE__ */ new Map();
|
|
6178
|
+
async clear() {
|
|
6179
|
+
this.contractData.clear();
|
|
6180
|
+
this.collections.clear();
|
|
6181
|
+
this.contractsByScript.clear();
|
|
6182
|
+
}
|
|
6183
|
+
// Contract entity management methods
|
|
6184
|
+
async getContracts(filter) {
|
|
6185
|
+
const contracts = this.contractsByScript.values();
|
|
6186
|
+
if (!filter) {
|
|
6187
|
+
return [...contracts];
|
|
6188
|
+
}
|
|
6189
|
+
const matches = (value, criterion) => {
|
|
6190
|
+
if (criterion === void 0) {
|
|
6191
|
+
return true;
|
|
6192
|
+
}
|
|
6193
|
+
return Array.isArray(criterion) ? criterion.includes(value) : value === criterion;
|
|
6194
|
+
};
|
|
6195
|
+
const results = [];
|
|
6196
|
+
for (const contract of contracts) {
|
|
6197
|
+
if (matches(contract.script, filter.script) && matches(contract.state, filter.state) && matches(contract.type, filter.type)) {
|
|
6198
|
+
results.push(contract);
|
|
6199
|
+
}
|
|
6200
|
+
}
|
|
6201
|
+
return results;
|
|
6202
|
+
}
|
|
6203
|
+
async saveContract(contract) {
|
|
6204
|
+
this.contractsByScript.set(contract.script, contract);
|
|
5597
6205
|
}
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
if (!vtxosStore.indexNames.contains("script")) {
|
|
5601
|
-
vtxosStore.createIndex("script", "script", { unique: false });
|
|
5602
|
-
}
|
|
5603
|
-
backfillVtxoScripts(transaction);
|
|
6206
|
+
async deleteContract(script) {
|
|
6207
|
+
this.contractsByScript.delete(script);
|
|
5604
6208
|
}
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
cursorRequest.onsuccess = () => {
|
|
5610
|
-
const cursor = cursorRequest.result;
|
|
5611
|
-
if (!cursor) return;
|
|
5612
|
-
const value = cursor.value;
|
|
5613
|
-
if (!value.script) {
|
|
5614
|
-
value.script = scriptFromArkAddress(value.address);
|
|
5615
|
-
cursor.update(value);
|
|
5616
|
-
}
|
|
5617
|
-
cursor.continue();
|
|
5618
|
-
};
|
|
5619
|
-
}
|
|
6209
|
+
async [Symbol.asyncDispose]() {
|
|
6210
|
+
return;
|
|
6211
|
+
}
|
|
6212
|
+
};
|
|
5620
6213
|
var serializeTapLeaf = ([cb, s]) => ({
|
|
5621
6214
|
cb: hex.encode(TaprootControlBlock.encode(cb)),
|
|
5622
6215
|
s: hex.encode(s)
|
|
@@ -5683,132 +6276,6 @@ var deserializeTransaction = (o) => ({
|
|
|
5683
6276
|
assets: deserializeAssets(o.assets)
|
|
5684
6277
|
});
|
|
5685
6278
|
|
|
5686
|
-
// src/repositories/indexedDB/manager.ts
|
|
5687
|
-
function getGlobalObject() {
|
|
5688
|
-
if (typeof globalThis !== "undefined") {
|
|
5689
|
-
if (typeof globalThis.self === "object" && globalThis.self !== null) {
|
|
5690
|
-
return { globalObject: globalThis.self };
|
|
5691
|
-
}
|
|
5692
|
-
if (typeof globalThis.window === "object" && globalThis.window !== null) {
|
|
5693
|
-
return { globalObject: globalThis.window };
|
|
5694
|
-
}
|
|
5695
|
-
return { globalObject: globalThis };
|
|
5696
|
-
}
|
|
5697
|
-
throw new Error("Global object not found");
|
|
5698
|
-
}
|
|
5699
|
-
var dbCache = /* @__PURE__ */ new Map();
|
|
5700
|
-
var refCounts = /* @__PURE__ */ new Map();
|
|
5701
|
-
async function openDatabase(dbName, dbVersion, initDatabase2) {
|
|
5702
|
-
const { globalObject } = getGlobalObject();
|
|
5703
|
-
if (!globalObject.indexedDB) {
|
|
5704
|
-
throw new Error("IndexedDB is not available in this environment");
|
|
5705
|
-
}
|
|
5706
|
-
const cached = dbCache.get(dbName);
|
|
5707
|
-
if (cached) {
|
|
5708
|
-
if (cached.version !== dbVersion) {
|
|
5709
|
-
throw new Error(
|
|
5710
|
-
`Database "${dbName}" already opened with version ${cached.version}; requested ${dbVersion}`
|
|
5711
|
-
);
|
|
5712
|
-
}
|
|
5713
|
-
refCounts.set(dbName, (refCounts.get(dbName) ?? 0) + 1);
|
|
5714
|
-
return cached.promise;
|
|
5715
|
-
}
|
|
5716
|
-
const dbPromise = new Promise((resolve, reject) => {
|
|
5717
|
-
const request = globalObject.indexedDB.open(dbName, dbVersion);
|
|
5718
|
-
request.onerror = () => {
|
|
5719
|
-
dbCache.delete(dbName);
|
|
5720
|
-
refCounts.delete(dbName);
|
|
5721
|
-
reject(request.error);
|
|
5722
|
-
};
|
|
5723
|
-
request.onsuccess = () => {
|
|
5724
|
-
const db = request.result;
|
|
5725
|
-
db.onversionchange = () => {
|
|
5726
|
-
db.close();
|
|
5727
|
-
dbCache.delete(dbName);
|
|
5728
|
-
refCounts.delete(dbName);
|
|
5729
|
-
};
|
|
5730
|
-
resolve(db);
|
|
5731
|
-
};
|
|
5732
|
-
request.onupgradeneeded = (event) => {
|
|
5733
|
-
const db = request.result;
|
|
5734
|
-
initDatabase2(db, event.oldVersion, request.transaction);
|
|
5735
|
-
};
|
|
5736
|
-
request.onblocked = () => {
|
|
5737
|
-
console.warn("Database upgrade blocked - close other tabs/connections");
|
|
5738
|
-
};
|
|
5739
|
-
});
|
|
5740
|
-
dbCache.set(dbName, { version: dbVersion, promise: dbPromise });
|
|
5741
|
-
refCounts.set(dbName, 1);
|
|
5742
|
-
return dbPromise;
|
|
5743
|
-
}
|
|
5744
|
-
async function closeDatabase(dbName) {
|
|
5745
|
-
const cachedEntry = dbCache.get(dbName);
|
|
5746
|
-
if (!cachedEntry) return false;
|
|
5747
|
-
const count = (refCounts.get(dbName) ?? 1) - 1;
|
|
5748
|
-
if (count > 0) {
|
|
5749
|
-
refCounts.set(dbName, count);
|
|
5750
|
-
return false;
|
|
5751
|
-
}
|
|
5752
|
-
refCounts.delete(dbName);
|
|
5753
|
-
dbCache.delete(dbName);
|
|
5754
|
-
try {
|
|
5755
|
-
const db = await cachedEntry.promise;
|
|
5756
|
-
db.close();
|
|
5757
|
-
} catch {
|
|
5758
|
-
}
|
|
5759
|
-
return true;
|
|
5760
|
-
}
|
|
5761
|
-
|
|
5762
|
-
// src/worker/browser/utils.ts
|
|
5763
|
-
var DEFAULT_DB_NAME = "arkade-service-worker";
|
|
5764
|
-
var DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS = 1e4;
|
|
5765
|
-
function normalizeOptions2(pathOrOptions) {
|
|
5766
|
-
if (typeof pathOrOptions === "string") {
|
|
5767
|
-
return {
|
|
5768
|
-
path: pathOrOptions,
|
|
5769
|
-
activationTimeoutMs: DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS
|
|
5770
|
-
};
|
|
5771
|
-
}
|
|
5772
|
-
return {
|
|
5773
|
-
path: pathOrOptions.path,
|
|
5774
|
-
activationTimeoutMs: pathOrOptions.activationTimeoutMs ?? DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS
|
|
5775
|
-
};
|
|
5776
|
-
}
|
|
5777
|
-
function waitForServiceWorkerReady(timeoutMs) {
|
|
5778
|
-
return new Promise((resolve, reject) => {
|
|
5779
|
-
const timeoutId = setTimeout(() => {
|
|
5780
|
-
reject(new Error(`Service worker activation timed out after ${timeoutMs}ms`));
|
|
5781
|
-
}, timeoutMs);
|
|
5782
|
-
navigator.serviceWorker.ready.then((registration) => {
|
|
5783
|
-
clearTimeout(timeoutId);
|
|
5784
|
-
resolve(registration);
|
|
5785
|
-
}).catch((error) => {
|
|
5786
|
-
clearTimeout(timeoutId);
|
|
5787
|
-
reject(error);
|
|
5788
|
-
});
|
|
5789
|
-
});
|
|
5790
|
-
}
|
|
5791
|
-
async function setupServiceWorker(pathOrOptions) {
|
|
5792
|
-
if (!("serviceWorker" in navigator)) {
|
|
5793
|
-
throw new Error("Service workers are not supported in this browser");
|
|
5794
|
-
}
|
|
5795
|
-
const { path, activationTimeoutMs } = normalizeOptions2(pathOrOptions);
|
|
5796
|
-
const registration = await navigator.serviceWorker.register(path);
|
|
5797
|
-
await registration.update();
|
|
5798
|
-
const serviceWorker = registration.active || registration.waiting || registration.installing;
|
|
5799
|
-
if (!serviceWorker) {
|
|
5800
|
-
throw new Error("Failed to get service worker instance");
|
|
5801
|
-
}
|
|
5802
|
-
if (serviceWorker.state === "activated") {
|
|
5803
|
-
return serviceWorker;
|
|
5804
|
-
}
|
|
5805
|
-
const readyRegistration = await waitForServiceWorkerReady(activationTimeoutMs);
|
|
5806
|
-
if (!readyRegistration.active) {
|
|
5807
|
-
throw new Error("Service worker registration is ready but has no active worker");
|
|
5808
|
-
}
|
|
5809
|
-
return readyRegistration.active;
|
|
5810
|
-
}
|
|
5811
|
-
|
|
5812
6279
|
// src/repositories/indexedDB/contractRepository.ts
|
|
5813
6280
|
var IndexedDBContractRepository = class {
|
|
5814
6281
|
constructor(dbName = DEFAULT_DB_NAME) {
|
|
@@ -7246,6 +7713,89 @@ var ContractWatcher = class {
|
|
|
7246
7713
|
}
|
|
7247
7714
|
};
|
|
7248
7715
|
|
|
7716
|
+
// src/wallet/intentReconciliation.ts
|
|
7717
|
+
var NON_TERMINAL_INTENT_STATES = ALL_INTENT_STATES.filter(
|
|
7718
|
+
(s) => !isTerminalIntentState(s)
|
|
7719
|
+
);
|
|
7720
|
+
function isConsumed(vtxo) {
|
|
7721
|
+
return vtxo !== void 0 && (vtxo.isSpent === true || vtxo.settledBy !== void 0);
|
|
7722
|
+
}
|
|
7723
|
+
async function classifyIntent(intent, vtxosByOutpoint, now) {
|
|
7724
|
+
if (isTerminalIntentState(intent.state)) return void 0;
|
|
7725
|
+
const inputs = intent.intentVtxos;
|
|
7726
|
+
const consumed = inputs.map((o) => vtxosByOutpoint.get(outpointKey(o)));
|
|
7727
|
+
if (inputs.length > 0 && consumed.every(isConsumed)) {
|
|
7728
|
+
const commitmentTransactionId = intent.commitmentTransactionId ?? consumed.map((v) => v?.settledBy).find((s) => !!s);
|
|
7729
|
+
return terminal(intent, "batch_succeeded", now, { commitmentTransactionId });
|
|
7730
|
+
}
|
|
7731
|
+
if (intent.state === "waiting_to_submit") {
|
|
7732
|
+
return terminal(intent, "cancelled", now, {
|
|
7733
|
+
cancellationReason: "reconciliation: intent was never submitted before restart"
|
|
7734
|
+
});
|
|
7735
|
+
}
|
|
7736
|
+
if (intent.validUntil !== void 0 && intent.validUntil < now) {
|
|
7737
|
+
return terminal(intent, "cancelled", now, {
|
|
7738
|
+
cancellationReason: "reconciliation: intent expired"
|
|
7739
|
+
});
|
|
7740
|
+
}
|
|
7741
|
+
return void 0;
|
|
7742
|
+
}
|
|
7743
|
+
async function reconcileIntents(deps) {
|
|
7744
|
+
const now = deps.now ?? Date.now;
|
|
7745
|
+
let intents;
|
|
7746
|
+
try {
|
|
7747
|
+
intents = await deps.intentRepository.getIntents({
|
|
7748
|
+
states: [...NON_TERMINAL_INTENT_STATES]
|
|
7749
|
+
});
|
|
7750
|
+
} catch (e) {
|
|
7751
|
+
console.error("reconcileIntents: failed to read non-terminal intents", e);
|
|
7752
|
+
return;
|
|
7753
|
+
}
|
|
7754
|
+
if (intents.length === 0) return;
|
|
7755
|
+
const outpoints = dedupeOutpoints(intents.flatMap((i) => i.intentVtxos));
|
|
7756
|
+
let vtxosByOutpoint = /* @__PURE__ */ new Map();
|
|
7757
|
+
if (outpoints.length > 0) {
|
|
7758
|
+
try {
|
|
7759
|
+
const { vtxos } = await deps.indexerProvider.getVtxos({ outpoints });
|
|
7760
|
+
vtxosByOutpoint = new Map(vtxos.map((v) => [outpointKey(v), v]));
|
|
7761
|
+
} catch (e) {
|
|
7762
|
+
console.error("reconcileIntents: failed to fetch input VTXO state", e);
|
|
7763
|
+
return;
|
|
7764
|
+
}
|
|
7765
|
+
}
|
|
7766
|
+
const nowMs = now();
|
|
7767
|
+
for (const intent of intents) {
|
|
7768
|
+
try {
|
|
7769
|
+
const next = await classifyIntent(intent, vtxosByOutpoint, nowMs);
|
|
7770
|
+
if (!next) continue;
|
|
7771
|
+
const [fresh] = await deps.intentRepository.getIntents({
|
|
7772
|
+
intentTxIds: [intent.intentTxId]
|
|
7773
|
+
});
|
|
7774
|
+
if (!fresh || fresh.state !== intent.state) continue;
|
|
7775
|
+
await deps.intentRepository.saveIntent(next);
|
|
7776
|
+
} catch (e) {
|
|
7777
|
+
console.error(`reconcileIntents: failed to reconcile intent ${intent.intentTxId}`, e);
|
|
7778
|
+
}
|
|
7779
|
+
}
|
|
7780
|
+
}
|
|
7781
|
+
function terminal(intent, state, now, patch) {
|
|
7782
|
+
return { ...intent, ...patch, state, updatedAt: now };
|
|
7783
|
+
}
|
|
7784
|
+
function outpointKey(o) {
|
|
7785
|
+
return `${o.txid}:${o.vout}`;
|
|
7786
|
+
}
|
|
7787
|
+
function dedupeOutpoints(outpoints) {
|
|
7788
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7789
|
+
const out = [];
|
|
7790
|
+
for (const o of outpoints) {
|
|
7791
|
+
const k = outpointKey(o);
|
|
7792
|
+
if (seen.has(k)) continue;
|
|
7793
|
+
seen.add(k);
|
|
7794
|
+
out.push(o);
|
|
7795
|
+
}
|
|
7796
|
+
return out;
|
|
7797
|
+
}
|
|
7798
|
+
|
|
7249
7799
|
// src/utils/syncCursors.ts
|
|
7250
7800
|
var SAFETY_LAG_MS = 3e4;
|
|
7251
7801
|
var OVERLAP_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -7369,6 +7919,26 @@ var ContractManager = class _ContractManager {
|
|
|
7369
7919
|
if (watched.length > 0) {
|
|
7370
7920
|
await this.reconcilePendingFrontier(watched);
|
|
7371
7921
|
}
|
|
7922
|
+
await this.reconcileStaleIntents();
|
|
7923
|
+
}
|
|
7924
|
+
/**
|
|
7925
|
+
* Crash-recovery for persisted settlement intents: reconcile any
|
|
7926
|
+
* non-terminal intent left behind by a mid-settle crash against
|
|
7927
|
+
* authoritative indexer state (see {@link reconcileIntents}). Runs on the
|
|
7928
|
+
* online sync path only — boot and subscription reconnect — never from a
|
|
7929
|
+
* wallet read API. Best-effort: intent recovery is not a sync invariant, so
|
|
7930
|
+
* a failure is logged and sync continues. No-op without an intent store.
|
|
7931
|
+
*/
|
|
7932
|
+
async reconcileStaleIntents() {
|
|
7933
|
+
if (!this.config.intentRepository) return;
|
|
7934
|
+
try {
|
|
7935
|
+
await reconcileIntents({
|
|
7936
|
+
intentRepository: this.config.intentRepository,
|
|
7937
|
+
indexerProvider: this.config.indexerProvider
|
|
7938
|
+
});
|
|
7939
|
+
} catch (e) {
|
|
7940
|
+
console.error("ContractManager: intent reconciliation failed", e);
|
|
7941
|
+
}
|
|
7372
7942
|
}
|
|
7373
7943
|
/**
|
|
7374
7944
|
* Create and register a new contract.
|
|
@@ -8739,6 +9309,22 @@ async function resolveBoardingBootTapscript(contractRepository, serverPubKey, ba
|
|
|
8739
9309
|
function hasToReadonly(identity) {
|
|
8740
9310
|
return typeof identity === "object" && identity !== null && "toReadonly" in identity && typeof identity.toReadonly === "function";
|
|
8741
9311
|
}
|
|
9312
|
+
function excludeLockedOutpoints(vtxos, locked) {
|
|
9313
|
+
if (locked.length === 0) return vtxos;
|
|
9314
|
+
const lockedKeys = new Set(locked.map((o) => `${o.txid}:${o.vout}`));
|
|
9315
|
+
return vtxos.filter((v) => !lockedKeys.has(`${v.txid}:${v.vout}`));
|
|
9316
|
+
}
|
|
9317
|
+
async function spendableVtxosExcludingLocked(vtxos, intentRepository) {
|
|
9318
|
+
if (!intentRepository) return vtxos;
|
|
9319
|
+
let locked;
|
|
9320
|
+
try {
|
|
9321
|
+
locked = await intentRepository.getLockedVtxoOutpoints();
|
|
9322
|
+
} catch (e) {
|
|
9323
|
+
console.error("getLockedVtxoOutpoints failed; reporting unfiltered balance", e);
|
|
9324
|
+
return vtxos;
|
|
9325
|
+
}
|
|
9326
|
+
return excludeLockedOutpoints(vtxos, locked);
|
|
9327
|
+
}
|
|
8742
9328
|
var ReadonlyWallet = class _ReadonlyWallet {
|
|
8743
9329
|
constructor(identity, network, onchainProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, dustAmount, walletRepository, contractRepository, delegateProvider, watcherConfig, walletContractTimelocks) {
|
|
8744
9330
|
this.identity = identity;
|
|
@@ -8769,6 +9355,20 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
8769
9355
|
_contractManager;
|
|
8770
9356
|
_contractManagerInitializing;
|
|
8771
9357
|
watcherConfig;
|
|
9358
|
+
/**
|
|
9359
|
+
* Opt-in intent-lifecycle repository. Assigned by the `create()`
|
|
9360
|
+
* factories from `config.storage.intentRepository`; `undefined` ⇒ all
|
|
9361
|
+
* intent-persistence code paths are no-ops (default behaviour unchanged).
|
|
9362
|
+
*/
|
|
9363
|
+
intentRepository;
|
|
9364
|
+
/**
|
|
9365
|
+
* **Experimental / inert.** Opt-in virtual-tx / exit-branch repository,
|
|
9366
|
+
* exposed so callers can pass it to {@link Unroll.Session.create} as a
|
|
9367
|
+
* best-effort raw-tx cache. Assigned by `create()` from
|
|
9368
|
+
* `config.storage.virtualTxRepository`. Normal sync never writes it
|
|
9369
|
+
* (ContractManager isn't given it); `undefined` ⇒ no-op.
|
|
9370
|
+
*/
|
|
9371
|
+
virtualTxRepository;
|
|
8772
9372
|
_assetManager;
|
|
8773
9373
|
walletContractTimelocks;
|
|
8774
9374
|
// Outpoints ("txid:vout") committed to an in-flight settle/send. Filtered
|
|
@@ -8826,9 +9426,12 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
8826
9426
|
_deprecatedSigners = /* @__PURE__ */ new Map();
|
|
8827
9427
|
/**
|
|
8828
9428
|
* Refresh the cached deprecated-signer set from a fresh server-info
|
|
8829
|
-
* snapshot. Called by the create() factories at construction
|
|
8830
|
-
* server-info-change handler mid-session
|
|
8831
|
-
*
|
|
9429
|
+
* snapshot. Called by the create() factories at construction, by the
|
|
9430
|
+
* server-info-change handler mid-session, and by the deprecated-signer
|
|
9431
|
+
* migration pass (`VtxoManager.migrateDeprecatedSignerVtxos`). This set feeds
|
|
9432
|
+
* {@link pendingRecoveryOutpoints}, which drops EXPIRED (past-cutoff)
|
|
9433
|
+
* deprecated-signer VTXOs from the wallet's own coin selection. Lenient: a
|
|
9434
|
+
* malformed deprecated entry is skipped, never fatal to wallet creation.
|
|
8832
9435
|
*/
|
|
8833
9436
|
refreshDeprecatedSigners(info) {
|
|
8834
9437
|
const next = /* @__PURE__ */ new Map();
|
|
@@ -9044,6 +9647,8 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
9044
9647
|
config.watcherConfig,
|
|
9045
9648
|
setup.walletContractTimelocks
|
|
9046
9649
|
);
|
|
9650
|
+
wallet.intentRepository = config.storage?.intentRepository;
|
|
9651
|
+
wallet.virtualTxRepository = config.storage?.virtualTxRepository;
|
|
9047
9652
|
wallet.refreshDeprecatedSigners(setup.info);
|
|
9048
9653
|
return wallet;
|
|
9049
9654
|
}
|
|
@@ -9075,6 +9680,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
9075
9680
|
this.pendingRecoveryOutpoints()
|
|
9076
9681
|
]);
|
|
9077
9682
|
const isPendingRecovery = (coin) => pendingOutpoints.has(`${coin.txid}:${coin.vout}`);
|
|
9683
|
+
const unlockedVtxos = await spendableVtxosExcludingLocked(vtxos, this.intentRepository);
|
|
9078
9684
|
let confirmed = 0;
|
|
9079
9685
|
let unconfirmed = 0;
|
|
9080
9686
|
for (const utxo of boardingUtxos) {
|
|
@@ -9084,14 +9690,15 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
9084
9690
|
unconfirmed += utxo.value;
|
|
9085
9691
|
}
|
|
9086
9692
|
}
|
|
9087
|
-
let settled = 0;
|
|
9088
|
-
let preconfirmed = 0;
|
|
9089
9693
|
let recoverable = 0;
|
|
9090
9694
|
let pendingRecovery = 0;
|
|
9091
|
-
settled = vtxos.filter((coin) => coin.virtualStatus.state === "settled" && !isPendingRecovery(coin)).reduce((sum, coin) => sum + coin.value, 0);
|
|
9092
|
-
preconfirmed = vtxos.filter(
|
|
9695
|
+
const settled = vtxos.filter((coin) => coin.virtualStatus.state === "settled" && !isPendingRecovery(coin)).reduce((sum, coin) => sum + coin.value, 0);
|
|
9696
|
+
const preconfirmed = vtxos.filter(
|
|
9093
9697
|
(coin) => coin.virtualStatus.state === "preconfirmed" && !isPendingRecovery(coin)
|
|
9094
9698
|
).reduce((sum, coin) => sum + coin.value, 0);
|
|
9699
|
+
const available = unlockedVtxos.filter(
|
|
9700
|
+
(coin) => (coin.virtualStatus.state === "settled" || coin.virtualStatus.state === "preconfirmed") && !isPendingRecovery(coin)
|
|
9701
|
+
).reduce((sum, coin) => sum + coin.value, 0);
|
|
9095
9702
|
recoverable = vtxos.filter((coin) => isSpendable(coin) && coin.virtualStatus.state === "swept").reduce((sum, coin) => sum + coin.value, 0);
|
|
9096
9703
|
pendingRecovery = vtxos.filter(isPendingRecovery).reduce((sum, coin) => sum + coin.value, 0);
|
|
9097
9704
|
const totalBoarding = confirmed + unconfirmed;
|
|
@@ -9118,7 +9725,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
9118
9725
|
},
|
|
9119
9726
|
settled,
|
|
9120
9727
|
preconfirmed,
|
|
9121
|
-
available
|
|
9728
|
+
available,
|
|
9122
9729
|
recoverable,
|
|
9123
9730
|
pendingRecovery,
|
|
9124
9731
|
total: totalBoarding + totalOffchain,
|
|
@@ -9595,6 +10202,7 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
9595
10202
|
indexerProvider: this.indexerProvider,
|
|
9596
10203
|
contractRepository: this.contractRepository,
|
|
9597
10204
|
walletRepository: this.walletRepository,
|
|
10205
|
+
intentRepository: this.intentRepository,
|
|
9598
10206
|
watcherConfig: this.watcherConfig
|
|
9599
10207
|
});
|
|
9600
10208
|
const baselinePubkey = await this.identity.xOnlyPublicKey();
|
|
@@ -10288,6 +10896,8 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
10288
10896
|
wallet.setBoardingTapscriptForRotation(resolvedBoarding);
|
|
10289
10897
|
}
|
|
10290
10898
|
}
|
|
10899
|
+
wallet.intentRepository = config.storage?.intentRepository;
|
|
10900
|
+
wallet.virtualTxRepository = config.storage?.virtualTxRepository;
|
|
10291
10901
|
await wallet.getVtxoManager();
|
|
10292
10902
|
return wallet;
|
|
10293
10903
|
}
|
|
@@ -10567,12 +11177,26 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
10567
11177
|
),
|
|
10568
11178
|
this.makeDeleteIntentSignature(params.inputs)
|
|
10569
11179
|
]);
|
|
11180
|
+
let intentTxId;
|
|
11181
|
+
try {
|
|
11182
|
+
intentTxId = Transaction$2.fromPSBT(base64.decode(intent.proof)).id;
|
|
11183
|
+
} catch {
|
|
11184
|
+
intentTxId = params.inputs.map((i) => `${i.txid}:${i.vout}`).sort().join("|");
|
|
11185
|
+
}
|
|
11186
|
+
await this.persistIntentSnapshot(
|
|
11187
|
+
intentTxId,
|
|
11188
|
+
"waiting_to_submit",
|
|
11189
|
+
intent,
|
|
11190
|
+
deleteIntent,
|
|
11191
|
+
params.inputs
|
|
11192
|
+
);
|
|
10570
11193
|
const topics = [
|
|
10571
11194
|
...signingPublicKeys,
|
|
10572
11195
|
...params.inputs.map((input) => `${input.txid}:${input.vout}`)
|
|
10573
11196
|
];
|
|
10574
11197
|
const abortController = new AbortController();
|
|
10575
11198
|
let stream;
|
|
11199
|
+
let committedTxid;
|
|
10576
11200
|
this._addPendingSpends(params.inputs);
|
|
10577
11201
|
try {
|
|
10578
11202
|
stream = this.arkProvider.getEventStream(abortController.signal, topics);
|
|
@@ -10587,16 +11211,43 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
10587
11211
|
yield* stream;
|
|
10588
11212
|
})();
|
|
10589
11213
|
const intentId = await this.safeRegisterIntent(intent, params.inputs);
|
|
10590
|
-
|
|
11214
|
+
await this.persistIntentSnapshot(
|
|
11215
|
+
intentTxId,
|
|
11216
|
+
"waiting_for_batch",
|
|
11217
|
+
intent,
|
|
11218
|
+
deleteIntent,
|
|
11219
|
+
params.inputs,
|
|
11220
|
+
{ intentId }
|
|
11221
|
+
);
|
|
11222
|
+
const handler = wrapHandlerWithIntentPersistence(
|
|
11223
|
+
this.createBatchHandler(intentId, params.inputs, recipients, session),
|
|
11224
|
+
{ intentRepository: this.intentRepository, intentTxId }
|
|
11225
|
+
);
|
|
10591
11226
|
const commitmentTxid = await Batch.join(primedStream, handler, {
|
|
10592
11227
|
abortController,
|
|
10593
11228
|
skipVtxoTreeSigning: !hasOffchainOutputs,
|
|
10594
|
-
|
|
11229
|
+
// async so a synchronous throw becomes a rejection Batch.join
|
|
11230
|
+
// swallows, keeping the observational callback off the stream path.
|
|
11231
|
+
eventCallback: eventCallback && (async (event) => {
|
|
11232
|
+
await eventCallback(event);
|
|
11233
|
+
})
|
|
10595
11234
|
});
|
|
11235
|
+
committedTxid = commitmentTxid;
|
|
10596
11236
|
await this.updateDbAfterSettle(params.inputs, commitmentTxid);
|
|
10597
11237
|
await this.maybeRotateBoardingAfterBoard(params.inputs);
|
|
10598
11238
|
return commitmentTxid;
|
|
10599
11239
|
} catch (error) {
|
|
11240
|
+
if (committedTxid !== void 0) {
|
|
11241
|
+
await this.persistIntentSnapshot(
|
|
11242
|
+
intentTxId,
|
|
11243
|
+
"batch_succeeded",
|
|
11244
|
+
intent,
|
|
11245
|
+
deleteIntent,
|
|
11246
|
+
params.inputs,
|
|
11247
|
+
{ commitmentTransactionId: committedTxid }
|
|
11248
|
+
);
|
|
11249
|
+
throw error;
|
|
11250
|
+
}
|
|
10600
11251
|
const inputIds = params.inputs.map((i) => `${i.txid}:${i.vout}`).join(",");
|
|
10601
11252
|
await this.arkProvider.deleteIntent(deleteIntent).catch((e) => {
|
|
10602
11253
|
console.warn(
|
|
@@ -10604,6 +11255,16 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
10604
11255
|
e
|
|
10605
11256
|
);
|
|
10606
11257
|
});
|
|
11258
|
+
await this.persistIntentSnapshot(
|
|
11259
|
+
intentTxId,
|
|
11260
|
+
"cancelled",
|
|
11261
|
+
intent,
|
|
11262
|
+
deleteIntent,
|
|
11263
|
+
params.inputs,
|
|
11264
|
+
{
|
|
11265
|
+
cancellationReason: error instanceof Error ? error.message : String(error)
|
|
11266
|
+
}
|
|
11267
|
+
);
|
|
10607
11268
|
throw error;
|
|
10608
11269
|
} finally {
|
|
10609
11270
|
this._removePendingSpends(params.inputs);
|
|
@@ -10878,6 +11539,42 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
10878
11539
|
}
|
|
10879
11540
|
return jobs;
|
|
10880
11541
|
}
|
|
11542
|
+
/**
|
|
11543
|
+
* Best-effort upsert of the current settlement intent into the optional
|
|
11544
|
+
* {@link intentRepository}. Never throws into the settle path — intent
|
|
11545
|
+
* persistence is observational and must not break money flow. Preserves
|
|
11546
|
+
* fields already written by the event reducer (state-event ordering).
|
|
11547
|
+
*/
|
|
11548
|
+
async persistIntentSnapshot(intentTxId, state, intent, deleteIntent, inputs, patch) {
|
|
11549
|
+
const repo = this.intentRepository;
|
|
11550
|
+
if (!repo) return;
|
|
11551
|
+
try {
|
|
11552
|
+
const now = Date.now();
|
|
11553
|
+
const existing = (await repo.getIntents({ intentTxIds: [intentTxId] }))[0];
|
|
11554
|
+
if (existing && isTerminalIntentState(existing.state)) {
|
|
11555
|
+
return;
|
|
11556
|
+
}
|
|
11557
|
+
await repo.saveIntent({
|
|
11558
|
+
...existing ?? {},
|
|
11559
|
+
intentTxId,
|
|
11560
|
+
registerProof: intent.proof,
|
|
11561
|
+
registerProofMessage: Intent.encodeMessage(intent.message),
|
|
11562
|
+
deleteProof: deleteIntent.proof,
|
|
11563
|
+
deleteProofMessage: Intent.encodeMessage(deleteIntent.message),
|
|
11564
|
+
intentVtxos: inputs.map((i) => ({
|
|
11565
|
+
txid: i.txid,
|
|
11566
|
+
vout: i.vout
|
|
11567
|
+
})),
|
|
11568
|
+
partialForfeits: existing?.partialForfeits ?? [],
|
|
11569
|
+
createdAt: existing?.createdAt ?? now,
|
|
11570
|
+
updatedAt: now,
|
|
11571
|
+
state,
|
|
11572
|
+
...patch
|
|
11573
|
+
});
|
|
11574
|
+
} catch (e) {
|
|
11575
|
+
console.error(`Failed to persist intent ${intentTxId} (state=${state})`, e);
|
|
11576
|
+
}
|
|
11577
|
+
}
|
|
10881
11578
|
/**
|
|
10882
11579
|
* @internal Sign an on-chain boarding exit / sweep transaction, routing
|
|
10883
11580
|
* each input to the correct key by its `witnessUtxo.script`: the identity
|
|
@@ -15821,6 +16518,20 @@ function craftBIP322ToSignSimple(toSpend, pkScript) {
|
|
|
15821
16518
|
});
|
|
15822
16519
|
return tx;
|
|
15823
16520
|
}
|
|
16521
|
+
function chainTxTypeToChainedExit(t) {
|
|
16522
|
+
switch (t) {
|
|
16523
|
+
case "INDEXER_CHAINED_TX_TYPE_COMMITMENT" /* COMMITMENT */:
|
|
16524
|
+
return 1 /* Commitment */;
|
|
16525
|
+
case "INDEXER_CHAINED_TX_TYPE_ARK" /* ARK */:
|
|
16526
|
+
return 2 /* Ark */;
|
|
16527
|
+
case "INDEXER_CHAINED_TX_TYPE_TREE" /* TREE */:
|
|
16528
|
+
return 3 /* Tree */;
|
|
16529
|
+
case "INDEXER_CHAINED_TX_TYPE_CHECKPOINT" /* CHECKPOINT */:
|
|
16530
|
+
return 4 /* Checkpoint */;
|
|
16531
|
+
default:
|
|
16532
|
+
return 0 /* Unspecified */;
|
|
16533
|
+
}
|
|
16534
|
+
}
|
|
15824
16535
|
var Unroll;
|
|
15825
16536
|
((Unroll2) => {
|
|
15826
16537
|
((StepType2) => {
|
|
@@ -15830,16 +16541,54 @@ var Unroll;
|
|
|
15830
16541
|
})(Unroll2.StepType || (Unroll2.StepType = {}));
|
|
15831
16542
|
class Session2 {
|
|
15832
16543
|
/** Create an unroll session from a virtual output outpoint and its dependency chain. */
|
|
15833
|
-
constructor(toUnroll, bumper, explorer, indexer) {
|
|
16544
|
+
constructor(toUnroll, bumper, explorer, indexer, virtualTxRepository) {
|
|
15834
16545
|
this.toUnroll = toUnroll;
|
|
15835
16546
|
this.bumper = bumper;
|
|
15836
16547
|
this.explorer = explorer;
|
|
15837
16548
|
this.indexer = indexer;
|
|
16549
|
+
this.virtualTxRepository = virtualTxRepository;
|
|
15838
16550
|
}
|
|
15839
16551
|
/** Create an unroll session by loading the virtual output chain from the indexer. */
|
|
15840
|
-
static async create(toUnroll, bumper, explorer, indexer) {
|
|
16552
|
+
static async create(toUnroll, bumper, explorer, indexer, virtualTxRepository) {
|
|
15841
16553
|
const { chain } = await indexer.getVtxoChain(toUnroll);
|
|
15842
|
-
return new Session2(
|
|
16554
|
+
return new Session2(
|
|
16555
|
+
{ ...toUnroll, chain },
|
|
16556
|
+
bumper,
|
|
16557
|
+
explorer,
|
|
16558
|
+
indexer,
|
|
16559
|
+
virtualTxRepository
|
|
16560
|
+
);
|
|
16561
|
+
}
|
|
16562
|
+
/**
|
|
16563
|
+
* Resolve a chain tx's raw PSBT (base64). Repo first when configured;
|
|
16564
|
+
* indexer on a miss, then best-effort cached so a later exit doesn't
|
|
16565
|
+
* re-fetch. Never throws from the cache
|
|
16566
|
+
* write — exit correctness must not depend on persistence.
|
|
16567
|
+
*/
|
|
16568
|
+
async resolveVirtualTxBase64(next) {
|
|
16569
|
+
const repo = this.virtualTxRepository;
|
|
16570
|
+
if (repo) {
|
|
16571
|
+
try {
|
|
16572
|
+
const stored = await repo.getVirtualTx(next.txid);
|
|
16573
|
+
if (stored?.psbt) return stored.psbt;
|
|
16574
|
+
} catch {
|
|
16575
|
+
}
|
|
16576
|
+
}
|
|
16577
|
+
const fetched = await this.indexer.getVirtualTxs([next.txid]);
|
|
16578
|
+
const psbt = fetched.txs[0];
|
|
16579
|
+
if (psbt && repo) {
|
|
16580
|
+
const cached = {
|
|
16581
|
+
txid: next.txid,
|
|
16582
|
+
psbt,
|
|
16583
|
+
expiresAt: null,
|
|
16584
|
+
type: chainTxTypeToChainedExit(next.type)
|
|
16585
|
+
};
|
|
16586
|
+
try {
|
|
16587
|
+
await repo.upsertVirtualTxs([cached]);
|
|
16588
|
+
} catch {
|
|
16589
|
+
}
|
|
16590
|
+
}
|
|
16591
|
+
return psbt;
|
|
15843
16592
|
}
|
|
15844
16593
|
/**
|
|
15845
16594
|
* Get the next step to be executed
|
|
@@ -15874,11 +16623,11 @@ var Unroll;
|
|
|
15874
16623
|
do: () => Promise.resolve()
|
|
15875
16624
|
};
|
|
15876
16625
|
}
|
|
15877
|
-
const
|
|
15878
|
-
if (
|
|
16626
|
+
const virtualTxBase64 = await this.resolveVirtualTxBase64(nextTxToBroadcast);
|
|
16627
|
+
if (!virtualTxBase64) {
|
|
15879
16628
|
throw new Error(`Tx ${nextTxToBroadcast.txid} not found`);
|
|
15880
16629
|
}
|
|
15881
|
-
const tx = Transaction.fromPSBT(base64.decode(
|
|
16630
|
+
const tx = Transaction.fromPSBT(base64.decode(virtualTxBase64));
|
|
15882
16631
|
if (nextTxToBroadcast.type === "INDEXER_CHAINED_TX_TYPE_TREE" /* TREE */) {
|
|
15883
16632
|
const input = tx.getInput(0);
|
|
15884
16633
|
if (!input) {
|
|
@@ -16098,6 +16847,6 @@ function isArkContract(str) {
|
|
|
16098
16847
|
return str.startsWith(ARKCONTRACT_PREFIX + "=");
|
|
16099
16848
|
}
|
|
16100
16849
|
|
|
16101
|
-
export { ActivityRegistry, ArkNote, AssetManager, BIP322, Batch, ContractManager, ContractRepositoryImpl, ContractWatcher, DB_VERSION, DEFAULT_MESSAGE_TIMEOUTS, DelegateManagerImpl, DelegateNotConfiguredError, DelegatorManagerImpl, DelegatorNotConfiguredError, DescriptorSigningProviderMissingError, DustChangeError, ELECTRUM_TCP_HOST, ELECTRUM_WS_URL, ESPLORA_URL, ElectrumOnchainProvider, EsploraProvider, Estimator, HDDescriptorProvider, InMemoryContractRepository, InMemoryWalletRepository, IndexedDBContractRepository, IndexedDBWalletRepository, MESSAGE_BUS_INITIALIZING, MESSAGE_BUS_NOT_INITIALIZED, MIGRATION_KEY, MessageBus, MessageBusInitializingError, MessageBusNotInitializedError, MissingSigningDescriptorError, MnemonicIdentity, OnchainWallet, P2A, Ramps, ReadonlyAssetManager, ReadonlyDescriptorIdentity, ReadonlySingleKey, ReadonlyWallet, ReadonlyWalletError, RestDelegateProvider, RestDelegatorProvider, SeedIdentity, ServiceWorkerReadonlyWallet, ServiceWorkerTimeoutError, ServiceWorkerWallet, SingleKey, TxTree, TxType, TxWeightEstimator, Unroll, VtxoManager, Wallet2 as Wallet, WalletMessageHandler, WalletNotInitializedError, WalletRepositoryImpl, WsElectrumChainSource, boardingResolver, buildForfeitTx, buildOffchainTx, classifyAgainstSignerSet, classifyContractSigner, closeDatabase, combineTapscriptSigs, contractFromArkContract, contractFromArkContractWithAddress, createDefaultActivityRegistry, decodeArkContract, deserializeAssets, deserializeUtxo, deserializeVtxo, encodeArkContract, extendVirtualCoinForContract, getMigrationStatus, getRandomId, hasBoardingTxExpired, isArkContract, isBatchSignable, isCooperativelyMigratable, isDiscoverable, isExpired, isRecoverable, isSpendable, isSubdust, isValidArkAddress, isVtxoExpiringSoon, isVtxoForScript, migrateWalletRepository, openDatabase, requiresMigration, rollbackMigration, saveVtxosForContract, scriptFromArkAddress, serializeAssets, serializeUtxo, serializeVtxo, setupServiceWorker, signerSetFromInfo, toXOnlySignerHex, validateConnectorsTxGraph, validateVtxoTxGraph, verifyTapscriptSignatures, waitForIncomingFunds, warnAndFilterVtxosForScript };
|
|
16102
|
-
//# sourceMappingURL=chunk-
|
|
16103
|
-
//# sourceMappingURL=chunk-
|
|
16850
|
+
export { ActivityRegistry, ArkNote, AssetManager, BIP322, Batch, ChainedTxType, ContractManager, ContractRepositoryImpl, ContractWatcher, DB_VERSION, DEFAULT_MESSAGE_TIMEOUTS, DelegateManagerImpl, DelegateNotConfiguredError, DelegatorManagerImpl, DelegatorNotConfiguredError, DescriptorSigningProviderMissingError, DustChangeError, ELECTRUM_TCP_HOST, ELECTRUM_WS_URL, ESPLORA_URL, ElectrumOnchainProvider, EsploraProvider, Estimator, HDDescriptorProvider, INTENT_TERMINAL_STATES, InMemoryContractRepository, InMemoryIntentRepository, InMemoryVirtualTxRepository, InMemoryWalletRepository, IndexedDBContractRepository, IndexedDBIntentRepository, IndexedDBVirtualTxRepository, IndexedDBWalletRepository, MESSAGE_BUS_INITIALIZING, MESSAGE_BUS_NOT_INITIALIZED, MIGRATION_KEY, MessageBus, MessageBusInitializingError, MessageBusNotInitializedError, MissingSigningDescriptorError, MnemonicIdentity, OnchainWallet, P2A, Ramps, ReadonlyAssetManager, ReadonlyDescriptorIdentity, ReadonlySingleKey, ReadonlyWallet, ReadonlyWalletError, RestDelegateProvider, RestDelegatorProvider, SeedIdentity, ServiceWorkerReadonlyWallet, ServiceWorkerTimeoutError, ServiceWorkerWallet, SingleKey, TxTree, TxType, TxWeightEstimator, Unroll, VtxoManager, Wallet2 as Wallet, WalletMessageHandler, WalletNotInitializedError, WalletRepositoryImpl, WsElectrumChainSource, assertIntentIdUnique, boardingResolver, buildForfeitTx, buildOffchainTx, classifyAgainstSignerSet, classifyContractSigner, closeDatabase, combineTapscriptSigs, contractFromArkContract, contractFromArkContractWithAddress, createDefaultActivityRegistry, decodeArkContract, deserializeAssets, deserializeUtxo, deserializeVtxo, encodeArkContract, extendVirtualCoinForContract, getMigrationStatus, getRandomId, hasBoardingTxExpired, intentMatchesFilter, intentPageBounds, isArkContract, isBatchSignable, isCooperativelyMigratable, isDiscoverable, isExpired, isRecoverable, isSpendable, isSubdust, isTerminalIntentState, isValidArkAddress, isVtxoExpiringSoon, isVtxoForScript, mergeChainedTxType, migrateWalletRepository, openDatabase, requiresMigration, rollbackMigration, saveVtxosForContract, scriptFromArkAddress, serializeAssets, serializeUtxo, serializeVtxo, setupServiceWorker, signerSetFromInfo, toXOnlySignerHex, validateConnectorsTxGraph, validateVtxoTxGraph, verifyTapscriptSignatures, waitForIncomingFunds, warnAndFilterVtxosForScript };
|
|
16851
|
+
//# sourceMappingURL=chunk-B4R36PRN.js.map
|
|
16852
|
+
//# sourceMappingURL=chunk-B4R36PRN.js.map
|