@arkade-os/sdk 0.3.2 → 0.3.4

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.
@@ -83,8 +83,8 @@ class SingleKey {
83
83
  }
84
84
  async signMessage(message, signatureType = "schnorr") {
85
85
  if (signatureType === "ecdsa")
86
- return (0, secp256k1_1.sign)(message, this.key, { prehash: false });
87
- return secp256k1_1.schnorr.sign(message, this.key);
86
+ return (0, secp256k1_1.signAsync)(message, this.key, { prehash: false });
87
+ return secp256k1_1.schnorr.signAsync(message, this.key);
88
88
  }
89
89
  }
90
90
  exports.SingleKey = SingleKey;
package/dist/cjs/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Transaction = exports.Unroll = exports.P2A = exports.TxTree = exports.Intent = exports.ContractRepositoryImpl = exports.WalletRepositoryImpl = exports.networks = exports.ArkNote = exports.hasBoardingTxExpired = exports.waitForIncomingFunds = exports.verifyTapscriptSignatures = exports.buildOffchainTx = exports.ConditionWitness = exports.VtxoTaprootTree = exports.VtxoTreeExpiry = exports.CosignerPublicKey = exports.getArkPsbtFields = exports.setArkPsbtField = exports.ArkPsbtFieldKeyType = exports.ArkPsbtFieldKey = exports.TapTreeCoder = 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.VtxoManager = exports.Ramps = exports.OnchainWallet = exports.SingleKey = exports.Wallet = void 0;
4
- exports.maybeArkError = exports.ArkError = void 0;
3
+ exports.Unroll = exports.P2A = exports.TxTree = exports.Intent = exports.ContractRepositoryImpl = exports.WalletRepositoryImpl = exports.networks = exports.ArkNote = exports.combineTapscriptSigs = exports.hasBoardingTxExpired = exports.waitForIncomingFunds = exports.verifyTapscriptSignatures = exports.buildOffchainTx = exports.ConditionWitness = exports.VtxoTaprootTree = exports.VtxoTreeExpiry = exports.CosignerPublicKey = exports.getArkPsbtFields = exports.setArkPsbtField = exports.ArkPsbtFieldKeyType = exports.ArkPsbtFieldKey = exports.TapTreeCoder = 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.VtxoManager = exports.Ramps = exports.OnchainWallet = exports.SingleKey = exports.Wallet = void 0;
4
+ exports.maybeArkError = exports.ArkError = exports.Transaction = void 0;
5
5
  const transaction_1 = require("./utils/transaction");
6
6
  Object.defineProperty(exports, "Transaction", { enumerable: true, get: function () { return transaction_1.Transaction; } });
7
7
  const singleKey_1 = require("./identity/singleKey");
@@ -55,6 +55,7 @@ const arkTransaction_1 = require("./utils/arkTransaction");
55
55
  Object.defineProperty(exports, "hasBoardingTxExpired", { enumerable: true, get: function () { return arkTransaction_1.hasBoardingTxExpired; } });
56
56
  Object.defineProperty(exports, "buildOffchainTx", { enumerable: true, get: function () { return arkTransaction_1.buildOffchainTx; } });
57
57
  Object.defineProperty(exports, "verifyTapscriptSignatures", { enumerable: true, get: function () { return arkTransaction_1.verifyTapscriptSignatures; } });
58
+ Object.defineProperty(exports, "combineTapscriptSigs", { enumerable: true, get: function () { return arkTransaction_1.combineTapscriptSigs; } });
58
59
  const unknownFields_1 = require("./utils/unknownFields");
59
60
  Object.defineProperty(exports, "VtxoTaprootTree", { enumerable: true, get: function () { return unknownFields_1.VtxoTaprootTree; } });
60
61
  Object.defineProperty(exports, "ConditionWitness", { enumerable: true, get: function () { return unknownFields_1.ConditionWitness; } });
@@ -139,7 +139,7 @@ class RestArkProvider {
139
139
  "Content-Type": "application/json",
140
140
  },
