@arkade-os/sdk 0.3.0-alpha.7 → 0.3.0

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.
Files changed (109) hide show
  1. package/README.md +99 -14
  2. package/dist/cjs/adapters/expo.js +8 -0
  3. package/dist/cjs/arknote/index.js +3 -3
  4. package/dist/cjs/forfeit.js +2 -2
  5. package/dist/cjs/identity/singleKey.js +8 -8
  6. package/dist/cjs/index.js +14 -5
  7. package/dist/cjs/{bip322 → intent}/index.js +38 -61
  8. package/dist/cjs/musig2/index.js +2 -1
  9. package/dist/cjs/musig2/nonces.js +4 -0
  10. package/dist/cjs/providers/ark.js +76 -45
  11. package/dist/cjs/providers/errors.js +59 -0
  12. package/dist/cjs/providers/expoArk.js +82 -0
  13. package/dist/cjs/providers/expoIndexer.js +105 -0
  14. package/dist/cjs/providers/expoUtils.js +124 -0
  15. package/dist/cjs/providers/indexer.js +3 -1
  16. package/dist/cjs/providers/onchain.js +19 -20
  17. package/dist/cjs/repositories/walletRepository.js +64 -28
  18. package/dist/cjs/script/base.js +15 -7
  19. package/dist/cjs/script/tapscript.js +20 -21
  20. package/dist/cjs/script/vhtlc.js +2 -2
  21. package/dist/cjs/tree/signingSession.js +44 -11
  22. package/dist/cjs/tree/txTree.js +3 -4
  23. package/dist/cjs/tree/validation.js +2 -3
  24. package/dist/cjs/utils/arkTransaction.js +118 -15
  25. package/dist/cjs/utils/transaction.js +28 -0
  26. package/dist/cjs/utils/unknownFields.js +7 -7
  27. package/dist/cjs/wallet/index.js +1 -1
  28. package/dist/cjs/wallet/onchain.js +6 -7
  29. package/dist/cjs/wallet/serviceWorker/response.js +32 -0
  30. package/dist/cjs/wallet/serviceWorker/utils.js +2 -9
  31. package/dist/cjs/wallet/serviceWorker/wallet.js +7 -8
  32. package/dist/cjs/wallet/serviceWorker/worker.js +48 -32
  33. package/dist/cjs/wallet/unroll.js +7 -9
  34. package/dist/cjs/wallet/utils.js +20 -0
  35. package/dist/cjs/wallet/vtxo-manager.js +323 -0
  36. package/dist/cjs/wallet/wallet.js +165 -174
  37. package/dist/esm/adapters/expo.js +3 -0
  38. package/dist/esm/arknote/index.js +2 -2
  39. package/dist/esm/forfeit.js +1 -1
  40. package/dist/esm/identity/singleKey.js +9 -9
  41. package/dist/esm/index.js +14 -10
  42. package/dist/esm/{bip322 → intent}/index.js +32 -54
  43. package/dist/esm/musig2/index.js +1 -1
  44. package/dist/esm/musig2/nonces.js +3 -0
  45. package/dist/esm/providers/ark.js +76 -45
  46. package/dist/esm/providers/errors.js +54 -0
  47. package/dist/esm/providers/expoArk.js +78 -0
  48. package/dist/esm/providers/expoIndexer.js +101 -0
  49. package/dist/esm/providers/expoUtils.js +87 -0
  50. package/dist/esm/providers/indexer.js +3 -1
  51. package/dist/esm/providers/onchain.js +19 -20
  52. package/dist/esm/repositories/walletRepository.js +64 -28
  53. package/dist/esm/script/base.js +12 -4
  54. package/dist/esm/script/tapscript.js +1 -2
  55. package/dist/esm/script/vhtlc.js +1 -1
  56. package/dist/esm/tree/signingSession.js +45 -12
  57. package/dist/esm/tree/txTree.js +3 -4
  58. package/dist/esm/tree/validation.js +2 -3
  59. package/dist/esm/utils/arkTransaction.js +110 -9
  60. package/dist/esm/utils/transaction.js +24 -0
  61. package/dist/esm/utils/unknownFields.js +3 -3
  62. package/dist/esm/wallet/index.js +1 -1
  63. package/dist/esm/wallet/onchain.js +3 -4
  64. package/dist/esm/wallet/serviceWorker/response.js +32 -0
  65. package/dist/esm/wallet/serviceWorker/utils.js +1 -8
  66. package/dist/esm/wallet/serviceWorker/wallet.js +8 -9
  67. package/dist/esm/wallet/serviceWorker/worker.js +49 -33
  68. package/dist/esm/wallet/unroll.js +5 -7
  69. package/dist/esm/wallet/utils.js +16 -0
  70. package/dist/esm/wallet/vtxo-manager.js +317 -0
  71. package/dist/esm/wallet/wallet.js +159 -168
  72. package/dist/types/adapters/expo.d.ts +4 -0
  73. package/dist/types/arknote/index.d.ts +1 -1
  74. package/dist/types/forfeit.d.ts +2 -2
  75. package/dist/types/identity/index.d.ts +2 -2
  76. package/dist/types/identity/singleKey.d.ts +2 -2
  77. package/dist/types/index.d.ts +11 -9
  78. package/dist/types/intent/index.d.ts +41 -0
  79. package/dist/types/musig2/index.d.ts +1 -1
  80. package/dist/types/musig2/nonces.d.ts +1 -0
  81. package/dist/types/providers/ark.d.ts +197 -27
  82. package/dist/types/providers/errors.d.ts +13 -0
  83. package/dist/types/providers/expoArk.d.ts +22 -0
  84. package/dist/types/providers/expoIndexer.d.ts +18 -0
  85. package/dist/types/providers/expoUtils.d.ts +18 -0
  86. package/dist/types/providers/indexer.d.ts +8 -8
  87. package/dist/types/providers/onchain.d.ts +6 -2
  88. package/dist/types/repositories/walletRepository.d.ts +9 -5
  89. package/dist/types/script/base.d.ts +5 -2
  90. package/dist/types/tree/signingSession.d.ts +16 -11
  91. package/dist/types/utils/anchor.d.ts +2 -2
  92. package/dist/types/utils/arkTransaction.d.ts +15 -5
  93. package/dist/types/utils/transaction.d.ts +13 -0
  94. package/dist/types/utils/unknownFields.d.ts +4 -4
  95. package/dist/types/wallet/index.d.ts +47 -7
  96. package/dist/types/wallet/onchain.d.ts +1 -1
  97. package/dist/types/wallet/serviceWorker/response.d.ts +16 -2
  98. package/dist/types/wallet/serviceWorker/utils.d.ts +1 -2
  99. package/dist/types/wallet/serviceWorker/wallet.d.ts +2 -2
  100. package/dist/types/wallet/serviceWorker/worker.d.ts +7 -1
  101. package/dist/types/wallet/unroll.d.ts +1 -1
  102. package/dist/types/wallet/utils.d.ts +3 -0
  103. package/dist/types/wallet/vtxo-manager.d.ts +179 -0
  104. package/dist/types/wallet/wallet.d.ts +17 -5
  105. package/package.json +11 -3
  106. package/dist/cjs/bip322/errors.js +0 -13
  107. package/dist/esm/bip322/errors.js +0 -9
  108. package/dist/types/bip322/errors.d.ts +0 -6
  109. package/dist/types/bip322/index.d.ts +0 -57
