@arkade-os/sdk 0.2.0 → 0.2.2
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 +1 -0
- package/dist/cjs/index.js +3 -1
- package/dist/cjs/providers/ark.js +9 -5
- package/dist/cjs/script/tapscript.js +15 -6
- package/dist/cjs/script/vhtlc.js +58 -0
- package/dist/cjs/wallet/serviceWorker/request.js +1 -1
- package/dist/cjs/wallet/serviceWorker/response.js +2 -1
- package/dist/cjs/wallet/serviceWorker/utils.js +23 -8
- package/dist/cjs/wallet/serviceWorker/wallet.js +8 -1
- package/dist/cjs/wallet/serviceWorker/worker.js +5 -2
- package/dist/esm/index.js +2 -1
- package/dist/esm/providers/ark.js +9 -5
- package/dist/esm/script/tapscript.js +15 -6
- package/dist/esm/script/vhtlc.js +58 -0
- package/dist/esm/wallet/serviceWorker/request.js +1 -1
- package/dist/esm/wallet/serviceWorker/response.js +2 -1
- package/dist/esm/wallet/serviceWorker/utils.js +23 -8
- package/dist/esm/wallet/serviceWorker/wallet.js +8 -1
- package/dist/esm/wallet/serviceWorker/worker.js +5 -2
- package/dist/types/index.d.ts +2 -1
- package/dist/types/wallet/serviceWorker/response.d.ts +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
The Arkade SDK is a TypeScript library for building Bitcoin wallets with support for both on-chain and off-chain transactions via the Ark protocol.
|
|
3
3
|
|
|
4
4
|
[](https://arkade-os.github.io/ts-sdk/)
|
|
5
|
+
[](https://deepwiki.com/arkade-os/ts-sdk)
|
|
5
6
|
|
|
6
7
|
## Installation
|
|
7
8
|
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Unroll = exports.P2A = exports.TxTree = exports.BIP322 = exports.IndexedDBVtxoRepository = exports.networks = exports.ArkNote = exports.waitForIncomingFunds = exports.buildOffchainTx = exports.ConditionWitness = exports.VtxoTaprootTree = exports.VtxoTreeExpiry = exports.CosignerPublicKey = exports.getArkPsbtFields = exports.setArkPsbtField = exports.ArkPsbtFieldKeyType = exports.ArkPsbtFieldKey = exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.decodeTapscript = exports.Response = exports.Request = exports.ServiceWorkerWallet = exports.Worker = exports.setupServiceWorker = exports.SettlementEventType = exports.ChainTxType = exports.IndexerTxType = exports.TxType = exports.VHTLC = exports.VtxoScript = exports.DefaultVtxo = exports.ArkAddress = exports.RestIndexerProvider = exports.RestArkProvider = exports.EsploraProvider = exports.ESPLORA_URL = exports.Ramps = exports.OnchainWallet = exports.SingleKey = exports.Wallet = void 0;
|
|
3
|
+
exports.Transaction = exports.Unroll = exports.P2A = exports.TxTree = exports.BIP322 = exports.IndexedDBVtxoRepository = exports.networks = exports.ArkNote = exports.waitForIncomingFunds = exports.buildOffchainTx = exports.ConditionWitness = exports.VtxoTaprootTree = exports.VtxoTreeExpiry = exports.CosignerPublicKey = exports.getArkPsbtFields = exports.setArkPsbtField = exports.ArkPsbtFieldKeyType = exports.ArkPsbtFieldKey = exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.decodeTapscript = exports.Response = exports.Request = exports.ServiceWorkerWallet = exports.Worker = exports.setupServiceWorker = exports.SettlementEventType = exports.ChainTxType = exports.IndexerTxType = exports.TxType = exports.VHTLC = exports.VtxoScript = exports.DefaultVtxo = exports.ArkAddress = exports.RestIndexerProvider = exports.RestArkProvider = exports.EsploraProvider = exports.ESPLORA_URL = exports.Ramps = exports.OnchainWallet = exports.SingleKey = exports.Wallet = void 0;
|
|
4
|
+
const btc_signer_1 = require("@scure/btc-signer");
|
|
5
|
+
Object.defineProperty(exports, "Transaction", { enumerable: true, get: function () { return btc_signer_1.Transaction; } });
|
|
4
6
|
const singleKey_1 = require("./identity/singleKey");
|
|
5
7
|
Object.defineProperty(exports, "SingleKey", { enumerable: true, get: function () { return singleKey_1.SingleKey; } });
|
|
6
8
|
const address_1 = require("./script/address");
|
|
@@ -469,11 +469,15 @@ function decodeMusig2Nonces(str) {
|
|
|
469
469
|
}
|
|
470
470
|
function isFetchTimeoutError(err) {
|
|
471
471
|
const checkError = (error) => {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
472
|
+
if (!(error instanceof Error))
|
|
473
|
+
return false;
|
|
474
|
+
// TODO: get something more robust than this
|
|
475
|
+
const isCloudflare524 = error.name === "TypeError" && error.message === "Failed to fetch";
|
|
476
|
+
return (isCloudflare524 ||
|
|
477
|
+
error.name === "HeadersTimeoutError" ||
|
|
478
|
+
error.name === "BodyTimeoutError" ||
|
|
479
|
+
error.code === "UND_ERR_HEADERS_TIMEOUT" ||
|
|
480
|
+
error.code === "UND_ERR_BODY_TIMEOUT");
|
|
477
481
|
};
|
|
478
482
|
return checkError(err) || checkError(err.cause);
|
|
479
483
|
}
|
|
@@ -39,6 +39,7 @@ const bip68 = __importStar(require("bip68"));
|
|
|
39
39
|
const script_1 = require("@scure/btc-signer/script");
|
|
40
40
|
const payment_1 = require("@scure/btc-signer/payment");
|
|
41
41
|
const base_1 = require("@scure/base");
|
|
42
|
+
const MinimalScriptNum = (0, script_1.ScriptNum)(undefined, true);
|
|
42
43
|
var TapscriptType;
|
|
43
44
|
(function (TapscriptType) {
|
|
44
45
|
TapscriptType["Multisig"] = "multisig";
|
|
@@ -267,10 +268,14 @@ var CSVMultisigTapscript;
|
|
|
267
268
|
throw new Error(`Invalid pubkey length: expected 32, got ${pubkey.length}`);
|
|
268
269
|
}
|
|
269
270
|
}
|
|
270
|
-
const sequence =
|
|
271
|
+
const sequence = MinimalScriptNum.encode(BigInt(bip68.encode(params.timelock.type === "blocks"
|
|
271
272
|
? { blocks: Number(params.timelock.value) }
|
|
272
273
|
: { seconds: Number(params.timelock.value) })));
|
|
273
|
-
const asm = [
|
|
274
|
+
const asm = [
|
|
275
|
+
sequence.length === 1 ? sequence[0] : sequence,
|
|
276
|
+
"CHECKSEQUENCEVERIFY",
|
|
277
|
+
"DROP",
|
|
278
|
+
];
|
|
274
279
|
const multisigScript = MultisigTapscript.encode(params);
|
|
275
280
|
const script = new Uint8Array([
|
|
276
281
|
...script_1.Script.encode(asm),
|
|
@@ -306,7 +311,7 @@ var CSVMultisigTapscript;
|
|
|
306
311
|
catch (error) {
|
|
307
312
|
throw new Error(`Invalid multisig script: ${error instanceof Error ? error.message : String(error)}`);
|
|
308
313
|
}
|
|
309
|
-
const sequenceNum = Number(
|
|
314
|
+
const sequenceNum = Number(MinimalScriptNum.decode(sequence));
|
|
310
315
|
const decodedTimelock = bip68.decode(sequenceNum);
|
|
311
316
|
const timelock = decodedTimelock.blocks !== undefined
|
|
312
317
|
? { type: "blocks", value: BigInt(decodedTimelock.blocks) }
|
|
@@ -498,8 +503,12 @@ var ConditionMultisigTapscript;
|
|
|
498
503
|
var CLTVMultisigTapscript;
|
|
499
504
|
(function (CLTVMultisigTapscript) {
|
|
500
505
|
function encode(params) {
|
|
501
|
-
const locktime =
|
|
502
|
-
const asm = [
|
|
506
|
+
const locktime = MinimalScriptNum.encode(params.absoluteTimelock);
|
|
507
|
+
const asm = [
|
|
508
|
+
locktime.length === 1 ? locktime[0] : locktime,
|
|
509
|
+
"CHECKLOCKTIMEVERIFY",
|
|
510
|
+
"DROP",
|
|
511
|
+
];
|
|
503
512
|
const timelockedScript = script_1.Script.encode(asm);
|
|
504
513
|
const script = new Uint8Array([
|
|
505
514
|
...timelockedScript,
|
|
@@ -535,7 +544,7 @@ var CLTVMultisigTapscript;
|
|
|
535
544
|
catch (error) {
|
|
536
545
|
throw new Error(`Invalid multisig script: ${error instanceof Error ? error.message : String(error)}`);
|
|
537
546
|
}
|
|
538
|
-
const absoluteTimelock =
|
|
547
|
+
const absoluteTimelock = MinimalScriptNum.decode(locktime);
|
|
539
548
|
const reconstructed = encode({
|
|
540
549
|
absoluteTimelock,
|
|
541
550
|
...multisig.params,
|
package/dist/cjs/script/vhtlc.js
CHANGED
|
@@ -36,6 +36,7 @@ var VHTLC;
|
|
|
36
36
|
(function (VHTLC) {
|
|
37
37
|
class Script extends base_2.VtxoScript {
|
|
38
38
|
constructor(options) {
|
|
39
|
+
validateOptions(options);
|
|
39
40
|
const { sender, receiver, server, preimageHash, refundLocktime, unilateralClaimDelay, unilateralRefundDelay, unilateralRefundWithoutReceiverDelay, } = options;
|
|
40
41
|
const conditionScript = preimageConditionScript(preimageHash);
|
|
41
42
|
const claimScript = tapscript_1.ConditionMultisigTapscript.encode({
|
|
@@ -98,6 +99,63 @@ var VHTLC;
|
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
101
|
VHTLC.Script = Script;
|
|
102
|
+
function validateOptions(options) {
|
|
103
|
+
const { sender, receiver, server, preimageHash, refundLocktime, unilateralClaimDelay, unilateralRefundDelay, unilateralRefundWithoutReceiverDelay, } = options;
|
|
104
|
+
if (!preimageHash || preimageHash.length !== 20) {
|
|
105
|
+
throw new Error("preimage hash must be 20 bytes");
|
|
106
|
+
}
|
|
107
|
+
if (!receiver || receiver.length !== 32) {
|
|
108
|
+
throw new Error("Invalid public key length (receiver)");
|
|
109
|
+
}
|
|
110
|
+
if (!sender || sender.length !== 32) {
|
|
111
|
+
throw new Error("Invalid public key length (sender)");
|
|
112
|
+
}
|
|
113
|
+
if (!server || server.length !== 32) {
|
|
114
|
+
throw new Error("Invalid public key length (server)");
|
|
115
|
+
}
|
|
116
|
+
if (typeof refundLocktime !== "bigint" || refundLocktime <= 0n) {
|
|
117
|
+
throw new Error("refund locktime must be greater than 0");
|
|
118
|
+
}
|
|
119
|
+
if (!unilateralClaimDelay ||
|
|
120
|
+
typeof unilateralClaimDelay.value !== "bigint" ||
|
|
121
|
+
unilateralClaimDelay.value <= 0n) {
|
|
122
|
+
throw new Error("unilateral claim delay must greater than 0");
|
|
123
|
+
}
|
|
124
|
+
if (unilateralClaimDelay.type === "seconds" &&
|
|
125
|
+
unilateralClaimDelay.value % 512n !== 0n) {
|
|
126
|
+
throw new Error("seconds timelock must be multiple of 512");
|
|
127
|
+
}
|
|
128
|
+
if (unilateralClaimDelay.type === "seconds" &&
|
|
129
|
+
unilateralClaimDelay.value < 512n) {
|
|
130
|
+
throw new Error("seconds timelock must be greater or equal to 512");
|
|
131
|
+
}
|
|
132
|
+
if (!unilateralRefundDelay ||
|
|
133
|
+
typeof unilateralRefundDelay.value !== "bigint" ||
|
|
134
|
+
unilateralRefundDelay.value <= 0n) {
|
|
135
|
+
throw new Error("unilateral refund delay must greater than 0");
|
|
136
|
+
}
|
|
137
|
+
if (unilateralRefundDelay.type === "seconds" &&
|
|
138
|
+
unilateralRefundDelay.value % 512n !== 0n) {
|
|
139
|
+
throw new Error("seconds timelock must be multiple of 512");
|
|
140
|
+
}
|
|
141
|
+
if (unilateralRefundDelay.type === "seconds" &&
|
|
142
|
+
unilateralRefundDelay.value < 512n) {
|
|
143
|
+
throw new Error("seconds timelock must be greater or equal to 512");
|
|
144
|
+
}
|
|
145
|
+
if (!unilateralRefundWithoutReceiverDelay ||
|
|
146
|
+
typeof unilateralRefundWithoutReceiverDelay.value !== "bigint" ||
|
|
147
|
+
unilateralRefundWithoutReceiverDelay.value <= 0n) {
|
|
148
|
+
throw new Error("unilateral refund without receiver delay must greater than 0");
|
|
149
|
+
}
|
|
150
|
+
if (unilateralRefundWithoutReceiverDelay.type === "seconds" &&
|
|
151
|
+
unilateralRefundWithoutReceiverDelay.value % 512n !== 0n) {
|
|
152
|
+
throw new Error("seconds timelock must be multiple of 512");
|
|
153
|
+
}
|
|
154
|
+
if (unilateralRefundWithoutReceiverDelay.type === "seconds" &&
|
|
155
|
+
unilateralRefundWithoutReceiverDelay.value < 512n) {
|
|
156
|
+
throw new Error("seconds timelock must be greater or equal to 512");
|
|
157
|
+
}
|
|
158
|
+
}
|
|
101
159
|
})(VHTLC || (exports.VHTLC = VHTLC = {}));
|
|
102
160
|
function preimageConditionScript(preimageHash) {
|
|
103
161
|
return btc_signer_1.Script.encode(["HASH160", preimageHash, "EQUAL"]);
|
|
@@ -73,7 +73,7 @@ var Request;
|
|
|
73
73
|
return (message.type === "SIGN" &&
|
|
74
74
|
"tx" in message &&
|
|
75
75
|
typeof message.tx === "string" &&
|
|
76
|
-
("inputIndexes" in message
|
|
76
|
+
("inputIndexes" in message && message.inputIndexes != undefined
|
|
77
77
|
? Array.isArray(message.inputIndexes) &&
|
|
78
78
|
message.inputIndexes.every((index) => typeof index === "number")
|
|
79
79
|
: true));
|
|
@@ -151,12 +151,13 @@ var Response;
|
|
|
151
151
|
return response.type === "WALLET_STATUS" && response.success === true;
|
|
152
152
|
}
|
|
153
153
|
Response.isWalletStatus = isWalletStatus;
|
|
154
|
-
function walletStatus(id, walletInitialized) {
|
|
154
|
+
function walletStatus(id, walletInitialized, xOnlyPublicKey) {
|
|
155
155
|
return {
|
|
156
156
|
type: "WALLET_STATUS",
|
|
157
157
|
success: true,
|
|
158
158
|
status: {
|
|
159
159
|
walletInitialized,
|
|
160
|
+
xOnlyPublicKey,
|
|
160
161
|
},
|
|
161
162
|
id,
|
|
162
163
|
};
|
|
@@ -23,12 +23,27 @@ async function setupServiceWorker(path) {
|
|
|
23
23
|
throw new Error("Failed to get service worker instance");
|
|
24
24
|
}
|
|
25
25
|
// wait for the service worker to be ready
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
if (serviceWorker.state === "activated")
|
|
28
|
+
return resolve(serviceWorker);
|
|
29
|
+
const onActivate = () => {
|
|
30
|
+
cleanup();
|
|
31
|
+
resolve(serviceWorker);
|
|
32
|
+
};
|
|
33
|
+
const onError = () => {
|
|
34
|
+
cleanup();
|
|
35
|
+
reject(new Error("Service worker failed to activate"));
|
|
36
|
+
};
|
|
37
|
+
const timeout = setTimeout(() => {
|
|
38
|
+
cleanup();
|
|
39
|
+
reject(new Error("Service worker activation timed out"));
|
|
40
|
+
}, 10000);
|
|
41
|
+
const cleanup = () => {
|
|
42
|
+
serviceWorker.removeEventListener("activate", onActivate);
|
|
43
|
+
serviceWorker.removeEventListener("error", onError);
|
|
44
|
+
clearTimeout(timeout);
|
|
45
|
+
};
|
|
46
|
+
serviceWorker.addEventListener("activate", onActivate);
|
|
47
|
+
serviceWorker.addEventListener("error", onError);
|
|
48
|
+
});
|
|
34
49
|
}
|
|
@@ -46,6 +46,9 @@ class ServiceWorkerWallet {
|
|
|
46
46
|
};
|
|
47
47
|
const response = await this.sendMessage(message);
|
|
48
48
|
if (response_1.Response.isWalletStatus(response)) {
|
|
49
|
+
const { walletInitialized, xOnlyPublicKey } = response.status;
|
|
50
|
+
if (walletInitialized)
|
|
51
|
+
this.cachedXOnlyPublicKey = xOnlyPublicKey;
|
|
49
52
|
return response.status;
|
|
50
53
|
}
|
|
51
54
|
throw new UnexpectedResponseError(response);
|
|
@@ -62,6 +65,7 @@ class ServiceWorkerWallet {
|
|
|
62
65
|
if (failIfInitialized) {
|
|
63
66
|
throw new Error("Wallet already initialized");
|
|
64
67
|
}
|
|
68
|
+
this.cachedXOnlyPublicKey = response.status.xOnlyPublicKey;
|
|
65
69
|
return;
|
|
66
70
|
}
|
|
67
71
|
// If not initialized, proceed with initialization
|
|
@@ -281,7 +285,10 @@ class ServiceWorkerWallet {
|
|
|
281
285
|
try {
|
|
282
286
|
const response = await this.sendMessage(message);
|
|
283
287
|
if (response_1.Response.isSignSuccess(response)) {
|
|
284
|
-
return btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(response.tx)
|
|
288
|
+
return btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(response.tx), {
|
|
289
|
+
allowUnknown: true,
|
|
290
|
+
allowUnknownInputs: true,
|
|
291
|
+
});
|
|
285
292
|
}
|
|
286
293
|
throw new UnexpectedResponseError(response);
|
|
287
294
|
}
|
|
@@ -407,7 +407,7 @@ class Worker {
|
|
|
407
407
|
event.source?.postMessage(response_1.Response.error(message.id, "Invalid GET_STATUS message format"));
|
|
408
408
|
return;
|
|
409
409
|
}
|
|
410
|
-
event.source?.postMessage(response_1.Response.walletStatus(message.id, this.wallet !== undefined));
|
|
410
|
+
event.source?.postMessage(response_1.Response.walletStatus(message.id, this.wallet !== undefined, this.wallet?.identity.xOnlyPublicKey()));
|
|
411
411
|
}
|
|
412
412
|
async handleSign(event) {
|
|
413
413
|
const message = event.data;
|
|
@@ -422,7 +422,10 @@ class Worker {
|
|
|
422
422
|
return;
|
|
423
423
|
}
|
|
424
424
|
try {
|
|
425
|
-
const tx = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(message.tx)
|
|
425
|
+
const tx = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(message.tx), {
|
|
426
|
+
allowUnknown: true,
|
|
427
|
+
allowUnknownInputs: true,
|
|
428
|
+
});
|
|
426
429
|
const signedTx = await this.wallet.identity.sign(tx, message.inputIndexes);
|
|
427
430
|
event.source?.postMessage(response_1.Response.signSuccess(message.id, base_1.base64.encode(signedTx.toPSBT())));
|
|
428
431
|
}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Transaction } from "@scure/btc-signer";
|
|
1
2
|
import { SingleKey } from './identity/singleKey.js';
|
|
2
3
|
import { ArkAddress } from './script/address.js';
|
|
3
4
|
import { VHTLC } from './script/vhtlc.js';
|
|
@@ -53,4 +54,4 @@ BIP322,
|
|
|
53
54
|
// TxTree
|
|
54
55
|
TxTree,
|
|
55
56
|
// Anchor
|
|
56
|
-
P2A, Unroll, };
|
|
57
|
+
P2A, Unroll, Transaction, };
|
|
@@ -464,11 +464,15 @@ function decodeMusig2Nonces(str) {
|
|
|
464
464
|
}
|
|
465
465
|
export function isFetchTimeoutError(err) {
|
|
466
466
|
const checkError = (error) => {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
467
|
+
if (!(error instanceof Error))
|
|
468
|
+
return false;
|
|
469
|
+
// TODO: get something more robust than this
|
|
470
|
+
const isCloudflare524 = error.name === "TypeError" && error.message === "Failed to fetch";
|
|
471
|
+
return (isCloudflare524 ||
|
|
472
|
+
error.name === "HeadersTimeoutError" ||
|
|
473
|
+
error.name === "BodyTimeoutError" ||
|
|
474
|
+
error.code === "UND_ERR_HEADERS_TIMEOUT" ||
|
|
475
|
+
error.code === "UND_ERR_BODY_TIMEOUT");
|
|
472
476
|
};
|
|
473
477
|
return checkError(err) || checkError(err.cause);
|
|
474
478
|
}
|
|
@@ -2,6 +2,7 @@ import * as bip68 from "bip68";
|
|
|
2
2
|
import { Script, ScriptNum } from "@scure/btc-signer/script";
|
|
3
3
|
import { p2tr_ms } from "@scure/btc-signer/payment";
|
|
4
4
|
import { hex } from "@scure/base";
|
|
5
|
+
const MinimalScriptNum = ScriptNum(undefined, true);
|
|
5
6
|
export var TapscriptType;
|
|
6
7
|
(function (TapscriptType) {
|
|
7
8
|
TapscriptType["Multisig"] = "multisig";
|
|
@@ -230,10 +231,14 @@ export var CSVMultisigTapscript;
|
|
|
230
231
|
throw new Error(`Invalid pubkey length: expected 32, got ${pubkey.length}`);
|
|
231
232
|
}
|
|
232
233
|
}
|
|
233
|
-
const sequence =
|
|
234
|
+
const sequence = MinimalScriptNum.encode(BigInt(bip68.encode(params.timelock.type === "blocks"
|
|
234
235
|
? { blocks: Number(params.timelock.value) }
|
|
235
236
|
: { seconds: Number(params.timelock.value) })));
|
|
236
|
-
const asm = [
|
|
237
|
+
const asm = [
|
|
238
|
+
sequence.length === 1 ? sequence[0] : sequence,
|
|
239
|
+
"CHECKSEQUENCEVERIFY",
|
|
240
|
+
"DROP",
|
|
241
|
+
];
|
|
237
242
|
const multisigScript = MultisigTapscript.encode(params);
|
|
238
243
|
const script = new Uint8Array([
|
|
239
244
|
...Script.encode(asm),
|
|
@@ -269,7 +274,7 @@ export var CSVMultisigTapscript;
|
|
|
269
274
|
catch (error) {
|
|
270
275
|
throw new Error(`Invalid multisig script: ${error instanceof Error ? error.message : String(error)}`);
|
|
271
276
|
}
|
|
272
|
-
const sequenceNum = Number(
|
|
277
|
+
const sequenceNum = Number(MinimalScriptNum.decode(sequence));
|
|
273
278
|
const decodedTimelock = bip68.decode(sequenceNum);
|
|
274
279
|
const timelock = decodedTimelock.blocks !== undefined
|
|
275
280
|
? { type: "blocks", value: BigInt(decodedTimelock.blocks) }
|
|
@@ -461,8 +466,12 @@ export var ConditionMultisigTapscript;
|
|
|
461
466
|
export var CLTVMultisigTapscript;
|
|
462
467
|
(function (CLTVMultisigTapscript) {
|
|
463
468
|
function encode(params) {
|
|
464
|
-
const locktime =
|
|
465
|
-
const asm = [
|
|
469
|
+
const locktime = MinimalScriptNum.encode(params.absoluteTimelock);
|
|
470
|
+
const asm = [
|
|
471
|
+
locktime.length === 1 ? locktime[0] : locktime,
|
|
472
|
+
"CHECKLOCKTIMEVERIFY",
|
|
473
|
+
"DROP",
|
|
474
|
+
];
|
|
466
475
|
const timelockedScript = Script.encode(asm);
|
|
467
476
|
const script = new Uint8Array([
|
|
468
477
|
...timelockedScript,
|
|
@@ -498,7 +507,7 @@ export var CLTVMultisigTapscript;
|
|
|
498
507
|
catch (error) {
|
|
499
508
|
throw new Error(`Invalid multisig script: ${error instanceof Error ? error.message : String(error)}`);
|
|
500
509
|
}
|
|
501
|
-
const absoluteTimelock =
|
|
510
|
+
const absoluteTimelock = MinimalScriptNum.decode(locktime);
|
|
502
511
|
const reconstructed = encode({
|
|
503
512
|
absoluteTimelock,
|
|
504
513
|
...multisig.params,
|
package/dist/esm/script/vhtlc.js
CHANGED
|
@@ -33,6 +33,7 @@ export var VHTLC;
|
|
|
33
33
|
(function (VHTLC) {
|
|
34
34
|
class Script extends VtxoScript {
|
|
35
35
|
constructor(options) {
|
|
36
|
+
validateOptions(options);
|
|
36
37
|
const { sender, receiver, server, preimageHash, refundLocktime, unilateralClaimDelay, unilateralRefundDelay, unilateralRefundWithoutReceiverDelay, } = options;
|
|
37
38
|
const conditionScript = preimageConditionScript(preimageHash);
|
|
38
39
|
const claimScript = ConditionMultisigTapscript.encode({
|
|
@@ -95,6 +96,63 @@ export var VHTLC;
|
|
|
95
96
|
}
|
|
96
97
|
}
|
|
97
98
|
VHTLC.Script = Script;
|
|
99
|
+
function validateOptions(options) {
|
|
100
|
+
const { sender, receiver, server, preimageHash, refundLocktime, unilateralClaimDelay, unilateralRefundDelay, unilateralRefundWithoutReceiverDelay, } = options;
|
|
101
|
+
if (!preimageHash || preimageHash.length !== 20) {
|
|
102
|
+
throw new Error("preimage hash must be 20 bytes");
|
|
103
|
+
}
|
|
104
|
+
if (!receiver || receiver.length !== 32) {
|
|
105
|
+
throw new Error("Invalid public key length (receiver)");
|
|
106
|
+
}
|
|
107
|
+
if (!sender || sender.length !== 32) {
|
|
108
|
+
throw new Error("Invalid public key length (sender)");
|
|
109
|
+
}
|
|
110
|
+
if (!server || server.length !== 32) {
|
|
111
|
+
throw new Error("Invalid public key length (server)");
|
|
112
|
+
}
|
|
113
|
+
if (typeof refundLocktime !== "bigint" || refundLocktime <= 0n) {
|
|
114
|
+
throw new Error("refund locktime must be greater than 0");
|
|
115
|
+
}
|
|
116
|
+
if (!unilateralClaimDelay ||
|
|
117
|
+
typeof unilateralClaimDelay.value !== "bigint" ||
|
|
118
|
+
unilateralClaimDelay.value <= 0n) {
|
|
119
|
+
throw new Error("unilateral claim delay must greater than 0");
|
|
120
|
+
}
|
|
121
|
+
if (unilateralClaimDelay.type === "seconds" &&
|
|
122
|
+
unilateralClaimDelay.value % 512n !== 0n) {
|
|
123
|
+
throw new Error("seconds timelock must be multiple of 512");
|
|
124
|
+
}
|
|
125
|
+
if (unilateralClaimDelay.type === "seconds" &&
|
|
126
|
+
unilateralClaimDelay.value < 512n) {
|
|
127
|
+
throw new Error("seconds timelock must be greater or equal to 512");
|
|
128
|
+
}
|
|
129
|
+
if (!unilateralRefundDelay ||
|
|
130
|
+
typeof unilateralRefundDelay.value !== "bigint" ||
|
|
131
|
+
unilateralRefundDelay.value <= 0n) {
|
|
132
|
+
throw new Error("unilateral refund delay must greater than 0");
|
|
133
|
+
}
|
|
134
|
+
if (unilateralRefundDelay.type === "seconds" &&
|
|
135
|
+
unilateralRefundDelay.value % 512n !== 0n) {
|
|
136
|
+
throw new Error("seconds timelock must be multiple of 512");
|
|
137
|
+
}
|
|
138
|
+
if (unilateralRefundDelay.type === "seconds" &&
|
|
139
|
+
unilateralRefundDelay.value < 512n) {
|
|
140
|
+
throw new Error("seconds timelock must be greater or equal to 512");
|
|
141
|
+
}
|
|
142
|
+
if (!unilateralRefundWithoutReceiverDelay ||
|
|
143
|
+
typeof unilateralRefundWithoutReceiverDelay.value !== "bigint" ||
|
|
144
|
+
unilateralRefundWithoutReceiverDelay.value <= 0n) {
|
|
145
|
+
throw new Error("unilateral refund without receiver delay must greater than 0");
|
|
146
|
+
}
|
|
147
|
+
if (unilateralRefundWithoutReceiverDelay.type === "seconds" &&
|
|
148
|
+
unilateralRefundWithoutReceiverDelay.value % 512n !== 0n) {
|
|
149
|
+
throw new Error("seconds timelock must be multiple of 512");
|
|
150
|
+
}
|
|
151
|
+
if (unilateralRefundWithoutReceiverDelay.type === "seconds" &&
|
|
152
|
+
unilateralRefundWithoutReceiverDelay.value < 512n) {
|
|
153
|
+
throw new Error("seconds timelock must be greater or equal to 512");
|
|
154
|
+
}
|
|
155
|
+
}
|
|
98
156
|
})(VHTLC || (VHTLC = {}));
|
|
99
157
|
function preimageConditionScript(preimageHash) {
|
|
100
158
|
return Script.encode(["HASH160", preimageHash, "EQUAL"]);
|
|
@@ -70,7 +70,7 @@ export var Request;
|
|
|
70
70
|
return (message.type === "SIGN" &&
|
|
71
71
|
"tx" in message &&
|
|
72
72
|
typeof message.tx === "string" &&
|
|
73
|
-
("inputIndexes" in message
|
|
73
|
+
("inputIndexes" in message && message.inputIndexes != undefined
|
|
74
74
|
? Array.isArray(message.inputIndexes) &&
|
|
75
75
|
message.inputIndexes.every((index) => typeof index === "number")
|
|
76
76
|
: true));
|
|
@@ -148,12 +148,13 @@ export var Response;
|
|
|
148
148
|
return response.type === "WALLET_STATUS" && response.success === true;
|
|
149
149
|
}
|
|
150
150
|
Response.isWalletStatus = isWalletStatus;
|
|
151
|
-
function walletStatus(id, walletInitialized) {
|
|
151
|
+
function walletStatus(id, walletInitialized, xOnlyPublicKey) {
|
|
152
152
|
return {
|
|
153
153
|
type: "WALLET_STATUS",
|
|
154
154
|
success: true,
|
|
155
155
|
status: {
|
|
156
156
|
walletInitialized,
|
|
157
|
+
xOnlyPublicKey,
|
|
157
158
|
},
|
|
158
159
|
id,
|
|
159
160
|
};
|
|
@@ -20,12 +20,27 @@ export async function setupServiceWorker(path) {
|
|
|
20
20
|
throw new Error("Failed to get service worker instance");
|
|
21
21
|
}
|
|
22
22
|
// wait for the service worker to be ready
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
if (serviceWorker.state === "activated")
|
|
25
|
+
return resolve(serviceWorker);
|
|
26
|
+
const onActivate = () => {
|
|
27
|
+
cleanup();
|
|
28
|
+
resolve(serviceWorker);
|
|
29
|
+
};
|
|
30
|
+
const onError = () => {
|
|
31
|
+
cleanup();
|
|
32
|
+
reject(new Error("Service worker failed to activate"));
|
|
33
|
+
};
|
|
34
|
+
const timeout = setTimeout(() => {
|
|
35
|
+
cleanup();
|
|
36
|
+
reject(new Error("Service worker activation timed out"));
|
|
37
|
+
}, 10000);
|
|
38
|
+
const cleanup = () => {
|
|
39
|
+
serviceWorker.removeEventListener("activate", onActivate);
|
|
40
|
+
serviceWorker.removeEventListener("error", onError);
|
|
41
|
+
clearTimeout(timeout);
|
|
42
|
+
};
|
|
43
|
+
serviceWorker.addEventListener("activate", onActivate);
|
|
44
|
+
serviceWorker.addEventListener("error", onError);
|
|
45
|
+
});
|
|
31
46
|
}
|
|
@@ -43,6 +43,9 @@ export class ServiceWorkerWallet {
|
|
|
43
43
|
};
|
|
44
44
|
const response = await this.sendMessage(message);
|
|
45
45
|
if (Response.isWalletStatus(response)) {
|
|
46
|
+
const { walletInitialized, xOnlyPublicKey } = response.status;
|
|
47
|
+
if (walletInitialized)
|
|
48
|
+
this.cachedXOnlyPublicKey = xOnlyPublicKey;
|
|
46
49
|
return response.status;
|
|
47
50
|
}
|
|
48
51
|
throw new UnexpectedResponseError(response);
|
|
@@ -59,6 +62,7 @@ export class ServiceWorkerWallet {
|
|
|
59
62
|
if (failIfInitialized) {
|
|
60
63
|
throw new Error("Wallet already initialized");
|
|
61
64
|
}
|
|
65
|
+
this.cachedXOnlyPublicKey = response.status.xOnlyPublicKey;
|
|
62
66
|
return;
|
|
63
67
|
}
|
|
64
68
|
// If not initialized, proceed with initialization
|
|
@@ -278,7 +282,10 @@ export class ServiceWorkerWallet {
|
|
|
278
282
|
try {
|
|
279
283
|
const response = await this.sendMessage(message);
|
|
280
284
|
if (Response.isSignSuccess(response)) {
|
|
281
|
-
return Transaction.fromPSBT(base64.decode(response.tx)
|
|
285
|
+
return Transaction.fromPSBT(base64.decode(response.tx), {
|
|
286
|
+
allowUnknown: true,
|
|
287
|
+
allowUnknownInputs: true,
|
|
288
|
+
});
|
|
282
289
|
}
|
|
283
290
|
throw new UnexpectedResponseError(response);
|
|
284
291
|
}
|
|
@@ -404,7 +404,7 @@ export class Worker {
|
|
|
404
404
|
event.source?.postMessage(Response.error(message.id, "Invalid GET_STATUS message format"));
|
|
405
405
|
return;
|
|
406
406
|
}
|
|
407
|
-
event.source?.postMessage(Response.walletStatus(message.id, this.wallet !== undefined));
|
|
407
|
+
event.source?.postMessage(Response.walletStatus(message.id, this.wallet !== undefined, this.wallet?.identity.xOnlyPublicKey()));
|
|
408
408
|
}
|
|
409
409
|
async handleSign(event) {
|
|
410
410
|
const message = event.data;
|
|
@@ -419,7 +419,10 @@ export class Worker {
|
|
|
419
419
|
return;
|
|
420
420
|
}
|
|
421
421
|
try {
|
|
422
|
-
const tx = Transaction.fromPSBT(base64.decode(message.tx)
|
|
422
|
+
const tx = Transaction.fromPSBT(base64.decode(message.tx), {
|
|
423
|
+
allowUnknown: true,
|
|
424
|
+
allowUnknownInputs: true,
|
|
425
|
+
});
|
|
423
426
|
const signedTx = await this.wallet.identity.sign(tx, message.inputIndexes);
|
|
424
427
|
event.source?.postMessage(Response.signSuccess(message.id, base64.encode(signedTx.toPSBT())));
|
|
425
428
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Transaction } from "@scure/btc-signer";
|
|
1
2
|
import { SingleKey } from "./identity/singleKey";
|
|
2
3
|
import { Identity } from "./identity";
|
|
3
4
|
import { ArkAddress } from "./script/address";
|
|
@@ -30,5 +31,5 @@ import { Nonces } from "./musig2/nonces";
|
|
|
30
31
|
import { PartialSig } from "./musig2/sign";
|
|
31
32
|
import { AnchorBumper, P2A } from "./utils/anchor";
|
|
32
33
|
import { Unroll } from "./wallet/unroll";
|
|
33
|
-
export { Wallet, SingleKey, OnchainWallet, Ramps, ESPLORA_URL, EsploraProvider, RestArkProvider, RestIndexerProvider, ArkAddress, DefaultVtxo, VtxoScript, VHTLC, TxType, IndexerTxType, ChainTxType, SettlementEventType, setupServiceWorker, Worker, ServiceWorkerWallet, Request, Response, decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CLTVMultisigTapscript, ArkPsbtFieldKey, ArkPsbtFieldKeyType, setArkPsbtField, getArkPsbtFields, CosignerPublicKey, VtxoTreeExpiry, VtxoTaprootTree, ConditionWitness, buildOffchainTx, waitForIncomingFunds, ArkNote, networks, IndexedDBVtxoRepository, BIP322, TxTree, P2A, Unroll, };
|
|
34
|
+
export { Wallet, SingleKey, OnchainWallet, Ramps, ESPLORA_URL, EsploraProvider, RestArkProvider, RestIndexerProvider, ArkAddress, DefaultVtxo, VtxoScript, VHTLC, TxType, IndexerTxType, ChainTxType, SettlementEventType, setupServiceWorker, Worker, ServiceWorkerWallet, Request, Response, decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CLTVMultisigTapscript, ArkPsbtFieldKey, ArkPsbtFieldKeyType, setArkPsbtField, getArkPsbtFields, CosignerPublicKey, VtxoTreeExpiry, VtxoTaprootTree, ConditionWitness, buildOffchainTx, waitForIncomingFunds, ArkNote, networks, IndexedDBVtxoRepository, BIP322, TxTree, P2A, Unroll, Transaction, };
|
|
34
35
|
export type { Identity, IWallet, WalletConfig, ArkTransaction, Coin, ExtendedCoin, ExtendedVirtualCoin, WalletBalance, SendBitcoinParams, Recipient, SettleParams, Status, VirtualStatus, Outpoint, VirtualCoin, TxKey, TapscriptType, VtxoRepository, ArkTxInput, OffchainTx, TapLeaves, IncomingFunds, IndexerProvider, PageResponse, Batch, ChainTx, CommitmentTx, TxHistoryRecord, Vtxo, VtxoChain, Tx, OnchainProvider, ArkProvider, SettlementEvent, ArkInfo, Intent, Output, TxNotification, ExplorerTransaction, BatchFinalizationEvent, BatchFinalizedEvent, BatchFailedEvent, TreeSigningStartedEvent, TreeNoncesAggregatedEvent, BatchStartedEvent, TreeTxEvent, TreeSignatureEvent, MarketHour, PaginationOptions, SubscriptionResponse, Network, NetworkName, ArkTapscript, RelativeTimelock, EncodedVtxoScript, TapLeafScript, SignerSession, TreeNonces, TreePartialSigs, GetVtxosFilter, Nonces, PartialSig, ArkPsbtFieldCoder, TxTreeNode, AnchorBumper, };
|
|
@@ -91,10 +91,11 @@ export declare namespace Response {
|
|
|
91
91
|
success: true;
|
|
92
92
|
status: {
|
|
93
93
|
walletInitialized: boolean;
|
|
94
|
+
xOnlyPublicKey: Uint8Array | undefined;
|
|
94
95
|
};
|
|
95
96
|
}
|
|
96
97
|
function isWalletStatus(response: Base): response is WalletStatus;
|
|
97
|
-
function walletStatus(id: string, walletInitialized: boolean): WalletStatus;
|
|
98
|
+
function walletStatus(id: string, walletInitialized: boolean, xOnlyPublicKey: Uint8Array | undefined): WalletStatus;
|
|
98
99
|
interface ClearResponse extends Base {
|
|
99
100
|
type: "CLEAR_RESPONSE";
|
|
100
101
|
}
|