141
141
  body: JSON.stringify({
142
- proof: {
142
+ intent: {
143
143
  proof: intent.proof,
144
144
  message: intent.message,
145
145
  },
@@ -1,22 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ContractRepositoryImpl = void 0;
4
+ const getContractStorageKey = (id, key) => `contract:${id}:${key}`;
5
+ const getCollectionStorageKey = (type) => `collection:${type}`;
4
6
  class ContractRepositoryImpl {
5
7
  constructor(storage) {
6
- this.cache = new Map();
7
8
  this.storage = storage;
8
9
  }
9
10
  async getContractData(contractId, key) {
10
- const storageKey = `contract:${contractId}:${key}`;
11
- const cached = this.cache.get(storageKey);
12
- if (cached !== undefined)
13
- return cached;
14
- const stored = await this.storage.getItem(storageKey);
11
+ const stored = await this.storage.getItem(getContractStorageKey(contractId, key));
15
12
  if (!stored)
16
13
  return null;
17
14
  try {
18
15
  const data = JSON.parse(stored);
19
- this.cache.set(storageKey, data);
20
16
  return data;
21
17
  }
22
18
  catch (error) {
@@ -25,49 +21,33 @@ class ContractRepositoryImpl {
25
21
  }
26
22
  }
27
23
  async setContractData(contractId, key, data) {
28
- const storageKey = `contract:${contractId}:${key}`;
29
24
  try {
30
- // First persist to storage, only update cache if successful
31
- await this.storage.setItem(storageKey, JSON.stringify(data));
32
- this.cache.set(storageKey, data);
25
+ await this.storage.setItem(getContractStorageKey(contractId, key), JSON.stringify(data));
33
26
  }
34
27
  catch (error) {
35
- // Storage operation failed, cache remains unchanged
36
28
  console.error(`Failed to persist contract data for ${contractId}:${key}:`, error);
37
29
  throw error; // Rethrow to notify caller of failure
38
30
  }
39
31
  }
40
32
  async deleteContractData(contractId, key) {
41
- const storageKey = `contract:${contractId}:${key}`;
42
33
  try {
43
- // First remove from persistent storage, only delete from cache if successful
44
- await this.storage.removeItem(storageKey);
45
- this.cache.delete(storageKey);
34
+ await this.storage.removeItem(getContractStorageKey(contractId, key));
46
35
  }
47
36
  catch (error) {
48
- // Storage operation failed, cache remains unchanged
49
37
  console.error(`Failed to remove contract data for ${contractId}:${key}:`, error);
50
38
  throw error; // Rethrow to notify caller of failure
51
39
  }
52
40
  }
53
41
  async getContractCollection(contractType) {
54
- const storageKey = `collection:${contractType}`;
55
- const cached = this.cache.get(storageKey);
56
- if (cached !== undefined)
57
- return cached;
58
- const stored = await this.storage.getItem(storageKey);
59
- if (!stored) {
60
- this.cache.set(storageKey, []);
42
+ const stored = await this.storage.getItem(getCollectionStorageKey(contractType));
43
+ if (!stored)
61
44
  return [];
62
- }
63
45
  try {
64
46
  const collection = JSON.parse(stored);
65
- this.cache.set(storageKey, collection);
66
47
  return collection;
67
48
  }
68
49
  catch (error) {
69
50
  console.error(`Failed to parse contract collection ${contractType}:`, error);
70
- this.cache.set(storageKey, []);
71
51
  return [];
72
52
  }
73
53
  }
@@ -94,14 +74,10 @@ class ContractRepositoryImpl {
94
74
  // Add new item
95
75
  newCollection = [...collection, item];
96
76
  }
97
- const storageKey = `collection:${contractType}`;
98
77
  try {
99
- // First persist to storage, only update cache if successful
100
- await this.storage.setItem(storageKey, JSON.stringify(newCollection));
101
- this.cache.set(storageKey, newCollection);
78
+ await this.storage.setItem(getCollectionStorageKey(contractType), JSON.stringify(newCollection));
102
79
  }
103
80
  catch (error) {
104
- // Storage operation failed, cache remains unchanged
105
81
  console.error(`Failed to persist contract collection ${contractType}:`, error);
106
82
  throw error; // Rethrow to notify caller of failure
107
83
  }
@@ -114,21 +90,16 @@ class ContractRepositoryImpl {
114
90
  const collection = await this.getContractCollection(contractType);
115
91
  // Build new collection without the specified item
116
92
  const filtered = collection.filter((item) => item[idField] !== id);
117
- const storageKey = `collection:${contractType}`;
118
93
  try {
119
- // First persist to storage, only update cache if successful
120
- await this.storage.setItem(storageKey, JSON.stringify(filtered));
121
- this.cache.set(storageKey, filtered);
94
+ await this.storage.setItem(getCollectionStorageKey(contractType), JSON.stringify(filtered));
122
95
  }
123
96
  catch (error) {
124
- // Storage operation failed, cache remains unchanged
125
97
  console.error(`Failed to persist contract collection removal for ${contractType}:`, error);
126
98
  throw error; // Rethrow to notify caller of failure
127
99
  }
128
100
  }
129
101
  async clearContractData() {
130
102
  await this.storage.clear();
131
- this.cache.clear();
132
103
  }
133
104
  }
134
105
  exports.ContractRepositoryImpl = ContractRepositoryImpl;
@@ -3,6 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.WalletRepositoryImpl = void 0;
4
4
  const base_1 = require("@scure/base");
5
5
  const btc_signer_1 = require("@scure/btc-signer");
6
+ const getVtxosStorageKey = (address) => `vtxos:${address}`;
7
+ const getUtxosStorageKey = (address) => `utxos:${address}`;
8
+ const getTransactionsStorageKey = (address) => `tx:${address}`;
9
+ const walletStateStorageKey = "wallet:state";
6
10
  // Utility functions for (de)serializing complex structures
7
11
  const toHex = (b) => (b ? base_1.hex.encode(b) : undefined);
8
12
  const fromHex = (h) => h ? base_1.hex.decode(h) : undefined;
@@ -47,33 +51,17 @@ const deserializeUtxo = (o) => ({
47
51
  class WalletRepositoryImpl {
48
52
  constructor(storage) {
49
53
  this.storage = storage;
50
- this.cache = {
51
- vtxos: new Map(),
52
- utxos: new Map(),
53
- transactions: new Map(),
54
- walletState: null,
55
- initialized: new Set(),
56
- };
57
54
  }
58
55
  async getVtxos(address) {
59
- const cacheKey = `vtxos:${address}`;
60
- if (this.cache.vtxos.has(address)) {
61
- return this.cache.vtxos.get(address);
62
- }
63
- const stored = await this.storage.getItem(cacheKey);
64
- if (!stored) {
65
- this.cache.vtxos.set(address, []);
56
+ const stored = await this.storage.getItem(getVtxosStorageKey(address));
57
+ if (!stored)
66
58
  return [];
67
- }
68
59
  try {
69
60
  const parsed = JSON.parse(stored);
70
- const vtxos = parsed.map(deserializeVtxo);
71
- this.cache.vtxos.set(address, vtxos.slice());
72
- return vtxos.slice();
61
+ return parsed.map(deserializeVtxo);
73
62
  }
74
63
  catch (error) {
75
64
  console.error(`Failed to parse VTXOs for address ${address}:`, error);
76
- this.cache.vtxos.set(address, []);
77
65
  return [];
78
66
  }
79
67
  }
@@ -88,39 +76,27 @@ class WalletRepositoryImpl {
88
76
  storedVtxos.push(vtxo);
89
77
  }
90
78
  }
91
- this.cache.vtxos.set(address, storedVtxos.slice());
92
- await this.storage.setItem(`vtxos:${address}`, JSON.stringify(storedVtxos.map(serializeVtxo)));
79
+ await this.storage.setItem(getVtxosStorageKey(address), JSON.stringify(storedVtxos.map(serializeVtxo)));
93
80
  }
94
81
  async removeVtxo(address, vtxoId) {
95
82
  const vtxos = await this.getVtxos(address);
96
83
  const [txid, vout] = vtxoId.split(":");
97
84
  const filtered = vtxos.filter((v) => !(v.txid === txid && v.vout === parseInt(vout, 10)));
98
- this.cache.vtxos.set(address, filtered.slice());
99
- await this.storage.setItem(`vtxos:${address}`, JSON.stringify(filtered.map(serializeVtxo)));
85
+ await this.storage.setItem(getVtxosStorageKey(address), JSON.stringify(filtered.map(serializeVtxo)));
100
86
  }
101
87
  async clearVtxos(address) {
102
- this.cache.vtxos.set(address, []);
103
- await this.storage.removeItem(`vtxos:${address}`);
88
+ await this.storage.removeItem(getVtxosStorageKey(address));
104
89
  }
105
90
  async getUtxos(address) {
106
- const cacheKey = `utxos:${address}`;
107
- if (this.cache.utxos.has(address)) {
108
- return this.cache.utxos.get(address);
109
- }
110
- const stored = await this.storage.getItem(cacheKey);
111
- if (!stored) {
112
- this.cache.utxos.set(address, []);
91
+ const stored = await this.storage.getItem(getUtxosStorageKey(address));
92
+ if (!stored)
113
93
  return [];
114
- }
115
94
  try {
116
95
  const parsed = JSON.parse(stored);
117
- const utxos = parsed.map(deserializeUtxo);
118
- this.cache.utxos.set(address, utxos.slice());
119
- return utxos.slice();
96
+ return parsed.map(deserializeUtxo);
120
97
  }
121
98
  catch (error) {
122
99
  console.error(`Failed to parse UTXOs for address ${address}:`, error);
123
- this.cache.utxos.set(address, []);
124
100
  return [];
125
101
  }
126
102
  }
@@ -135,38 +111,27 @@ class WalletRepositoryImpl {
135
111
  storedUtxos.push(utxo);
136
112
  }
137
113
  });
138
- this.cache.utxos.set(address, storedUtxos.slice());
139
- await this.storage.setItem(`utxos:${address}`, JSON.stringify(storedUtxos.map(serializeUtxo)));
114
+ await this.storage.setItem(getUtxosStorageKey(address), JSON.stringify(storedUtxos.map(serializeUtxo)));
140
115
  }
141
116
  async removeUtxo(address, utxoId) {
142
117
  const utxos = await this.getUtxos(address);
143
118
  const [txid, vout] = utxoId.split(":");
144
119
  const filtered = utxos.filter((v) => !(v.txid === txid && v.vout === parseInt(vout, 10)));
145
- this.cache.utxos.set(address, filtered.slice());
146
- await this.storage.setItem(`utxos:${address}`, JSON.stringify(filtered.map(serializeUtxo)));
120
+ await this.storage.setItem(getUtxosStorageKey(address), JSON.stringify(filtered.map(serializeUtxo)));
147
121
  }
148
122
  async clearUtxos(address) {
149
- this.cache.utxos.set(address, []);
150
- await this.storage.removeItem(`utxos:${address}`);
123
+ await this.storage.removeItem(getUtxosStorageKey(address));
151
124
  }
152
125
  async getTransactionHistory(address) {
153
- const cacheKey = `tx:${address}`;
154
- if (this.cache.transactions.has(address)) {
155
- return this.cache.transactions.get(address);
156
- }
157
- const stored = await this.storage.getItem(cacheKey);
158
- if (!stored) {
159
- this.cache.transactions.set(address, []);
126
+ const storageKey = getTransactionsStorageKey(address);
127
+ const stored = await this.storage.getItem(storageKey);
128
+ if (!stored)
160
129
  return [];
161
- }
162
130
  try {
163
- const transactions = JSON.parse(stored);
164
- this.cache.transactions.set(address, transactions);
165
- return transactions.slice();
131
+ return JSON.parse(stored);
166
132
  }
167
133
  catch (error) {
168
134
  console.error(`Failed to parse transactions for address ${address}:`, error);
169
- this.cache.transactions.set(address, []);
170
135
  return [];
171
136
  }
172
137
  }
@@ -181,40 +146,26 @@ class WalletRepositoryImpl {
181
146
  storedTransactions.push(tx);
182
147
  }
183
148
  }
184
- this.cache.transactions.set(address, storedTransactions);
185
- await this.storage.setItem(`tx:${address}`, JSON.stringify(storedTransactions));
149
+ await this.storage.setItem(getTransactionsStorageKey(address), JSON.stringify(storedTransactions));
186
150
  }