@@ -21,9 +21,10 @@ exports.ESPLORA_URL = {
21
21
  * ```
22
22
  */
23
23
  class EsploraProvider {
24
- constructor(baseUrl) {
24
+ constructor(baseUrl, opts) {
25
25
  this.baseUrl = baseUrl;
26
- this.polling = false;
26
+ this.pollingInterval = opts?.pollingInterval ?? 15000;
27
+ this.forcePolling = opts?.forcePolling ?? false;
27
28
  }
28
29
  async getCoins(address) {
29
30
  const response = await fetch(`${this.baseUrl}/address/${address}/utxo`);
@@ -94,13 +95,9 @@ class EsploraProvider {
94
95
  let intervalId = null;
95
96
  const wsUrl = this.baseUrl.replace(/^http(s)?:/, "ws$1:") + "/v1/ws";
96
97
  const poll = async () => {
97
- if (this.polling)
98
- return;
99
- this.polling = true;
100
- // websocket is not reliable, so we will fallback to polling
101
- const pollingInterval = 5000; // 5 seconds
102
- const getAllTxs = () => {
103
- return Promise.all(addresses.map((address) => this.getTransactions(address))).then((txArrays) => txArrays.flat());
98
+ const getAllTxs = async () => {
99
+ const txArrays = await Promise.all(addresses.map((address) => this.getTransactions(address)));
100
+ return txArrays.flat();
104
101
  };
105
102
  // initial fetch to get existing transactions
106
103
  const initialTxs = await getAllTxs();
@@ -125,9 +122,19 @@ class EsploraProvider {
125
122
  catch (error) {
126
123
  console.error("Error in polling mechanism:", error);
127
124
  }
128
- }, pollingInterval);
125
+ }, this.pollingInterval);
129
126
  };
130
127
  let ws = null;
128
+ const stopFunc = () => {
129
+ if (ws)
130
+ ws.close();
131
+ if (intervalId)
132
+ clearInterval(intervalId);
133
+ };
134
+ if (this.forcePolling) {
135
+ await poll();
136
+ return stopFunc;
137
+ }
131
138
  try {
132
139
  ws = new WebSocket(wsUrl);
133
140
  ws.addEventListener("open", () => {
@@ -174,13 +181,6 @@ class EsploraProvider {
174
181
  // if websocket is not available, fallback to polling
175
182
  await poll();
176
183
  }
177
- const stopFunc = () => {
178
- if (ws && ws.readyState === WebSocket.OPEN)
179
- ws.close();
180
- if (intervalId)
181
- clearInterval(intervalId);
182
- this.polling = false;
183
- };
184
184
  return stopFunc;
185
185
  }
186
186
  async getChainTip() {
@@ -249,8 +249,7 @@ const isExplorerTransaction = (tx) => {
249
249
  return (typeof tx.txid === "string" &&
250
250
  Array.isArray(tx.vout) &&
251
251
  tx.vout.every((vout) => typeof vout.scriptpubkey_address === "string" &&
252
- typeof vout.value === "string") &&
252
+ typeof vout.value === "number") &&
253
253
  typeof tx.status === "object" &&
254
- typeof tx.status.confirmed === "boolean" &&
255
- typeof tx.status.block_time === "number");
254
+ typeof tx.status.confirmed === "boolean");
256
255
  };
@@ -15,7 +15,14 @@ const serializeVtxo = (v) => ({
15
15
  tapTree: toHex(v.tapTree),
16
16
  forfeitTapLeafScript: serializeTapLeaf(v.forfeitTapLeafScript),
17
17
  intentTapLeafScript: serializeTapLeaf(v.intentTapLeafScript),
18
- extraWitness: v.extraWitness?.map((w) => toHex(w)),
18
+ extraWitness: v.extraWitness?.map(toHex),
19
+ });
20
+ const serializeUtxo = (u) => ({
21
+ ...u,
22
+ tapTree: toHex(u.tapTree),
23
+ forfeitTapLeafScript: serializeTapLeaf(u.forfeitTapLeafScript),
24
+ intentTapLeafScript: serializeTapLeaf(u.intentTapLeafScript),
25
+ extraWitness: u.extraWitness?.map(toHex),
19
26
  });
20
27
  const deserializeTapLeaf = (t) => {
21
28
  const cb = btc_signer_1.TaprootControlBlock.decode(fromHex(t.cb));
@@ -27,13 +34,21 @@ const deserializeVtxo = (o) => ({
27
34
  tapTree: fromHex(o.tapTree),
28
35
  forfeitTapLeafScript: deserializeTapLeaf(o.forfeitTapLeafScript),
29
36
  intentTapLeafScript: deserializeTapLeaf(o.intentTapLeafScript),
30
- extraWitness: o.extraWitness?.map((w) => fromHex(w)),
37
+ extraWitness: o.extraWitness?.map(fromHex),
38
+ });
39
+ const deserializeUtxo = (o) => ({
40
+ ...o,
41
+ tapTree: fromHex(o.tapTree),
42
+ forfeitTapLeafScript: deserializeTapLeaf(o.forfeitTapLeafScript),
43
+ intentTapLeafScript: deserializeTapLeaf(o.intentTapLeafScript),
44
+ extraWitness: o.extraWitness?.map(fromHex),
31
45
  });
32
46
  class WalletRepositoryImpl {
33
47
  constructor(storage) {
34
48
  this.storage = storage;
35
49
  this.cache = {
36
50
  vtxos: new Map(),
51
+ utxos: new Map(),
37
52
  transactions: new Map(),
38
53
  walletState: null,
39
54
  initialized: new Set(),
@@ -61,18 +76,6 @@ class WalletRepositoryImpl {
61
76
  return [];
62
77
  }
63
78
  }
64
- async saveVtxo(address, vtxo) {
65
- const vtxos = await this.getVtxos(address);
66
- const existing = vtxos.findIndex((v) => v.txid === vtxo.txid && v.vout === vtxo.vout);
67
- if (existing !== -1) {
68
- vtxos[existing] = vtxo;
69
- }
70
- else {
71
- vtxos.push(vtxo);
72
- }
73
- this.cache.vtxos.set(address, vtxos.slice());
74
- await this.storage.setItem(`vtxos:${address}`, JSON.stringify(vtxos.map(serializeVtxo)));
75
- }
76
79
  async saveVtxos(address, vtxos) {
77
80
  const storedVtxos = await this.getVtxos(address);
78
81
  for (const vtxo of vtxos) {
@@ -98,6 +101,53 @@ class WalletRepositoryImpl {
98
101
  this.cache.vtxos.set(address, []);
99
102
  await this.storage.removeItem(`vtxos:${address}`);
100
103
  }
104
+ async getUtxos(address) {
105
+ const cacheKey = `utxos:${address}`;
106
+ if (this.cache.utxos.has(address)) {
107
+ return this.cache.utxos.get(address);
108
+ }
109
+ const stored = await this.storage.getItem(cacheKey);
110
+ if (!stored) {
111
+ this.cache.utxos.set(address, []);
112
+ return [];
113
+ }
114
+ try {
115
+ const parsed = JSON.parse(stored);
116
+ const utxos = parsed.map(deserializeUtxo);
117
+ this.cache.utxos.set(address, utxos.slice());
118
+ return utxos.slice();
119
+ }
120
+ catch (error) {
121
+ console.error(`Failed to parse UTXOs for address ${address}:`, error);
122
+ this.cache.utxos.set(address, []);
123
+ return [];
124
+ }
125
+ }
126
+ async saveUtxos(address, utxos) {
127
+ const storedUtxos = await this.getUtxos(address);
128
+ utxos.forEach((utxo) => {
129
+ const existing = storedUtxos.findIndex((u) => u.txid === utxo.txid && u.vout === utxo.vout);
130
+ if (existing !== -1) {
131
+ storedUtxos[existing] = utxo;
132
+ }
133
+ else {
134
+ storedUtxos.push(utxo);
135
+ }
136
+ });
137
+ this.cache.utxos.set(address, storedUtxos.slice());
138
+ await this.storage.setItem(`utxos:${address}`, JSON.stringify(storedUtxos.map(serializeUtxo)));
139
+ }
140
+ async removeUtxo(address, utxoId) {
141
+ const utxos = await this.getUtxos(address);
142
+ const [txid, vout] = utxoId.split(":");
143
+ const filtered = utxos.filter((v) => !(v.txid === txid && v.vout === parseInt(vout, 10)));
144
+ this.cache.utxos.set(address, filtered.slice());
145
+ await this.storage.setItem(`utxos:${address}`, JSON.stringify(filtered.map(serializeUtxo)));
146
+ }
147
+ async clearUtxos(address) {
148
+ this.cache.utxos.set(address, []);
149
+ await this.storage.removeItem(`utxos:${address}`);
150
+ }
101
151
  async getTransactionHistory(address) {
102
152
  const cacheKey = `tx:${address}`;
103
153
  if (this.cache.transactions.has(address)) {
@@ -119,20 +169,6 @@ class WalletRepositoryImpl {
119
169
  return [];
120
170
  }
121
171
  }
122
- async saveTransaction(address, tx) {
123
- const transactions = await this.getTransactionHistory(address);
124
- const existing = transactions.findIndex((t) => t.key === tx.key);
125
- if (existing !== -1) {
126
- transactions[existing] = tx;
127
- }
128
- else {
129
- transactions.push(tx);
130
- }
131
- // Sort by createdAt descending
132
- transactions.sort((a, b) => b.createdAt - a.createdAt);
133
- this.cache.transactions.set(address, transactions);
134
- await this.storage.setItem(`tx:${address}`, JSON.stringify(transactions));
135
- }
136
172
  async saveTransactions(address, txs) {
137
173
  const storedTransactions = await this.getTransactionHistory(address);
138
174
  for (const tx of txs) {
@@ -1,15 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VtxoScript = void 0;
3
+ exports.VtxoScript = exports.TapTreeCoder = void 0;
4
4
  exports.scriptFromTapLeafScript = scriptFromTapLeafScript;
5
5
  const btc_signer_1 = require("@scure/btc-signer");
6
6
  const payment_js_1 = require("@scure/btc-signer/payment.js");
7
7
  const psbt_js_1 = require("@scure/btc-signer/psbt.js");
8
- const utils_js_1 = require("@scure/btc-signer/utils.js");
9
8
  const base_1 = require("@scure/base");
10
9
  const address_1 = require("./address");
11
10
  const tapscript_1 = require("./tapscript");
12
- const TapTreeCoder = psbt_js_1.PSBTOutput.tapTree[2];
11
+ exports.TapTreeCoder = psbt_js_1.PSBTOutput.tapTree[2];
13
12
  function scriptFromTapLeafScript(leaf) {
14
13
  return leaf[1].subarray(0, leaf[1].length - 1); // remove the version byte
15
14
  }
@@ -23,14 +22,23 @@ function scriptFromTapLeafScript(leaf) {
23
22
  */
24
23
  class VtxoScript {
25
24
  static decode(tapTree) {
26
- const leaves = TapTreeCoder.decode(tapTree);
25
+ const leaves = exports.TapTreeCoder.decode(tapTree);
27
26
  const scripts = leaves.map((leaf) => leaf.script);
28
27
  return new VtxoScript(scripts);
29
28
  }
30
29
  constructor(scripts) {
31
30
  this.scripts = scripts;
32
- const tapTree = (0, btc_signer_1.taprootListToTree)(scripts.map((script) => ({ script, leafVersion: payment_js_1.TAP_LEAF_VERSION })));
33
- const payment = (0, btc_signer_1.p2tr)(utils_js_1.TAPROOT_UNSPENDABLE_KEY, tapTree, undefined, true);
31
+ // reverse the scripts if the number of scripts is odd
32
+ // this is to be compatible with arkd algorithm computing taproot tree from list of tapscripts
33
+ // the scripts must be reversed only HERE while we compute the tweaked public key
34
+ // but the original order should be preserved while encoding as taptree
35
+ // note: .slice().reverse() is used instead of .reverse() to avoid mutating the original array
36
+ const list = scripts.length % 2 !== 0 ? scripts.slice().reverse() : scripts;
37
+ const tapTree = (0, btc_signer_1.taprootListToTree)(list.map((script) => ({
38
+ script,
39
+ leafVersion: payment_js_1.TAP_LEAF_VERSION,
40
+ })));
41
+ const payment = (0, btc_signer_1.p2tr)(btc_signer_1.TAPROOT_UNSPENDABLE_KEY, tapTree, undefined, true);
34
42
  if (!payment.tapLeafScript ||
35
43
  payment.tapLeafScript.length !== scripts.length) {
36
44
  throw new Error("invalid scripts");
@@ -39,7 +47,7 @@ class VtxoScript {
39
47
  this.tweakedPublicKey = payment.tweakedPubkey;
40
48
  }
41
49
  encode() {
42
- const tapTree = TapTreeCoder.encode(this.scripts.map((script) => ({
50
+ const tapTree = exports.TapTreeCoder.encode(this.scripts.map((script) => ({
43
51
  depth: 1,
44
52
  version: payment_js_1.TAP_LEAF_VERSION,
45
53
  script,
@@ -36,10 +36,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.TapscriptType = void 0;
37
37
  exports.decodeTapscript = decodeTapscript;
38
38
  const bip68 = __importStar(require("bip68"));
39
- const script_js_1 = require("@scure/btc-signer/script.js");
40
- const payment_js_1 = require("@scure/btc-signer/payment.js");
39
+ const btc_signer_1 = require("@scure/btc-signer");
41
40
  const base_1 = require("@scure/base");
42
- const MinimalScriptNum = (0, script_js_1.ScriptNum)(undefined, true);
41
+ const MinimalScriptNum = (0, btc_signer_1.ScriptNum)(undefined, true);
43
42
  var TapscriptType;
44
43
  (function (TapscriptType) {
45
44
  TapscriptType["Multisig"] = "multisig";
@@ -109,7 +108,7 @@ var MultisigTapscript;
109
108
  return {
110
109
  type: TapscriptType.Multisig,
111
110
  params,
112
- script: (0, payment_js_1.p2tr_ms)(params.pubkeys.length, params.pubkeys).script,
111
+ script: (0, btc_signer_1.p2tr_ms)(params.pubkeys.length, params.pubkeys).script,
113
112
  };
114
113
  }
115
114
  const asm = [];
@@ -126,7 +125,7 @@ var MultisigTapscript;
126
125
  return {
127
126
  type: TapscriptType.Multisig,
128
127
  params,
129
- script: script_js_1.Script.encode(asm),
128
+ script: btc_signer_1.Script.encode(asm),
130
129
  };
131
130
  }
132
131
  MultisigTapscript.encode = encode;
@@ -151,7 +150,7 @@ var MultisigTapscript;
151
150
  MultisigTapscript.decode = decode;
152
151
  // <pubkey> CHECKSIG <pubkey> CHECKSIGADD <len_keys> NUMEQUAL
153
152
  function decodeChecksigAdd(script) {
154
- const asm = script_js_1.Script.decode(script);
153
+ const asm = btc_signer_1.Script.decode(script);
155
154
  const pubkeys = [];
156
155
  let foundNumEqual = false;
157
156
  // Parse through ASM operations
@@ -201,7 +200,7 @@ var MultisigTapscript;
201
200
  }
202
201
  // <pubkey> CHECKSIGVERIFY <pubkey> CHECKSIG
203
202
  function decodeChecksig(script) {
204
- const asm = script_js_1.Script.decode(script);
203
+ const asm = btc_signer_1.Script.decode(script);
205
204
  const pubkeys = [];
206
205
  // Parse through ASM operations
207
206
  for (let i = 0; i < asm.length; i++) {
@@ -278,7 +277,7 @@ var CSVMultisigTapscript;
278
277
  ];
279
278
  const multisigScript = MultisigTapscript.encode(params);
280
279
  const script = new Uint8Array([
281
- ...script_js_1.Script.encode(asm),
280
+ ...btc_signer_1.Script.encode(asm),
282
281
  ...multisigScript.script,
283
282
  ]);
284
283
  return {
@@ -292,7 +291,7 @@ var CSVMultisigTapscript;
292
291
  if (script.length === 0) {
293
292
  throw new Error("Failed to decode: script is empty");
294
293
  }
295
- const asm = script_js_1.Script.decode(script);
294
+ const asm = btc_signer_1.Script.decode(script);
296
295
  if (asm.length < 3) {
297
296
  throw new Error(`Invalid script: too short (expected at least 3)`);
298
297
  }
@@ -303,7 +302,7 @@ var CSVMultisigTapscript;
303
302
  if (asm[1] !== "CHECKSEQUENCEVERIFY" || asm[2] !== "DROP") {
304
303
  throw new Error("Invalid script: expected CHECKSEQUENCEVERIFY DROP");
305
304
  }
306
- const multisigScript = new Uint8Array(script_js_1.Script.encode(asm.slice(3)));
305
+ const multisigScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(3)));
307
306
  let multisig;
308
307
  try {
309
308
  multisig = MultisigTapscript.decode(multisigScript);
@@ -361,7 +360,7 @@ var ConditionCSVMultisigTapscript;
361
360
  function encode(params) {
362
361
  const script = new Uint8Array([
363
362
  ...params.conditionScript,
364
- ...script_js_1.Script.encode(["VERIFY"]),
363
+ ...btc_signer_1.Script.encode(["VERIFY"]),
365
364
  ...CSVMultisigTapscript.encode(params).script,
366
365
  ]);
367
366
  return {
@@ -375,7 +374,7 @@ var ConditionCSVMultisigTapscript;
375
374
  if (script.length === 0) {
376
375
  throw new Error("Failed to decode: script is empty");
377
376
  }
378
- const asm = script_js_1.Script.decode(script);
377
+ const asm = btc_signer_1.Script.decode(script);
379
378
  if (asm.length < 1) {
380
379
  throw new Error(`Invalid script: too short (expected at least 1)`);
381
380
  }
@@ -388,8 +387,8 @@ var ConditionCSVMultisigTapscript;
388
387
  if (verifyIndex === -1) {
389
388
  throw new Error("Invalid script: missing VERIFY operation");
390
389
  }
391
- const conditionScript = new Uint8Array(script_js_1.Script.encode(asm.slice(0, verifyIndex)));
392
- const csvMultisigScript = new Uint8Array(script_js_1.Script.encode(asm.slice(verifyIndex + 1)));
390
+ const conditionScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(0, verifyIndex)));
391
+ const csvMultisigScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(verifyIndex + 1)));
393
392
  let csvMultisig;
394
393
  try {
395
394
  csvMultisig = CSVMultisigTapscript.decode(csvMultisigScript);
@@ -436,7 +435,7 @@ var ConditionMultisigTapscript;
436
435
  function encode(params) {
437
436
  const script = new Uint8Array([
438
437
  ...params.conditionScript,
439
- ...script_js_1.Script.encode(["VERIFY"]),
438
+ ...btc_signer_1.Script.encode(["VERIFY"]),
440
439
  ...MultisigTapscript.encode(params).script,
441
440
  ]);
442
441
  return {
@@ -450,7 +449,7 @@ var ConditionMultisigTapscript;
450
449
  if (script.length === 0) {
451
450
  throw new Error("Failed to decode: script is empty");
452
451
  }
453
- const asm = script_js_1.Script.decode(script);
452
+ const asm = btc_signer_1.Script.decode(script);
454
453
  if (asm.length < 1) {
455
454
  throw new Error(`Invalid script: too short (expected at least 1)`);
456
455
  }
@@ -463,8 +462,8 @@ var ConditionMultisigTapscript;
463
462
  if (verifyIndex === -1) {
464
463
  throw new Error("Invalid script: missing VERIFY operation");
465
464
  }
466
- const conditionScript = new Uint8Array(script_js_1.Script.encode(asm.slice(0, verifyIndex)));
467
- const multisigScript = new Uint8Array(script_js_1.Script.encode(asm.slice(verifyIndex + 1)));
465
+ const conditionScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(0, verifyIndex)));
466
+ const multisigScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(verifyIndex + 1)));
468
467
  let multisig;
469
468
  try {
470
469
  multisig = MultisigTapscript.decode(multisigScript);
@@ -515,7 +514,7 @@ var CLTVMultisigTapscript;
515
514
  "CHECKLOCKTIMEVERIFY",
516
515
  "DROP",
517
516
  ];
518
- const timelockedScript = script_js_1.Script.encode(asm);
517
+ const timelockedScript = btc_signer_1.Script.encode(asm);
519
518
  const script = new Uint8Array([
520
519
  ...timelockedScript,
521
520
  ...MultisigTapscript.encode(params).script,
@@ -531,7 +530,7 @@ var CLTVMultisigTapscript;
531
530
  if (script.length === 0) {
532
531
  throw new Error("Failed to decode: script is empty");
533
532
  }
534
- const asm = script_js_1.Script.decode(script);
533
+ const asm = btc_signer_1.Script.decode(script);
535
534
  if (asm.length < 3) {
536
535
  throw new Error(`Invalid script: too short (expected at least 3)`);
537
536
  }
@@ -542,7 +541,7 @@ var CLTVMultisigTapscript;
542
541
  if (asm[1] !== "CHECKLOCKTIMEVERIFY" || asm[2] !== "DROP") {
543
542
  throw new Error("Invalid script: expected CHECKLOCKTIMEVERIFY DROP");
544
543
  }
545
- const multisigScript = new Uint8Array(script_js_1.Script.encode(asm.slice(3)));
544
+ const multisigScript = new Uint8Array(btc_signer_1.Script.encode(asm.slice(3)));
546
545
  let multisig;
547
546
  try {
548
547
  multisig = MultisigTapscript.decode(multisigScript);
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VHTLC = void 0;
4
- const script_js_1 = require("@scure/btc-signer/script.js");
4
+ const btc_signer_1 = require("@scure/btc-signer");
5
5
  const tapscript_1 = require("./tapscript");
6
6
  const base_1 = require("@scure/base");
7
7
  const base_2 = require("./base");
@@ -158,5 +158,5 @@ var VHTLC;
158
158
  }
159
159
  })(VHTLC || (exports.VHTLC = VHTLC = {}));
160
160
  function preimageConditionScript(preimageHash) {
161
- return script_js_1.Script.encode(["HASH160", preimageHash, "EQUAL"]);
161
+ return btc_signer_1.Script.encode(["HASH160", preimageHash, "EQUAL"]);
162
162
  }
@@ -57,15 +57,15 @@ class TreeSignerSession {
57
57
  const secretKey = (0, utils_js_1.randomPrivateKeyBytes)();
58
58
  return new TreeSignerSession(secretKey);
59
59
  }
60
- init(tree, scriptRoot, rootInputAmount) {
60
+ async init(tree, scriptRoot, rootInputAmount) {
61
61
  this.graph = tree;
62
62
  this.scriptRoot = scriptRoot;
63
63
  this.rootSharedOutputAmount = rootInputAmount;
64
64
  }
65
- getPublicKey() {
65
+ async getPublicKey() {
66
66
  return secp256k1_js_1.secp256k1.getPublicKey(this.secretKey);
67
67
  }
68
- getNonces() {
68
+ async getNonces() {
69
69
  if (!this.graph)
70
70
  throw exports.ErrMissingVtxoGraph;
71
71
  if (!this.myNonces) {
@@ -77,12 +77,46 @@ class TreeSignerSession {
77
77
  }
78
78
  return publicNonces;
79
79
  }
80
- setAggregatedNonces(nonces) {
81
- if (this.aggregateNonces)
82
- throw new Error("nonces already set");
83
- this.aggregateNonces = nonces;
80
+ async aggregatedNonces(txid, noncesByPubkey) {
81
+ if (!this.graph)
82
+ throw exports.ErrMissingVtxoGraph;
83
+ if (!this.aggregateNonces) {
84
+ this.aggregateNonces = new Map();
85
+ }
86
+ if (!this.myNonces) {
87
+ await this.getNonces(); // generate nonces if not generated yet
88
+ }
89
+ if (this.aggregateNonces.has(txid)) {
90
+ return {
91
+ hasAllNonces: this.aggregateNonces.size === this.myNonces?.size,
92
+ };
93
+ }
94
+ const myNonce = this.myNonces.get(txid);
95
+ if (!myNonce)
96
+ throw new Error(`missing nonce for txid ${txid}`);
97
+ const myPublicKey = await this.getPublicKey();
98
+ // set my nonce to not rely on server
99
+ noncesByPubkey.set(base_1.hex.encode(myPublicKey.subarray(1)), myNonce);
100
+ const tx = this.graph.find(txid);
101
+ if (!tx)
102
+ throw new Error(`missing tx for txid ${txid}`);
103
+ const cosigners = (0, unknownFields_1.getArkPsbtFields)(tx.root, 0, unknownFields_1.CosignerPublicKey).map((c) => base_1.hex.encode(c.key.subarray(1)) // xonly pubkey
104
+ );
105
+ const pubNonces = [];
106
+ for (const cosigner of cosigners) {
107
+ const nonce = noncesByPubkey.get(cosigner);
108
+ if (!nonce) {
109
+ throw new Error(`missing nonce for cosigner ${cosigner}`);
110
+ }
111
+ pubNonces.push(nonce.pubNonce);
112
+ }
113
+ const aggregateNonce = musig2.aggregateNonces(pubNonces);
114
+ this.aggregateNonces.set(txid, { pubNonce: aggregateNonce });
115
+ return {
116
+ hasAllNonces: this.aggregateNonces.size === this.myNonces?.size,
117
+ };
84
118
  }
85
- sign() {
119
+ async sign() {
86
120
  if (!this.graph)
87
121
  throw exports.ErrMissingVtxoGraph;
88
122
  if (!this.aggregateNonces)
@@ -166,9 +200,8 @@ async function validateTreeSigs(finalAggregatedKey, sharedOutputAmount, vtxoTree
166
200
  function getPrevOutput(finalKey, graph, sharedOutputAmount, tx) {
167
201
  // generate P2TR script from musig2 final key
168
202
  const pkScript = script_js_1.Script.encode(["OP_1", finalKey.slice(1)]);
169
- const txid = base_1.hex.encode((0, utils_js_1.sha256x2)(tx.toBytes(true)).reverse());
170
203
  // if the input is the root input, return the shared output amount
171
- if (txid === graph.txid) {
204
+ if (tx.id === graph.txid) {
172
205
  return {
173
206
  amount: sharedOutputAmount,
174
207
  script: pkScript,
@@ -178,7 +211,7 @@ function getPrevOutput(finalKey, graph, sharedOutputAmount, tx) {
178
211
  const parentInput = tx.getInput(0);
179
212
  if (!parentInput.txid)
180
213
  throw new Error("missing parent input txid");
181
- const parentTxid = base_1.hex.encode(new Uint8Array(parentInput.txid));
214
+ const parentTxid = base_1.hex.encode(parentInput.txid);
182
215
  const parent = graph.find(parentTxid);
183
216
  if (!parent)
184
217
  throw new Error("parent tx not found");
@@ -4,7 +4,6 @@ exports.TxTree = void 0;
4
4
  const transaction_js_1 = require("@scure/btc-signer/transaction.js");
5
5
  const base_1 = require("@scure/base");
6
6
  const base_2 = require("@scure/base");
7
- const utils_js_1 = require("@scure/btc-signer/utils.js");
8
7
  /**
9
8
  * TxTree is a graph of bitcoin transactions.
10
9
  * It is used to represent batch tree created during settlement session
@@ -22,7 +21,7 @@ class TxTree {
22
21
  const chunksByTxid = new Map();
23
22
  for (const chunk of chunks) {
24
23
  const decodedChunk = decodeNode(chunk);
25
- const txid = base_2.hex.encode((0, utils_js_1.sha256x2)(decodedChunk.tx.toBytes(true)).reverse());
24
+ const txid = decodedChunk.tx.id;
26
25
  chunksByTxid.set(txid, decodedChunk);
27
26
  }
28
27
  // Find the root chunks (the ones that aren't referenced as a child)
@@ -91,7 +90,7 @@ class TxTree {
91
90
  }
92
91
  child.validate();
93
92
  const childInput = child.root.getInput(0);
94
- const parentTxid = base_2.hex.encode((0, utils_js_1.sha256x2)(this.root.toBytes(true)).reverse());
93
+ const parentTxid = this.root.id;
95
94
  // verify the input of the child is the output of the parent
96
95
  if (!childInput.txid ||
97
96
  base_2.hex.encode(childInput.txid) !== parentTxid ||
@@ -126,7 +125,7 @@ class TxTree {
126
125
  return leaves;
127
126
  }
128
127
  get txid() {
129
- return base_2.hex.encode((0, utils_js_1.sha256x2)(this.root.toBytes(true)).reverse());
128
+ return this.root.id;
130
129
  }
131
130
  find(txid) {
132
131
  if (txid === this.txid) {
@@ -6,7 +6,6 @@ exports.validateVtxoTxGraph = validateVtxoTxGraph;
6
6
  const base_1 = require("@scure/base");
7
7
  const transaction_js_1 = require("@scure/btc-signer/transaction.js");
8
8
  const base_2 = require("@scure/base");
9
- const utils_js_1 = require("@scure/btc-signer/utils.js");
10
9
  const musig2_1 = require("../musig2");
11
10
  const unknownFields_1 = require("../utils/unknownFields");
12
11
  const ErrInvalidSettlementTx = (tx) => new Error(`invalid settlement transaction: ${tx}`);
@@ -31,7 +30,7 @@ function validateConnectorsTxGraph(settlementTxB64, connectorsGraph) {
31
30
  const settlementTx = transaction_js_1.Transaction.fromPSBT(base_2.base64.decode(settlementTxB64));
32
31
  if (settlementTx.outputsLength <= BATCH_OUTPUT_CONNECTORS_INDEX)
33
32
  throw exports.ErrInvalidSettlementTxOutputs;
34
- const expectedRootTxid = base_1.hex.encode((0, utils_js_1.sha256x2)(settlementTx.toBytes(true)).reverse());
33
+ const expectedRootTxid = settlementTx.id;
35
34
  if (!rootInput.txid)
36
35
  throw exports.ErrWrongSettlementTxid;
37
36
  if (base_1.hex.encode(rootInput.txid) !== expectedRootTxid)
@@ -58,7 +57,7 @@ function validateVtxoTxGraph(graph, roundTransaction, sweepTapTreeRoot) {
58
57
  throw exports.ErrEmptyTree;
59
58
  }
60
59
  const rootInput = graph.root.getInput(0);
61
- const commitmentTxid = base_1.hex.encode((0, utils_js_1.sha256x2)(roundTransaction.toBytes(true)).reverse());
60
+ const commitmentTxid = roundTransaction.id;
62
61
  if (!rootInput.txid ||
63
62
  base_1.hex.encode(rootInput.txid) !== commitmentTxid ||
64
63
  rootInput.index !== BATCH_OUTPUT_VTXO_INDEX) {