@arkade-os/sdk 0.4.23 → 0.4.25
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 +21 -1
- package/dist/cjs/contracts/contractManager.js +66 -5
- package/dist/cjs/contracts/contractWatcher.js +9 -3
- package/dist/cjs/contracts/handlers/default.js +3 -2
- package/dist/cjs/contracts/handlers/delegate.js +3 -2
- package/dist/cjs/contracts/handlers/helpers.js +2 -58
- package/dist/cjs/contracts/handlers/vhtlc.js +7 -6
- package/dist/cjs/contracts/vtxoOwnership.js +78 -0
- package/dist/cjs/index.js +3 -3
- package/dist/cjs/repositories/inMemory/walletRepository.js +35 -0
- package/dist/cjs/repositories/indexedDB/walletRepository.js +117 -0
- package/dist/cjs/repositories/realm/walletRepository.js +28 -0
- package/dist/cjs/repositories/sqlite/walletRepository.js +23 -0
- package/dist/cjs/script/base.js +12 -47
- package/dist/cjs/script/tapscript.js +97 -73
- package/dist/cjs/utils/timelock.js +59 -0
- package/dist/cjs/utils/unknownFields.js +2 -39
- package/dist/cjs/wallet/serviceWorker/wallet-message-handler.js +71 -10
- package/dist/cjs/wallet/serviceWorker/wallet.js +10 -0
- package/dist/cjs/wallet/unroll.js +79 -67
- package/dist/cjs/wallet/vtxo-manager.js +112 -16
- package/dist/cjs/wallet/wallet.js +64 -8
- package/dist/cjs/worker/expo/processors/contractPollProcessor.js +7 -2
- package/dist/esm/contracts/contractManager.js +66 -5
- package/dist/esm/contracts/contractWatcher.js +9 -3
- package/dist/esm/contracts/handlers/default.js +2 -1
- package/dist/esm/contracts/handlers/delegate.js +2 -1
- package/dist/esm/contracts/handlers/helpers.js +1 -22
- package/dist/esm/contracts/handlers/vhtlc.js +2 -1
- package/dist/esm/contracts/vtxoOwnership.js +69 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/repositories/inMemory/walletRepository.js +35 -0
- package/dist/esm/repositories/indexedDB/walletRepository.js +117 -0
- package/dist/esm/repositories/realm/walletRepository.js +28 -0
- package/dist/esm/repositories/sqlite/walletRepository.js +23 -0
- package/dist/esm/script/base.js +12 -14
- package/dist/esm/script/tapscript.js +97 -40
- package/dist/esm/utils/timelock.js +22 -0
- package/dist/esm/utils/unknownFields.js +2 -6
- package/dist/esm/wallet/serviceWorker/wallet-message-handler.js +71 -10
- package/dist/esm/wallet/serviceWorker/wallet.js +10 -0
- package/dist/esm/wallet/unroll.js +78 -67
- package/dist/esm/wallet/vtxo-manager.js +112 -16
- package/dist/esm/wallet/wallet.js +62 -6
- package/dist/esm/worker/expo/processors/contractPollProcessor.js +7 -2
- package/dist/types/contracts/contractManager.d.ts +17 -1
- package/dist/types/contracts/handlers/helpers.d.ts +0 -9
- package/dist/types/contracts/vtxoOwnership.d.ts +33 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/repositories/inMemory/walletRepository.d.ts +4 -1
- package/dist/types/repositories/indexedDB/walletRepository.d.ts +4 -1
- package/dist/types/repositories/realm/walletRepository.d.ts +4 -1
- package/dist/types/repositories/sqlite/walletRepository.d.ts +4 -1
- package/dist/types/repositories/walletRepository.d.ts +21 -0
- package/dist/types/script/tapscript.d.ts +4 -0
- package/dist/types/utils/timelock.d.ts +9 -0
- package/dist/types/wallet/serviceWorker/wallet-message-handler.d.ts +14 -2
- package/dist/types/wallet/unroll.d.ts +10 -0
- package/dist/types/wallet/vtxo-manager.d.ts +32 -5
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.SQLiteWalletRepository = void 0;
|
|
4
4
|
const serialization_1 = require("../serialization");
|
|
5
5
|
const scriptFromAddress_1 = require("../scriptFromAddress");
|
|
6
|
+
const vtxoOwnership_1 = require("../../contracts/vtxoOwnership");
|
|
6
7
|
/**
|
|
7
8
|
* SQLite-based implementation of WalletRepository.
|
|
8
9
|
*
|
|
@@ -245,6 +246,28 @@ class SQLiteWalletRepository {
|
|
|
245
246
|
await this.ensureInit();
|
|
246
247
|
await this.db.run(`DELETE FROM ${this.tables.vtxos} WHERE address = ?`, [address]);
|
|
247
248
|
}
|
|
249
|
+
async getVtxosForScript(script) {
|
|
250
|
+
await this.ensureInit();
|
|
251
|
+
const rows = await this.db.all(`SELECT * FROM ${this.tables.vtxos} WHERE script = ?`, [script]);
|
|
252
|
+
return rows.map(vtxoRowToDomain);
|
|
253
|
+
}
|
|
254
|
+
async saveVtxosForScript(key, vtxos) {
|
|
255
|
+
if (!key.address) {
|
|
256
|
+
throw new Error("SQLiteWalletRepository requires an address");
|
|
257
|
+
}
|
|
258
|
+
for (const vtxo of vtxos) {
|
|
259
|
+
if (!(0, vtxoOwnership_1.isVtxoForScript)(vtxo, key.script)) {
|
|
260
|
+
throw new Error(`VTXO ${vtxo.txid}:${vtxo.vout} script mismatch: expected ${key.script}, got ${vtxo.script}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return this.saveVtxos(key.address, vtxos);
|
|
264
|
+
}
|
|
265
|
+
async deleteVtxosForScript(script) {
|
|
266
|
+
await this.ensureInit();
|
|
267
|
+
await this.db.run(`DELETE FROM ${this.tables.vtxos} WHERE script = ?`, [
|
|
268
|
+
script,
|
|
269
|
+
]);
|
|
270
|
+
}
|
|
248
271
|
// ── UTXO management ────────────────────────────────────────────────
|
|
249
272
|
async getUtxos(address) {
|
|
250
273
|
await this.ensureInit();
|
package/dist/cjs/script/base.js
CHANGED
|
@@ -1,47 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
3
|
exports.VtxoScript = exports.TapTreeCoder = void 0;
|
|
37
4
|
exports.scriptFromTapLeafScript = scriptFromTapLeafScript;
|
|
38
5
|
exports.getSequence = getSequence;
|
|
39
6
|
const btc_signer_1 = require("@scure/btc-signer");
|
|
40
|
-
const bip68 = __importStar(require("bip68"));
|
|
41
7
|
const payment_js_1 = require("@scure/btc-signer/payment.js");
|
|
42
8
|
const psbt_js_1 = require("@scure/btc-signer/psbt.js");
|
|
43
9
|
const base_1 = require("@scure/base");
|
|
44
10
|
const address_1 = require("./address");
|
|
11
|
+
const timelock_1 = require("../utils/timelock");
|
|
45
12
|
const tapscript_1 = require("./tapscript");
|
|
46
13
|
exports.TapTreeCoder = psbt_js_1.PSBTOutput.tapTree[2];
|
|
47
14
|
function scriptFromTapLeafScript(leaf) {
|
|
@@ -163,19 +130,19 @@ class VtxoScript {
|
|
|
163
130
|
const paths = [];
|
|
164
131
|
for (const leaf of this.leaves) {
|
|
165
132
|
try {
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
catch (e) {
|
|
171
|
-
try {
|
|
172
|
-
const tapscript = tapscript_1.ConditionCSVMultisigTapscript.decode(scriptFromTapLeafScript(leaf));
|
|
173
|
-
paths.push(tapscript);
|
|
133
|
+
const script = scriptFromTapLeafScript(leaf);
|
|
134
|
+
if (tapscript_1.CSVMultisigTapscript.isScriptValid(script) === true) {
|
|
135
|
+
const tapScript = tapscript_1.CSVMultisigTapscript.decode(script);
|
|
136
|
+
paths.push(tapScript);
|
|
174
137
|
}
|
|
175
|
-
|
|
176
|
-
|
|
138
|
+
else if (tapscript_1.ConditionCSVMultisigTapscript.isScriptValid(script) === true) {
|
|
139
|
+
const tapScript = tapscript_1.ConditionCSVMultisigTapscript.decode(script);
|
|
140
|
+
paths.push(tapScript);
|
|
177
141
|
}
|
|
178
142
|
}
|
|
143
|
+
catch (e) {
|
|
144
|
+
console.debug("Failed to decode script", e);
|
|
145
|
+
}
|
|
179
146
|
}
|
|
180
147
|
return paths;
|
|
181
148
|
}
|
|
@@ -206,9 +173,7 @@ function getSequence(tapLeafScript) {
|
|
|
206
173
|
const script = scriptWithLeafVersion.subarray(0, scriptWithLeafVersion.length - 1);
|
|
207
174
|
try {
|
|
208
175
|
const params = tapscript_1.CSVMultisigTapscript.decode(script).params;
|
|
209
|
-
sequence =
|
|
210
|
-
? { blocks: Number(params.timelock.value) }
|
|
211
|
-
: { seconds: Number(params.timelock.value) });
|
|
176
|
+
sequence = (0, timelock_1.timelockToSequence)(params.timelock);
|
|
212
177
|
}
|
|
213
178
|
catch {
|
|
214
179
|
const params = tapscript_1.CLTVMultisigTapscript.decode(script).params;
|
|
@@ -1,43 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
3
|
exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.TapscriptType = void 0;
|
|
37
4
|
exports.decodeTapscript = decodeTapscript;
|
|
38
|
-
const bip68 = __importStar(require("bip68"));
|
|
39
5
|
const btc_signer_1 = require("@scure/btc-signer");
|
|
40
6
|
const base_1 = require("@scure/base");
|
|
7
|
+
const timelock_1 = require("../utils/timelock");
|
|
41
8
|
const MinimalScriptNum = (0, btc_signer_1.ScriptNum)(undefined, true);
|
|
42
9
|
var TapscriptType;
|
|
43
10
|
(function (TapscriptType) {
|
|
@@ -271,9 +238,7 @@ var CSVMultisigTapscript;
|
|
|
271
238
|
throw new Error(`Invalid pubkey length: expected 32, got ${pubkey.length}`);
|
|
272
239
|
}
|
|
273
240
|
}
|
|
274
|
-
const sequence = MinimalScriptNum.encode(BigInt(
|
|
275
|
-
? { blocks: Number(params.timelock.value) }
|
|
276
|
-
: { seconds: Number(params.timelock.value) })));
|
|
241
|
+
const sequence = MinimalScriptNum.encode(BigInt((0, timelock_1.timelockToSequence)(params.timelock)));
|
|
277
242
|
const asm = [
|
|
278
243
|
sequence.length === 1 ? sequence[0] : sequence,
|
|
279
244
|
"CHECKSEQUENCEVERIFY",
|
|
@@ -296,17 +261,12 @@ var CSVMultisigTapscript;
|
|
|
296
261
|
if (script.length === 0) {
|
|
297
262
|
throw new Error("Failed to decode: script is empty");
|
|
298
263
|
}
|
|
299
|
-
const
|
|
300
|
-
if (
|
|
301
|
-
throw
|
|
264
|
+
const isValid = isScriptValid(script);
|
|
265
|
+
if (isValid instanceof Error) {
|
|
266
|
+
throw isValid;
|
|
302
267
|
}
|
|
268
|
+
const asm = btc_signer_1.Script.decode(script);
|
|
303
269
|
const sequence = asm[0];
|
|
304
|
-
if (typeof sequence === "string") {
|
|
305
|
-
throw new Error("Invalid script: expected sequence number");
|
|
306
|
-
}
|
|
307
|
-
if (asm[1] !== "CHECKSEQUENCEVERIFY" || asm[2] !== "DROP") {
|
|
308
|
-
throw new Error("Invalid script: expected CHECKSEQUENCEVERIFY DROP");
|
|
309
|
-
}
|
|
310
270
|
const multisigScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(3)));
|
|
311
271
|
let multisig;
|
|
312
272
|
try {
|
|
@@ -322,10 +282,7 @@ var CSVMultisigTapscript;
|
|
|
322
282
|
else {
|
|
323
283
|
sequenceNum = Number(MinimalScriptNum.decode(sequence));
|
|
324
284
|
}
|
|
325
|
-
const
|
|
326
|
-
const timelock = decodedTimelock.blocks !== undefined
|
|
327
|
-
? { type: "blocks", value: BigInt(decodedTimelock.blocks) }
|
|
328
|
-
: { type: "seconds", value: BigInt(decodedTimelock.seconds) };
|
|
285
|
+
const timelock = (0, timelock_1.sequenceToTimelock)(sequenceNum);
|
|
329
286
|
const reconstructed = encode({
|
|
330
287
|
timelock,
|
|
331
288
|
...multisig.params,
|
|
@@ -348,6 +305,21 @@ var CSVMultisigTapscript;
|
|
|
348
305
|
return tapscript.type === TapscriptType.CSVMultisig;
|
|
349
306
|
}
|
|
350
307
|
CSVMultisigTapscript.is = is;
|
|
308
|
+
function isScriptValid(script) {
|
|
309
|
+
const asm = btc_signer_1.Script.decode(script);
|
|
310
|
+
if (asm.length < 3) {
|
|
311
|
+
return new Error(`Invalid script: too short (expected at least 3)`);
|
|
312
|
+
}
|
|
313
|
+
const sequence = asm[0];
|
|
314
|
+
if (typeof sequence === "string") {
|
|
315
|
+
return new Error("Invalid script: expected sequence number");
|
|
316
|
+
}
|
|
317
|
+
if (asm[1] !== "CHECKSEQUENCEVERIFY" || asm[2] !== "DROP") {
|
|
318
|
+
return new Error("Invalid script: expected CHECKSEQUENCEVERIFY DROP");
|
|
319
|
+
}
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
CSVMultisigTapscript.isScriptValid = isScriptValid;
|
|
351
323
|
})(CSVMultisigTapscript || (exports.CSVMultisigTapscript = CSVMultisigTapscript = {}));
|
|
352
324
|
/**
|
|
353
325
|
* Combines a condition script with an exit closure. The resulting script requires
|
|
@@ -382,18 +354,14 @@ var ConditionCSVMultisigTapscript;
|
|
|
382
354
|
if (script.length === 0) {
|
|
383
355
|
throw new Error("Failed to decode: script is empty");
|
|
384
356
|
}
|
|
385
|
-
const
|
|
386
|
-
if (
|
|
387
|
-
throw
|
|
388
|
-
}
|
|
389
|
-
let verifyIndex = -1;
|
|
390
|
-
for (let i = asm.length - 1; i >= 0; i--) {
|
|
391
|
-
if (asm[i] === "VERIFY") {
|
|
392
|
-
verifyIndex = i;
|
|
393
|
-
}
|
|
357
|
+
const isValid = isScriptValid(script);
|
|
358
|
+
if (isValid instanceof Error) {
|
|
359
|
+
throw isValid;
|
|
394
360
|
}
|
|
361
|
+
const asm = btc_signer_1.Script.decode(script);
|
|
362
|
+
let verifyIndex = getVerifyIndex(asm);
|
|
395
363
|
if (verifyIndex === -1) {
|
|
396
|
-
throw
|
|
364
|
+
throw Error("Invalid script: missing VERIFY operation");
|
|
397
365
|
}
|
|
398
366
|
const conditionScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(0, verifyIndex)));
|
|
399
367
|
const csvMultisigScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(verifyIndex + 1)));
|
|
@@ -426,6 +394,28 @@ var ConditionCSVMultisigTapscript;
|
|
|
426
394
|
return tapscript.type === TapscriptType.ConditionCSVMultisig;
|
|
427
395
|
}
|
|
428
396
|
ConditionCSVMultisigTapscript.is = is;
|
|
397
|
+
function getVerifyIndex(asm) {
|
|
398
|
+
let verifyIndex = -1;
|
|
399
|
+
for (let i = asm.length - 1; i >= 0; i--) {
|
|
400
|
+
if (asm[i] === "VERIFY") {
|
|
401
|
+
verifyIndex = i;
|
|
402
|
+
return verifyIndex;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return verifyIndex;
|
|
406
|
+
}
|
|
407
|
+
function isScriptValid(script) {
|
|
408
|
+
const asm = btc_signer_1.Script.decode(script);
|
|
409
|
+
if (asm.length < 1) {
|
|
410
|
+
return new Error(`Invalid script: too short (expected at least 1)`);
|
|
411
|
+
}
|
|
412
|
+
let verifyIndex = getVerifyIndex(asm);
|
|
413
|
+
if (verifyIndex === -1) {
|
|
414
|
+
return new Error("Invalid script: missing VERIFY operation");
|
|
415
|
+
}
|
|
416
|
+
return true;
|
|
417
|
+
}
|
|
418
|
+
ConditionCSVMultisigTapscript.isScriptValid = isScriptValid;
|
|
429
419
|
})(ConditionCSVMultisigTapscript || (exports.ConditionCSVMultisigTapscript = ConditionCSVMultisigTapscript = {}));
|
|
430
420
|
/**
|
|
431
421
|
* Combines a condition script with a forfeit closure. The resulting script requires
|
|
@@ -460,18 +450,14 @@ var ConditionMultisigTapscript;
|
|
|
460
450
|
if (script.length === 0) {
|
|
461
451
|
throw new Error("Failed to decode: script is empty");
|
|
462
452
|
}
|
|
463
|
-
const
|
|
464
|
-
if (
|
|
465
|
-
throw
|
|
466
|
-
}
|
|
467
|
-
let verifyIndex = -1;
|
|
468
|
-
for (let i = asm.length - 1; i >= 0; i--) {
|
|
469
|
-
if (asm[i] === "VERIFY") {
|
|
470
|
-
verifyIndex = i;
|
|
471
|
-
}
|
|
453
|
+
const isValid = isScriptValid(script);
|
|
454
|
+
if (isValid instanceof Error) {
|
|
455
|
+
throw isValid;
|
|
472
456
|
}
|
|
457
|
+
const asm = btc_signer_1.Script.decode(script);
|
|
458
|
+
let verifyIndex = getVerifyIndex(asm);
|
|
473
459
|
if (verifyIndex === -1) {
|
|
474
|
-
throw
|
|
460
|
+
throw Error("Invalid script: missing VERIFY operation");
|
|
475
461
|
}
|
|
476
462
|
const conditionScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(0, verifyIndex)));
|
|
477
463
|
const multisigScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(verifyIndex + 1)));
|
|
@@ -504,6 +490,28 @@ var ConditionMultisigTapscript;
|
|
|
504
490
|
return tapscript.type === TapscriptType.ConditionMultisig;
|
|
505
491
|
}
|
|
506
492
|
ConditionMultisigTapscript.is = is;
|
|
493
|
+
function getVerifyIndex(asm) {
|
|
494
|
+
let verifyIndex = -1;
|
|
495
|
+
for (let i = asm.length - 1; i >= 0; i--) {
|
|
496
|
+
if (asm[i] === "VERIFY") {
|
|
497
|
+
verifyIndex = i;
|
|
498
|
+
return verifyIndex;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return verifyIndex;
|
|
502
|
+
}
|
|
503
|
+
function isScriptValid(script) {
|
|
504
|
+
const asm = btc_signer_1.Script.decode(script);
|
|
505
|
+
if (asm.length < 1) {
|
|
506
|
+
return new Error(`Invalid script: too short (expected at least 1)`);
|
|
507
|
+
}
|
|
508
|
+
let verifyIndex = getVerifyIndex(asm);
|
|
509
|
+
if (verifyIndex === -1) {
|
|
510
|
+
return new Error("Invalid script: missing VERIFY operation");
|
|
511
|
+
}
|
|
512
|
+
return true;
|
|
513
|
+
}
|
|
514
|
+
ConditionMultisigTapscript.isScriptValid = isScriptValid;
|
|
507
515
|
})(ConditionMultisigTapscript || (exports.ConditionMultisigTapscript = ConditionMultisigTapscript = {}));
|
|
508
516
|
/**
|
|
509
517
|
* Implements an absolute timelock (CLTV) script combined with a forfeit closure.
|
|
@@ -544,10 +552,11 @@ var CLTVMultisigTapscript;
|
|
|
544
552
|
if (script.length === 0) {
|
|
545
553
|
throw new Error("Failed to decode: script is empty");
|
|
546
554
|
}
|
|
547
|
-
const
|
|
548
|
-
if (
|
|
549
|
-
throw
|
|
555
|
+
const isValid = isScriptValid(script);
|
|
556
|
+
if (isValid instanceof Error) {
|
|
557
|
+
throw isValid;
|
|
550
558
|
}
|
|
559
|
+
const asm = btc_signer_1.Script.decode(script);
|
|
551
560
|
const locktime = asm[0];
|
|
552
561
|
if (typeof locktime === "string") {
|
|
553
562
|
throw new Error("Invalid script: expected locktime number");
|
|
@@ -592,4 +601,19 @@ var CLTVMultisigTapscript;
|
|
|
592
601
|
return tapscript.type === TapscriptType.CLTVMultisig;
|
|
593
602
|
}
|
|
594
603
|
CLTVMultisigTapscript.is = is;
|
|
604
|
+
function isScriptValid(script) {
|
|
605
|
+
const asm = btc_signer_1.Script.decode(script);
|
|
606
|
+
if (asm.length < 3) {
|
|
607
|
+
return new Error(`Invalid script: too short (expected at least 3)`);
|
|
608
|
+
}
|
|
609
|
+
const locktime = asm[0];
|
|
610
|
+
if (typeof locktime === "string") {
|
|
611
|
+
return new Error("Invalid script: expected locktime as number or bytes");
|
|
612
|
+
}
|
|
613
|
+
if (asm[1] !== "CHECKLOCKTIMEVERIFY" || asm[2] !== "DROP") {
|
|
614
|
+
return new Error("Invalid script: expected CHECKLOCKTIMEVERIFY DROP");
|
|
615
|
+
}
|
|
616
|
+
return true;
|
|
617
|
+
}
|
|
618
|
+
CLTVMultisigTapscript.isScriptValid = isScriptValid;
|
|
595
619
|
})(CLTVMultisigTapscript || (exports.CLTVMultisigTapscript = CLTVMultisigTapscript = {}));
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.timelockToSequence = timelockToSequence;
|
|
37
|
+
exports.sequenceToTimelock = sequenceToTimelock;
|
|
38
|
+
const bip68 = __importStar(require("bip68"));
|
|
39
|
+
/**
|
|
40
|
+
* Convert RelativeTimelock to BIP68 sequence number.
|
|
41
|
+
*/
|
|
42
|
+
function timelockToSequence(timelock) {
|
|
43
|
+
return bip68.encode(timelock.type === "blocks"
|
|
44
|
+
? { blocks: Number(timelock.value) }
|
|
45
|
+
: { seconds: Number(timelock.value) });
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Convert BIP68 sequence number back to RelativeTimelock.
|
|
49
|
+
*/
|
|
50
|
+
function sequenceToTimelock(sequence) {
|
|
51
|
+
const decoded = bip68.decode(sequence);
|
|
52
|
+
if ("blocks" in decoded && decoded.blocks !== undefined) {
|
|
53
|
+
return { type: "blocks", value: BigInt(decoded.blocks) };
|
|
54
|
+
}
|
|
55
|
+
if ("seconds" in decoded && decoded.seconds !== undefined) {
|
|
56
|
+
return { type: "seconds", value: BigInt(decoded.seconds) };
|
|
57
|
+
}
|
|
58
|
+
throw new Error(`Invalid BIP68 sequence: ${sequence}`);
|
|
59
|
+
}
|
|
@@ -1,44 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
3
|
exports.VtxoTreeExpiry = exports.CosignerPublicKey = exports.ConditionWitness = exports.VtxoTaprootTree = exports.ArkPsbtFieldKeyType = exports.ArkPsbtFieldKey = void 0;
|
|
37
4
|
exports.setArkPsbtField = setArkPsbtField;
|
|
38
5
|
exports.getArkPsbtFields = getArkPsbtFields;
|
|
39
|
-
const bip68 = __importStar(require("bip68"));
|
|
40
6
|
const btc_signer_1 = require("@scure/btc-signer");
|
|
41
7
|
const base_1 = require("@scure/base");
|
|
8
|
+
const timelock_1 = require("./timelock");
|
|
42
9
|
/**
|
|
43
10
|
* ArkPsbtFieldKey are the available key names for the Arkade PSBT custom fields.
|
|
44
11
|
*/
|
|
@@ -184,11 +151,7 @@ exports.VtxoTreeExpiry = {
|
|
|
184
151
|
const v = (0, btc_signer_1.ScriptNum)(6, true).decode(unknown[1]);
|
|
185
152
|
if (!v)
|
|
186
153
|
return null;
|
|
187
|
-
|
|
188
|
-
return {
|
|
189
|
-
type: blocks ? "blocks" : "seconds",
|
|
190
|
-
value: BigInt(blocks ?? seconds ?? 0),
|
|
191
|
-
};
|
|
154
|
+
return (0, timelock_1.sequenceToTimelock)(Number(v));
|
|
192
155
|
}),
|
|
193
156
|
};
|
|
194
157
|
const encodedPsbtFieldKey = Object.fromEntries(Object.values(ArkPsbtFieldKey).map((key) => [
|
|
@@ -5,6 +5,8 @@ const indexer_1 = require("../../providers/indexer");
|
|
|
5
5
|
const index_1 = require("../index");
|
|
6
6
|
const utils_1 = require("../utils");
|
|
7
7
|
const transactionHistory_1 = require("../../utils/transactionHistory");
|
|
8
|
+
const vtxoOwnership_1 = require("../../contracts/vtxoOwnership");
|
|
9
|
+
const scriptFromAddress_1 = require("../../repositories/scriptFromAddress");
|
|
8
10
|
class WalletNotInitializedError extends Error {
|
|
9
11
|
constructor() {
|
|
10
12
|
super("Wallet handler not initialized");
|
|
@@ -317,6 +319,16 @@ class WalletMessageHandler {
|
|
|
317
319
|
type: "REFRESH_VTXOS_SUCCESS",
|
|
318
320
|
});
|
|
319
321
|
}
|
|
322
|
+
case "REFRESH_OUTPOINTS": {
|
|
323
|
+
const manager = await this.readonlyWallet.getContractManager();
|
|
324
|
+
const { outpoints } = message
|
|
325
|
+
.payload;
|
|
326
|
+
await manager.refreshOutpoints(outpoints);
|
|
327
|
+
return this.tagged({
|
|
328
|
+
id,
|
|
329
|
+
type: "REFRESH_OUTPOINTS_SUCCESS",
|
|
330
|
+
});
|
|
331
|
+
}
|
|
320
332
|
case "SEND": {
|
|
321
333
|
const { recipients } = message.payload;
|
|
322
334
|
const txid = await this.wallet.send(...recipients);
|
|
@@ -587,11 +599,47 @@ class WalletMessageHandler {
|
|
|
587
599
|
const { newVtxos, spentVtxos } = funds;
|
|
588
600
|
if (newVtxos.length + spentVtxos.length === 0)
|
|
589
601
|
return;
|
|
590
|
-
//
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
602
|
+
// Save virtual outputs using unified repository. The
|
|
603
|
+
// event may carry rows for several scripts (other
|
|
604
|
+
// contracts the wallet watches), so split by script and
|
|
605
|
+
// save each bucket under its own contract address rather
|
|
606
|
+
// than saving a mixed-script array under one address.
|
|
607
|
+
const byScript = new Map();
|
|
608
|
+
for (const v of [...newVtxos, ...spentVtxos]) {
|
|
609
|
+
if (!v.script) {
|
|
610
|
+
// Without a script we can't route the row to the
|
|
611
|
+
// right contract bucket; surface the drop instead
|
|
612
|
+
// of silently losing the VTXO.
|
|
613
|
+
console.warn(`WalletMessageHandler.notifyIncomingFunds: dropping VTXO without script ${v.txid}:${v.vout}`);
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
616
|
+
const arr = byScript.get(v.script) ?? [];
|
|
617
|
+
arr.push(v);
|
|
618
|
+
byScript.set(v.script, arr);
|
|
619
|
+
}
|
|
620
|
+
let walletScript;
|
|
621
|
+
try {
|
|
622
|
+
walletScript = (0, scriptFromAddress_1.scriptFromArkAddress)(address);
|
|
623
|
+
}
|
|
624
|
+
catch {
|
|
625
|
+
walletScript = undefined;
|
|
626
|
+
}
|
|
627
|
+
const cm = await this.readonlyWallet.getContractManager();
|
|
628
|
+
const contracts = await cm.getContracts();
|
|
629
|
+
const addrByScript = new Map(contracts.map((c) => [c.script, c.address]));
|
|
630
|
+
for (const [script, vtxos] of byScript) {
|
|
631
|
+
const filtered = (0, vtxoOwnership_1.warnAndFilterVtxosForScript)(vtxos, script, "WalletMessageHandler.notifyIncomingFunds");
|
|
632
|
+
if (filtered.length === 0)
|
|
633
|
+
continue;
|
|
634
|
+
const targetAddress = script === walletScript
|
|
635
|
+
? address
|
|
636
|
+
: addrByScript.get(script);
|
|
637
|
+
if (!targetAddress)
|
|
638
|
+
continue;
|
|
639
|
+
if (this.walletRepository) {
|
|
640
|
+
await (0, vtxoOwnership_1.saveVtxosForContract)(this.walletRepository, { script, address: targetAddress }, filtered);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
595
643
|
// notify all clients about the virtual output state update
|
|
596
644
|
this.scheduleForNextTick(() => this.tagged({
|
|
597
645
|
type: "VTXO_UPDATE",
|
|
@@ -803,17 +851,30 @@ class WalletMessageHandler {
|
|
|
803
851
|
}
|
|
804
852
|
}
|
|
805
853
|
};
|
|
806
|
-
// Aggregate virtual outputs from all contract addresses
|
|
854
|
+
// Aggregate virtual outputs from all contract addresses. Address
|
|
855
|
+
// buckets may carry legacy duplicate rows from other contracts; gate
|
|
856
|
+
// each bucket by its owning contract script before deduplication so a
|
|
857
|
+
// wrong-script row never wins the txid:vout race.
|
|
807
858
|
const manager = await this.readonlyWallet.getContractManager();
|
|
808
859
|
const contracts = await manager.getContracts();
|
|
809
860
|
for (const contract of contracts) {
|
|
810
|
-
|
|
811
|
-
addVtxos(vtxos);
|
|
861
|
+
addVtxos(await (0, vtxoOwnership_1.getVtxosForContract)(this.walletRepository, contract));
|
|
812
862
|
}
|
|
813
|
-
// Also check the wallet's primary address
|
|
863
|
+
// Also check the wallet's primary address. Decode it to its script
|
|
864
|
+
// and apply the same script gate. Failing to decode the wallet's own
|
|
865
|
+
// address is a structural bug — surfacing the error is safer than
|
|
866
|
+
// silently dropping the primary bucket and zeroing the user's
|
|
867
|
+
// visible balance.
|
|
814
868
|
const walletAddress = await this.readonlyWallet.getAddress();
|
|
869
|
+
let walletScript;
|
|
870
|
+
try {
|
|
871
|
+
walletScript = (0, scriptFromAddress_1.scriptFromArkAddress)(walletAddress);
|
|
872
|
+
}
|
|
873
|
+
catch (e) {
|
|
874
|
+
throw new Error(`WalletMessageHandler.getVtxosFromRepo: failed to derive script from wallet address ${walletAddress}: ${e instanceof Error ? e.message : String(e)}`);
|
|
875
|
+
}
|
|
815
876
|
const walletVtxos = await this.walletRepository.getVtxos(walletAddress);
|
|
816
|
-
addVtxos(walletVtxos);
|
|
877
|
+
addVtxos((0, vtxoOwnership_1.filterVtxosForScript)(walletVtxos, walletScript));
|
|
817
878
|
return allVtxos;
|
|
818
879
|
}
|
|
819
880
|
/**
|
|
@@ -60,6 +60,7 @@ exports.DEFAULT_MESSAGE_TIMEOUTS = {
|
|
|
60
60
|
UPDATE_CONTRACT: 30000,
|
|
61
61
|
DELETE_CONTRACT: 10000,
|
|
62
62
|
REFRESH_VTXOS: 30000,
|
|
63
|
+
REFRESH_OUTPOINTS: 30000,
|
|
63
64
|
};
|
|
64
65
|
const DEDUPABLE_REQUEST_TYPES = new Set([
|
|
65
66
|
"GET_ADDRESS",
|
|
@@ -824,6 +825,15 @@ class ServiceWorkerReadonlyWallet {
|
|
|
824
825
|
};
|
|
825
826
|
await sendContractMessage(message);
|
|
826
827
|
},
|
|
828
|
+
async refreshOutpoints(outpoints) {
|
|
829
|
+
const message = {
|
|
830
|
+
type: "REFRESH_OUTPOINTS",
|
|
831
|
+
id: (0, utils_2.getRandomId)(),
|
|
832
|
+
tag: messageTag,
|
|
833
|
+
payload: { outpoints },
|
|
834
|
+
};
|
|
835
|
+
await sendContractMessage(message);
|
|
836
|
+
},
|
|
827
837
|
async isWatching() {
|
|
828
838
|
const message = {
|
|
829
839
|
type: "IS_CONTRACT_MANAGER_WATCHING",
|