@arkade-os/sdk 0.4.0-next.0 → 0.4.0-next.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.
Files changed (60) hide show
  1. package/README.md +127 -24
  2. package/dist/cjs/bip322/index.js +270 -0
  3. package/dist/cjs/index.js +4 -2
  4. package/dist/cjs/intent/index.js +19 -9
  5. package/dist/cjs/repositories/indexedDB/contractRepository.js +1 -1
  6. package/dist/cjs/repositories/indexedDB/db.js +8 -46
  7. package/dist/cjs/repositories/indexedDB/walletRepository.js +1 -1
  8. package/dist/cjs/repositories/realm/contractRepository.js +120 -0
  9. package/dist/cjs/repositories/realm/index.js +9 -0
  10. package/dist/cjs/repositories/realm/schemas.js +108 -0
  11. package/dist/cjs/repositories/realm/types.js +7 -0
  12. package/dist/cjs/repositories/realm/walletRepository.js +273 -0
  13. package/dist/cjs/repositories/serialization.js +49 -0
  14. package/dist/cjs/repositories/sqlite/contractRepository.js +139 -0
  15. package/dist/cjs/repositories/sqlite/index.js +7 -0
  16. package/dist/cjs/repositories/sqlite/types.js +2 -0
  17. package/dist/cjs/repositories/sqlite/walletRepository.js +328 -0
  18. package/dist/cjs/wallet/serviceWorker/wallet.js +9 -1
  19. package/dist/cjs/wallet/vtxo-manager.js +7 -0
  20. package/dist/cjs/worker/messageBus.js +22 -2
  21. package/dist/esm/bip322/index.js +267 -0
  22. package/dist/esm/index.js +4 -1
  23. package/dist/esm/intent/index.js +16 -7
  24. package/dist/esm/repositories/indexedDB/contractRepository.js +1 -1
  25. package/dist/esm/repositories/indexedDB/db.js +2 -40
  26. package/dist/esm/repositories/indexedDB/walletRepository.js +1 -1
  27. package/dist/esm/repositories/realm/contractRepository.js +116 -0
  28. package/dist/esm/repositories/realm/index.js +3 -0
  29. package/dist/esm/repositories/realm/schemas.js +105 -0
  30. package/dist/esm/repositories/realm/types.js +6 -0
  31. package/dist/esm/repositories/realm/walletRepository.js +269 -0
  32. package/dist/esm/repositories/serialization.js +40 -0
  33. package/dist/esm/repositories/sqlite/contractRepository.js +135 -0
  34. package/dist/esm/repositories/sqlite/index.js +2 -0
  35. package/dist/esm/repositories/sqlite/types.js +1 -0
  36. package/dist/esm/repositories/sqlite/walletRepository.js +324 -0
  37. package/dist/esm/wallet/serviceWorker/wallet.js +9 -1
  38. package/dist/esm/wallet/vtxo-manager.js +7 -0
  39. package/dist/esm/worker/messageBus.js +22 -2
  40. package/dist/types/bip322/index.d.ts +55 -0
  41. package/dist/types/index.d.ts +3 -2
  42. package/dist/types/intent/index.d.ts +13 -0
  43. package/dist/types/repositories/indexedDB/db.d.ts +2 -54
  44. package/dist/types/repositories/realm/contractRepository.d.ts +24 -0
  45. package/dist/types/repositories/realm/index.d.ts +4 -0
  46. package/dist/types/repositories/realm/schemas.d.ts +208 -0
  47. package/dist/types/repositories/realm/types.d.ts +16 -0
  48. package/dist/types/repositories/realm/walletRepository.d.ts +31 -0
  49. package/dist/types/repositories/serialization.d.ts +40 -0
  50. package/dist/types/repositories/sqlite/contractRepository.d.ts +33 -0
  51. package/dist/types/repositories/sqlite/index.d.ts +3 -0
  52. package/dist/types/repositories/sqlite/types.d.ts +18 -0
  53. package/dist/types/repositories/sqlite/walletRepository.d.ts +40 -0
  54. package/package.json +18 -14
  55. package/dist/cjs/adapters/expo-db.js +0 -35
  56. package/dist/esm/adapters/expo-db.js +0 -27
  57. package/dist/types/adapters/expo-db.d.ts +0 -7
  58. /package/dist/cjs/{db → repositories/indexedDB}/manager.js +0 -0
  59. /package/dist/esm/{db → repositories/indexedDB}/manager.js +0 -0
  60. /package/dist/types/{db → repositories/indexedDB}/manager.d.ts +0 -0
