@breeztech/breez-sdk-spark 0.23.1 → 0.24.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.
@@ -730,7 +730,7 @@ class PostgresStorage {
730
730
  async listDeposits() {
731
731
  try {
732
732
  const result = await this.pool.query(
733
- "SELECT txid, vout, amount_sats, is_mature, claim_error, refund_tx, refund_tx_id, instant_claim_status FROM brz_unclaimed_deposits WHERE user_id = $1",
733
+ "SELECT txid, vout, amount_sats, is_mature, claim_error, refund_tx, refund_tx_id, instant_claim_status, refund_state FROM brz_unclaimed_deposits WHERE user_id = $1",
734
734
  [this.identity]
735
735
  );
736
736
 
@@ -743,6 +743,7 @@ class PostgresStorage {
743
743
  refundTx: row.refund_tx,
744
744
  refundTxId: row.refund_tx_id,
745
745
  instantClaimStatus: row.instant_claim_status || null,
746
+ refundState: row.refund_state || null,
746
747
  }));
747
748
  } catch (error) {
748
749
  throw new StorageError(
@@ -757,16 +758,23 @@ class PostgresStorage {
757
758
  if (payload.type === "claimError") {
758
759
  await this.pool.query(
759
760
  `UPDATE brz_unclaimed_deposits
760
- SET claim_error = $1, refund_tx = NULL, refund_tx_id = NULL
761
+ SET claim_error = $1
761
762
  WHERE user_id = $2 AND txid = $3 AND vout = $4`,
762
763
  [JSON.stringify(payload.error), this.identity, txid, vout]
763
764
  );
764
765
  } else if (payload.type === "refund") {
765
766
  await this.pool.query(
766
767
  `UPDATE brz_unclaimed_deposits
767
- SET refund_tx = $1, refund_tx_id = $2, claim_error = NULL
768
- WHERE user_id = $3 AND txid = $4 AND vout = $5`,
769
- [payload.refundTx, payload.refundTxid, this.identity, txid, vout]
768
+ SET refund_tx = $1, refund_tx_id = $2, refund_state = $3, claim_error = NULL
769
+ WHERE user_id = $4 AND txid = $5 AND vout = $6`,
770
+ [
771
+ payload.refundTx,
772
+ payload.refundTxid,
773
+ JSON.stringify(payload.state),
774
+ this.identity,
775
+ txid,
776
+ vout,
777
+ ]
770
778
  );
771
779
  } else if (payload.type === "instantClaim") {
772
780
  await this.pool.query(
@@ -775,6 +783,19 @@ class PostgresStorage {
775
783
  WHERE user_id = $2 AND txid = $3 AND vout = $4`,
776
784
  [JSON.stringify(payload.status), this.identity, txid, vout]
777
785
  );
786
+ } else if (payload.type === "refundBroadcastState") {
787
+ await this.pool.query(
788
+ `UPDATE brz_unclaimed_deposits
789
+ SET refund_state = $1
790
+ WHERE user_id = $2 AND txid = $3 AND vout = $4 AND refund_tx_id = $5`,
791
+ [
792
+ JSON.stringify(payload.state),
793
+ this.identity,
794
+ txid,
795
+ vout,
796
+ payload.refundTxid,
797
+ ]
798
+ );
778
799
  } else {
779
800
  throw new StorageError(`Unknown payload type: ${payload.type}`);
780
801
  }
@@ -1606,6 +1627,112 @@ async function createPostgresStorage(config, identity, logger = null) {
1606
1627
  );
1607
1628
  }
1608
1629
 
1630
+ /**
1631
+ * Translates the `sslmode` URI parameter into pg's `ssl` option.
1632
+ * The pinned `pg` maps `require` to verified TLS but warns that pg v9 reverts
1633
+ * it to libpq semantics (no verification); handling `sslmode` here keeps the
1634
+ * documented guarantees stable across driver versions. The parameter (and the
1635
+ * `ssl`/`sslcert`/`sslkey`/`sslrootcert` parameters folded into the option)
1636
+ * are stripped from the URI because a connection string's ssl settings
1637
+ * override the explicit option.
1638
+ *
1639
+ * Spellings mirror the Rust SDK where pg has an equivalent:
1640
+ * - absent or `disable`: no TLS
1641
+ * - `prefer` / `require` / `verify-full`: TLS with certificate chain and
1642
+ * hostname verification (pg cannot fall back to plaintext, so `prefer`
1643
+ * behaves like `require`)
1644
+ * - `verify-ca`: chain verification only, hostname not checked
1645
+ * - `no-verify`: TLS without certificate verification (explicit opt-in)
1646
+ *
1647
+ * An unrecognized value throws rather than silently changing the TLS level.
1648
+ * Pin a private CA with `sslrootcert=<path>` (required for `verify-ca`); for
1649
+ * the hostname-verified modes, adding the CA to Node's trust store
1650
+ * (e.g. NODE_EXTRA_CA_CERTS) also works, though it extends the store rather
1651
+ * than pinning.
1652
+ */
1653
+ function extractSslMode(connectionString) {
1654
+ if (
1655
+ !connectionString.startsWith("postgres://") &&
1656
+ !connectionString.startsWith("postgresql://")
1657
+ ) {
1658
+ return { connectionString, ssl: undefined };
1659
+ }
1660
+ const queryIndex = connectionString.indexOf("?");
1661
+ if (queryIndex === -1) {
1662
+ return { connectionString, ssl: undefined };
1663
+ }
1664
+ const params = connectionString.slice(queryIndex + 1).split("&");
1665
+ if (!params.some((param) => param.startsWith("sslmode="))) {
1666
+ return { connectionString, ssl: undefined };
1667
+ }
1668
+
1669
+ const base = connectionString.slice(0, queryIndex);
1670
+ const retained = [];
1671
+ const files = {};
1672
+ let sslMode;
1673
+ for (const param of params) {
1674
+ const eq = param.indexOf("=");
1675
+ const key = eq === -1 ? param : param.slice(0, eq);
1676
+ const value = eq === -1 ? "" : decodeURIComponent(param.slice(eq + 1));
1677
+ if (key === "sslmode") {
1678
+ sslMode = value;
1679
+ } else if (key === "sslcert" || key === "sslkey" || key === "sslrootcert") {
1680
+ files[key] = value;
1681
+ } else if (key === "ssl") {
1682
+ // consumed: sslmode governs TLS when present
1683
+ } else {
1684
+ retained.push(param);
1685
+ }
1686
+ }
1687
+ const rebuilt = retained.length ? `${base}?${retained.join("&")}` : base;
1688
+ return { connectionString: rebuilt, ssl: sslOptionForMode(sslMode, files) };
1689
+ }
1690
+
1691
+ function sslOptionForMode(mode, files) {
1692
+ let ssl;
1693
+ switch (mode) {
1694
+ case "disable":
1695
+ return false;
1696
+ case "prefer":
1697
+ case "require":
1698
+ case "verify-full":
1699
+ ssl = { rejectUnauthorized: true };
1700
+ break;
1701
+ case "verify-ca":
1702
+ // Without a pinned CA, chain-only verification accepts a certificate
1703
+ // from any trusted CA for any host, which authenticates nothing.
1704
+ if (!files.sslrootcert) {
1705
+ throw new StorageError(
1706
+ "sslmode=verify-ca requires sslrootcert=<path>; supply the CA to " +
1707
+ "pin, or use sslmode=require / verify-full for " +
1708
+ "hostname-verified TLS"
1709
+ );
1710
+ }
1711
+ ssl = { rejectUnauthorized: true, checkServerIdentity: () => undefined };
1712
+ break;
1713
+ case "no-verify":
1714
+ ssl = { rejectUnauthorized: false };
1715
+ break;
1716
+ default:
1717
+ throw new StorageError(
1718
+ `Unrecognized sslmode value \`${mode}\`; expected one of: ` +
1719
+ "disable, prefer, require, verify-ca, verify-full, no-verify"
1720
+ );
1721
+ }
1722
+ const fs =
1723
+ files.sslcert || files.sslkey || files.sslrootcert ? require("fs") : null;
1724
+ if (files.sslcert) {
1725
+ ssl.cert = fs.readFileSync(files.sslcert).toString();
1726
+ }
1727
+ if (files.sslkey) {
1728
+ ssl.key = fs.readFileSync(files.sslkey).toString();
1729
+ }
1730
+ if (files.sslrootcert) {
1731
+ ssl.ca = fs.readFileSync(files.sslrootcert).toString();
1732
+ }
1733
+ return ssl;
1734
+ }
1735
+
1609
1736
  /**
1610
1737
  * Create a pg.Pool from a config object.
1611
1738
  * The returned pool can be shared across multiple store implementations.
@@ -1614,8 +1741,10 @@ async function createPostgresStorage(config, identity, logger = null) {
1614
1741
  * @returns {pg.Pool}
1615
1742
  */
1616
1743
  function createPostgresPool(config) {
1744
+ const { connectionString, ssl } = extractSslMode(config.connectionString);
1617
1745
  return new pg.Pool({
1618
- connectionString: config.connectionString,
1746
+ connectionString,
1747
+ ...(ssl !== undefined && { ssl }),
1619
1748
  max: config.maxPoolSize,
1620
1749
  connectionTimeoutMillis: config.createTimeoutSecs * 1000,
1621
1750
  idleTimeoutMillis: config.recycleTimeoutSecs * 1000,
@@ -524,6 +524,22 @@ class PostgresMigrationManager {
524
524
  `ALTER TABLE brz_unclaimed_deposits ADD COLUMN instant_claim_status JSONB`,
525
525
  ],
526
526
  },
527
+ {
528
+ // Only the Declined shape changed, and clearing it costs at most one
529
+ // extra quote. Submitted rows are left alone: their shape is unchanged
530
+ // and they are the only guard against re-claiming a settling deposit.
531
+ name: "Clear declined instant claim status after its shape changed",
532
+ sql: [
533
+ `UPDATE brz_unclaimed_deposits SET instant_claim_status = NULL
534
+ WHERE LOWER(instant_claim_status::text) LIKE '%declined%'`,
535
+ ],
536
+ },
537
+ {
538
+ name: "Add refund state to brz_unclaimed_deposits",
539
+ sql: [
540
+ `ALTER TABLE brz_unclaimed_deposits ADD COLUMN refund_state JSONB`,
541
+ ],
542
+ },
527
543
  ];
528
544
  }
529
545
  }
@@ -1267,6 +1267,112 @@ class PostgresTokenStore {
1267
1267
  }
1268
1268
  }
