@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.
- package/README.md +65 -0
- 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-BtSOVZFn.d.ts} +2 -2
- package/dist/{asyncStorageTaskQueue-BpN8RRHR.d.cts → asyncStorageTaskQueue-Dap7qUlF.d.cts} +2 -2
- package/dist/{chunk-V3FCNVJE.js → chunk-FMI2GKYO.js} +2158 -502
- package/dist/chunk-FMI2GKYO.js.map +1 -0
- package/dist/{chunk-FLPPSRYH.js → chunk-LCQQPOIT.js} +3 -3
- package/dist/{chunk-FLPPSRYH.js.map → chunk-LCQQPOIT.js.map} +1 -1
- package/dist/{chunk-5X5YMZHB.js → chunk-LJ35HPJW.js} +39 -5
- package/dist/chunk-LJ35HPJW.js.map +1 -0
- package/dist/{chunk-HHPUCCB3.cjs → chunk-RJ6XVCX4.cjs} +7 -7
- package/dist/{chunk-HHPUCCB3.cjs.map → chunk-RJ6XVCX4.cjs.map} +1 -1
- package/dist/{chunk-YEF5FL6E.cjs → chunk-UBYY2TPK.cjs} +39 -4
- package/dist/chunk-UBYY2TPK.cjs.map +1 -0
- package/dist/{chunk-2V3BU2X6.js → chunk-V7WIZR2B.js} +3 -3
- package/dist/{chunk-2V3BU2X6.js.map → chunk-V7WIZR2B.js.map} +1 -1
- package/dist/{chunk-T2ACJD22.cjs → chunk-X37EKDIH.cjs} +11 -11
- package/dist/{chunk-T2ACJD22.cjs.map → chunk-X37EKDIH.cjs.map} +1 -1
- package/dist/{chunk-ED7JDFX4.cjs → chunk-XIVCG6EP.cjs} +2248 -578
- package/dist/chunk-XIVCG6EP.cjs.map +1 -0
- 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 +162 -118
- package/dist/index.d.cts +344 -9
- package/dist/index.d.ts +344 -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-BdiRy4iI.d.ts → taskRunner-Bptw9UMa.d.ts} +2 -2
- package/dist/{taskRunner-CuTk8RYg.d.cts → taskRunner-CeFxKhSH.d.cts} +2 -2
- 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,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
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 =
|
|
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 =
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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 ===
|
|
1544
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
1884
|
-
const group =
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 ?
|
|
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 =
|
|
4690
|
-
const issuedAssetGroup =
|
|
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(
|
|
4856
|
+
changeInputs.push(chunkUBYY2TPK_cjs.AssetInput.create(inputIndex, asset.amount));
|
|
4706
4857
|
}
|
|
4707
4858
|
}
|
|
4708
4859
|
groups.push(
|
|
4709
|
-
|
|
4710
|
-
|
|
4860
|
+
chunkUBYY2TPK_cjs.AssetGroup.create(
|
|
4861
|
+
chunkUBYY2TPK_cjs.AssetId.fromString(assetId),
|
|
4711
4862
|
null,
|
|
4712
4863
|
changeInputs,
|
|
4713
|
-
[
|
|
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([
|
|
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:
|
|
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(
|
|
4957
|
+
reissueInputs.push(chunkUBYY2TPK_cjs.AssetInput.create(inputIndex, asset.amount));
|
|
4807
4958
|
}
|
|
4808
4959
|
}
|
|
4809
4960
|
const totalAssetAmount = assetToReissueAmount + params.amount;
|
|
4810
|
-
const reissueAssetIdObj =
|
|
4811
|
-
const reissueAssetGroup =
|
|
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
|
-
[
|
|
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(
|
|
4975
|
+
changeInputs.push(chunkUBYY2TPK_cjs.AssetInput.create(inputIndex, asset.amount));
|
|
4825
4976
|
}
|
|
4826
4977
|
}
|
|
4827
4978
|
groups.push(
|
|
4828
|
-
|
|
4829
|
-
|
|
4979
|
+
chunkUBYY2TPK_cjs.AssetGroup.create(
|
|
4980
|
+
chunkUBYY2TPK_cjs.AssetId.fromString(assetId),
|
|
4830
4981
|
null,
|
|
4831
4982
|
changeInputs,
|
|
4832
|
-
[
|
|
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([
|
|
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(
|
|
5062
|
+
changeInputs.push(chunkUBYY2TPK_cjs.AssetInput.create(inputIndex, asset.amount));
|
|
4912
5063
|
}
|
|
4913
5064
|
}
|
|
4914
5065
|
groups.push(
|
|
4915
|
-
|
|
4916
|
-
|
|
5066
|
+
chunkUBYY2TPK_cjs.AssetGroup.create(
|
|
5067
|
+
chunkUBYY2TPK_cjs.AssetId.fromString(assetId),
|
|
4917
5068
|
null,
|
|
4918
5069
|
changeInputs,
|
|
4919
|
-
amount > 0n ? [
|
|
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([
|
|
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(
|
|
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 =
|
|
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/
|
|
5271
|
-
|
|
5272
|
-
|
|
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
|
-
|
|
5621
|
-
|
|
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 (
|
|
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
|
|
8852
|
-
* server-info-change handler mid-session
|
|
8853
|
-
*
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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(
|
|
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
|
|
15900
|
-
if (
|
|
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 =
|
|
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
|
|
15973
|
-
|
|
15974
|
-
|
|
15975
|
-
|
|
15976
|
-
|
|
15977
|
-
|
|
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
|
|
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
|
-
|
|
16050
|
-
|
|
16051
|
-
|
|
16052
|
-
|
|
16053
|
-
|
|
16054
|
-
|
|
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
|
-
|
|
16057
|
-
|
|
16058
|
-
|
|
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
|
|
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-
|
|
16228
|
-
//# sourceMappingURL=chunk-
|
|
17897
|
+
//# sourceMappingURL=chunk-XIVCG6EP.cjs.map
|
|
17898
|
+
//# sourceMappingURL=chunk-XIVCG6EP.cjs.map
|