@@ -0,0 +1,324 @@
1
+ import { serializeVtxo, serializeUtxo, deserializeVtxo, deserializeUtxo, } from '../serialization.js';
2
+ /**
3
+ * SQLite-based implementation of WalletRepository.
4
+ *
5
+ * Uses the SQLExecutor interface so consumers can plug in any SQLite driver
6
+ * (expo-sqlite, better-sqlite3, etc.).
7
+ *
8
+ * Tables are created lazily on first operation via `ensureInit()`.
9
+ * The consumer owns the SQLExecutor lifecycle — `[Symbol.asyncDispose]` is a no-op.
10
+ */
11
+ export class SQLiteWalletRepository {
12
+ constructor(db, options) {
13
+ this.db = db;
14
+ this.version = 1;
15
+ this.initPromise = null;
16
+ this.prefix = sanitizePrefix(options?.prefix ?? "ark_");
17
+ this.tables = {
18
+ vtxos: `${this.prefix}vtxos`,
19
+ utxos: `${this.prefix}utxos`,
20
+ transactions: `${this.prefix}transactions`,
21
+ walletState: `${this.prefix}wallet_state`,
22
+ };
23
+ }
24
+ // ── Lifecycle ──────────────────────────────────────────────────────
25
+ ensureInit() {
26
+ if (!this.initPromise) {
27
+ this.initPromise = this.init();
28
+ }
29
+ return this.initPromise;
30
+ }
31
+ async init() {
32
+ await this.db.run(`
33
+ CREATE TABLE IF NOT EXISTS ${this.tables.vtxos} (
34
+ txid TEXT NOT NULL,
35
+ vout INTEGER NOT NULL,
36
+ value INTEGER NOT NULL,
37
+ address TEXT NOT NULL,
38
+ tap_tree TEXT NOT NULL,
39
+ forfeit_cb TEXT NOT NULL,
40
+ forfeit_s TEXT NOT NULL,
41
+ intent_cb TEXT NOT NULL,
42
+ intent_s TEXT NOT NULL,
43
+ status_json TEXT NOT NULL,
44
+ virtual_status_json TEXT NOT NULL,
45
+ created_at TEXT NOT NULL,
46
+ is_unrolled INTEGER NOT NULL DEFAULT 0,
47
+ is_spent INTEGER,
48
+ spent_by TEXT,
49
+ settled_by TEXT,
50
+ ark_tx_id TEXT,
51
+ extra_witness_json TEXT,
52
+ assets_json TEXT,
53
+ PRIMARY KEY (txid, vout)
54
+ )
55
+ `);
56
+ await this.db.run(`
57
+ CREATE TABLE IF NOT EXISTS ${this.tables.utxos} (
58
+ txid TEXT NOT NULL,
59
+ vout INTEGER NOT NULL,
60
+ value INTEGER NOT NULL,
61
+ address TEXT NOT NULL,
62
+ tap_tree TEXT NOT NULL,
63
+ forfeit_cb TEXT NOT NULL,
64
+ forfeit_s TEXT NOT NULL,
65
+ intent_cb TEXT NOT NULL,
66
+ intent_s TEXT NOT NULL,
67
+ status_json TEXT NOT NULL,
68
+ extra_witness_json TEXT,
69
+ PRIMARY KEY (txid, vout)
70
+ )
71
+ `);
72
+ await this.db.run(`
73
+ CREATE TABLE IF NOT EXISTS ${this.tables.transactions} (
74
+ address TEXT NOT NULL,
75
+ boarding_txid TEXT NOT NULL,
76
+ commitment_txid TEXT NOT NULL,
77
+ ark_txid TEXT NOT NULL,
78
+ type TEXT NOT NULL,
79
+ amount INTEGER NOT NULL,
80
+ settled INTEGER NOT NULL DEFAULT 0,
81
+ created_at INTEGER NOT NULL,
82
+ assets_json TEXT,
83
+ PRIMARY KEY (address, boarding_txid, commitment_txid, ark_txid)
84
+ )
85
+ `);
86
+ await this.db.run(`
87
+ CREATE TABLE IF NOT EXISTS ${this.tables.walletState} (
88
+ key TEXT PRIMARY KEY,
89
+ last_sync_time INTEGER,
90
+ settings_json TEXT
91
+ )
92
+ `);
93
+ await this.db.run(`CREATE INDEX IF NOT EXISTS idx_${this.prefix}vtxos_address ON ${this.tables.vtxos} (address)`);
94
+ await this.db.run(`CREATE INDEX IF NOT EXISTS idx_${this.prefix}utxos_address ON ${this.tables.utxos} (address)`);
95
+ await this.db.run(`CREATE INDEX IF NOT EXISTS idx_${this.prefix}transactions_address ON ${this.tables.transactions} (address)`);
96
+ }
97
+ async [Symbol.asyncDispose]() {
98
+ // no-op — consumer owns the SQLExecutor lifecycle
99
+ }
100
+ // ── Clear ──────────────────────────────────────────────────────────
101
+ async clear() {
102
+ await this.ensureInit();
103
+ await this.db.run(`DELETE FROM ${this.tables.vtxos}`);
104
+ await this.db.run(`DELETE FROM ${this.tables.utxos}`);
105
+ await this.db.run(`DELETE FROM ${this.tables.transactions}`);
106
+ await this.db.run(`DELETE FROM ${this.tables.walletState}`);
107
+ }
108
+ // ── VTXO management ────────────────────────────────────────────────
109
+ async getVtxos(address) {
110
+ await this.ensureInit();
111
+ const rows = await this.db.all(`SELECT * FROM ${this.tables.vtxos} WHERE address = ?`, [address]);
112
+ return rows.map(vtxoRowToDomain);
113
+ }
114
+ async saveVtxos(address, vtxos) {
115
+ await this.ensureInit();
116
+ for (const vtxo of vtxos) {
117
+ const s = serializeVtxo(vtxo);
118
+ await this.db.run(`INSERT OR REPLACE INTO ${this.tables.vtxos}
119
+ (txid, vout, value, address,
120
+ tap_tree, forfeit_cb, forfeit_s, intent_cb, intent_s,
121
+ status_json, virtual_status_json, created_at,
122
+ is_unrolled, is_spent, spent_by, settled_by, ark_tx_id,
123
+ extra_witness_json, assets_json)
124
+ VALUES (?, ?, ?, ?,
125
+ ?, ?, ?, ?, ?,
126
+ ?, ?, ?,
127
+ ?, ?, ?, ?, ?,
128
+ ?, ?)`, [
129
+ s.txid,
130
+ s.vout,
131
+ s.value,
132
+ address,
133
+ s.tapTree,
134
+ s.forfeitTapLeafScript.cb,
135
+ s.forfeitTapLeafScript.s,
136
+ s.intentTapLeafScript.cb,
137
+ s.intentTapLeafScript.s,
138
+ JSON.stringify(s.status),
139
+ JSON.stringify(s.virtualStatus),
140
+ typeof s.createdAt === "string"
141
+ ? s.createdAt
142
+ : s.createdAt instanceof Date
143
+ ? s.createdAt.toISOString()
144
+ : new Date(s.createdAt).toISOString(),
145
+ s.isUnrolled ? 1 : 0,
146
+ s.isSpent === undefined ? null : s.isSpent ? 1 : 0,
147
+ s.spentBy ?? null,
148
+ s.settledBy ?? null,
149
+ s.arkTxId ?? null,
150
+ s.extraWitness ? JSON.stringify(s.extraWitness) : null,
151
+ s.assets ? JSON.stringify(s.assets) : null,
152
+ ]);
153
+ }
154
+ }
155
+ async deleteVtxos(address) {
156
+ await this.ensureInit();
157
+ await this.db.run(`DELETE FROM ${this.tables.vtxos} WHERE address = ?`, [address]);
158
+ }
159
+ // ── UTXO management ────────────────────────────────────────────────
160
+ async getUtxos(address) {
161
+ await this.ensureInit();
162
+ const rows = await this.db.all(`SELECT * FROM ${this.tables.utxos} WHERE address = ?`, [address]);
163
+ return rows.map(utxoRowToDomain);
164
+ }
165
+ async saveUtxos(address, utxos) {
166
+ await this.ensureInit();
167
+ for (const utxo of utxos) {
168
+ const s = serializeUtxo(utxo);
169
+ await this.db.run(`INSERT OR REPLACE INTO ${this.tables.utxos}
170
+ (txid, vout, value, address,
171
+ tap_tree, forfeit_cb, forfeit_s, intent_cb, intent_s,
172
+ status_json, extra_witness_json)
173
+ VALUES (?, ?, ?, ?,
174
+ ?, ?, ?, ?, ?,
175
+ ?, ?)`, [
176
+ s.txid,
177
+ s.vout,
178
+ s.value,
179
+ address,
180
+ s.tapTree,
181
+ s.forfeitTapLeafScript.cb,
182
+ s.forfeitTapLeafScript.s,
183
+ s.intentTapLeafScript.cb,
184
+ s.intentTapLeafScript.s,
185
+ JSON.stringify(s.status),
186
+ s.extraWitness ? JSON.stringify(s.extraWitness) : null,
187
+ ]);
188
+ }
189
+ }
190
+ async deleteUtxos(address) {
191
+ await this.ensureInit();
192
+ await this.db.run(`DELETE FROM ${this.tables.utxos} WHERE address = ?`, [address]);
193
+ }
194
+ // ── Transaction history ────────────────────────────────────────────
195
+ async getTransactionHistory(address) {
196
+ await this.ensureInit();
197
+ const rows = await this.db.all(`SELECT * FROM ${this.tables.transactions} WHERE address = ? ORDER BY created_at ASC`, [address]);
198
+ return rows.map(txRowToDomain);
199
+ }
200
+ async saveTransactions(address, txs) {
201
+ await this.ensureInit();
202
+ for (const tx of txs) {
203
+ await this.db.run(`INSERT OR REPLACE INTO ${this.tables.transactions}
204
+ (address, boarding_txid, commitment_txid, ark_txid,
205
+ type, amount, settled, created_at, assets_json)
206
+ VALUES (?, ?, ?, ?,
207
+ ?, ?, ?, ?, ?)`, [
208
+ address,
209
+ tx.key.boardingTxid,
210
+ tx.key.commitmentTxid,
211
+ tx.key.arkTxid,
212
+ tx.type,
213
+ tx.amount,
214
+ tx.settled ? 1 : 0,
215
+ tx.createdAt,
216
+ tx.assets ? JSON.stringify(tx.assets) : null,
217
+ ]);
218
+ }
219
+ }
220
+ async deleteTransactions(address) {
221
+ await this.ensureInit();
222
+ await this.db.run(`DELETE FROM ${this.tables.transactions} WHERE address = ?`, [address]);
223
+ }
224
+ // ── Wallet state ───────────────────────────────────────────────────
225
+ async getWalletState() {
226
+ await this.ensureInit();
227
+ const row = await this.db.get(`SELECT * FROM ${this.tables.walletState} WHERE key = ?`, ["state"]);
228
+ if (!row)
229
+ return null;
230
+ const state = {};
231
+ if (row.last_sync_time !== null && row.last_sync_time !== undefined) {
232
+ state.lastSyncTime = row.last_sync_time;
233
+ }
234
+ if (row.settings_json) {
235
+ state.settings = JSON.parse(row.settings_json);
236
+ }
237
+ return state;
238
+ }
239
+ async saveWalletState(state) {
240
+ await this.ensureInit();
241
+ await this.db.run(`INSERT OR REPLACE INTO ${this.tables.walletState}
242
+ (key, last_sync_time, settings_json)
243
+ VALUES (?, ?, ?)`, [
244
+ "state",
245
+ state.lastSyncTime ?? null,
246
+ state.settings ? JSON.stringify(state.settings) : null,
247
+ ]);
248
+ }
249
+ }
250
+ const SAFE_PREFIX = /^[a-zA-Z0-9_]+$/;
251
+ function sanitizePrefix(prefix) {
252
+ if (!SAFE_PREFIX.test(prefix)) {
253
+ throw new Error(`Invalid table prefix "${prefix}": only letters, digits, and underscores are allowed`);
254
+ }
255
+ return prefix;
256
+ }
257
+ // ── Row → Domain converters ────────────────────────────────────────────
258
+ function vtxoRowToDomain(row) {
259
+ const serialized = {
260
+ txid: row.txid,
261
+ vout: row.vout,
262
+ value: row.value,
263
+ tapTree: row.tap_tree,
264
+ forfeitTapLeafScript: {
265
+ cb: row.forfeit_cb,
266
+ s: row.forfeit_s,
267
+ },
268
+ intentTapLeafScript: {
269
+ cb: row.intent_cb,
270
+ s: row.intent_s,
271
+ },
272
+ status: JSON.parse(row.status_json),
273
+ virtualStatus: JSON.parse(row.virtual_status_json),
274
+ createdAt: new Date(row.created_at),
275
+ isUnrolled: row.is_unrolled === 1,
276
+ isSpent: row.is_spent === null ? undefined : row.is_spent === 1,
277
+ spentBy: row.spent_by ?? undefined,
278
+ settledBy: row.settled_by ?? undefined,
279
+ arkTxId: row.ark_tx_id ?? undefined,
280
+ extraWitness: row.extra_witness_json
281
+ ? JSON.parse(row.extra_witness_json)
282
+ : undefined,
283
+ assets: row.assets_json ? JSON.parse(row.assets_json) : undefined,
284
+ };
285
+ return deserializeVtxo(serialized);
286
+ }
287
+ function utxoRowToDomain(row) {
288
+ const serialized = {
289
+ txid: row.txid,
290
+ vout: row.vout,
291
+ value: row.value,
292
+ tapTree: row.tap_tree,
293
+ forfeitTapLeafScript: {
294
+ cb: row.forfeit_cb,
295
+ s: row.forfeit_s,
296
+ },
297
+ intentTapLeafScript: {
298
+ cb: row.intent_cb,
299
+ s: row.intent_s,
300
+ },
301
+ status: JSON.parse(row.status_json),
302
+ extraWitness: row.extra_witness_json
303
+ ? JSON.parse(row.extra_witness_json)
304
+ : undefined,
305
+ };
306
+ return deserializeUtxo(serialized);
307
+ }
308
+ function txRowToDomain(row) {
309
+ const tx = {
310
+ key: {
311
+ boardingTxid: row.boarding_txid,
312
+ commitmentTxid: row.commitment_txid,
313
+ arkTxid: row.ark_txid,
314
+ },
315
+ type: row.type,
316
+ amount: row.amount,
317
+ settled: row.settled === 1,
318
+ createdAt: row.created_at,
319
+ };
320
+ if (row.assets_json) {
321
+ tx.assets = JSON.parse(row.assets_json);
322
+ }
323
+ return tx;
324
+ }
@@ -166,12 +166,20 @@ export class ServiceWorkerReadonlyWallet {
166
166
  // send a message and wait for a response
167
167
  async sendMessage(request) {
168
168
  return new Promise((resolve, reject) => {
169
+ const cleanup = () => {
170
+ clearTimeout(timeoutId);
171
+ navigator.serviceWorker.removeEventListener("message", messageHandler);
172
+ };
173
+ const timeoutId = setTimeout(() => {
174
+ cleanup();
175
+ reject(new Error(`Service worker message timed out (${request.type})`));
176
+ }, 30000);
169
177
  const messageHandler = (event) => {
170
178
  const response = event.data;
171
179
  if (request.id !== response.id) {
172
180
  return;
173
181
  }
174
- navigator.serviceWorker.removeEventListener("message", messageHandler);
182
+ cleanup();
175
183
  if (response.error) {
176
184
  reject(response.error);
177
185
  }
@@ -88,6 +88,13 @@ export function isVtxoExpiringSoon(vtxo, thresholdMs // in milliseconds
88
88
  const { batchExpiry } = vtxo.virtualStatus;
89
89
  if (!batchExpiry)
90
90
  return false; // it doesn't expire
91
+ // we use this as a workaround to avoid issue on regtest where expiry date is
92
+ // expressed in blockheight instead of timestamp. If expiry, as Date, is before 2025,
93
+ // then we admit it's too small to be a timestamp
94
+ // TODO: API should return the expiry unit
95
+ const expireAt = new Date(batchExpiry);
96
+ if (expireAt.getFullYear() < 2025)
97
+ return false;
91
98
  const now = Date.now();
92
99
  if (batchExpiry <= now)
93
100
  return false; // already expired
@@ -104,8 +104,19 @@ export class MessageBus {
104
104
  }
105
105
  }
106
106
  async waitForInit(config) {
107
- if (this.initialized)
108
- return;
107
+ if (this.initialized) {
108
+ // Stop existing handlers before re-initializing.
109
+ // This handles the case where CLEAR was called, which nullifies
110
+ // handler state (readonlyWallet, etc.) without resetting the
111
+ // initialized flag. Without this, handlers never get start()
112
+ // called again and all messages fail with "not initialized".
113
+ //
114
+ // Clear the flag first so onMessage() rejects incoming messages
115
+ // during the stop/start window instead of routing them to
116
+ // half-reset handlers. Restored to true after start() completes.
117
+ this.initialized = false;
118
+ await Promise.all(Array.from(this.handlers.values()).map((h) => h.stop().catch(() => { })));
119
+ }
109
120
  const services = await this.buildServicesFn(config);
110
121
  // Start all handlers
111
122
  for (const updater of this.handlers.values()) {
@@ -165,6 +176,15 @@ export class MessageBus {
165
176
  if (!this.initialized) {
166
177
  if (this.debug)
167
178
  console.warn("Event received before initialization, dropping", event.data);
179
+ // Send error response so the caller's promise rejects instead of
180
+ // hanging forever. This happens when the browser kills and restarts
181
+ // the service worker — the new instance has initialized=false and
182
+ // messages arrive before INITIALIZE_MESSAGE_BUS is re-sent.
183
+ event.source?.postMessage({
184
+ id,
185
+ tag: tag ?? "unknown",
186
+ error: new Error("MessageBus not initialized"),
187
+ });
168
188
  return;
169
189
  }
170
190
  if (!id || !tag) {
@@ -0,0 +1,55 @@
1
+ import { type BTC_NETWORK } from "@scure/btc-signer/utils.js";
2
+ import type { Identity } from "../identity";
3
+ /**
4
+ * BIP-322 simple message signing and verification.
5
+ *
6
+ * Supports P2TR (Taproot) signing and verification, P2WPKH verification,
7
+ * and legacy P2PKH verification (Bitcoin Core signmessage format).
8
+ *
9
+ * Reuses the same toSpend/toSign transaction construction as Intent proofs,
10
+ * but with the standard BIP-322 tagged hash ("BIP0322-signed-message")
11
+ * instead of the Ark-specific tag.
12
+ *
13
+ * @see https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * // Sign a message (P2TR)
18
+ * const signature = await BIP322.sign("Hello Bitcoin!", identity);
19
+ *
20
+ * // Verify a signature (P2TR or P2WPKH)
21
+ * const valid = BIP322.verify("Hello Bitcoin!", signature, "bc1p...");
22
+ * const valid2 = BIP322.verify("Hello Bitcoin!", signature, "bc1q...");
23
+ * ```
24
+ */
25
+ export declare namespace BIP322 {
26
+ /**
27
+ * Sign a message using the BIP-322 simple signature scheme.
28
+ *
29
+ * Constructs the standard BIP-322 toSpend and toSign transactions,
30
+ * signs via the Identity interface, and returns the base64-encoded
31
+ * witness stack.
32
+ *
33
+ * @param message - The message to sign
34
+ * @param identity - Identity instance (holds the private key internally)
35
+ * @param network - Optional Bitcoin network for P2TR address derivation
36
+ * @returns Base64-encoded BIP-322 simple signature (witness stack)
37
+ */
38
+ function sign(message: string, identity: Identity, network?: BTC_NETWORK): Promise<string>;
39
+ /**
40
+ * Verify a BIP-322 signature for a P2TR, P2WPKH, or legacy P2PKH address.
41
+ *
42
+ * For segwit addresses (P2TR, P2WPKH), reconstructs the BIP-322
43
+ * toSpend/toSign transactions and verifies the witness signature.
44
+ *
45
+ * For P2PKH addresses, verifies using the Bitcoin Core legacy
46
+ * `signmessage` format (compact recoverable ECDSA signature).
47
+ *
48
+ * @param message - The original message that was signed
49
+ * @param signature - Base64-encoded signature (BIP-322 witness or legacy compact)
50
+ * @param address - P2TR, P2WPKH, or P2PKH address of the signer
51
+ * @param network - Optional Bitcoin network for address decoding
52
+ * @returns true if the signature is valid
53
+ */
54
+ function verify(message: string, signature: string, address: string, network?: BTC_NETWORK): boolean;
55
+ }
@@ -26,6 +26,7 @@ import { CLTVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisig
26
26
  import { hasBoardingTxExpired, buildOffchainTx, verifyTapscriptSignatures, ArkTxInput, OffchainTx, combineTapscriptSigs, isValidArkAddress } from "./utils/arkTransaction";
27
27
  import { VtxoTaprootTree, ConditionWitness, getArkPsbtFields, setArkPsbtField, ArkPsbtFieldCoder, ArkPsbtFieldKey, ArkPsbtFieldKeyType, CosignerPublicKey, VtxoTreeExpiry } from "./utils/unknownFields";
28
28
  import { Intent } from "./intent";
29
+ import { BIP322 } from "./bip322";
29
30
  import { ArkNote } from "./arknote";
30
31
  import { networks, Network, NetworkName } from "./networks";
31
32
  import { RestIndexerProvider, IndexerProvider, IndexerTxType, ChainTxType, PageResponse, BatchInfo, ChainTx, CommitmentTx, TxHistoryRecord, VtxoChain, Tx, Vtxo, PaginationOptions, SubscriptionResponse, SubscriptionHeartbeat, SubscriptionEvent } from "./providers/indexer";
@@ -44,7 +45,7 @@ export * as asset from "./asset";
44
45
  import { ContractManager, ContractWatcher, contractHandlers, DefaultContractHandler, DelegateContractHandler, VHTLCContractHandler, encodeArkContract, decodeArkContract, contractFromArkContract, contractFromArkContractWithAddress, isArkContract } from "./contracts";
45
46
  import type { Contract, ContractVtxo, ContractState, ContractEvent, ContractEventCallback, ContractBalance, ContractWithVtxos, ContractHandler, PathSelection, PathContext, ContractManagerConfig, CreateContractParams, ContractWatcherConfig, ParsedArkContract, DefaultContractParams, DelegateContractParams, VHTLCContractParams } from "./contracts";
46
47
  import { IContractManager } from "./contracts/contractManager";
47
- import { closeDatabase, openDatabase } from "./db/manager";
48
+ import { closeDatabase, openDatabase } from "./repositories/indexedDB/manager";
48
49
  import { WalletMessageHandler } from "./wallet/serviceWorker/wallet-message-handler";
49
- export { Wallet, ReadonlyWallet, SingleKey, ReadonlySingleKey, SeedIdentity, MnemonicIdentity, ReadonlyDescriptorIdentity, OnchainWallet, Ramps, VtxoManager, DelegatorManagerImpl, RestDelegatorProvider, ESPLORA_URL, EsploraProvider, RestArkProvider, RestIndexerProvider, ArkAddress, DefaultVtxo, DelegateVtxo, VtxoScript, VHTLC, TxType, IndexerTxType, ChainTxType, SettlementEventType, setupServiceWorker, MessageBus, WalletMessageHandler, ServiceWorkerWallet, ServiceWorkerReadonlyWallet, decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CLTVMultisigTapscript, TapTreeCoder, ArkPsbtFieldKey, ArkPsbtFieldKeyType, setArkPsbtField, getArkPsbtFields, CosignerPublicKey, VtxoTreeExpiry, VtxoTaprootTree, ConditionWitness, buildOffchainTx, verifyTapscriptSignatures, waitForIncomingFunds, hasBoardingTxExpired, combineTapscriptSigs, isVtxoExpiringSoon, isValidArkAddress, ArkNote, networks, closeDatabase, openDatabase, IndexedDBWalletRepository, IndexedDBContractRepository, InMemoryWalletRepository, InMemoryContractRepository, MIGRATION_KEY, migrateWalletRepository, requiresMigration, getMigrationStatus, rollbackMigration, WalletRepositoryImpl, ContractRepositoryImpl, Intent, TxTree, P2A, Unroll, Transaction, ArkError, maybeArkError, Batch, validateVtxoTxGraph, validateConnectorsTxGraph, buildForfeitTx, isRecoverable, isSpendable, isSubdust, isExpired, getSequence, ContractManager, ContractWatcher, contractHandlers, DefaultContractHandler, DelegateContractHandler, VHTLCContractHandler, encodeArkContract, decodeArkContract, contractFromArkContract, contractFromArkContractWithAddress, isArkContract, };
50
+ export { Wallet, ReadonlyWallet, SingleKey, ReadonlySingleKey, SeedIdentity, MnemonicIdentity, ReadonlyDescriptorIdentity, OnchainWallet, Ramps, VtxoManager, DelegatorManagerImpl, RestDelegatorProvider, ESPLORA_URL, EsploraProvider, RestArkProvider, RestIndexerProvider, ArkAddress, DefaultVtxo, DelegateVtxo, VtxoScript, VHTLC, TxType, IndexerTxType, ChainTxType, SettlementEventType, setupServiceWorker, MessageBus, WalletMessageHandler, ServiceWorkerWallet, ServiceWorkerReadonlyWallet, decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CLTVMultisigTapscript, TapTreeCoder, ArkPsbtFieldKey, ArkPsbtFieldKeyType, setArkPsbtField, getArkPsbtFields, CosignerPublicKey, VtxoTreeExpiry, VtxoTaprootTree, ConditionWitness, buildOffchainTx, verifyTapscriptSignatures, waitForIncomingFunds, hasBoardingTxExpired, combineTapscriptSigs, isVtxoExpiringSoon, isValidArkAddress, ArkNote, networks, closeDatabase, openDatabase, IndexedDBWalletRepository, IndexedDBContractRepository, InMemoryWalletRepository, InMemoryContractRepository, MIGRATION_KEY, migrateWalletRepository, requiresMigration, getMigrationStatus, rollbackMigration, WalletRepositoryImpl, ContractRepositoryImpl, Intent, BIP322, TxTree, P2A, Unroll, Transaction, ArkError, maybeArkError, Batch, validateVtxoTxGraph, validateConnectorsTxGraph, buildForfeitTx, isRecoverable, isSpendable, isSubdust, isExpired, getSequence, ContractManager, ContractWatcher, contractHandlers, DefaultContractHandler, DelegateContractHandler, VHTLCContractHandler, encodeArkContract, decodeArkContract, contractFromArkContract, contractFromArkContractWithAddress, isArkContract, };
50
51
  export type { Identity, ReadonlyIdentity, IWallet, IReadonlyWallet, BaseWalletConfig, WalletConfig, ReadonlyWalletConfig, ProviderClass, ArkTransaction, Coin, ExtendedCoin, ExtendedVirtualCoin, WalletBalance, SendBitcoinParams, SettleParams, Status, VirtualStatus, Outpoint, VirtualCoin, TxKey, TapscriptType, ArkTxInput, OffchainTx, TapLeaves, IncomingFunds, SeedIdentityOptions, MnemonicOptions, NetworkOptions, DescriptorOptions, IndexerProvider, PageResponse, BatchInfo, ChainTx, CommitmentTx, TxHistoryRecord, Vtxo, VtxoChain, Tx, OnchainProvider, ArkProvider, SettlementEvent, FeeInfo, 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, Asset, Recipient, IssuanceParams, IssuanceResult, ReissuanceParams, BurnParams, AssetDetails, AssetMetadata, KnownMetadata, Nonces, PartialSig, ArkPsbtFieldCoder, TxTreeNode, AnchorBumper, StorageConfig, Contract, ContractVtxo, ContractState, ContractEvent, ContractEventCallback, ContractBalance, ContractWithVtxos, ContractHandler, IContractManager, PathSelection, PathContext, ContractManagerConfig, CreateContractParams, ContractWatcherConfig, ParsedArkContract, DefaultContractParams, DelegateContractParams, VHTLCContractParams, MessageHandler, RequestEnvelope, ResponseEnvelope, DelegatorManager, DelegatorProvider, DelegateInfo, DelegateOptions, WalletRepository, ContractRepository, MigrationStatus, };
@@ -58,3 +58,16 @@ export declare namespace Intent {
58
58
  type Message = RegisterMessage | DeleteMessage | GetPendingTxMessage;
59
59
  function encodeMessage(message: Message): string;
60
60
  }
61
+ export declare const OP_RETURN_EMPTY_PKSCRIPT: Uint8Array<ArrayBuffer>;
62
+ export declare const TAG_INTENT_PROOF = "ark-intent-proof-message";
63
+ /**
64
+ * Creates the "to_spend" transaction used by both intent proofs and BIP-322.
65
+ *
66
+ * The message is hashed with the given tagged-hash tag before being placed
67
+ * into the scriptSig as `OP_0 <hash>`.
68
+ *
69
+ * @param message - The message to embed
70
+ * @param pkScript - The scriptPubKey of the signer's address
71
+ * @param tag - Tagged-hash tag (defaults to the Ark intent proof tag)
72
+ */
73
+ export declare function craftToSpendTx(message: string, pkScript: Uint8Array, tag?: string): Transaction;
@@ -1,56 +1,4 @@
1
- import { TapLeafScript } from "../../script/base";
2
- import { ExtendedCoin, ExtendedVirtualCoin } from "../../wallet";
3
1
  import { DB_VERSION, STORE_CONTRACTS, LEGACY_STORE_CONTRACT_COLLECTIONS, STORE_TRANSACTIONS, STORE_UTXOS, STORE_VTXOS, STORE_WALLET_STATE } from "./schema";
4
2
  export { STORE_VTXOS, STORE_UTXOS, STORE_TRANSACTIONS, STORE_WALLET_STATE, STORE_CONTRACTS, LEGACY_STORE_CONTRACT_COLLECTIONS, DB_VERSION, };
5
- export type SerializedVtxo = ReturnType<typeof serializeVtxo>;
6
- export type SerializedUtxo = ReturnType<typeof serializeUtxo>;
7
- export declare const serializeTapLeaf: ([cb, s]: TapLeafScript) => {
8
- cb: string;
9
- s: string;
10
- };
11
- export declare const serializeVtxo: (v: ExtendedVirtualCoin) => {
12
- tapTree: string;
13
- forfeitTapLeafScript: {
14
- cb: string;
15
- s: string;
16
- };
17
- intentTapLeafScript: {
18
- cb: string;
19
- s: string;
20
- };
21
- extraWitness: string[] | undefined;
22
- virtualStatus: import("../../wallet").VirtualStatus;
23
- spentBy?: string;
24
- settledBy?: string;
25
- arkTxId?: string;
26
- createdAt: Date;
27
- isUnrolled: boolean;
28
- isSpent?: boolean;
29
- assets?: import("../../wallet").Asset[];
30
- value: number;
31
- status: import("../../wallet").Status;
32
- txid: string;
33
- vout: number;
34
- };
35
- export declare const serializeUtxo: (u: ExtendedCoin) => {
36
- tapTree: string;
37
- forfeitTapLeafScript: {
38
- cb: string;
39
- s: string;
40
- };
41
- intentTapLeafScript: {
42
- cb: string;
43
- s: string;
44
- };
45
- extraWitness: string[] | undefined;
46
- value: number;
47
- status: import("../../wallet").Status;
48
- txid: string;
49
- vout: number;
50
- };
51
- export declare const deserializeTapLeaf: (t: {
52
- cb: string;
53
- s: string;
54
- }) => TapLeafScript;
55
- export declare const deserializeVtxo: (o: SerializedVtxo) => ExtendedVirtualCoin;
56
- export declare const deserializeUtxo: (o: SerializedUtxo) => ExtendedCoin;
3
+ export { serializeTapLeaf, serializeVtxo, serializeUtxo, deserializeTapLeaf, deserializeVtxo, deserializeUtxo, } from "../serialization";
4
+ export type { SerializedTapLeaf, SerializedVtxo, SerializedUtxo, } from "../serialization";
@@ -0,0 +1,24 @@
1
+ import { Contract } from "../../contracts/types";
2
+ import { ContractFilter, ContractRepository } from "../contractRepository";
3
+ import { RealmLike } from "./types";
4
+ /**
5
+ * Realm-based implementation of ContractRepository.
6
+ *
7
+ * Consumers must open Realm with the schemas from `./schemas.ts` and pass
8
+ * the instance to the constructor.
9
+ *
10
+ * Realm handles schema creation on open, so `ensureInit()` is a no-op.
11
+ * The consumer owns the Realm lifecycle — `[Symbol.asyncDispose]` is a no-op.
12
+ */
13
+ export declare class RealmContractRepository implements ContractRepository {
14
+ private readonly realm;
15
+ readonly version: 1;
16
+ constructor(realm: RealmLike);
17
+ private ensureInit;
18
+ [Symbol.asyncDispose](): Promise<void>;
19
+ clear(): Promise<void>;
20
+ getContracts(filter?: ContractFilter): Promise<Contract[]>;
21
+ saveContract(contract: Contract): Promise<void>;
22
+ deleteContract(script: string): Promise<void>;
23
+ private addFilterCondition;
24
+ }
@@ -0,0 +1,4 @@
1
+ export { RealmWalletRepository } from "./walletRepository";
2
+ export { RealmContractRepository } from "./contractRepository";
3
+ export { ArkRealmSchemas } from "./schemas";
4
+ export type { RealmLike, RealmResults } from "./types";