1269
1269
 
1270
+ /**
1271
+ * Translates the `sslmode` URI parameter into pg's `ssl` option.
1272
+ * The pinned `pg` maps `require` to verified TLS but warns that pg v9 reverts
1273
+ * it to libpq semantics (no verification); handling `sslmode` here keeps the
1274
+ * documented guarantees stable across driver versions. The parameter (and the
1275
+ * `ssl`/`sslcert`/`sslkey`/`sslrootcert` parameters folded into the option)
1276
+ * are stripped from the URI because a connection string's ssl settings
1277
+ * override the explicit option.
1278
+ *
1279
+ * Spellings mirror the Rust SDK where pg has an equivalent:
1280
+ * - absent or `disable`: no TLS
1281
+ * - `prefer` / `require` / `verify-full`: TLS with certificate chain and
1282
+ * hostname verification (pg cannot fall back to plaintext, so `prefer`
1283
+ * behaves like `require`)
1284
+ * - `verify-ca`: chain verification only, hostname not checked
1285
+ * - `no-verify`: TLS without certificate verification (explicit opt-in)
1286
+ *
1287
+ * An unrecognized value throws rather than silently changing the TLS level.
1288
+ * Pin a private CA with `sslrootcert=<path>` (required for `verify-ca`); for
1289
+ * the hostname-verified modes, adding the CA to Node's trust store
1290
+ * (e.g. NODE_EXTRA_CA_CERTS) also works, though it extends the store rather
1291
+ * than pinning.
1292
+ */
1293
+ function extractSslMode(connectionString) {
1294
+ if (
1295
+ !connectionString.startsWith("postgres://") &&
1296
+ !connectionString.startsWith("postgresql://")
1297
+ ) {
1298
+ return { connectionString, ssl: undefined };
1299
+ }
1300
+ const queryIndex = connectionString.indexOf("?");
1301
+ if (queryIndex === -1) {
1302
+ return { connectionString, ssl: undefined };
1303
+ }
1304
+ const params = connectionString.slice(queryIndex + 1).split("&");
1305
+ if (!params.some((param) => param.startsWith("sslmode="))) {
1306
+ return { connectionString, ssl: undefined };
1307
+ }
1308
+
1309
+ const base = connectionString.slice(0, queryIndex);
1310
+ const retained = [];
1311
+ const files = {};
1312
+ let sslMode;
1313
+ for (const param of params) {
1314
+ const eq = param.indexOf("=");
1315
+ const key = eq === -1 ? param : param.slice(0, eq);
1316
+ const value = eq === -1 ? "" : decodeURIComponent(param.slice(eq + 1));
1317
+ if (key === "sslmode") {
1318
+ sslMode = value;
1319
+ } else if (key === "sslcert" || key === "sslkey" || key === "sslrootcert") {
1320
+ files[key] = value;
1321
+ } else if (key === "ssl") {
1322
+ // consumed: sslmode governs TLS when present
1323
+ } else {
1324
+ retained.push(param);
1325
+ }
1326
+ }
1327
+ const rebuilt = retained.length ? `${base}?${retained.join("&")}` : base;
1328
+ return { connectionString: rebuilt, ssl: sslOptionForMode(sslMode, files) };
1329
+ }
1330
+
1331
+ function sslOptionForMode(mode, files) {
1332
+ let ssl;
1333
+ switch (mode) {
1334
+ case "disable":
1335
+ return false;
1336
+ case "prefer":
1337
+ case "require":
1338
+ case "verify-full":
1339
+ ssl = { rejectUnauthorized: true };
1340
+ break;
1341
+ case "verify-ca":
1342
+ // Without a pinned CA, chain-only verification accepts a certificate
1343
+ // from any trusted CA for any host, which authenticates nothing.
1344
+ if (!files.sslrootcert) {
1345
+ throw new TokenStoreError(
1346
+ "sslmode=verify-ca requires sslrootcert=<path>; supply the CA to " +
1347
+ "pin, or use sslmode=require / verify-full for " +
1348
+ "hostname-verified TLS"
1349
+ );
1350
+ }
1351
+ ssl = { rejectUnauthorized: true, checkServerIdentity: () => undefined };
1352
+ break;
1353
+ case "no-verify":
1354
+ ssl = { rejectUnauthorized: false };
1355
+ break;
1356
+ default:
1357
+ throw new TokenStoreError(
1358
+ `Unrecognized sslmode value \`${mode}\`; expected one of: ` +
1359
+ "disable, prefer, require, verify-ca, verify-full, no-verify"
1360
+ );
1361
+ }
1362
+ const fs =
1363
+ files.sslcert || files.sslkey || files.sslrootcert ? require("fs") : null;
1364
+ if (files.sslcert) {
1365
+ ssl.cert = fs.readFileSync(files.sslcert).toString();
1366
+ }
1367
+ if (files.sslkey) {
1368
+ ssl.key = fs.readFileSync(files.sslkey).toString();
1369
+ }
1370
+ if (files.sslrootcert) {
1371
+ ssl.ca = fs.readFileSync(files.sslrootcert).toString();
1372
+ }
1373
+ return ssl;
1374
+ }
1375
+
1270
1376
  /**
1271
1377
  * Create a PostgresTokenStore instance from a config object.
1272
1378
  *
@@ -1280,8 +1386,10 @@ class PostgresTokenStore {
1280
1386
  * @returns {Promise<PostgresTokenStore>}
1281
1387
  */
