@arkade-os/sdk 0.5.0-rc.8 → 0.5.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.
- package/README.md +231 -50
- package/dist/adapters/expo.cjs +5 -5
- package/dist/adapters/expo.d.cts +5 -5
- package/dist/adapters/expo.d.ts +5 -5
- package/dist/adapters/expo.js +3 -3
- package/dist/{asyncStorageTaskQueue-CeYTmuGU.d.ts → asyncStorageTaskQueue-BFv2O1y1.d.ts} +1 -1
- package/dist/{asyncStorageTaskQueue-Bn3FoNkc.d.cts → asyncStorageTaskQueue-qRRQUOE8.d.cts} +1 -1
- package/dist/{chunk-ZTCDT7KB.cjs → chunk-AU77RYOC.cjs} +6304 -4613
- package/dist/chunk-AU77RYOC.cjs.map +1 -0
- package/dist/{chunk-K5RED5VE.cjs → chunk-GN2K4WBP.cjs} +494 -1122
- package/dist/chunk-GN2K4WBP.cjs.map +1 -0
- package/dist/{chunk-BMPGUGYZ.cjs → chunk-GOM26K6H.cjs} +12 -10
- package/dist/chunk-GOM26K6H.cjs.map +1 -0
- package/dist/{chunk-LN4ZJZK4.js → chunk-GWJWXVUB.js} +5906 -4235
- package/dist/chunk-GWJWXVUB.js.map +1 -0
- package/dist/{chunk-G6S4XSFI.js → chunk-HABGVRLF.js} +112 -243
- package/dist/chunk-HABGVRLF.js.map +1 -0
- package/dist/{chunk-2AWDPVZI.cjs → chunk-IQWLFYT2.cjs} +124 -254
- package/dist/chunk-IQWLFYT2.cjs.map +1 -0
- package/dist/{chunk-56RCBKNF.cjs → chunk-JT5YVMCJ.cjs} +39 -46
- package/dist/chunk-JT5YVMCJ.cjs.map +1 -0
- package/dist/{chunk-INTEA7D3.js → chunk-K2E6X6JT.js} +32 -39
- package/dist/chunk-K2E6X6JT.js.map +1 -0
- package/dist/{chunk-XCNAGBFR.js → chunk-K352GMVL.js} +427 -1078
- package/dist/chunk-K352GMVL.js.map +1 -0
- package/dist/{chunk-BIRUBLP3.js → chunk-TZSKLERU.js} +7 -5
- package/dist/chunk-TZSKLERU.js.map +1 -0
- package/dist/{chunk-NY5FN4VV.cjs → chunk-UUSKJOSX.cjs} +548 -399
- package/dist/chunk-UUSKJOSX.cjs.map +1 -0
- package/dist/{chunk-MTYQXZ2T.js → chunk-ZOEB53NQ.js} +541 -400
- package/dist/chunk-ZOEB53NQ.js.map +1 -0
- package/dist/contracts/handlers/index.cjs +9 -9
- package/dist/contracts/handlers/index.d.cts +5 -5
- package/dist/contracts/handlers/index.d.ts +5 -5
- package/dist/contracts/handlers/index.js +2 -2
- package/dist/{delegate-Cyv2suFd.d.cts → delegate-B5BmYkEY.d.cts} +1 -1
- package/dist/{delegate-CyBnNY9v.d.ts → delegate-DNbiUgy8.d.ts} +1 -1
- package/dist/index-B4QuO9sH.d.cts +645 -0
- package/dist/index-CYeyT3kn.d.ts +645 -0
- package/dist/{index-BIL9boGL.d.cts → index-CZw-MMfY.d.cts} +1644 -2291
- package/dist/{index-BIL9boGL.d.ts → index-CZw-MMfY.d.ts} +1644 -2291
- package/dist/index.cjs +399 -311
- package/dist/index.d.cts +953 -1124
- package/dist/index.d.ts +953 -1124
- package/dist/index.js +4 -4
- package/dist/repositories/realm/index.cjs +165 -103
- package/dist/repositories/realm/index.cjs.map +1 -1
- package/dist/repositories/realm/index.d.cts +18 -14
- package/dist/repositories/realm/index.d.ts +18 -14
- package/dist/repositories/realm/index.js +153 -91
- package/dist/repositories/realm/index.js.map +1 -1
- package/dist/repositories/sqlite/index.cjs +226 -184
- package/dist/repositories/sqlite/index.cjs.map +1 -1
- package/dist/repositories/sqlite/index.d.cts +22 -21
- package/dist/repositories/sqlite/index.d.ts +22 -21
- package/dist/repositories/sqlite/index.js +218 -176
- package/dist/repositories/sqlite/index.js.map +1 -1
- package/dist/{taskRunner-z_BGID85.d.cts → taskRunner-B3SLvUfL.d.cts} +1 -1
- package/dist/{taskRunner-CRbuzUXR.d.ts → taskRunner-wm1bhmtP.d.ts} +1 -1
- package/dist/wallet/expo/background.cjs +14 -14
- package/dist/wallet/expo/background.cjs.map +1 -1
- package/dist/wallet/expo/background.d.cts +10 -16
- package/dist/wallet/expo/background.d.ts +10 -16
- package/dist/wallet/expo/background.js +6 -6
- package/dist/wallet/expo/background.js.map +1 -1
- package/dist/wallet/expo/index.cjs +19 -14
- package/dist/wallet/expo/index.cjs.map +1 -1
- package/dist/wallet/expo/index.d.cts +8 -6
- package/dist/wallet/expo/index.d.ts +8 -6
- package/dist/wallet/expo/index.js +10 -5
- package/dist/wallet/expo/index.js.map +1 -1
- package/dist/wallet-DCZ2-Bea.d.cts +1874 -0
- package/dist/wallet-DGLXuwXT.d.ts +1874 -0
- package/dist/worker/expo/index.cjs +9 -9
- package/dist/worker/expo/index.d.cts +6 -6
- package/dist/worker/expo/index.d.ts +6 -6
- package/dist/worker/expo/index.js +5 -5
- package/package.json +10 -11
- package/dist/chunk-2AWDPVZI.cjs.map +0 -1
- package/dist/chunk-56RCBKNF.cjs.map +0 -1
- package/dist/chunk-BIRUBLP3.js.map +0 -1
- package/dist/chunk-BMPGUGYZ.cjs.map +0 -1
- package/dist/chunk-G6S4XSFI.js.map +0 -1
- package/dist/chunk-INTEA7D3.js.map +0 -1
- package/dist/chunk-K5RED5VE.cjs.map +0 -1
- package/dist/chunk-LN4ZJZK4.js.map +0 -1
- package/dist/chunk-MTYQXZ2T.js.map +0 -1
- package/dist/chunk-NY5FN4VV.cjs.map +0 -1
- package/dist/chunk-XCNAGBFR.js.map +0 -1
- package/dist/chunk-ZTCDT7KB.cjs.map +0 -1
- package/dist/hdWalletCapable-D1ImQqlu.d.cts +0 -2214
- package/dist/hdWalletCapable-DvumVrKW.d.ts +0 -2214
- package/dist/index-DleNQcz5.d.cts +0 -850
- package/dist/index-QVhQgYvU.d.ts +0 -850
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { scriptFromArkAddress, legacyVtxoFacts, serializeVtxo,
|
|
2
|
-
export { ChainedTxType } from '../../chunk-
|
|
3
|
-
import '../../chunk-
|
|
4
|
-
import '../../chunk-
|
|
5
|
-
import '../../chunk-
|
|
1
|
+
import { scriptFromArkAddress, legacyVtxoFacts, serializeVtxo, createdAtToIso, assertPageRequest, pageResult, checkSaveVtxosForScript, utxoEntries, serializeUtxo, assertHistoryPageFilter, serializeAssets, deserializeVtxo, deserializeUtxo, deserializeAssets, contractFilterMatchesNothing, intentMatchesFilter, isTerminalIntentState, mergeChainedTxType } from '../../chunk-GWJWXVUB.js';
|
|
2
|
+
export { ChainedTxType } from '../../chunk-GWJWXVUB.js';
|
|
3
|
+
import '../../chunk-HABGVRLF.js';
|
|
4
|
+
import '../../chunk-K352GMVL.js';
|
|
5
|
+
import { isVtxoSpent } from '../../chunk-ZOEB53NQ.js';
|
|
6
6
|
|
|
7
7
|
// src/repositories/sqlite/transaction.ts
|
|
8
8
|
var writeChains = /* @__PURE__ */ new WeakMap();
|
|
@@ -29,11 +29,19 @@ function runInTransaction(db, fn) {
|
|
|
29
29
|
return run;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
// src/repositories/sqlite/prefix.ts
|
|
33
|
+
var SAFE_PREFIX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
34
|
+
function sanitizeTablePrefix(p) {
|
|
35
|
+
if (!SAFE_PREFIX.test(p)) throw new Error(`Invalid table prefix "${p}"`);
|
|
36
|
+
return p;
|
|
37
|
+
}
|
|
38
|
+
|
|
32
39
|
// src/repositories/sqlite/walletRepository.ts
|
|
40
|
+
var MAX_BOUND_PARAMETERS = 999;
|
|
33
41
|
var SQLiteWalletRepository = class {
|
|
34
42
|
constructor(db, options) {
|
|
35
43
|
this.db = db;
|
|
36
|
-
this.prefix =
|
|
44
|
+
this.prefix = sanitizeTablePrefix(options?.prefix ?? "ark_");
|
|
37
45
|
this.tables = {
|
|
38
46
|
vtxos: `${this.prefix}vtxos`,
|
|
39
47
|
utxos: `${this.prefix}utxos`,
|
|
@@ -42,7 +50,7 @@ var SQLiteWalletRepository = class {
|
|
|
42
50
|
};
|
|
43
51
|
}
|
|
44
52
|
db;
|
|
45
|
-
version =
|
|
53
|
+
version = 2;
|
|
46
54
|
initPromise = null;
|
|
47
55
|
prefix;
|
|
48
56
|
tables;
|
|
@@ -98,23 +106,42 @@ var SQLiteWalletRepository = class {
|
|
|
98
106
|
await this.db.run(
|
|
99
107
|
`CREATE INDEX IF NOT EXISTS idx_${this.prefix}vtxos_script ON ${this.tables.vtxos} (script)`
|
|
100
108
|
);
|
|
109
|
+
await this.db.run(
|
|
110
|
+
`CREATE INDEX IF NOT EXISTS idx_${this.prefix}vtxos_script_page ON ${this.tables.vtxos} (script, address, txid, vout)`
|
|
111
|
+
);
|
|
112
|
+
await this.db.run(
|
|
113
|
+
`CREATE INDEX IF NOT EXISTS idx_${this.prefix}vtxos_address_page ON ${this.tables.vtxos} (address, txid, vout)`
|
|
114
|
+
);
|
|
115
|
+
await this.db.run(`DROP INDEX IF EXISTS idx_${this.prefix}vtxos_live_script`);
|
|
116
|
+
await this.db.run(
|
|
117
|
+
`CREATE INDEX IF NOT EXISTS idx_${this.prefix}vtxos_live_script_page ON ${this.tables.vtxos} (script, address, txid, vout)
|
|
118
|
+
WHERE ${UNSPENT_VTXO_PREDICATE}`
|
|
119
|
+
);
|
|
101
120
|
await this.db.run(
|
|
102
121
|
`CREATE INDEX IF NOT EXISTS idx_${this.prefix}utxos_address ON ${this.tables.utxos} (address)`
|
|
103
122
|
);
|
|
123
|
+
await this.db.run(
|
|
124
|
+
`CREATE INDEX IF NOT EXISTS idx_${this.prefix}utxos_address_page ON ${this.tables.utxos} (address, txid, vout)`
|
|
125
|
+
);
|
|
104
126
|
await this.db.run(
|
|
105
127
|
`CREATE INDEX IF NOT EXISTS idx_${this.prefix}transactions_address ON ${this.tables.transactions} (address)`
|
|
106
128
|
);
|
|
129
|
+
await this.db.run(
|
|
130
|
+
`CREATE INDEX IF NOT EXISTS idx_${this.prefix}transactions_history ON ${this.tables.transactions} (address, created_at, boarding_txid, commitment_txid, ark_txid)`
|
|
131
|
+
);
|
|
107
132
|
}
|
|
108
133
|
/**
|
|
109
134
|
* Bring the `vtxos` table to the current schema (v1 = `script` NOT NULL).
|
|
110
135
|
*
|
|
111
|
-
*
|
|
136
|
+
* Four cases:
|
|
112
137
|
* - Fresh install: create the v1 schema directly.
|
|
113
138
|
* - Legacy install without a `script` column: add it, backfill from
|
|
114
139
|
* `address`, then rebuild the table with NOT NULL (SQLite cannot add
|
|
115
140
|
* the NOT NULL constraint in place).
|
|
116
141
|
* - Legacy install with a nullable `script` column: backfill the NULLs
|
|
117
142
|
* and rebuild.
|
|
143
|
+
* - 0.4.x install: `script` is already NOT NULL but `virtual_status_json
|
|
144
|
+
* NOT NULL` is still there, and `saveVtxos` no longer writes it.
|
|
118
145
|
*
|
|
119
146
|
* The backfill derives `script` from the Ark address, matching what the
|
|
120
147
|
* indexer would have returned — new rows from the indexer always carry a
|
|
@@ -158,10 +185,11 @@ var SQLiteWalletRepository = class {
|
|
|
158
185
|
addedCanonicalColumns = true;
|
|
159
186
|
}
|
|
160
187
|
}
|
|
161
|
-
|
|
188
|
+
const hasLegacyBlob = cols.some((c) => c.name === "virtual_status_json");
|
|
189
|
+
if (addedCanonicalColumns && hasLegacyBlob) {
|
|
162
190
|
await this.backfillCanonicalVtxoColumns();
|
|
163
191
|
}
|
|
164
|
-
if (scriptCol && scriptCol.notnull === 1) {
|
|
192
|
+
if (scriptCol && scriptCol.notnull === 1 && !hasLegacyBlob) {
|
|
165
193
|
return;
|
|
166
194
|
}
|
|
167
195
|
if (!scriptCol) {
|
|
@@ -262,6 +290,23 @@ var SQLiteWalletRepository = class {
|
|
|
262
290
|
PRIMARY KEY (txid, vout)
|
|
263
291
|
)`;
|
|
264
292
|
}
|
|
293
|
+
/**
|
|
294
|
+
* `INSERT OR REPLACE` rows, as many to a statement as the parameter budget allows.
|
|
295
|
+
* No enclosing transaction: on the shared connection it would take in other repos' writes.
|
|
296
|
+
*/
|
|
297
|
+
async insertOrReplace(table, columns, rows) {
|
|
298
|
+
const width = columns.split(",").length;
|
|
299
|
+
const tuple = `(${Array(width).fill("?").join(", ")})`;
|
|
300
|
+
const rowsPerStatement = Math.floor(MAX_BOUND_PARAMETERS / width);
|
|
301
|
+
for (let i = 0; i < rows.length; i += rowsPerStatement) {
|
|
302
|
+
const chunk = rows.slice(i, i + rowsPerStatement);
|
|
303
|
+
await this.db.run(
|
|
304
|
+
`INSERT OR REPLACE INTO ${table} (${columns})
|
|
305
|
+
VALUES ${chunk.map(() => tuple).join(", ")}`,
|
|
306
|
+
chunk.flat()
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
265
310
|
async [Symbol.asyncDispose]() {
|
|
266
311
|
}
|
|
267
312
|
// ── Clear ──────────────────────────────────────────────────────────
|
|
@@ -273,33 +318,22 @@ var SQLiteWalletRepository = class {
|
|
|
273
318
|
await this.db.run(`DELETE FROM ${this.tables.walletState}`);
|
|
274
319
|
}
|
|
275
320
|
// ── VTXO management ────────────────────────────────────────────────
|
|
276
|
-
async
|
|
277
|
-
|
|
278
|
-
const rows = await this.db.all(
|
|
279
|
-
`SELECT * FROM ${this.tables.vtxos} WHERE address = ?`,
|
|
280
|
-
[address]
|
|
281
|
-
);
|
|
282
|
-
return rows.map(vtxoRowToDomain);
|
|
321
|
+
async getVtxosPage(address, page) {
|
|
322
|
+
return this.pageByAddress(this.tables.vtxos, address, page, vtxoRowToDomain);
|
|
283
323
|
}
|
|
284
324
|
async saveVtxos(address, vtxos) {
|
|
285
325
|
await this.ensureInit();
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
?, ?, ?, ?, ?,
|
|
298
|
-
?, ?, ?, ?,
|
|
299
|
-
?, ?, ?,
|
|
300
|
-
?, ?,
|
|
301
|
-
?, ?, ?, ?, ?, ?)`,
|
|
302
|
-
[
|
|
326
|
+
await this.insertOrReplace(
|
|
327
|
+
this.tables.vtxos,
|
|
328
|
+
`txid, vout, value, address,
|
|
329
|
+
tap_tree, forfeit_cb, forfeit_s, intent_cb, intent_s,
|
|
330
|
+
status_json, created_at, is_unrolled, is_spent,
|
|
331
|
+
is_swept, is_preconfirmed, commitment_txids_json,
|
|
332
|
+
expires_at, expires_at_height,
|
|
333
|
+
spent_by, settled_by, ark_tx_id, extra_witness_json, assets_json, script`,
|
|
334
|
+
vtxos.map((vtxo) => {
|
|
335
|
+
const s = serializeVtxo(vtxo);
|
|
336
|
+
return [
|
|
303
337
|
s.txid,
|
|
304
338
|
s.vout,
|
|
305
339
|
s.value,
|
|
@@ -310,13 +344,13 @@ var SQLiteWalletRepository = class {
|
|
|
310
344
|
s.intentTapLeafScript.cb,
|
|
311
345
|
s.intentTapLeafScript.s,
|
|
312
346
|
JSON.stringify(s.status),
|
|
313
|
-
|
|
347
|
+
createdAtToIso(s.createdAt),
|
|
314
348
|
s.isUnrolled ? 1 : 0,
|
|
315
349
|
s.isSpent === void 0 ? null : s.isSpent ? 1 : 0,
|
|
316
350
|
s.isSwept === void 0 ? null : s.isSwept ? 1 : 0,
|
|
317
351
|
s.isPreconfirmed === void 0 ? null : s.isPreconfirmed ? 1 : 0,
|
|
318
352
|
s.commitmentTxIds ? JSON.stringify(s.commitmentTxIds) : null,
|
|
319
|
-
s.expiresAt === void 0 ? null :
|
|
353
|
+
s.expiresAt === void 0 ? null : new Date(s.expiresAt).toISOString(),
|
|
320
354
|
s.expiresAtHeight ?? null,
|
|
321
355
|
s.spentBy ?? null,
|
|
322
356
|
s.settledBy ?? null,
|
|
@@ -324,61 +358,68 @@ var SQLiteWalletRepository = class {
|
|
|
324
358
|
s.extraWitness ? JSON.stringify(s.extraWitness) : null,
|
|
325
359
|
s.assets ? JSON.stringify(s.assets) : null,
|
|
326
360
|
s.script ?? null
|
|
327
|
-
]
|
|
328
|
-
)
|
|
329
|
-
|
|
361
|
+
];
|
|
362
|
+
})
|
|
363
|
+
);
|
|
330
364
|
}
|
|
331
365
|
async deleteVtxos(address) {
|
|
332
366
|
await this.ensureInit();
|
|
333
367
|
await this.db.run(`DELETE FROM ${this.tables.vtxos} WHERE address = ?`, [address]);
|
|
334
368
|
}
|
|
335
|
-
async
|
|
369
|
+
async getVtxosForScriptPage(script, page, options) {
|
|
370
|
+
assertPageRequest(page);
|
|
336
371
|
await this.ensureInit();
|
|
372
|
+
const after = page.after;
|
|
337
373
|
const rows = await this.db.all(
|
|
338
|
-
`SELECT * FROM ${this.tables.vtxos} WHERE script =
|
|
339
|
-
|
|
374
|
+
`SELECT * FROM ${this.tables.vtxos} WHERE script = ?
|
|
375
|
+
${options?.unspentOnly ? `AND ${UNSPENT_VTXO_PREDICATE}` : ""}
|
|
376
|
+
${after ? "AND (address > ? OR (address = ? AND (txid > ? OR (txid = ? AND vout > ?))))" : ""}
|
|
377
|
+
ORDER BY address, txid, vout LIMIT ?`,
|
|
378
|
+
after ? [
|
|
379
|
+
script,
|
|
380
|
+
after.address,
|
|
381
|
+
after.address,
|
|
382
|
+
after.txid,
|
|
383
|
+
after.txid,
|
|
384
|
+
after.vout,
|
|
385
|
+
page.limit + 1
|
|
386
|
+
] : [script, page.limit + 1]
|
|
387
|
+
);
|
|
388
|
+
const result = pageResult(
|
|
389
|
+
rows.map((row) => ({ address: row.address, vtxo: vtxoRowToDomain(row) })),
|
|
390
|
+
page.limit,
|
|
391
|
+
(row) => ({ address: row.address, txid: row.vtxo.txid, vout: row.vtxo.vout })
|
|
340
392
|
);
|
|
341
|
-
return
|
|
393
|
+
return options?.unspentOnly ? { ...result, items: result.items.filter((row) => !isVtxoSpent(row.vtxo)) } : result;
|
|
342
394
|
}
|
|
343
395
|
async saveVtxosForScript(key, vtxos) {
|
|
344
|
-
|
|
345
|
-
throw new Error("SQLiteWalletRepository requires an address");
|
|
346
|
-
}
|
|
347
|
-
for (const vtxo of vtxos) {
|
|
348
|
-
if (!isVtxoForScript(vtxo, key.script)) {
|
|
349
|
-
throw new Error(
|
|
350
|
-
`VTXO ${vtxo.txid}:${vtxo.vout} script mismatch: expected ${key.script}, got ${vtxo.script}`
|
|
351
|
-
);
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
return this.saveVtxos(key.address, vtxos);
|
|
396
|
+
return this.saveVtxos(checkSaveVtxosForScript("SQLiteWalletRepository", key, vtxos), vtxos);
|
|
355
397
|
}
|
|
356
398
|
async deleteVtxosForScript(script) {
|
|
357
399
|
await this.ensureInit();
|
|
358
400
|
await this.db.run(`DELETE FROM ${this.tables.vtxos} WHERE script = ?`, [script]);
|
|
359
401
|
}
|
|
360
402
|
// ── UTXO management ────────────────────────────────────────────────
|
|
361
|
-
async
|
|
403
|
+
async getUtxosPage(address, page) {
|
|
404
|
+
return this.pageByAddress(this.tables.utxos, address, page, utxoRowToDomain);
|
|
405
|
+
}
|
|
406
|
+
async pageByAddress(table, address, page, deserialize) {
|
|
407
|
+
assertPageRequest(page);
|
|
362
408
|
await this.ensureInit();
|
|
363
409
|
const rows = await this.db.all(
|
|
364
|
-
`SELECT * FROM ${
|
|
365
|
-
|
|
410
|
+
`SELECT * FROM ${table} WHERE address = ? AND (txid, vout) > (?, ?)
|
|
411
|
+
ORDER BY txid, vout LIMIT ?`,
|
|
412
|
+
[address, page.after?.txid ?? "", page.after?.vout ?? -1, page.limit + 1]
|
|
366
413
|
);
|
|
367
|
-
|
|
414
|
+
const result = pageResult(rows, page.limit, ({ txid, vout }) => ({ txid, vout }));
|
|
415
|
+
return { items: result.items.map(deserialize), nextCursor: result.nextCursor };
|
|
368
416
|
}
|
|
369
|
-
async saveUtxos(
|
|
417
|
+
async saveUtxos(addressOrBatch, utxos) {
|
|
370
418
|
await this.ensureInit();
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
(txid, vout, value, address,
|
|
376
|
-
tap_tree, forfeit_cb, forfeit_s, intent_cb, intent_s,
|
|
377
|
-
status_json, extra_witness_json)
|
|
378
|
-
VALUES (?, ?, ?, ?,
|
|
379
|
-
?, ?, ?, ?, ?,
|
|
380
|
-
?, ?)`,
|
|
381
|
-
[
|
|
419
|
+
const rows = utxoEntries(addressOrBatch, utxos).flatMap(
|
|
420
|
+
([address, list]) => list.map((utxo) => {
|
|
421
|
+
const s = serializeUtxo(utxo);
|
|
422
|
+
return [
|
|
382
423
|
s.txid,
|
|
383
424
|
s.vout,
|
|
384
425
|
s.value,
|
|
@@ -390,45 +431,71 @@ var SQLiteWalletRepository = class {
|
|
|
390
431
|
s.intentTapLeafScript.s,
|
|
391
432
|
JSON.stringify(s.status),
|
|
392
433
|
s.extraWitness ? JSON.stringify(s.extraWitness) : null
|
|
393
|
-
]
|
|
394
|
-
)
|
|
395
|
-
|
|
434
|
+
];
|
|
435
|
+
})
|
|
436
|
+
);
|
|
437
|
+
await this.insertOrReplace(
|
|
438
|
+
this.tables.utxos,
|
|
439
|
+
`txid, vout, value, address,
|
|
440
|
+
tap_tree, forfeit_cb, forfeit_s, intent_cb, intent_s,
|
|
441
|
+
status_json, extra_witness_json`,
|
|
442
|
+
rows
|
|
443
|
+
);
|
|
396
444
|
}
|
|
397
445
|
async deleteUtxos(address) {
|
|
398
446
|
await this.ensureInit();
|
|
399
447
|
await this.db.run(`DELETE FROM ${this.tables.utxos} WHERE address = ?`, [address]);
|
|
400
448
|
}
|
|
401
449
|
// ── Transaction history ────────────────────────────────────────────
|
|
402
|
-
async
|
|
450
|
+
async getTransactionHistoryPage(filter, page) {
|
|
451
|
+
assertPageRequest(page);
|
|
452
|
+
assertHistoryPageFilter(filter);
|
|
403
453
|
await this.ensureInit();
|
|
454
|
+
const conditions = ["address = ?"];
|
|
455
|
+
const params = [filter.address];
|
|
456
|
+
if (filter.since !== void 0) {
|
|
457
|
+
conditions.push("created_at >= ?");
|
|
458
|
+
params.push(filter.since);
|
|
459
|
+
}
|
|
460
|
+
if (page.after !== void 0) {
|
|
461
|
+
conditions.push(
|
|
462
|
+
"(created_at, boarding_txid, commitment_txid, ark_txid) > (?, ?, ?, ?)"
|
|
463
|
+
);
|
|
464
|
+
params.push(
|
|
465
|
+
page.after.createdAt,
|
|
466
|
+
page.after.key.boardingTxid,
|
|
467
|
+
page.after.key.commitmentTxid,
|
|
468
|
+
page.after.key.arkTxid
|
|
469
|
+
);
|
|
470
|
+
}
|
|
404
471
|
const rows = await this.db.all(
|
|
405
|
-
`SELECT * FROM ${this.tables.transactions} WHERE
|
|
406
|
-
|
|
472
|
+
`SELECT * FROM ${this.tables.transactions} WHERE ${conditions.join(" AND ")}
|
|
473
|
+
ORDER BY created_at, boarding_txid, commitment_txid, ark_txid LIMIT ?`,
|
|
474
|
+
[...params, page.limit + 1]
|
|
407
475
|
);
|
|
408
|
-
return rows.map(txRowToDomain)
|
|
476
|
+
return pageResult(rows.map(txRowToDomain), page.limit, (tx) => ({
|
|
477
|
+
createdAt: tx.createdAt,
|
|
478
|
+
key: tx.key
|
|
479
|
+
}));
|
|
409
480
|
}
|
|
410
481
|
async saveTransactions(address, txs) {
|
|
411
482
|
await this.ensureInit();
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
tx.assets ? JSON.stringify(serializeAssets(tx.assets)) : null
|
|
429
|
-
]
|
|
430
|
-
);
|
|
431
|
-
}
|
|
483
|
+
await this.insertOrReplace(
|
|
484
|
+
this.tables.transactions,
|
|
485
|
+
`address, boarding_txid, commitment_txid, ark_txid,
|
|
486
|
+
type, amount, settled, created_at, assets_json`,
|
|
487
|
+
txs.map((tx) => [
|
|
488
|
+
address,
|
|
489
|
+
tx.key.boardingTxid,
|
|
490
|
+
tx.key.commitmentTxid,
|
|
491
|
+
tx.key.arkTxid,
|
|
492
|
+
tx.type,
|
|
493
|
+
tx.amount,
|
|
494
|
+
tx.settled ? 1 : 0,
|
|
495
|
+
tx.createdAt,
|
|
496
|
+
tx.assets ? JSON.stringify(serializeAssets(tx.assets)) : null
|
|
497
|
+
])
|
|
498
|
+
);
|
|
432
499
|
}
|
|
433
500
|
async deleteTransactions(address) {
|
|
434
501
|
await this.ensureInit();
|
|
@@ -463,15 +530,7 @@ var SQLiteWalletRepository = class {
|
|
|
463
530
|
);
|
|
464
531
|
}
|
|
465
532
|
};
|
|
466
|
-
var
|
|
467
|
-
function sanitizePrefix(prefix) {
|
|
468
|
-
if (!SAFE_PREFIX.test(prefix)) {
|
|
469
|
-
throw new Error(
|
|
470
|
-
`Invalid table prefix "${prefix}": only letters, digits, and underscores are allowed`
|
|
471
|
-
);
|
|
472
|
-
}
|
|
473
|
-
return prefix;
|
|
474
|
-
}
|
|
533
|
+
var UNSPENT_VTXO_PREDICATE = "(is_spent IS NULL OR is_spent = 0) AND (spent_by IS NULL OR spent_by = '') AND (settled_by IS NULL OR settled_by = '')";
|
|
475
534
|
function vtxoRowToDomain(row) {
|
|
476
535
|
const serialized = {
|
|
477
536
|
txid: row.txid,
|
|
@@ -545,14 +604,15 @@ function txRowToDomain(row) {
|
|
|
545
604
|
}
|
|
546
605
|
|
|
547
606
|
// src/repositories/sqlite/contractRepository.ts
|
|
607
|
+
var WATCH_STATE = "IFNULL(watch, 'watched')";
|
|
548
608
|
var SQLiteContractRepository = class {
|
|
549
609
|
constructor(db, options) {
|
|
550
610
|
this.db = db;
|
|
551
|
-
this.prefix =
|
|
611
|
+
this.prefix = sanitizeTablePrefix(options?.prefix ?? "ark_");
|
|
552
612
|
this.table = `${this.prefix}contracts`;
|
|
553
613
|
}
|
|
554
614
|
db;
|
|
555
|
-
version =
|
|
615
|
+
version = 3;
|
|
556
616
|
initPromise = null;
|
|
557
617
|
prefix;
|
|
558
618
|
table;
|
|
@@ -585,6 +645,12 @@ var SQLiteContractRepository = class {
|
|
|
585
645
|
await this.db.run(
|
|
586
646
|
`CREATE INDEX IF NOT EXISTS idx_${this.prefix}contracts_state ON ${this.table} (state)`
|
|
587
647
|
);
|
|
648
|
+
await this.db.run(
|
|
649
|
+
`CREATE INDEX IF NOT EXISTS idx_${this.prefix}contracts_watch_state ON ${this.table} (${WATCH_STATE}, script)`
|
|
650
|
+
);
|
|
651
|
+
await this.db.run(
|
|
652
|
+
`CREATE INDEX IF NOT EXISTS idx_${this.prefix}contracts_type_watch_state ON ${this.table} (type, ${WATCH_STATE}, script)`
|
|
653
|
+
);
|
|
588
654
|
}
|
|
589
655
|
async addColumnIfMissing(column, type) {
|
|
590
656
|
const columns = await this.db.all(`PRAGMA table_info(${this.table})`);
|
|
@@ -599,22 +665,28 @@ var SQLiteContractRepository = class {
|
|
|
599
665
|
await this.db.run(`DELETE FROM ${this.table}`);
|
|
600
666
|
}
|
|
601
667
|
// ── Contract management ────────────────────────────────────────────
|
|
602
|
-
async
|
|
668
|
+
async getContractsPage(filter, page) {
|
|
669
|
+
assertPageRequest(page);
|
|
603
670
|
await this.ensureInit();
|
|
671
|
+
if (contractFilterMatchesNothing(filter)) return { items: [] };
|
|
604
672
|
const conditions = [];
|
|
605
673
|
const params = [];
|
|
606
674
|
if (filter) {
|
|
607
675
|
this.addFilterCondition(conditions, params, "script", filter.script);
|
|
608
676
|
this.addFilterCondition(conditions, params, "state", filter.state);
|
|
609
677
|
this.addFilterCondition(conditions, params, "type", filter.type);
|
|
610
|
-
this.
|
|
678
|
+
this.addFilterCondition(conditions, params, WATCH_STATE, filter.watch);
|
|
611
679
|
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
680
|
+
if (page.after !== void 0) {
|
|
681
|
+
conditions.push("script > ?");
|
|
682
|
+
params.push(page.after);
|
|
615
683
|
}
|
|
616
|
-
const
|
|
617
|
-
|
|
684
|
+
const where = conditions.length ? ` WHERE ${conditions.join(" AND ")}` : "";
|
|
685
|
+
const rows = await this.db.all(
|
|
686
|
+
`SELECT * FROM ${this.table}${where} ORDER BY script LIMIT ?`,
|
|
687
|
+
[...params, page.limit + 1]
|
|
688
|
+
);
|
|
689
|
+
return pageResult(rows.map(contractRowToDomain), page.limit, (contract) => contract.script);
|
|
618
690
|
}
|
|
619
691
|
async saveContract(contract) {
|
|
620
692
|
await this.ensureInit();
|
|
@@ -654,29 +726,7 @@ var SQLiteContractRepository = class {
|
|
|
654
726
|
params.push(value);
|
|
655
727
|
}
|
|
656
728
|
}
|
|
657
|
-
/**
|
|
658
|
-
* Same as {@link addFilterCondition}, except a row predating the
|
|
659
|
-
* column stores NULL and must match `"watched"`.
|
|
660
|
-
*/
|
|
661
|
-
addWatchCondition(conditions, params, value) {
|
|
662
|
-
if (value === void 0) return;
|
|
663
|
-
const wanted = Array.isArray(value) ? value : [value];
|
|
664
|
-
if (wanted.length === 0) return;
|
|
665
|
-
const placeholders = wanted.map(() => "?").join(", ");
|
|
666
|
-
const clause = `watch IN (${placeholders})`;
|
|
667
|
-
conditions.push(wanted.includes("watched") ? `(${clause} OR watch IS NULL)` : clause);
|
|
668
|
-
params.push(...wanted);
|
|
669
|
-
}
|
|
670
729
|
};
|
|
671
|
-
var SAFE_PREFIX2 = /^[a-zA-Z0-9_]+$/;
|
|
672
|
-
function sanitizePrefix2(prefix) {
|
|
673
|
-
if (!SAFE_PREFIX2.test(prefix)) {
|
|
674
|
-
throw new Error(
|
|
675
|
-
`Invalid table prefix "${prefix}": only letters, digits, and underscores are allowed`
|
|
676
|
-
);
|
|
677
|
-
}
|
|
678
|
-
return prefix;
|
|
679
|
-
}
|
|
680
730
|
function contractRowToDomain(row) {
|
|
681
731
|
const contract = {
|
|
682
732
|
script: row.script,
|
|
@@ -699,15 +749,10 @@ function contractRowToDomain(row) {
|
|
|
699
749
|
}
|
|
700
750
|
|
|
701
751
|
// src/repositories/sqlite/intentRepository.ts
|
|
702
|
-
var SAFE_PREFIX3 = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
703
|
-
function sanitizePrefix3(p) {
|
|
704
|
-
if (!SAFE_PREFIX3.test(p)) throw new Error(`Invalid table prefix "${p}"`);
|
|
705
|
-
return p;
|
|
706
|
-
}
|
|
707
752
|
var SQLiteIntentRepository = class {
|
|
708
753
|
constructor(db, options) {
|
|
709
754
|
this.db = db;
|
|
710
|
-
this.prefix =
|
|
755
|
+
this.prefix = sanitizeTablePrefix(options?.prefix ?? "ark_");
|
|
711
756
|
this.t = `${this.prefix}intents`;
|
|
712
757
|
}
|
|
713
758
|
db;
|
|
@@ -746,19 +791,16 @@ var SQLiteIntentRepository = class {
|
|
|
746
791
|
`CREATE INDEX IF NOT EXISTS idx_${this.prefix}intent_state ON ${this.t} (state)`
|
|
747
792
|
);
|
|
748
793
|
}
|
|
749
|
-
withTx(fn) {
|
|
750
|
-
return runInTransaction(this.db, fn);
|
|
751
|
-
}
|
|
752
794
|
async clear() {
|
|
753
795
|
await this.ensureInit();
|
|
754
|
-
await this.
|
|
796
|
+
await runInTransaction(this.db, async () => {
|
|
755
797
|
await this.db.run(`DELETE FROM ${this.t}`);
|
|
756
798
|
});
|
|
757
799
|
}
|
|
758
800
|
async saveIntent(intent) {
|
|
759
801
|
await this.ensureInit();
|
|
760
802
|
const now = Date.now();
|
|
761
|
-
await this.
|
|
803
|
+
await runInTransaction(this.db, async () => {
|
|
762
804
|
await this.db.run(
|
|
763
805
|
`INSERT INTO ${this.t}
|
|
764
806
|
(intent_tx_id, intent_id, state, valid_from, valid_until,
|
|
@@ -806,15 +848,30 @@ var SQLiteIntentRepository = class {
|
|
|
806
848
|
);
|
|
807
849
|
});
|
|
808
850
|
}
|
|
809
|
-
async
|
|
851
|
+
async getIntentsPage(filter, page) {
|
|
852
|
+
assertPageRequest(page);
|
|
810
853
|
await this.ensureInit();
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
854
|
+
if (filter?.states?.length === 0) return { items: [] };
|
|
855
|
+
const stateSql = filter?.states ? ` AND state IN (${filter.states.map(() => "?").join(", ")})` : "";
|
|
856
|
+
const stateParams = filter?.states ?? [];
|
|
857
|
+
const batchSize = Math.max(64, page.limit + 1);
|
|
858
|
+
const rows = [];
|
|
859
|
+
let after = page.after;
|
|
860
|
+
while (rows.length <= page.limit) {
|
|
861
|
+
const batch = await this.db.all(
|
|
862
|
+
`SELECT * FROM ${this.t} WHERE intent_tx_id > ?${stateSql} ORDER BY intent_tx_id LIMIT ?`,
|
|
863
|
+
[after ?? "", ...stateParams, batchSize]
|
|
864
|
+
);
|
|
865
|
+
if (batch.length === 0) break;
|
|
866
|
+
for (const row of batch) {
|
|
867
|
+
after = row.intent_tx_id;
|
|
868
|
+
const intent = rowToIntent(row);
|
|
869
|
+
if (!filter || intentMatchesFilter(intent, filter)) rows.push(intent);
|
|
870
|
+
if (rows.length > page.limit) break;
|
|
871
|
+
}
|
|
872
|
+
if (batch.length < batchSize) break;
|
|
873
|
+
}
|
|
874
|
+
return pageResult(rows, page.limit, (intent) => intent.intentTxId);
|
|
818
875
|
}
|
|
819
876
|
async getLockedVtxoOutpoints() {
|
|
820
877
|
await this.ensureInit();
|
|
@@ -852,15 +909,10 @@ function rowToIntent(r) {
|
|
|
852
909
|
}
|
|
853
910
|
|
|
854
911
|
// src/repositories/sqlite/virtualTxRepository.ts
|
|
855
|
-
var SAFE_PREFIX4 = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
856
|
-
function sanitizePrefix4(p) {
|
|
857
|
-
if (!SAFE_PREFIX4.test(p)) throw new Error(`Invalid table prefix "${p}"`);
|
|
858
|
-
return p;
|
|
859
|
-
}
|
|
860
912
|
var SQLiteVirtualTxRepository = class {
|
|
861
913
|
constructor(db, options) {
|
|
862
914
|
this.db = db;
|
|
863
|
-
this.prefix =
|
|
915
|
+
this.prefix = sanitizeTablePrefix(options?.prefix ?? "ark_");
|
|
864
916
|
this.tTx = `${this.prefix}virtual_txs`;
|
|
865
917
|
this.tBranch = `${this.prefix}vtxo_branches`;
|
|
866
918
|
}
|
|
@@ -892,12 +944,9 @@ var SQLiteVirtualTxRepository = class {
|
|
|
892
944
|
`CREATE INDEX IF NOT EXISTS idx_${this.prefix}branch_vtxid ON ${this.tBranch} (virtual_txid)`
|
|
893
945
|
);
|
|
894
946
|
}
|
|
895
|
-
tx(fn) {
|
|
896
|
-
return runInTransaction(this.db, fn);
|
|
897
|
-
}
|
|
898
947
|
async clear() {
|
|
899
948
|
await this.ensureInit();
|
|
900
|
-
await this.
|
|
949
|
+
await runInTransaction(this.db, async () => {
|
|
901
950
|
await this.db.run(`DELETE FROM ${this.tTx}`);
|
|
902
951
|
await this.db.run(`DELETE FROM ${this.tBranch}`);
|
|
903
952
|
});
|
|
@@ -905,7 +954,7 @@ var SQLiteVirtualTxRepository = class {
|
|
|
905
954
|
async upsertVirtualTxs(txs) {
|
|
906
955
|
if (txs.length === 0) return;
|
|
907
956
|
await this.ensureInit();
|
|
908
|
-
await this.
|
|
957
|
+
await runInTransaction(this.db, async () => {
|
|
909
958
|
const placeholders = txs.map(() => "?").join(", ");
|
|
910
959
|
const existing = await this.db.all(
|
|
911
960
|
`SELECT * FROM ${this.tTx} WHERE txid IN (${placeholders})`,
|
|
@@ -932,7 +981,7 @@ var SQLiteVirtualTxRepository = class {
|
|
|
932
981
|
}
|
|
933
982
|
async setBranch(vtxo, branch) {
|
|
934
983
|
await this.ensureInit();
|
|
935
|
-
await this.
|
|
984
|
+
await runInTransaction(this.db, async () => {
|
|
936
985
|
await this.db.run(`DELETE FROM ${this.tBranch} WHERE vtxo_txid = ? AND vtxo_vout = ?`, [
|
|
937
986
|
vtxo.txid,
|
|
938
987
|
vtxo.vout
|
|
@@ -967,7 +1016,7 @@ var SQLiteVirtualTxRepository = class {
|
|
|
967
1016
|
}
|
|
968
1017
|
async pruneForSpentVtxo(vtxo) {
|
|
969
1018
|
await this.ensureInit();
|
|
970
|
-
await this.
|
|
1019
|
+
await runInTransaction(this.db, async () => {
|
|
971
1020
|
const removed = await this.db.all(
|
|
972
1021
|
`SELECT DISTINCT virtual_txid FROM ${this.tBranch} WHERE vtxo_txid = ? AND vtxo_vout = ?`,
|
|
973
1022
|
[vtxo.txid, vtxo.vout]
|
|
@@ -1000,13 +1049,6 @@ function rowToTx(r) {
|
|
|
1000
1049
|
};
|
|
1001
1050
|
}
|
|
1002
1051
|
|
|
1003
|
-
// src/repositories/sqlite/prefix.ts
|
|
1004
|
-
var SAFE_PREFIX5 = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
1005
|
-
function sanitizeTablePrefix(p) {
|
|
1006
|
-
if (!SAFE_PREFIX5.test(p)) throw new Error(`Invalid table prefix "${p}"`);
|
|
1007
|
-
return p;
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
1052
|
export { SQLiteContractRepository, SQLiteIntentRepository, SQLiteVirtualTxRepository, SQLiteWalletRepository, runInTransaction, sanitizeTablePrefix };
|
|
1011
1053
|
//# sourceMappingURL=index.js.map
|
|
1012
1054
|
//# sourceMappingURL=index.js.map
|