@breeztech/breez-sdk-spark 0.25.0 → 0.27.0-dev1

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.
@@ -1974,6 +1974,24 @@ function newSharedSdkContext(config) {
1974
1974
  }
1975
1975
  exports.newSharedSdkContext = newSharedSdkContext;
1976
1976
 
1977
+ /**
1978
+ * Reads a `SparkConfig` from JSON, for a deployment that publishes its
1979
+ * operators, service provider and certificates as a file. Set it on
1980
+ * `Config.sparkConfig` to connect a wallet to that deployment.
1981
+ * @param {string} json
1982
+ * @returns {SparkConfig}
1983
+ */
1984
+ function parseSparkConfig(json) {
1985
+ const ptr0 = passStringToWasm0(json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1986
+ const len0 = WASM_VECTOR_LEN;
1987
+ const ret = wasm.parseSparkConfig(ptr0, len0);
1988
+ if (ret[2]) {
1989
+ throw takeFromExternrefTable0(ret[1]);
1990
+ }
1991
+ return takeFromExternrefTable0(ret[0]);
1992
+ }
1993
+ exports.parseSparkConfig = parseSparkConfig;
1994
+
1977
1995
  /**
1978
1996
  * `PostgreSQL`-backed storage built from `config`.
1979
1997
  * @param {PostgresStorageConfig} config
@@ -2935,6 +2953,10 @@ function __wbg_get_imports() {
2935
2953
  const ret = arg0.listTokensOutputs();
2936
2954
  return ret;
2937
2955
  }, arguments); },
2956
+ __wbg_listWatchedDepositAddresses_55fe8af0643e3ea9: function() { return handleError(function (arg0) {
2957
+ const ret = arg0.listWatchedDepositAddresses();
2958
+ return ret;
2959
+ }, arguments); },
2938
2960
  __wbg_log_cf86719f8acabfda: function(arg0, arg1) {
2939
2961
  arg0.log(arg1);
2940
2962
  },
@@ -3634,6 +3656,18 @@ function __wbg_get_imports() {
3634
3656
  const ret = arg0.updateTokenOutputs(arg1, arg2);
3635
3657
  return ret;
3636
3658
  }, arguments); },
3659
+ __wbg_updateWatchedDepositAddress_260322f77dee1443: function() { return handleError(function (arg0, arg1, arg2, arg3) {
3660
+ let deferred0_0;
3661
+ let deferred0_1;
3662
+ try {
3663
+ deferred0_0 = arg1;
3664
+ deferred0_1 = arg2;
3665
+ const ret = arg0.updateWatchedDepositAddress(getStringFromWasm0(arg1, arg2), arg3);
3666
+ return ret;
3667
+ } finally {
3668
+ wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
3669
+ }
3670
+ }, arguments); },
3637
3671
  __wbg_url_68fd9a221360e0db: function(arg0, arg1) {
3638
3672
  const ret = arg1.url;
3639
3673
  const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
@@ -3669,47 +3703,47 @@ function __wbg_get_imports() {
3669
3703
  return ret;
3670
3704
  },
3671
3705
  __wbindgen_cast_0000000000000001: function(arg0, arg1) {
3672
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 22, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3673
- const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h62bc116e0c266522);
3706
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 21, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3707
+ const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h0a7e586e9273e375);
3674
3708
  return ret;
3675
3709
  },
3676
3710
  __wbindgen_cast_0000000000000002: function(arg0, arg1) {
3677
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 397, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
3711
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 384, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
3678
3712
  const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h3b28fbbc8b451a25);
3679
3713
  return ret;
3680
3714
  },
3681
3715
  __wbindgen_cast_0000000000000003: function(arg0, arg1) {
3682
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("CloseEvent")], shim_idx: 397, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
3716
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("CloseEvent")], shim_idx: 384, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
3683
3717
  const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h3b28fbbc8b451a25_2);
3684
3718
  return ret;
3685
3719
  },
3686
3720
  __wbindgen_cast_0000000000000004: function(arg0, arg1) {
3687
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("MessageEvent")], shim_idx: 397, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
3721
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("MessageEvent")], shim_idx: 384, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
3688
3722
  const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h3b28fbbc8b451a25_3);
3689
3723
  return ret;
3690
3724
  },
3691
3725
  __wbindgen_cast_0000000000000005: function(arg0, arg1) {
3692
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("SessionStore")], shim_idx: 22, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3693
- const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h62bc116e0c266522_4);
3726
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("SessionStore")], shim_idx: 21, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3727
+ const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h0a7e586e9273e375_4);
3694
3728
  return ret;
3695
3729
  },
3696
3730
  __wbindgen_cast_0000000000000006: function(arg0, arg1) {
3697
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Storage")], shim_idx: 22, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3698
- const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h62bc116e0c266522_5);
3731
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Storage")], shim_idx: 21, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3732
+ const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h0a7e586e9273e375_5);
3699
3733
  return ret;
3700
3734
  },
3701
3735
  __wbindgen_cast_0000000000000007: function(arg0, arg1) {
3702
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("TokenStore")], shim_idx: 22, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3703
- const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h62bc116e0c266522_6);
3736
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("TokenStore")], shim_idx: 21, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3737
+ const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h0a7e586e9273e375_6);
3704
3738
  return ret;
3705
3739
  },
3706
3740
  __wbindgen_cast_0000000000000008: function(arg0, arg1) {
3707
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("TreeStore")], shim_idx: 22, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3708
- const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h62bc116e0c266522_7);
3741
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("TreeStore")], shim_idx: 21, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
3742
+ const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h0a7e586e9273e375_7);
3709
3743
  return ret;
3710
3744
  },
3711
3745
  __wbindgen_cast_0000000000000009: function(arg0, arg1) {
3712
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 402, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
3746
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 388, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
3713
3747
  const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h484cd36e13f37bd7);
3714
3748
  return ret;
3715
3749
  },
@@ -3817,36 +3851,36 @@ function wasm_bindgen__convert__closures_____invoke__h3b28fbbc8b451a25_3(arg0, a
3817
3851
  wasm.wasm_bindgen__convert__closures_____invoke__h3b28fbbc8b451a25_3(arg0, arg1, arg2);
3818
3852
  }
3819
3853
 
3820
- function wasm_bindgen__convert__closures_____invoke__h62bc116e0c266522(arg0, arg1, arg2) {
3821
- const ret = wasm.wasm_bindgen__convert__closures_____invoke__h62bc116e0c266522(arg0, arg1, arg2);
3854
+ function wasm_bindgen__convert__closures_____invoke__h0a7e586e9273e375(arg0, arg1, arg2) {
3855
+ const ret = wasm.wasm_bindgen__convert__closures_____invoke__h0a7e586e9273e375(arg0, arg1, arg2);
3822
3856
  if (ret[1]) {
3823
3857
  throw takeFromExternrefTable0(ret[0]);
3824
3858
  }
3825
3859
  }
3826
3860
 
3827
- function wasm_bindgen__convert__closures_____invoke__h62bc116e0c266522_4(arg0, arg1, arg2) {
3828
- const ret = wasm.wasm_bindgen__convert__closures_____invoke__h62bc116e0c266522_4(arg0, arg1, arg2);
3861
+ function wasm_bindgen__convert__closures_____invoke__h0a7e586e9273e375_4(arg0, arg1, arg2) {
3862
+ const ret = wasm.wasm_bindgen__convert__closures_____invoke__h0a7e586e9273e375_4(arg0, arg1, arg2);
3829
3863
  if (ret[1]) {
3830
3864
  throw takeFromExternrefTable0(ret[0]);
3831
3865
  }
3832
3866
  }
3833
3867
 
3834
- function wasm_bindgen__convert__closures_____invoke__h62bc116e0c266522_5(arg0, arg1, arg2) {
3835
- const ret = wasm.wasm_bindgen__convert__closures_____invoke__h62bc116e0c266522_5(arg0, arg1, arg2);
3868
+ function wasm_bindgen__convert__closures_____invoke__h0a7e586e9273e375_5(arg0, arg1, arg2) {
3869
+ const ret = wasm.wasm_bindgen__convert__closures_____invoke__h0a7e586e9273e375_5(arg0, arg1, arg2);
3836
3870
  if (ret[1]) {
3837
3871
  throw takeFromExternrefTable0(ret[0]);
3838
3872
  }
3839
3873
  }
3840
3874
 
3841
- function wasm_bindgen__convert__closures_____invoke__h62bc116e0c266522_6(arg0, arg1, arg2) {
3842
- const ret = wasm.wasm_bindgen__convert__closures_____invoke__h62bc116e0c266522_6(arg0, arg1, arg2);
3875
+ function wasm_bindgen__convert__closures_____invoke__h0a7e586e9273e375_6(arg0, arg1, arg2) {
3876
+ const ret = wasm.wasm_bindgen__convert__closures_____invoke__h0a7e586e9273e375_6(arg0, arg1, arg2);
3843
3877
  if (ret[1]) {
3844
3878
  throw takeFromExternrefTable0(ret[0]);
3845
3879
  }
3846
3880
  }
3847
3881
 
3848
- function wasm_bindgen__convert__closures_____invoke__h62bc116e0c266522_7(arg0, arg1, arg2) {
3849
- const ret = wasm.wasm_bindgen__convert__closures_____invoke__h62bc116e0c266522_7(arg0, arg1, arg2);
3882
+ function wasm_bindgen__convert__closures_____invoke__h0a7e586e9273e375_7(arg0, arg1, arg2) {
3883
+ const ret = wasm.wasm_bindgen__convert__closures_____invoke__h0a7e586e9273e375_7(arg0, arg1, arg2);
3850
3884
  if (ret[1]) {
3851
3885
  throw takeFromExternrefTable0(ret[0]);
3852
3886
  }
Binary file
@@ -128,6 +128,7 @@ export const initLogging: (a: any, b: number, c: number) => any;
128
128
  export const mysqlStorage: (a: any) => number;
129
129
  export const newRestChainService: (a: number, b: number, c: any, d: any, e: number) => any;
130
130
  export const newSharedSdkContext: (a: any) => any;
131
+ export const parseSparkConfig: (a: number, b: number) => [number, number, number];
131
132
  export const passkeyclient_checkAvailability: (a: number) => any;
132
133
  export const passkeyclient_connectWithPasskey: (a: number, b: any) => any;
133
134
  export const passkeyclient_labels: (a: number) => number;
@@ -189,11 +190,11 @@ export const __wbg_externalsparksignerhandle_free: (a: number, b: number) => voi
189
190
  export const __wbg_signingonlyexternalsigners_free: (a: number, b: number) => void;
190
191
  export const signingonlyexternalsigners_breezSigner: (a: number) => number;
191
192
  export const signingonlyexternalsigners_sparkSigner: (a: number) => number;
192
- export const wasm_bindgen__convert__closures_____invoke__h62bc116e0c266522: (a: number, b: number, c: any) => [number, number];
193
- export const wasm_bindgen__convert__closures_____invoke__h62bc116e0c266522_4: (a: number, b: number, c: any) => [number, number];
194
- export const wasm_bindgen__convert__closures_____invoke__h62bc116e0c266522_5: (a: number, b: number, c: any) => [number, number];
195
- export const wasm_bindgen__convert__closures_____invoke__h62bc116e0c266522_6: (a: number, b: number, c: any) => [number, number];
196
- export const wasm_bindgen__convert__closures_____invoke__h62bc116e0c266522_7: (a: number, b: number, c: any) => [number, number];
193
+ export const wasm_bindgen__convert__closures_____invoke__h0a7e586e9273e375: (a: number, b: number, c: any) => [number, number];
194
+ export const wasm_bindgen__convert__closures_____invoke__h0a7e586e9273e375_4: (a: number, b: number, c: any) => [number, number];
195
+ export const wasm_bindgen__convert__closures_____invoke__h0a7e586e9273e375_5: (a: number, b: number, c: any) => [number, number];
196
+ export const wasm_bindgen__convert__closures_____invoke__h0a7e586e9273e375_6: (a: number, b: number, c: any) => [number, number];
197
+ export const wasm_bindgen__convert__closures_____invoke__h0a7e586e9273e375_7: (a: number, b: number, c: any) => [number, number];
197
198
  export const wasm_bindgen__convert__closures_____invoke__h41057d61edf43a32: (a: number, b: number, c: any, d: any) => void;
198
199
  export const wasm_bindgen__convert__closures_____invoke__h3b28fbbc8b451a25: (a: number, b: number, c: any) => void;
199
200
  export const wasm_bindgen__convert__closures_____invoke__h3b28fbbc8b451a25_2: (a: number, b: number, c: any) => void;
package/nodejs/index.mjs CHANGED
@@ -21,6 +21,7 @@ export const {
21
21
  mysqlStorage,
22
22
  newRestChainService,
23
23
  newSharedSdkContext,
24
+ parseSparkConfig,
24
25
  postgresStorage,
25
26
  singleKeyCpfpSigner,
26
27
  start,
@@ -801,7 +801,7 @@ class MysqlStorage {
801
801
  async listDeposits() {
802
802
  try {
803
803
  const [rows] = await this.pool.query(
804
- "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 = ?",
804
+ "SELECT txid, vout, amount_sats, is_mature, claim_error, refund_tx, refund_tx_id, instant_claim_status, refund_state, max_claim_fee FROM brz_unclaimed_deposits WHERE user_id = ?",
805
805
  [this.identity]
806
806
  );
807
807
 
@@ -816,6 +816,7 @@ class MysqlStorage {
816
816
  refundTxId: row.refund_tx_id,
817
817
  instantClaimStatus: parseJson(row.instant_claim_status),
818
818
  refundState: parseJson(row.refund_state),
819
+ maxClaimFee: parseJson(row.max_claim_fee),
819
820
  }));
820
821
  } catch (error) {
821
822
  throw new StorageError(
@@ -868,6 +869,18 @@ class MysqlStorage {
868
869
  payload.refundTxid,
869
870
  ]
870
871
  );
872
+ } else if (payload.type === "maxClaimFee") {
873
+ await this.pool.query(
874
+ `UPDATE brz_unclaimed_deposits
875
+ SET max_claim_fee = ?
876
+ WHERE user_id = ? AND txid = ? AND vout = ?`,
877
+ [
878
+ payload.maxFee ? JSON.stringify(payload.maxFee) : null,
879
+ this.identity,
880
+ txid,
881
+ vout,
882
+ ]
883
+ );
871
884
  } else {
872
885
  throw new StorageError(`Unknown payload type: ${payload.type}`);
873
886
  }
@@ -880,6 +893,59 @@ class MysqlStorage {
880
893
  }
881
894
  }
882
895
 
896
+ async listWatchedDepositAddresses() {
897
+ try {
898
+ const [rows] = await this.pool.query(
899
+ `SELECT address, issued_at, seen FROM brz_watched_deposit_addresses
900
+ WHERE user_id = ? ORDER BY issued_at DESC`,
901
+ [this.identity]
902
+ );
903
+ return rows.map((row) => ({
904
+ address: row.address,
905
+ issuedAt: BigInt(row.issued_at),
906
+ seen: toBool(row.seen) ?? false,
907
+ }));
908
+ } catch (error) {
909
+ throw new StorageError(
910
+ `Failed to list watched deposit addresses: ${error.message}`,
911
+ error
912
+ );
913
+ }
914
+ }
915
+
916
+ async updateWatchedDepositAddress(address, payload) {
917
+ try {
918
+ if (payload.type === "watch") {
919
+ await this.pool.query(
920
+ `INSERT INTO brz_watched_deposit_addresses (user_id, address, issued_at, seen)
921
+ VALUES (?, ?, ?, FALSE)
922
+ ON DUPLICATE KEY UPDATE issued_at = VALUES(issued_at), seen = FALSE`,
923
+ [this.identity, address, String(payload.issuedAt)]
924
+ );
925
+ } else if (payload.type === "seen") {
926
+ await this.pool.query(
927
+ `UPDATE brz_watched_deposit_addresses SET seen = TRUE
928
+ WHERE user_id = ? AND address = ?`,
929
+ [this.identity, address]
930
+ );
931
+ } else if (payload.type === "unwatch") {
932
+ await this.pool.query(
933
+ `DELETE FROM brz_watched_deposit_addresses
934
+ WHERE user_id = ? AND address = ? AND issued_at = ?`,
935
+ [this.identity, address, String(payload.issuedAt)]
936
+ );
937
+ } else {
938
+ throw new StorageError(`Unknown payload type: ${payload.type}`);
939
+ }
940
+ } catch (error) {
941
+ if (error instanceof StorageError) throw error;
942
+ throw new StorageError(
943
+ `Failed to update watched deposit address '${address}': ${error.message}`,
944
+ error
945
+ );
946
+ }
947
+ }
948
+
883
949
  async setLnurlMetadata(metadata) {
884
950
  try {
885
951
  await this._withTransaction(async (conn) => {
@@ -584,6 +584,29 @@ class MysqlMigrationManager {
584
584
  `ALTER TABLE brz_unclaimed_deposits ADD COLUMN refund_state JSON NULL`,
585
585
  ],
586
586
  },
587
+ {
588
+ // Deposit addresses polled on-chain for deposits still in the mempool.
589
+ // Rows are removed once watching them can no longer lead to an early
590
+ // claim, so the table holds only the live watch set.
591
+ name: "Add brz_watched_deposit_addresses",
592
+ sql: [
593
+ `CREATE TABLE IF NOT EXISTS brz_watched_deposit_addresses (
594
+ user_id VARBINARY(33) NOT NULL,
595
+ address VARCHAR(255) NOT NULL,
596
+ issued_at BIGINT NOT NULL,
597
+ seen BOOLEAN NOT NULL DEFAULT FALSE,
598
+ PRIMARY KEY (user_id, address)
599
+ )`,
600
+ ],
601
+ },
602
+ {
603
+ // The fee ceiling standing for one deposit as a JSON-encoded MaxFee,
604
+ // overriding the configured one. NULL when the configured one applies.
605
+ name: "Add max claim fee to brz_unclaimed_deposits",
606
+ sql: [
607
+ `ALTER TABLE brz_unclaimed_deposits ADD COLUMN max_claim_fee JSON NULL`,
608
+ ],
609
+ },
587
610
  ];
588
611
  }
589
612
  }
@@ -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, refund_state 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, max_claim_fee FROM brz_unclaimed_deposits WHERE user_id = $1",
734
734
  [this.identity]
735
735
  );
736
736
 
@@ -744,6 +744,7 @@ class PostgresStorage {
744
744
  refundTxId: row.refund_tx_id,
745
745
  instantClaimStatus: row.instant_claim_status || null,
746
746
  refundState: row.refund_state || null,
747
+ maxClaimFee: row.max_claim_fee || null,
747
748
  }));
748
749
  } catch (error) {
749
750
  throw new StorageError(
@@ -796,6 +797,18 @@ class PostgresStorage {
796
797
  payload.refundTxid,
797
798
  ]
798
799
  );
800
+ } else if (payload.type === "maxClaimFee") {
801
+ await this.pool.query(
802
+ `UPDATE brz_unclaimed_deposits
803
+ SET max_claim_fee = $1
804
+ WHERE user_id = $2 AND txid = $3 AND vout = $4`,
805
+ [
806
+ payload.maxFee ? JSON.stringify(payload.maxFee) : null,
807
+ this.identity,
808
+ txid,
809
+ vout,
810
+ ]
811
+ );
799
812
  } else {
800
813
  throw new StorageError(`Unknown payload type: ${payload.type}`);
801
814
  }
@@ -808,6 +821,59 @@ class PostgresStorage {
808
821
  }
809
822
  }
810
823
 
824
+ async listWatchedDepositAddresses() {
825
+ try {
826
+ const result = await this.pool.query(
827
+ `SELECT address, issued_at, seen FROM brz_watched_deposit_addresses
828
+ WHERE user_id = $1 ORDER BY issued_at DESC`,
829
+ [this.identity]
830
+ );
831
+ return result.rows.map((row) => ({
832
+ address: row.address,
833
+ issuedAt: BigInt(row.issued_at),
834
+ seen: row.seen ?? false,
835
+ }));
836
+ } catch (error) {
837
+ throw new StorageError(
838
+ `Failed to list watched deposit addresses: ${error.message}`,
839
+ error
840
+ );
841
+ }
842
+ }
843
+
844
+ async updateWatchedDepositAddress(address, payload) {
845
+ try {
846
+ if (payload.type === "watch") {
847
+ await this.pool.query(
848
+ `INSERT INTO brz_watched_deposit_addresses (user_id, address, issued_at, seen)
849
+ VALUES ($1, $2, $3, FALSE)
850
+ ON CONFLICT(user_id, address) DO UPDATE SET issued_at = EXCLUDED.issued_at, seen = FALSE`,
851
+ [this.identity, address, String(payload.issuedAt)]
852
+ );
853
+ } else if (payload.type === "seen") {
854
+ await this.pool.query(
855
+ `UPDATE brz_watched_deposit_addresses SET seen = TRUE
856
+ WHERE user_id = $1 AND address = $2`,
857
+ [this.identity, address]
858
+ );
859
+ } else if (payload.type === "unwatch") {
860
+ await this.pool.query(
861
+ `DELETE FROM brz_watched_deposit_addresses
862
+ WHERE user_id = $1 AND address = $2 AND issued_at = $3`,
863
+ [this.identity, address, String(payload.issuedAt)]
864
+ );
865
+ } else {
866
+ throw new StorageError(`Unknown payload type: ${payload.type}`);
867
+ }
868
+ } catch (error) {
869
+ if (error instanceof StorageError) throw error;
870
+ throw new StorageError(
871
+ `Failed to update watched deposit address '${address}': ${error.message}`,
872
+ error
873
+ );
874
+ }
875
+ }
876
+
811
877
  async setLnurlMetadata(metadata) {
812
878
  try {
813
879
  await this._withTransaction(async (client) => {
@@ -540,6 +540,29 @@ class PostgresMigrationManager {
540
540
  `ALTER TABLE brz_unclaimed_deposits ADD COLUMN refund_state JSONB`,
541
541
  ],
542
542
  },
543
+ {
544
+ // Deposit addresses polled on-chain for deposits still in the mempool.
545
+ // Rows are removed once watching them can no longer lead to an early
546
+ // claim, so the table holds only the live watch set.
547
+ name: "Add brz_watched_deposit_addresses",
548
+ sql: [
549
+ `CREATE TABLE IF NOT EXISTS brz_watched_deposit_addresses (
550
+ user_id BYTEA NOT NULL,
551
+ address TEXT NOT NULL,
552
+ issued_at BIGINT NOT NULL,
553
+ seen BOOLEAN NOT NULL DEFAULT FALSE,
554
+ PRIMARY KEY (user_id, address)
555
+ )`,
556
+ ],
557
+ },
558
+ {
559
+ // The fee ceiling standing for one deposit as a JSON-encoded MaxFee,
560
+ // overriding the configured one. NULL when the configured one applies.
561
+ name: "Add max claim fee to brz_unclaimed_deposits",
562
+ sql: [
563
+ `ALTER TABLE brz_unclaimed_deposits ADD COLUMN max_claim_fee JSONB`,
564
+ ],
565
+ },
543
566
  ];
544
567
  }
545
568
  }
@@ -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, refund_state
703
+ SELECT txid, vout, amount_sats, is_mature, claim_error, refund_tx, refund_tx_id, instant_claim_status, refund_state, max_claim_fee
704
704
  FROM unclaimed_deposits
705
705
  `);
706
706
 
@@ -718,6 +718,9 @@ class SqliteStorage {
718
718
  ? JSON.parse(row.instant_claim_status)
719
719
  : null,
720
720
  refundState: row.refund_state ? JSON.parse(row.refund_state) : null,
721
+ maxClaimFee: row.max_claim_fee
722
+ ? JSON.parse(row.max_claim_fee)
723
+ : null,
721
724
  }))
722
725
  );
723
726
  } catch (error) {
@@ -767,6 +770,18 @@ class SqliteStorage {
767
770
  `);
768
771
 
769
772
  stmt.run(JSON.stringify(payload.state), txid, vout, payload.refundTxid);
773
+ } else if (payload.type === "maxClaimFee") {
774
+ const stmt = this.db.prepare(`
775
+ UPDATE unclaimed_deposits
776
+ SET max_claim_fee = ?
777
+ WHERE txid = ? AND vout = ?
778
+ `);
779
+
780
+ stmt.run(
781
+ payload.maxFee ? JSON.stringify(payload.maxFee) : null,
782
+ txid,
783
+ vout
784
+ );
770
785
  } else {
771
786
  return Promise.reject(
772
787
  new StorageError(`Unknown payload type: ${payload.type}`)
@@ -784,6 +799,71 @@ class SqliteStorage {
784
799
  }
785
800
  }
786
801
 
802
+ listWatchedDepositAddresses() {
803
+ try {
804
+ const rows = this.db
805
+ .prepare(
806
+ `SELECT address, issued_at, seen FROM watched_deposit_addresses
807
+ ORDER BY issued_at DESC`
808
+ )
809
+ .all();
810
+ return Promise.resolve(
811
+ rows.map((row) => ({
812
+ address: row.address,
813
+ issuedAt: row.issued_at,
814
+ seen: Boolean(row.seen),
815
+ }))
816
+ );
817
+ } catch (error) {
818
+ return Promise.reject(
819
+ new StorageError(
820
+ `Failed to list watched deposit addresses: ${error.message}`,
821
+ error
822
+ )
823
+ );
824
+ }
825
+ }
826
+
827
+ updateWatchedDepositAddress(address, payload) {
828
+ try {
829
+ if (payload.type === "watch") {
830
+ this.db
831
+ .prepare(
832
+ `INSERT INTO watched_deposit_addresses (address, issued_at, seen)
833
+ VALUES (?, ?, 0)
834
+ ON CONFLICT(address) DO UPDATE SET issued_at = excluded.issued_at, seen = 0`
835
+ )
836
+ .run(address, payload.issuedAt);
837
+ } else if (payload.type === "seen") {
838
+ this.db
839
+ .prepare(
840
+ "UPDATE watched_deposit_addresses SET seen = 1 WHERE address = ?"
841
+ )
842
+ .run(address);
843
+ } else if (payload.type === "unwatch") {
844
+ this.db
845
+ .prepare(
846
+ `DELETE FROM watched_deposit_addresses
847
+ WHERE address = ? AND issued_at = ?`
848
+ )
849
+ .run(address, payload.issuedAt);
850
+ } else {
851
+ return Promise.reject(
852
+ new StorageError(`Unknown payload type: ${payload.type}`)
853
+ );
854
+ }
855
+ return Promise.resolve();
856
+ } catch (error) {
857
+ if (error instanceof StorageError) return Promise.reject(error);
858
+ return Promise.reject(
859
+ new StorageError(
860
+ `Failed to update watched deposit address '${address}': ${error.message}`,
861
+ error
862
+ )
863
+ );
864
+ }
865
+ }
866
+
787
867
  setLnurlMetadata(metadata) {
788
868
  try {
789
869
  const stmt = this.db.prepare(
@@ -500,6 +500,27 @@ class MigrationManager {
500
500
  name: "Add refund state to unclaimed_deposits",
501
501
  sql: [`ALTER TABLE unclaimed_deposits ADD COLUMN refund_state TEXT`],
502
502
  },
503
+ {
504
+ // Deposit addresses polled on-chain for deposits still in the mempool.
505
+ // Rows are removed once watching them can no longer lead to an early
506
+ // claim, so the table holds only the live watch set.
507
+ name: "Add watched_deposit_addresses",
508
+ sql: [
509
+ `CREATE TABLE IF NOT EXISTS watched_deposit_addresses (
510
+ address TEXT PRIMARY KEY,
511
+ issued_at INTEGER NOT NULL,
512
+ seen INTEGER NOT NULL DEFAULT 0
513
+ )`,
514
+ ],
515
+ },
516
+ {
517
+ // The fee ceiling standing for one deposit as a JSON-encoded MaxFee,
518
+ // overriding the configured one. NULL when the configured one applies.
519
+ name: "Add max claim fee to unclaimed_deposits",
520
+ sql: [
521
+ `ALTER TABLE unclaimed_deposits ADD COLUMN max_claim_fee TEXT`,
522
+ ],
523
+ },
503
524
  ];
504
525
  }
505
526
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@breeztech/breez-sdk-spark",
3
- "version": "0.25.0",
3
+ "version": "0.27.0-dev1",
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)",
package/ssr/index.js CHANGED
@@ -108,6 +108,11 @@ export function newSharedSdkContext(...args) {
108
108
  return _module.newSharedSdkContext(...args);
109
109
  }
110
110
 
111
+ export function parseSparkConfig(...args) {
112
+ if (!_module) _notInitialized('parseSparkConfig');
113
+ return _module.parseSparkConfig(...args);
114
+ }
115
+
111
116
  export function postgresStorage(...args) {
112
117
  if (!_module) _notInitialized('postgresStorage');
113
118
  return _module.postgresStorage(...args);