1282
1388
  async function createPostgresTokenStore(config, identity, logger = null) {
1389
+ const { connectionString, ssl } = extractSslMode(config.connectionString);
1283
1390
  const pool = new pg.Pool({
1284
- connectionString: config.connectionString,
1391
+ connectionString,
1392
+ ...(ssl !== undefined && { ssl }),
1285
1393
  max: config.maxPoolSize,
1286
1394
  connectionTimeoutMillis: config.createTimeoutSecs * 1000,
1287
1395
  idleTimeoutMillis: config.recycleTimeoutSecs * 1000,
@@ -1392,6 +1392,112 @@ class PostgresTreeStore {
1392
1392
  }
1393
1393
  }
1394
1394
 
1395
+ /**
1396
+ * Translates the `sslmode` URI parameter into pg's `ssl` option.
1397
+ * The pinned `pg` maps `require` to verified TLS but warns that pg v9 reverts
1398
+ * it to libpq semantics (no verification); handling `sslmode` here keeps the
1399
+ * documented guarantees stable across driver versions. The parameter (and the
1400
+ * `ssl`/`sslcert`/`sslkey`/`sslrootcert` parameters folded into the option)
1401
+ * are stripped from the URI because a connection string's ssl settings
1402
+ * override the explicit option.
1403
+ *
1404
+ * Spellings mirror the Rust SDK where pg has an equivalent:
1405
+ * - absent or `disable`: no TLS
1406
+ * - `prefer` / `require` / `verify-full`: TLS with certificate chain and
1407
+ * hostname verification (pg cannot fall back to plaintext, so `prefer`
1408
+ * behaves like `require`)
1409
+ * - `verify-ca`: chain verification only, hostname not checked
1410
+ * - `no-verify`: TLS without certificate verification (explicit opt-in)
1411
+ *
1412
+ * An unrecognized value throws rather than silently changing the TLS level.
1413
+ * Pin a private CA with `sslrootcert=<path>` (required for `verify-ca`); for
1414
+ * the hostname-verified modes, adding the CA to Node's trust store
1415
+ * (e.g. NODE_EXTRA_CA_CERTS) also works, though it extends the store rather
1416
+ * than pinning.
1417
+ */
1418
+ function extractSslMode(connectionString) {
1419
+ if (
1420
+ !connectionString.startsWith("postgres://") &&
1421
+ !connectionString.startsWith("postgresql://")
1422
+ ) {
1423
+ return { connectionString, ssl: undefined };
1424
+ }
1425
+ const queryIndex = connectionString.indexOf("?");
1426
+ if (queryIndex === -1) {
1427
+ return { connectionString, ssl: undefined };
1428
+ }
1429
+ const params = connectionString.slice(queryIndex + 1).split("&");
1430
+ if (!params.some((param) => param.startsWith("sslmode="))) {
1431
+ return { connectionString, ssl: undefined };
1432
+ }
1433
+
1434
+ const base = connectionString.slice(0, queryIndex);
1435
+ const retained = [];
1436
+ const files = {};
1437
+ let sslMode;
1438
+ for (const param of params) {
1439
+ const eq = param.indexOf("=");
1440
+ const key = eq === -1 ? param : param.slice(0, eq);
1441
+ const value = eq === -1 ? "" : decodeURIComponent(param.slice(eq + 1));
1442
+ if (key === "sslmode") {
1443
+ sslMode = value;
1444
+ } else if (key === "sslcert" || key === "sslkey" || key === "sslrootcert") {
1445
+ files[key] = value;
1446
+ } else if (key === "ssl") {
1447
+ // consumed: sslmode governs TLS when present
1448
+ } else {
1449
+ retained.push(param);
1450
+ }
1451
+ }
1452
+ const rebuilt = retained.length ? `${base}?${retained.join("&")}` : base;
1453
+ return { connectionString: rebuilt, ssl: sslOptionForMode(sslMode, files) };
1454
+ }
1455
+
1456
+ function sslOptionForMode(mode, files) {
1457
+ let ssl;
1458
+ switch (mode) {
1459
+ case "disable":
1460
+ return false;
1461
+ case "prefer":
1462
+ case "require":
1463
+ case "verify-full":
1464
+ ssl = { rejectUnauthorized: true };
1465
+ break;
1466
+ case "verify-ca":
1467
+ // Without a pinned CA, chain-only verification accepts a certificate
1468
+ // from any trusted CA for any host, which authenticates nothing.
1469
+ if (!files.sslrootcert) {
1470
+ throw new TreeStoreError(
1471
+ "sslmode=verify-ca requires sslrootcert=<path>; supply the CA to " +
1472
+ "pin, or use sslmode=require / verify-full for " +
1473
+ "hostname-verified TLS"
1474
+ );
1475
+ }
1476
+ ssl = { rejectUnauthorized: true, checkServerIdentity: () => undefined };
1477
+ break;
1478
+ case "no-verify":
1479
+ ssl = { rejectUnauthorized: false };
1480
+ break;
1481
+ default:
1482
+ throw new TreeStoreError(
1483
+ `Unrecognized sslmode value \`${mode}\`; expected one of: ` +
1484
+ "disable, prefer, require, verify-ca, verify-full, no-verify"
1485
+ );
1486
+ }
1487
+ const fs =
1488
+ files.sslcert || files.sslkey || files.sslrootcert ? require("fs") : null;
1489
+ if (files.sslcert) {
1490
+ ssl.cert = fs.readFileSync(files.sslcert).toString();
1491
+ }
1492
+ if (files.sslkey) {
1493
+ ssl.key = fs.readFileSync(files.sslkey).toString();
1494
+ }
1495
+ if (files.sslrootcert) {
1496
+ ssl.ca = fs.readFileSync(files.sslrootcert).toString();
1497
+ }
1498
+ return ssl;
1499
+ }
1500
+
1395
1501
  /**
1396
1502
  * Create a PostgresTreeStore instance from a config object.
1397
1503
  *
@@ -1405,8 +1511,10 @@ class PostgresTreeStore {
1405
1511
  * @returns {Promise<PostgresTreeStore>}
1406
1512
  */
1407
1513
  async function createPostgresTreeStore(config, identity, logger = null) {
1514
+ const { connectionString, ssl } = extractSslMode(config.connectionString);
1408
1515
  const pool = new pg.Pool({
1409
- connectionString: config.connectionString,
1516
+ connectionString,
1517
+ ...(ssl !== undefined && { ssl }),
1410
1518
  max: config.maxPoolSize,
1411
1519
  connectionTimeoutMillis: config.createTimeoutSecs * 1000,
1412
1520
  idleTimeoutMillis: config.recycleTimeoutSecs * 1000,
@@ -700,7 +700,7 @@ class SqliteStorage {
700
700
  listDeposits() {
701
701
  try {
702
702
  const stmt = this.db.prepare(`
703
- SELECT txid, vout, amount_sats, is_mature, claim_error, refund_tx, refund_tx_id, instant_claim_status
703
+ SELECT txid, vout, amount_sats, is_mature, claim_error, refund_tx, refund_tx_id, instant_claim_status, refund_state
704
704
  FROM unclaimed_deposits
705
705
  `);
706
706
 
@@ -717,6 +717,7 @@ class SqliteStorage {
717
717
  instantClaimStatus: row.instant_claim_status
718
718
  ? JSON.parse(row.instant_claim_status)
719
719
  : null,
720
+ refundState: row.refund_state ? JSON.parse(row.refund_state) : null,
720
721
  }))
721
722
  );
722
723
  } catch (error) {
@@ -731,7 +732,7 @@ class SqliteStorage {
731
732
  if (payload.type === "claimError") {
732
733
  const stmt = this.db.prepare(`
733
734
  UPDATE unclaimed_deposits
734
- SET claim_error = ?, refund_tx = NULL, refund_tx_id = NULL
735
+ SET claim_error = ?
735
736
  WHERE txid = ? AND vout = ?
736
737
  `);
737
738
 
@@ -739,11 +740,17 @@ class SqliteStorage {
739
740
  } else if (payload.type === "refund") {
740
741
  const stmt = this.db.prepare(`
741
742
  UPDATE unclaimed_deposits
742
- SET refund_tx = ?, refund_tx_id = ?, claim_error = NULL
743
+ SET refund_tx = ?, refund_tx_id = ?, refund_state = ?, claim_error = NULL
743
744
  WHERE txid = ? AND vout = ?
744
745
  `);
745
746
 
746
- stmt.run(payload.refundTx, payload.refundTxid, txid, vout);
747
+ stmt.run(
748
+ payload.refundTx,
749
+ payload.refundTxid,
750
+ JSON.stringify(payload.state),
751
+ txid,
752
+ vout
753
+ );
747
754
  } else if (payload.type === "instantClaim") {
748
755
  const stmt = this.db.prepare(`
749
756
  UPDATE unclaimed_deposits
@@ -752,6 +759,14 @@ class SqliteStorage {
752
759
  `);
753
760
 
754
761
  stmt.run(JSON.stringify(payload.status), txid, vout);
762
+ } else if (payload.type === "refundBroadcastState") {
763
+ const stmt = this.db.prepare(`
764
+ UPDATE unclaimed_deposits
765
+ SET refund_state = ?
766
+ WHERE txid = ? AND vout = ? AND refund_tx_id = ?
767
+ `);
768
+
769
+ stmt.run(JSON.stringify(payload.state), txid, vout, payload.refundTxid);
755
770
  } else {
756
771
  return Promise.reject(
757
772
  new StorageError(`Unknown payload type: ${payload.type}`)
@@ -486,6 +486,20 @@ class MigrationManager {
486
486
  `ALTER TABLE unclaimed_deposits ADD COLUMN instant_claim_status TEXT`,
487
487
  ],
488
488
  },
489
+ {
490
+ // Only the Declined shape changed, and clearing it costs at most one
491
+ // extra quote. Submitted rows are left alone: their shape is unchanged
492
+ // and they are the only guard against re-claiming a settling deposit.
493
+ name: "Clear declined instant claim status after its shape changed",
494
+ sql: [
495
+ `UPDATE unclaimed_deposits SET instant_claim_status = NULL
496
+ WHERE LOWER(instant_claim_status) LIKE '%declined%'`,
497
+ ],
498
+ },
499
+ {
500
+ name: "Add refund state to unclaimed_deposits",
501
+ sql: [`ALTER TABLE unclaimed_deposits ADD COLUMN refund_state TEXT`],
502
+ },
489
503
  ];
490
504
  }
491
505
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@breeztech/breez-sdk-spark",
3
- "version": "0.23.1",
3
+ "version": "0.24.0",
4
4
  "description": "Breez Spark SDK",
5
5
  "repository": "https://github.com/breez/spark-sdk",
6
6
  "author": "Breez <contact@breez.technology> (https://github.com/breez)",