@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 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
  [![TypeScript Documentation](https://img.shields.io/badge/TypeScript-Documentation-blue?style=flat-square)](https://arkade-os.github.io/ts-sdk/)
5
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](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
- return (error instanceof Error &&
473
- (error.name === "HeadersTimeoutError" ||
474
- error.name === "BodyTimeoutError" ||
475
- error.code === "UND_ERR_HEADERS_TIMEOUT" ||
476
- error.code === "UND_ERR_BODY_TIMEOUT"));
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 = (0, script_1.ScriptNum)().encode(BigInt(bip68.encode(params.timelock.type === "blocks"
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 = [sequence, "CHECKSEQUENCEVERIFY", "DROP"];
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((0, script_1.ScriptNum)().decode(sequence));
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 = (0, script_1.ScriptNum)().encode(params.absoluteTimelock);
502
- const asm = [locktime, "CHECKLOCKTIMEVERIFY", "DROP"];
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 = (0, script_1.ScriptNum)().decode(locktime);
547
+ const absoluteTimelock = MinimalScriptNum.decode(locktime);
539
548
  const reconstructed = encode({
540
549
  absoluteTimelock,
541
550
  ...multisig.params,
@@ -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
- if (serviceWorker.state !== "activated") {
27
- await new Promise((resolve) => {
28
- if (!serviceWorker)
29
- return resolve();
30
- serviceWorker.addEventListener("activate", () => resolve());
31
- });
32
- }
33
- return serviceWorker;
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
- return (error instanceof Error &&
468
- (error.name === "HeadersTimeoutError" ||
469
- error.name === "BodyTimeoutError" ||
470
- error.code === "UND_ERR_HEADERS_TIMEOUT" ||
471
- error.code === "UND_ERR_BODY_TIMEOUT"));
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 = ScriptNum().encode(BigInt(bip68.encode(params.timelock.type === "blocks"
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 = [sequence, "CHECKSEQUENCEVERIFY", "DROP"];
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(ScriptNum().decode(sequence));
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 = ScriptNum().encode(params.absoluteTimelock);
465
- const asm = [locktime, "CHECKLOCKTIMEVERIFY", "DROP"];
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 = ScriptNum().decode(locktime);
510
+ const absoluteTimelock = MinimalScriptNum.decode(locktime);
502
511
  const reconstructed = encode({
503
512
  absoluteTimelock,
504
513
  ...multisig.params,
@@ -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
- if (serviceWorker.state !== "activated") {
24
- await new Promise((resolve) => {
25
- if (!serviceWorker)
26
- return resolve();
27
- serviceWorker.addEventListener("activate", () => resolve());
28
- });
29
- }
30
- return serviceWorker;
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
  }
@@ -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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkade-os/sdk",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Bitcoin wallet SDK with Taproot and Ark integration",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",