187
151
  async clearTransactions(address) {
188
- this.cache.transactions.set(address, []);
189
- await this.storage.removeItem(`tx:${address}`);
152
+ await this.storage.removeItem(getTransactionsStorageKey(address));
190
153
  }
191
154
  async getWalletState() {
192
- if (this.cache.walletState !== null ||
193
- this.cache.initialized.has("walletState")) {
194
- return this.cache.walletState;
195
- }
196
- const stored = await this.storage.getItem("wallet:state");
197
- if (!stored) {
198
- this.cache.walletState = null;
199
- this.cache.initialized.add("walletState");
155
+ const stored = await this.storage.getItem(walletStateStorageKey);
156
+ if (!stored)
200
157
  return null;
201
- }
202
158
  try {
203
159
  const state = JSON.parse(stored);
204
- this.cache.walletState = state;
205
- this.cache.initialized.add("walletState");
206
160
  return state;
207
161
  }
208
162
  catch (error) {
209
163
  console.error("Failed to parse wallet state:", error);
210
- this.cache.walletState = null;
211
- this.cache.initialized.add("walletState");
212
164
  return null;
213
165
  }
214
166
  }
215
167
  async saveWalletState(state) {
216
- this.cache.walletState = state;
217
- await this.storage.setItem("wallet:state", JSON.stringify(state));
168
+ await this.storage.setItem(walletStateStorageKey, JSON.stringify(state));
218
169
  }
219
170
  }
220
171
  exports.WalletRepositoryImpl = WalletRepositoryImpl;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.buildOffchainTx = buildOffchainTx;
4
4
  exports.hasBoardingTxExpired = hasBoardingTxExpired;
5
5
  exports.verifyTapscriptSignatures = verifyTapscriptSignatures;
6
+ exports.combineTapscriptSigs = combineTapscriptSigs;
6
7
  const secp256k1_js_1 = require("@noble/curves/secp256k1.js");
7
8
  const base_1 = require("@scure/base");
8
9
  const btc_signer_1 = require("@scure/btc-signer");
@@ -25,6 +26,17 @@ const transaction_1 = require("./transaction");
25
26
  * @returns Object containing the virtual transaction and checkpoint transactions
26
27
  */
27
28
  function buildOffchainTx(inputs, outputs, serverUnrollScript) {
29
+ let hasOpReturn = false;
30
+ for (const [index, output] of outputs.entries()) {
31
+ if (!output.script)
32
+ throw new Error(`missing output script ${index}`);
33
+ const isOpReturn = btc_signer_1.Script.decode(output.script)[0] === "RETURN";
34
+ if (!isOpReturn)
35
+ continue;
36
+ if (hasOpReturn)
37
+ throw new Error("multiple OP_RETURN outputs");
38
+ hasOpReturn = true;
39
+ }
28
40
  const checkpoints = inputs.map((input) => buildCheckpointTx(input, serverUnrollScript));
29
41
  const arkTx = buildVirtualTx(checkpoints.map((c) => c.input), outputs);
30
42
  return {
@@ -209,3 +221,20 @@ function verifyTapscriptSignatures(tx, inputIndex, requiredSigners, excludePubke
209
221
  throw new Error(`Missing signatures from: ${missingSigners.map((pk) => pk.slice(0, 16)).join(", ")}...`);
210
222
  }
211
223
  }
224
+ /**
225
+ * Merges the signed transaction with the original transaction
226
+ * @param signedTx signed transaction
227
+ * @param originalTx original transaction
228
+ */
229
+ function combineTapscriptSigs(signedTx, originalTx) {
230
+ for (let i = 0; i < signedTx.inputsLength; i++) {
231
+ const input = originalTx.getInput(i);
232
+ const signedInput = signedTx.getInput(i);
233
+ if (!input.tapScriptSig)
234
+ throw new Error("No tapScriptSig");
235
+ originalTx.updateInput(i, {
236
+ tapScriptSig: input.tapScriptSig?.concat(signedInput.tapScriptSig),
237
+ });
238
+ }
239
+ return originalTx;
240
+ }
@@ -77,7 +77,6 @@ class Worker {
77
77
  const { boardingTxs, commitmentsToIgnore: roundsToIgnore } = await this.wallet.getBoardingTxs();
78
78
  const { spendable, spent } = await this.getAllVtxos();
79
79
  // convert VTXOs to offchain transactions
80
- console.log("getTransactionHistory - vtxosToTxs:", spendable);
81
80
  const offchainTxs = (0, transactionHistory_1.vtxosToTxs)(spendable, spent, roundsToIgnore);
82
81
  txs = [...boardingTxs, ...offchainTxs];
83
82
  // sort transactions by creation time in descending order (newest first)
@@ -142,6 +141,10 @@ class Worker {
142
141
  // Get wallet address and save vtxos using unified repository
143
142
  const address = await this.wallet.getAddress();
144
143
  await this.walletRepository.saveVtxos(address, vtxos);
144
+ // Fetch boarding utxos and save using unified repository
145
+ const boardingAddress = await this.wallet.getBoardingAddress();
146
+ const coins = await this.wallet.onchainProvider.getCoins(boardingAddress);
147
+ await this.walletRepository.saveUtxos(boardingAddress, coins.map((utxo) => (0, utils_1.extendCoin)(this.wallet, utxo)));
145
148
  // Get transaction history to cache boarding txs
146
149
  const txs = await this.getTransactionHistory();
147
150
  if (txs)
@@ -146,12 +146,29 @@ class Wallet {
146
146
  const esploraUrl = config.esploraUrl || onchain_1.ESPLORA_URL[info.network];
147
147
  // Use provided onchainProvider instance or create a new one
148
148
  const onchainProvider = config.onchainProvider || new onchain_1.EsploraProvider(esploraUrl);
149
- // Generate timelocks
150
- const exitTimelock = {
149
+ // validate unilateral exit timelock passed in config if any
150
+ if (config.exitTimelock) {
151
+ const { value, type } = config.exitTimelock;
152
+ if ((value < 512n && type !== "blocks") ||
153
+ (value >= 512n && type !== "seconds")) {
154
+ throw new Error("invalid exitTimelock");
155
+ }
156
+ }
157
+ // create unilateral exit timelock
158
+ const exitTimelock = config.exitTimelock ?? {
151
159
  value: info.unilateralExitDelay,
152
160
  type: info.unilateralExitDelay < 512n ? "blocks" : "seconds",
153
161
  };
154
- const boardingTimelock = {
162
+ // validate boarding timelock passed in config if any
163
+ if (config.boardingTimelock) {
164
+ const { value, type } = config.boardingTimelock;
165
+ if ((value < 512n && type !== "blocks") ||
166
+ (value >= 512n && type !== "seconds")) {
167
+ throw new Error("invalid boardingTimelock");
168
+ }
169
+ }
170
+ // create boarding timelock
171
+ const boardingTimelock = config.boardingTimelock ?? {
155
172
  value: info.boardingExitDelay,
156
173
  type: info.boardingExitDelay < 512n ? "blocks" : "seconds",
157
174
  };
@@ -869,13 +886,12 @@ class Wallet {
869
886
  }
870
887
  }
871
888
  async makeRegisterIntentSignature(coins, outputs, onchainOutputsIndexes, cosignerPubKeys) {
872
- const nowSeconds = Math.floor(Date.now() / 1000);
873
889
  const inputs = this.prepareIntentProofInputs(coins);
874
890
  const message = {
875
891
  type: "register",
876
892
  onchain_output_indexes: onchainOutputsIndexes,
877
- valid_at: nowSeconds,
878
- expire_at: nowSeconds + 2 * 60, // valid for 2 minutes
893
+ valid_at: 0,
894
+ expire_at: 0,
879
895
  cosigners_public_keys: cosignerPubKeys,
880
896
  };
881
897
  const encodedMessage = JSON.stringify(message, null, 0);
@@ -887,11 +903,10 @@ class Wallet {
887
903
  };
888
904
  }
889
905
  async makeDeleteIntentSignature(coins) {
890
- const nowSeconds = Math.floor(Date.now() / 1000);
891
906
  const inputs = this.prepareIntentProofInputs(coins);
892
907
  const message = {
893
908
  type: "delete",
894
- expire_at: nowSeconds + 2 * 60, // valid for 2 minutes
909
+ expire_at: 0,
895
910
  };
896
911
  const encodedMessage = JSON.stringify(message, null, 0);
897
912
  const proof = intent_1.Intent.create(encodedMessage, inputs, []);
@@ -2,7 +2,7 @@ import { pubECDSA, pubSchnorr, randomPrivateKeyBytes, } from "@scure/btc-signer/
2
2
  import { SigHash } from "@scure/btc-signer";
3
3
  import { hex } from "@scure/base";
4
4
  import { TreeSignerSession } from '../tree/signingSession.js';
5
- import { schnorr, sign } from "@noble/secp256k1";
5
+ import { schnorr, signAsync } from "@noble/secp256k1";
6
6
  const ALL_SIGHASH = Object.values(SigHash).filter((x) => typeof x === "number");
7
7
  /**
8
8
  * In-memory single key implementation for Bitcoin transaction signing.
@@ -80,7 +80,7 @@ export class SingleKey {
80
80
  }
81
81
  async signMessage(message, signatureType = "schnorr") {
82
82
  if (signatureType === "ecdsa")
83
- return sign(message, this.key, { prehash: false });
84
- return schnorr.sign(message, this.key);
83
+ return signAsync(message, this.key, { prehash: false });
84
+ return schnorr.signAsync(message, this.key);
85
85
  }
86
86
  }
package/dist/esm/index.js CHANGED
@@ -18,7 +18,7 @@ import { Response } from './wallet/serviceWorker/response.js';
18
18
  import { ESPLORA_URL, EsploraProvider, } from './providers/onchain.js';
19
19
  import { RestArkProvider, SettlementEventType, } from './providers/ark.js';
20
20
  import { CLTVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CSVMultisigTapscript, decodeTapscript, MultisigTapscript, } from './script/tapscript.js';
21
- import { hasBoardingTxExpired, buildOffchainTx, verifyTapscriptSignatures, } from './utils/arkTransaction.js';
21
+ import { hasBoardingTxExpired, buildOffchainTx, verifyTapscriptSignatures, combineTapscriptSigs, } from './utils/arkTransaction.js';
22
22
  import { VtxoTaprootTree, ConditionWitness, getArkPsbtFields, setArkPsbtField, ArkPsbtFieldKey, ArkPsbtFieldKeyType, CosignerPublicKey, VtxoTreeExpiry, } from './utils/unknownFields.js';
23
23
  import { Intent } from './intent/index.js';
24
24
  import { ArkNote } from './arknote/index.js';
@@ -45,7 +45,7 @@ decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTa
45
45
  // Ark PSBT fields
46
46
  ArkPsbtFieldKey, ArkPsbtFieldKeyType, setArkPsbtField, getArkPsbtFields, CosignerPublicKey, VtxoTreeExpiry, VtxoTaprootTree, ConditionWitness,
47
47
  // Utils
48
- buildOffchainTx, verifyTapscriptSignatures, waitForIncomingFunds, hasBoardingTxExpired,
48
+ buildOffchainTx, verifyTapscriptSignatures, waitForIncomingFunds, hasBoardingTxExpired, combineTapscriptSigs,
49
49
  // Arknote
50
50
  ArkNote,
51
51
  // Network
@@ -135,7 +135,7 @@ export class RestArkProvider {
135
135
  "Content-Type": "application/json",
136
136
  },
137
137
  body: JSON.stringify({
138
- proof: {
138
+ intent: {
139
139
  proof: intent.proof,
140
140
  message: intent.message,
141
141
  },
@@ -1,19 +1,15 @@
1
+ const getContractStorageKey = (id, key) => `contract:${id}:${key}`;
2
+ const getCollectionStorageKey = (type) => `collection:${type}`;
1
3
  export class ContractRepositoryImpl {
2
4
  constructor(storage) {
3
- this.cache = new Map();
4
5
  this.storage = storage;
5
6
  }
6
7
  async getContractData(contractId, key) {
7
- const storageKey = `contract:${contractId}:${key}`;
8
- const cached = this.cache.get(storageKey);
9
- if (cached !== undefined)
10
- return cached;
11
- const stored = await this.storage.getItem(storageKey);
8
+ const stored = await this.storage.getItem(getContractStorageKey(contractId, key));
12
9
  if (!stored)
13
10
  return null;
14
11
  try {
15
12
  const data = JSON.parse(stored);
16
- this.cache.set(storageKey, data);
17
13
  return data;
18
14
  }
19
15
  catch (error) {
@@ -22,49 +18,33 @@ export class ContractRepositoryImpl {
22
18
  }
23
19
  }
24
20
  async setContractData(contractId, key, data) {
25
- const storageKey = `contract:${contractId}:${key}`;
26
21
  try {
27
- // First persist to storage, only update cache if successful
28
- await this.storage.setItem(storageKey, JSON.stringify(data));
29
- this.cache.set(storageKey, data);
22
+ await this.storage.setItem(getContractStorageKey(contractId, key), JSON.stringify(data));
30
23
  }
31
24
  catch (error) {
32
- // Storage operation failed, cache remains unchanged
33
25
  console.error(`Failed to persist contract data for ${contractId}:${key}:`, error);
34
26
  throw error; // Rethrow to notify caller of failure
35
27
  }
36
28
  }
37
29
  async deleteContractData(contractId, key) {
38
- const storageKey = `contract:${contractId}:${key}`;
39
30
  try {
40
- // First remove from persistent storage, only delete from cache if successful
41
- await this.storage.removeItem(storageKey);
42
- this.cache.delete(storageKey);
31
+ await this.storage.removeItem(getContractStorageKey(contractId, key));
43
32
  }
44
33
  catch (error) {
45
- // Storage operation failed, cache remains unchanged
46
34
  console.error(`Failed to remove contract data for ${contractId}:${key}:`, error);
47
35
  throw error; // Rethrow to notify caller of failure
48
36
  }
49
37
  }
50
38
  async getContractCollection(contractType) {
51
- const storageKey = `collection:${contractType}`;
52
- const cached = this.cache.get(storageKey);
53
- if (cached !== undefined)
54
- return cached;
55
- const stored = await this.storage.getItem(storageKey);
56
- if (!stored) {
57
- this.cache.set(storageKey, []);
39
+ const stored = await this.storage.getItem(getCollectionStorageKey(contractType));
40
+ if (!stored)
58
41
  return [];
59
- }
60
42
  try {
61
43
  const collection = JSON.parse(stored);
62
- this.cache.set(storageKey, collection);
63
44
  return collection;
64
45
  }
65
46
  catch (error) {
66
47
  console.error(`Failed to parse contract collection ${contractType}:`, error);
67
- this.cache.set(storageKey, []);
68
48
  return [];
69
49
  }
70
50
  }
@@ -91,14 +71,10 @@ export class ContractRepositoryImpl {
91
71
  // Add new item
92
72
  newCollection = [...collection, item];
93
73
  }
94
- const storageKey = `collection:${contractType}`;
95
74
  try {
96
- // First persist to storage, only update cache if successful
97
- await this.storage.setItem(storageKey, JSON.stringify(newCollection));
98
- this.cache.set(storageKey, newCollection);
75
+ await this.storage.setItem(getCollectionStorageKey(contractType), JSON.stringify(newCollection));
99
76
  }
100
77
  catch (error) {
101
- // Storage operation failed, cache remains unchanged
102
78
  console.error(`Failed to persist contract collection ${contractType}:`, error);
103
79
  throw error; // Rethrow to notify caller of failure
104
80
  }
@@ -111,20 +87,15 @@ export class ContractRepositoryImpl {
111
87
  const collection = await this.getContractCollection(contractType);
112
88
  // Build new collection without the specified item
113
89
  const filtered = collection.filter((item) => item[idField] !== id);
114
- const storageKey = `collection:${contractType}`;
115
90
  try {
116
- // First persist to storage, only update cache if successful
117
- await this.storage.setItem(storageKey, JSON.stringify(filtered));
118
- this.cache.set(storageKey, filtered);
91
+ await this.storage.setItem(getCollectionStorageKey(contractType), JSON.stringify(filtered));
119
92
  }
120
93
  catch (error) {
121
- // Storage operation failed, cache remains unchanged
122
94
  console.error(`Failed to persist contract collection removal for ${contractType}:`, error);
123
95
  throw error; // Rethrow to notify caller of failure
124
96
  }
125
97
  }
126
98
  async clearContractData() {
127
99
  await this.storage.clear();
128
- this.cache.clear();
129
100
  }
130
101
  }
@@ -1,5 +1,9 @@
1
1
  import { hex } from "@scure/base";
2
2
  import { TaprootControlBlock } from "@scure/btc-signer";
3
+ const getVtxosStorageKey = (address) => `vtxos:${address}`;
4
+ const getUtxosStorageKey = (address) => `utxos:${address}`;
5
+ const getTransactionsStorageKey = (address) => `tx:${address}`;
6
+ const walletStateStorageKey = "wallet:state";
3
7
  // Utility functions for (de)serializing complex structures
4
8
  const toHex = (b) => (b ? hex.encode(b) : undefined);
5
9
  const fromHex = (h) => h ? hex.decode(h) : undefined;
@@ -44,33 +48,17 @@ const deserializeUtxo = (o) => ({
44
48
  export class WalletRepositoryImpl {
45
49
  constructor(storage) {
46
50
  this.storage = storage;
47
- this.cache = {
48
- vtxos: new Map(),
49
- utxos: new Map(),
50
- transactions: new Map(),
51
- walletState: null,
52
- initialized: new Set(),
53
- };
54
51
  }
55
52
  async getVtxos(address) {
56
- const cacheKey = `vtxos:${address}`;
57
- if (this.cache.vtxos.has(address)) {
58
- return this.cache.vtxos.get(address);
59
- }
60
- const stored = await this.storage.getItem(cacheKey);
61
- if (!stored) {
62
- this.cache.vtxos.set(address, []);
53
+ const stored = await this.storage.getItem(getVtxosStorageKey(address));
54
+ if (!stored)
63
55
  return [];
64
- }
65
56
  try {
66
57
  const parsed = JSON.parse(stored);
67
- const vtxos = parsed.map(deserializeVtxo);
68
- this.cache.vtxos.set(address, vtxos.slice());
69
- return vtxos.slice();
58
+ return parsed.map(deserializeVtxo);
70
59
  }
71
60
  catch (error) {
72
61
  console.error(`Failed to parse VTXOs for address ${address}:`, error);
73
- this.cache.vtxos.set(address, []);
74
62
  return [];
75
63
  }
76
64
  }
@@ -85,39 +73,27 @@ export class WalletRepositoryImpl {
85
73
  storedVtxos.push(vtxo);
86
74
  }
87
75
  }
88
- this.cache.vtxos.set(address, storedVtxos.slice());
89
- await this.storage.setItem(`vtxos:${address}`, JSON.stringify(storedVtxos.map(serializeVtxo)));
76
+ await this.storage.setItem(getVtxosStorageKey(address), JSON.stringify(storedVtxos.map(serializeVtxo)));
90
77
  }
91
78
  async removeVtxo(address, vtxoId) {
92
79
  const vtxos = await this.getVtxos(address);
93
80
  const [txid, vout] = vtxoId.split(":");
94
81
  const filtered = vtxos.filter((v) => !(v.txid === txid && v.vout === parseInt(vout, 10)));
95
- this.cache.vtxos.set(address, filtered.slice());
96
- await this.storage.setItem(`vtxos:${address}`, JSON.stringify(filtered.map(serializeVtxo)));
82
+ await this.storage.setItem(getVtxosStorageKey(address), JSON.stringify(filtered.map(serializeVtxo)));
97
83
  }
98
84
  async clearVtxos(address) {
99
- this.cache.vtxos.set(address, []);
100
- await this.storage.removeItem(`vtxos:${address}`);
85
+ await this.storage.removeItem(getVtxosStorageKey(address));
101
86
  }
102
87
  async getUtxos(address) {
103
- const cacheKey = `utxos:${address}`;
104
- if (this.cache.utxos.has(address)) {
105
- return this.cache.utxos.get(address);
106
- }
107
- const stored = await this.storage.getItem(cacheKey);
108
- if (!stored) {
109
- this.cache.utxos.set(address, []);
88
+ const stored = await this.storage.getItem(getUtxosStorageKey(address));
89
+ if (!stored)
110
90
  return [];
111
- }
112
91
  try {
113
92
  const parsed = JSON.parse(stored);
114
- const utxos = parsed.map(deserializeUtxo);
115
- this.cache.utxos.set(address, utxos.slice());
116
- return utxos.slice();
93
+ return parsed.map(deserializeUtxo);
117
94
  }
118
95
  catch (error) {
119
96
  console.error(`Failed to parse UTXOs for address ${address}:`, error);
120
- this.cache.utxos.set(address, []);
121
97
  return [];
122
98
  }
123
99
  }
@@ -132,38 +108,27 @@ export class WalletRepositoryImpl {
132
108
  storedUtxos.push(utxo);
133
109
  }
134
110
  });
135
- this.cache.utxos.set(address, storedUtxos.slice());
136
- await this.storage.setItem(`utxos:${address}`, JSON.stringify(storedUtxos.map(serializeUtxo)));
111
+ await this.storage.setItem(getUtxosStorageKey(address), JSON.stringify(storedUtxos.map(serializeUtxo)));
137
112
  }
138
113
  async removeUtxo(address, utxoId) {
139
114
  const utxos = await this.getUtxos(address);
140
115
  const [txid, vout] = utxoId.split(":");
141
116
  const filtered = utxos.filter((v) => !(v.txid === txid && v.vout === parseInt(vout, 10)));
142
- this.cache.utxos.set(address, filtered.slice());
143
- await this.storage.setItem(`utxos:${address}`, JSON.stringify(filtered.map(serializeUtxo)));
117
+ await this.storage.setItem(getUtxosStorageKey(address), JSON.stringify(filtered.map(serializeUtxo)));
144
118
  }
145
119
  async clearUtxos(address) {
146
- this.cache.utxos.set(address, []);
147
- await this.storage.removeItem(`utxos:${address}`);
120
+ await this.storage.removeItem(getUtxosStorageKey(address));
148
121
  }
149
122
  async getTransactionHistory(address) {
150
- const cacheKey = `tx:${address}`;
151
- if (this.cache.transactions.has(address)) {
152
- return this.cache.transactions.get(address);
153
- }
154
- const stored = await this.storage.getItem(cacheKey);
155
- if (!stored) {
156
- this.cache.transactions.set(address, []);
123
+ const storageKey = getTransactionsStorageKey(address);
124
+ const stored = await this.storage.getItem(storageKey);
125
+ if (!stored)
157
126
  return [];
158
- }
159
127
  try {
160
- const transactions = JSON.parse(stored);
161
- this.cache.transactions.set(address, transactions);
162
- return transactions.slice();
128
+ return JSON.parse(stored);
163
129
  }
164
130
  catch (error) {
165
131
  console.error(`Failed to parse transactions for address ${address}:`, error);
166
- this.cache.transactions.set(address, []);
167
132
  return [];
168
133
  }
169
134
  }
@@ -178,39 +143,25 @@ export class WalletRepositoryImpl {
178
143
  storedTransactions.push(tx);
179
144
  }
180
145
  }
181
- this.cache.transactions.set(address, storedTransactions);
182
- await this.storage.setItem(`tx:${address}`, JSON.stringify(storedTransactions));
146
+ await this.storage.setItem(getTransactionsStorageKey(address), JSON.stringify(storedTransactions));
183
147
  }
184
148
  async clearTransactions(address) {
185
- this.cache.transactions.set(address, []);
186
- await this.storage.removeItem(`tx:${address}`);
149
+ await this.storage.removeItem(getTransactionsStorageKey(address));
187
150
  }
188
151
  async getWalletState() {
189
- if (this.cache.walletState !== null ||
190
- this.cache.initialized.has("walletState")) {
191
- return this.cache.walletState;
192
- }
193
- const stored = await this.storage.getItem("wallet:state");
194
- if (!stored) {
195
- this.cache.walletState = null;
196
- this.cache.initialized.add("walletState");
152
+ const stored = await this.storage.getItem(walletStateStorageKey);
153
+ if (!stored)
197
154
  return null;
198
- }
199
155
  try {
200
156
  const state = JSON.parse(stored);
201
- this.cache.walletState = state;
202
- this.cache.initialized.add("walletState");
203
157
  return state;
204
158
  }
205
159
  catch (error) {
206
160
  console.error("Failed to parse wallet state:", error);
207
- this.cache.walletState = null;
208
- this.cache.initialized.add("walletState");
209
161
  return null;
210
162
  }
211
163
  }
212
164
  async saveWalletState(state) {
213
- this.cache.walletState = state;
214
- await this.storage.setItem("wallet:state", JSON.stringify(state));
165
+ await this.storage.setItem(walletStateStorageKey, JSON.stringify(state));
215
166
  }
216
167
  }
@@ -1,6 +1,6 @@
1
1
  import { schnorr } from "@noble/curves/secp256k1.js";
2
2
  import { hex } from "@scure/base";
3
- import { DEFAULT_SEQUENCE, SigHash } from "@scure/btc-signer";
3
+ import { DEFAULT_SEQUENCE, Script, SigHash } from "@scure/btc-signer";
4
4
  import { tapLeafHash } from "@scure/btc-signer/payment.js";
5
5
  import { CLTVMultisigTapscript, decodeTapscript, } from '../script/tapscript.js';
6
6
  import { scriptFromTapLeafScript, VtxoScript, } from '../script/base.js';
@@ -20,6 +20,17 @@ import { Transaction } from './transaction.js';
20
20
  * @returns Object containing the virtual transaction and checkpoint transactions
21
21
  */
22
22
  export function buildOffchainTx(inputs, outputs, serverUnrollScript) {
23
+ let hasOpReturn = false;
24
+ for (const [index, output] of outputs.entries()) {
25
+ if (!output.script)
26
+ throw new Error(`missing output script ${index}`);
27
+ const isOpReturn = Script.decode(output.script)[0] === "RETURN";
28
+ if (!isOpReturn)
29
+ continue;
30
+ if (hasOpReturn)
31
+ throw new Error("multiple OP_RETURN outputs");
32
+ hasOpReturn = true;
33
+ }
23
34
  const checkpoints = inputs.map((input) => buildCheckpointTx(input, serverUnrollScript));
24
35
  const arkTx = buildVirtualTx(checkpoints.map((c) => c.input), outputs);
25
36
  return {
@@ -204,3 +215,20 @@ export function verifyTapscriptSignatures(tx, inputIndex, requiredSigners, exclu
204
215
  throw new Error(`Missing signatures from: ${missingSigners.map((pk) => pk.slice(0, 16)).join(", ")}...`);
205
216
  }
206
217
  }
218
+ /**
219
+ * Merges the signed transaction with the original transaction
220
+ * @param signedTx signed transaction
221
+ * @param originalTx original transaction
222
+ */
223
+ export function combineTapscriptSigs(signedTx, originalTx) {
224
+ for (let i = 0; i < signedTx.inputsLength; i++) {
225
+ const input = originalTx.getInput(i);
226
+ const signedInput = signedTx.getInput(i);
227
+ if (!input.tapScriptSig)
228
+ throw new Error("No tapScriptSig");
229
+ originalTx.updateInput(i, {
230
+ tapScriptSig: input.tapScriptSig?.concat(signedInput.tapScriptSig),
231
+ });
232
+ }
233
+ return originalTx;
234
+ }
@@ -74,7 +74,6 @@ export class Worker {
74
74
  const { boardingTxs, commitmentsToIgnore: roundsToIgnore } = await this.wallet.getBoardingTxs();
75
75
  const { spendable, spent } = await this.getAllVtxos();
76
76
  // convert VTXOs to offchain transactions
77
- console.log("getTransactionHistory - vtxosToTxs:", spendable);
78
77
  const offchainTxs = vtxosToTxs(spendable, spent, roundsToIgnore);
79
78
  txs = [...boardingTxs, ...offchainTxs];
80
79
  // sort transactions by creation time in descending order (newest first)
@@ -139,6 +138,10 @@ export class Worker {
139
138
  // Get wallet address and save vtxos using unified repository
140
139
  const address = await this.wallet.getAddress();
141
140
  await this.walletRepository.saveVtxos(address, vtxos);
141
+ // Fetch boarding utxos and save using unified repository
142
+ const boardingAddress = await this.wallet.getBoardingAddress();
143
+ const coins = await this.wallet.onchainProvider.getCoins(boardingAddress);
144
+ await this.walletRepository.saveUtxos(boardingAddress, coins.map((utxo) => extendCoin(this.wallet, utxo)));
142
145
  // Get transaction history to cache boarding txs
143
146
  const txs = await this.getTransactionHistory();
144
147
  if (txs)
@@ -109,12 +109,29 @@ export class Wallet {
109
109
  const esploraUrl = config.esploraUrl || ESPLORA_URL[info.network];
110
110
  // Use provided onchainProvider instance or create a new one
111
111
  const onchainProvider = config.onchainProvider || new EsploraProvider(esploraUrl);
112
- // Generate timelocks
113
- const exitTimelock = {
112
+ // validate unilateral exit timelock passed in config if any
113
+ if (config.exitTimelock) {
114
+ const { value, type } = config.exitTimelock;
115
+ if ((value < 512n && type !== "blocks") ||
116
+ (value >= 512n && type !== "seconds")) {
117
+ throw new Error("invalid exitTimelock");
118
+ }
119
+ }
120
+ // create unilateral exit timelock
121
+ const exitTimelock = config.exitTimelock ?? {
114
122
  value: info.unilateralExitDelay,
115
123
  type: info.unilateralExitDelay < 512n ? "blocks" : "seconds",
116
124
  };
117
- const boardingTimelock = {
125
+ // validate boarding timelock passed in config if any
126
+ if (config.boardingTimelock) {
127
+ const { value, type } = config.boardingTimelock;
128
+ if ((value < 512n && type !== "blocks") ||
129
+ (value >= 512n && type !== "seconds")) {
130
+ throw new Error("invalid boardingTimelock");
131
+ }
132
+ }
133
+ // create boarding timelock
134
+ const boardingTimelock = config.boardingTimelock ?? {
118
135
  value: info.boardingExitDelay,
119
136
  type: info.boardingExitDelay < 512n ? "blocks" : "seconds",
120
137
  };
@@ -832,13 +849,12 @@ export class Wallet {
832
849
  }
833
850
  }
834
851
  async makeRegisterIntentSignature(coins, outputs, onchainOutputsIndexes, cosignerPubKeys) {
835
- const nowSeconds = Math.floor(Date.now() / 1000);
836
852
  const inputs = this.prepareIntentProofInputs(coins);
837
853
  const message = {
838
854
  type: "register",
839
855
  onchain_output_indexes: onchainOutputsIndexes,
840
- valid_at: nowSeconds,
841
- expire_at: nowSeconds + 2 * 60, // valid for 2 minutes
856
+ valid_at: 0,
857
+ expire_at: 0,
842
858
  cosigners_public_keys: cosignerPubKeys,
843
859
  };
844
860
  const encodedMessage = JSON.stringify(message, null, 0);
@@ -850,11 +866,10 @@ export class Wallet {
850
866
  };
851
867
  }
852
868
  async makeDeleteIntentSignature(coins) {
853
- const nowSeconds = Math.floor(Date.now() / 1000);
854
869
  const inputs = this.prepareIntentProofInputs(coins);
855
870
  const message = {
856
871
  type: "delete",
857
- expire_at: nowSeconds + 2 * 60, // valid for 2 minutes
872
+ expire_at: 0,
858
873
  };
859
874
  const encodedMessage = JSON.stringify(message, null, 0);
860
875
  const proof = Intent.create(encodedMessage, inputs, []);
@@ -20,7 +20,7 @@ import { Response } from "./wallet/serviceWorker/response";
20
20
  import { ESPLORA_URL, EsploraProvider, OnchainProvider, ExplorerTransaction } from "./providers/onchain";
21
21
  import { RestArkProvider, ArkProvider, SettlementEvent, SettlementEventType, ArkInfo, SignedIntent, Output, TxNotification, BatchFinalizationEvent, BatchFinalizedEvent, BatchFailedEvent, TreeSigningStartedEvent, TreeNoncesEvent, BatchStartedEvent, TreeTxEvent, TreeSignatureEvent, ScheduledSession } from "./providers/ark";
22
22
  import { CLTVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CSVMultisigTapscript, decodeTapscript, MultisigTapscript, TapscriptType, ArkTapscript, RelativeTimelock } from "./script/tapscript";
23
- import { hasBoardingTxExpired, buildOffchainTx, verifyTapscriptSignatures, ArkTxInput, OffchainTx } from "./utils/arkTransaction";
23
+ import { hasBoardingTxExpired, buildOffchainTx, verifyTapscriptSignatures, ArkTxInput, OffchainTx, combineTapscriptSigs } from "./utils/arkTransaction";
24
24
  import { VtxoTaprootTree, ConditionWitness, getArkPsbtFields, setArkPsbtField, ArkPsbtFieldCoder, ArkPsbtFieldKey, ArkPsbtFieldKeyType, CosignerPublicKey, VtxoTreeExpiry } from "./utils/unknownFields";
25
25
  import { Intent } from "./intent";
26
26
  import { ArkNote } from "./arknote";
@@ -33,5 +33,5 @@ import { Unroll } from "./wallet/unroll";
33
33
  import { WalletRepositoryImpl } from "./repositories/walletRepository";
34
34
  import { ContractRepositoryImpl } from "./repositories/contractRepository";
35
35
  import { ArkError, maybeArkError } from "./providers/errors";
36
- export { Wallet, SingleKey, OnchainWallet, Ramps, VtxoManager, ESPLORA_URL, EsploraProvider, RestArkProvider, RestIndexerProvider, ArkAddress, DefaultVtxo, VtxoScript, VHTLC, TxType, IndexerTxType, ChainTxType, SettlementEventType, setupServiceWorker, Worker, ServiceWorkerWallet, Request, Response, decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CLTVMultisigTapscript, TapTreeCoder, ArkPsbtFieldKey, ArkPsbtFieldKeyType, setArkPsbtField, getArkPsbtFields, CosignerPublicKey, VtxoTreeExpiry, VtxoTaprootTree, ConditionWitness, buildOffchainTx, verifyTapscriptSignatures, waitForIncomingFunds, hasBoardingTxExpired, ArkNote, networks, WalletRepositoryImpl, ContractRepositoryImpl, Intent, TxTree, P2A, Unroll, Transaction, ArkError, maybeArkError, };
36
+ export { Wallet, SingleKey, OnchainWallet, Ramps, VtxoManager, ESPLORA_URL, EsploraProvider, RestArkProvider, RestIndexerProvider, ArkAddress, DefaultVtxo, VtxoScript, VHTLC, TxType, IndexerTxType, ChainTxType, SettlementEventType, setupServiceWorker, Worker, ServiceWorkerWallet, Request, Response, decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CLTVMultisigTapscript, TapTreeCoder, ArkPsbtFieldKey, ArkPsbtFieldKeyType, setArkPsbtField, getArkPsbtFields, CosignerPublicKey, VtxoTreeExpiry, VtxoTaprootTree, ConditionWitness, buildOffchainTx, verifyTapscriptSignatures, waitForIncomingFunds, hasBoardingTxExpired, combineTapscriptSigs, ArkNote, networks, WalletRepositoryImpl, ContractRepositoryImpl, Intent, TxTree, P2A, Unroll, Transaction, ArkError, maybeArkError, };
37
37
  export type { Identity, IWallet, WalletConfig, ProviderClass, ArkTransaction, Coin, ExtendedCoin, ExtendedVirtualCoin, WalletBalance, SendBitcoinParams, Recipient, SettleParams, Status, VirtualStatus, Outpoint, VirtualCoin, TxKey, TapscriptType, ArkTxInput, OffchainTx, TapLeaves, IncomingFunds, IndexerProvider, PageResponse, Batch, ChainTx, CommitmentTx, TxHistoryRecord, Vtxo, VtxoChain, Tx, OnchainProvider, ArkProvider, SettlementEvent, ArkInfo, SignedIntent, Output, TxNotification, ExplorerTransaction, BatchFinalizationEvent, BatchFinalizedEvent, BatchFailedEvent, TreeSigningStartedEvent, TreeNoncesEvent, BatchStartedEvent, TreeTxEvent, TreeSignatureEvent, ScheduledSession, PaginationOptions, SubscriptionResponse, SubscriptionHeartbeat, SubscriptionEvent, Network, NetworkName, ArkTapscript, RelativeTimelock, EncodedVtxoScript, TapLeafScript, SignerSession, TreeNonces, TreePartialSigs, GetVtxosFilter, Nonces, PartialSig, ArkPsbtFieldCoder, TxTreeNode, AnchorBumper, };
@@ -10,7 +10,6 @@ export interface ContractRepository {
10
10
  }
11
11
  export declare class ContractRepositoryImpl implements ContractRepository {
12
12
  private storage;
13
- private cache;
14
13
  constructor(storage: StorageAdapter);
15
14
  getContractData<T>(contractId: string, key: string): Promise<T | null>;
16
15
  setContractData<T>(contractId: string, key: string, data: T): Promise<void>;
@@ -21,7 +21,6 @@ export interface WalletRepository {
21
21
  }
22
22
  export declare class WalletRepositoryImpl implements WalletRepository {
23
23
  private storage;
24
- private cache;
25
24
  constructor(storage: StorageAdapter);
26
25
  getVtxos(address: string): Promise<ExtendedVirtualCoin[]>;
27
26
  saveVtxos(address: string, vtxos: ExtendedVirtualCoin[]): Promise<void>;
@@ -35,3 +35,9 @@ export declare function hasBoardingTxExpired(coin: ExtendedCoin, boardingTimeloc
35
35
  * @throws Error if verification fails
36
36
  */
37
37
  export declare function verifyTapscriptSignatures(tx: Transaction, inputIndex: number, requiredSigners: string[], excludePubkeys?: string[], allowedSighashTypes?: number[]): void;
38
+ /**
39
+ * Merges the signed transaction with the original transaction
40
+ * @param signedTx signed transaction
41
+ * @param originalTx original transaction
42
+ */
43
+ export declare function combineTapscriptSigs(signedTx: Transaction, originalTx: Transaction): Transaction;
@@ -1,9 +1,10 @@
1
+ import { TransactionOutput } from "@scure/btc-signer/psbt.js";
1
2
  import { Bytes } from "@scure/btc-signer/utils.js";
2
3
  import { ArkAddress } from "../script/address";
3
4
  import { DefaultVtxo } from "../script/default";
4
5
  import { Network, NetworkName } from "../networks";
5
6
  import { OnchainProvider } from "../providers/onchain";
6
- import { SettlementEvent, ArkProvider } from "../providers/ark";
7
+ import { SettlementEvent, ArkProvider, SignedIntent } from "../providers/ark";
7
8
  import { Identity } from "../identity";
8
9
  import { ArkTransaction, Coin, ExtendedCoin, ExtendedVirtualCoin, GetVtxosFilter, IWallet, SendBitcoinParams, SettleParams, WalletBalance, WalletConfig } from ".";
9
10
  import { CSVMultisigTapscript } from "../script/tapscript";
@@ -93,8 +94,8 @@ export declare class Wallet implements IWallet {
93
94
  private handleSettlementSigningEvent;
94
95
  private handleSettlementTreeNoncesEvent;
95
96
  private handleSettlementFinalizationEvent;
96
- private makeRegisterIntentSignature;
97
- private makeDeleteIntentSignature;
97
+ makeRegisterIntentSignature(coins: ExtendedCoin[], outputs: TransactionOutput[], onchainOutputsIndexes: number[], cosignerPubKeys: string[]): Promise<SignedIntent>;
98
+ makeDeleteIntentSignature(coins: ExtendedCoin[]): Promise<SignedIntent>;
98
99
  private prepareIntentProofInputs;
99
100
  }
100
101
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkade-os/sdk",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "Bitcoin wallet SDK with Taproot and Ark integration",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",