@gvnrdao/dh-sdk 0.0.301 → 0.0.303

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.
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import type { Wallet, Signer, Provider } from "ethers";
5
5
  import { LitNetwork } from "@gvnrdao/dh-lit-ops";
6
+ import type { ServerSessionStore } from "../../utils/server-session-store";
6
7
  export type SDKMode = "standalone" | "service";
7
8
  /**
8
9
  * Contract Addresses Configuration
@@ -55,6 +56,27 @@ interface BaseSDKConfig {
55
56
  * is a SafeModuleSignerAdapter that routes transactions through the module.
56
57
  */
57
58
  authSigner?: Wallet | Signer;
59
+ /**
60
+ * Persistence for the lit-ops-server session (service mode). The signed
61
+ * EIP-712 login envelope silently re-mints 15-minute JWTs for up to 24h, so
62
+ * persisting it means at most one wallet signature per day per device
63
+ * instead of one per 15 minutes / page reload. Defaults to enabled with
64
+ * `localStorage` in browsers; degrades to per-instance memory elsewhere.
65
+ * Note the persisted envelope is a re-mint credential for its window —
66
+ * call `clearServerSession()` on wallet disconnect.
67
+ */
68
+ sessionPersistence?: {
69
+ /** Default true. Set false for a fresh signature per instance + expiry. */
70
+ enabled?: boolean;
71
+ /** Custom store (e.g. a file-backed store for CLI daemons). */
72
+ store?: ServerSessionStore;
73
+ };
74
+ /**
75
+ * Fires immediately before the server-session login requests a wallet
76
+ * signature — never on the silent re-mint paths. Lets UIs show a
77
+ * "check your wallet" prompt.
78
+ */
79
+ onSessionSignaturePrompt?: () => void;
58
80
  ethRpcUrl?: string;
59
81
  chainId?: number;
60
82
  networkOverride?: {
@@ -111,6 +111,13 @@ export declare class DiamondHandsSDK {
111
111
  * reject the call with 401, which is the correct outcome.
112
112
  */
113
113
  private getAuthHeader;
114
+ /**
115
+ * Remove the persisted lit-ops-server session (the 24h re-mint envelope and
116
+ * its JWT) for the current auth signer. Call on wallet disconnect or
117
+ * explicit sign-out so the credential does not outlive the wallet
118
+ * connection. No-op in standalone mode.
119
+ */
120
+ clearServerSession(): Promise<void>;
114
121
  /**
115
122
  * Audit H-9: invalidate the LoanQuery cache so subsequent reads return
116
123
  * the post-write state. We clear the entire loan-query cache (not just
@@ -456,6 +463,17 @@ export declare class DiamondHandsSDK {
456
463
  * Action can cross-check against the on-chain authorizer record.
457
464
  */
458
465
  targetAmount: number;
466
+ /**
467
+ * FULL input set the phase-2 signer may spend (P6/#8 — bound into the
468
+ * signature via a canonical set hash). Omit for the common single-UTXO
469
+ * withdrawal; multi-UTXO consolidation withdrawals MUST list every input
470
+ * (each with its full sat value), including the authorized UTXO.
471
+ */
472
+ utxos?: Array<{
473
+ txid: string;
474
+ vout: number;
475
+ value: number;
476
+ }>;
459
477
  }): Promise<{
460
478
  success: boolean;
461
479
  txid?: string;
@@ -658,6 +676,13 @@ export declare class DiamondHandsSDK {
658
676
  * Get all events for a loan position from the subgraph
659
677
  */
660
678
  getLoanEvents(positionId: string, filter?: import("../types/event-types").LoanEventsFilter): Promise<Result<import("../types/event-types").LoanEvents, SDKError>>;
679
+ /**
680
+ * Get loan-event history for every position owned by a wallet.
681
+ * Composes getUserPositions + getLoanEvents; returns one LoanEvents per
682
+ * position (each carrying its own positionId). Bitcoin deposits are not on the
683
+ * subgraph — merge those in from an Esplora source on the consumer side.
684
+ */
685
+ getWalletLoanEvents(userAddress: string, filter?: import("../types/event-types").LoanEventsFilter): Promise<Result<import("../types/event-types").LoanEvents[], SDKError>>;
661
686
  /**
662
687
  * Get protocol-wide events across all indexed entity types.
663
688
  * Returns a merged, sorted discriminated-union timeline.
@@ -839,6 +864,22 @@ export declare class DiamondHandsSDK {
839
864
  signature: string;
840
865
  validatorPkp: string;
841
866
  }, SDKError>>;
867
+ /**
868
+ * List a vault address's PENDING (not-yet-confirmed) incoming BTC deposits.
869
+ *
870
+ * A deposit is "pending" until it reaches the protocol's minimum confirmation
871
+ * depth (6) — the confirmed balance endpoints (`getVaultBalance` /
872
+ * `getAddressBalance`) omit anything shallower, so this surfaces in-flight
873
+ * deposits (0–5 confirmations). Server-delegated: lit-ops-server queries its
874
+ * configured premium Esplora provider (`/api/lit/pending-deposits`). Requires
875
+ * service mode.
876
+ */
877
+ getPendingDeposits(vaultAddress: string): Promise<Array<{
878
+ txid: string;
879
+ sats: number;
880
+ confirmations: number;
881
+ minConfirmations: number;
882
+ }>>;
842
883
  /**
843
884
  * Mint mock BTC tokens (test networks only)
844
885
  *
@@ -187,6 +187,29 @@ export declare class LoanQuery {
187
187
  * ```
188
188
  */
189
189
  getLoanEvents(positionId: string, filter?: LoanEventsFilter): Promise<Result<LoanEvents, SDKError>>;
190
+ /**
191
+ * Get loan-event history for every position owned by a wallet.
192
+ *
193
+ * Composes getUserPositions + getLoanEvents (the subgraph has no wallet-scoped
194
+ * event collection). Each returned LoanEvents carries its own positionId, so
195
+ * callers can flatten and merge into a single per-wallet timeline.
196
+ *
197
+ * Bitcoin deposits never appear in the subgraph — merge those in from an
198
+ * Esplora source on the consumer side.
199
+ *
200
+ * @param userAddress - Loan-owner wallet address.
201
+ * @param filter - Optional per-position event filter (same semantics as getLoanEvents).
202
+ * @returns One LoanEvents object per position (empty array if the wallet has none).
203
+ *
204
+ * @example
205
+ * ```typescript
206
+ * const result = await loanQuery.getWalletLoanEvents('0x830b...');
207
+ * if (result.success) {
208
+ * const allEvents = result.value; // LoanEvents[] — one per position
209
+ * }
210
+ * ```
211
+ */
212
+ getWalletLoanEvents(userAddress: string, filter?: LoanEventsFilter): Promise<Result<LoanEvents[], SDKError>>;
190
213
  /**
191
214
  * Clear loan cache
192
215
  *
@@ -40,8 +40,6 @@ export interface MintUCDAuthParams extends BaseAuthParams {
40
40
  amount: UCD;
41
41
  /** PKP public key (optional - validator PKP is passed separately to authorizeMintUCD) */
42
42
  publicKey?: string;
43
- /** Bitcoin provider URL for balance verification */
44
- bitcoinProviderUrl?: string;
45
43
  /** Selected term in months (override when contract state is zero) */
46
44
  selectedTerm?: number;
47
45
  }
@@ -14,6 +14,24 @@ export interface BtcExecuteSignParams {
14
14
  chainId: number;
15
15
  signer: Signer;
16
16
  }
17
+ /** One Bitcoin input the borrower authorizes the phase-2 signer to spend. */
18
+ export interface BtcExecuteInput {
19
+ txid: string;
20
+ vout: number;
21
+ /** Full output value in sats (feeds the BIP143 sighash). */
22
+ value: number;
23
+ }
24
+ /**
25
+ * Canonical hash of a Bitcoin input set (remediation P6/#8).
26
+ *
27
+ * CANONICALIZATION MUST MATCH
28
+ * `lit-actions/src/modules/bitcoin/utxo-set-hash.ts` byte-for-byte: entries
29
+ * normalized to `${txidLowercase}:${vout}:${value}`, sorted by (txid asc,
30
+ * vout asc), joined with "|", then keccak256 over the UTF-8 bytes. The Lit
31
+ * Action recomputes this over the `utxos[]` it actually signs, so an operator
32
+ * cannot spend inputs beyond the set hashed here.
33
+ */
34
+ export declare function hashBtcInputSet(utxos: BtcExecuteInput[]): string;
17
35
  export interface BtcExecuteSignedEnvelope {
18
36
  positionId: string;
19
37
  txid: string;
@@ -31,6 +49,12 @@ export interface BtcExecuteSignedEnvelope {
31
49
  userSignature: string;
32
50
  /** Address recovered from the signer (borrower). */
33
51
  borrowerAddress: string;
52
+ /**
53
+ * The FULL input set bound into the signature (P6/#8). The server must
54
+ * forward exactly this set to the phase-2 signer — any other set fails
55
+ * signature verification in the Lit Action.
56
+ */
57
+ utxos: BtcExecuteInput[];
34
58
  }
35
59
  /**
36
60
  * Build the quantum-aligned timestamp + EIP-191 signature the
@@ -57,4 +81,11 @@ export declare function buildBtcExecuteEnvelope(params: BtcExecuteSignParams & {
57
81
  satoshis: number;
58
82
  targetAddress: string;
59
83
  targetAmount: number;
84
+ /**
85
+ * FULL input set the phase-2 signer may spend (P6/#8). Omit for the
86
+ * common single-UTXO withdrawal — it defaults to exactly the authorized
87
+ * UTXO, the most restrictive set. Multi-UTXO consolidation withdrawals
88
+ * MUST list every input here; the authorized UTXO must be among them.
89
+ */
90
+ utxos?: BtcExecuteInput[];
60
91
  }): Promise<BtcExecuteSignedEnvelope>;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Client-side persistence for the lit-ops-server session.
3
+ *
4
+ * The server accepts re-mints of the SAME signed login envelope until
5
+ * `issuedAt + REAUTH_WINDOW_SECONDS` (see lit-ops-server `login-handler.ts`),
6
+ * so caching the signed payload means an expired 15-minute JWT — or a full
7
+ * page reload — costs one silent API call instead of another wallet popup.
8
+ * Mirrors the frontend's `wallet-login-cache.ts` pattern for the Firebase
9
+ * `DhWebLogin` flow.
10
+ *
11
+ * Browser-safe: the default store auto-detects `localStorage` and degrades to
12
+ * a per-instance in-memory map elsewhere (Node, blocked storage), which keeps
13
+ * exactly the pre-persistence behavior.
14
+ */
15
+ import type { DhServerLoginPayload } from "./eip712-login";
16
+ /** Must match lit-ops-server's REAUTH_WINDOW_SECONDS (`eip712-digest.ts`). */
17
+ export declare const REAUTH_WINDOW_SECONDS: number;
18
+ /** Stop using a cached payload this long before the server would reject it. */
19
+ export declare const SAFETY_MARGIN_SECONDS: number;
20
+ /** One persisted session: the 24h re-mint credential plus the current JWT. */
21
+ export interface PersistedServerSession {
22
+ version: 1;
23
+ /** Lowercased signer address the payload authenticates. */
24
+ address: string;
25
+ chainId: number;
26
+ /** Normalized service endpoint (trailing slashes stripped) — the signed audience. */
27
+ endpoint: string;
28
+ payload: DhServerLoginPayload;
29
+ /** Current JWT, if still live — lets a reload skip even the silent re-mint call. */
30
+ token?: string;
31
+ /** JWT expiry, unix seconds. */
32
+ tokenExpiresAt?: number;
33
+ }
34
+ /**
35
+ * Pluggable persistence. Implementations must be best-effort and never throw
36
+ * out of `save`/`clear`; `load` returns null for absent or malformed entries.
37
+ */
38
+ export interface ServerSessionStore {
39
+ load(key: string): PersistedServerSession | null;
40
+ save(key: string, session: PersistedServerSession): void;
41
+ clear(key: string): void;
42
+ }
43
+ export declare function sessionStoreKey(address: string, chainId: number, endpoint: string): string;
44
+ export declare class LocalStorageSessionStore implements ServerSessionStore {
45
+ load(key: string): PersistedServerSession | null;
46
+ save(key: string, session: PersistedServerSession): void;
47
+ clear(key: string): void;
48
+ }
49
+ export declare class MemorySessionStore implements ServerSessionStore {
50
+ private readonly entries;
51
+ load(key: string): PersistedServerSession | null;
52
+ save(key: string, session: PersistedServerSession): void;
53
+ clear(key: string): void;
54
+ }
55
+ /** `localStorage` when usable (browser, not blocked), else per-instance memory. */
56
+ export declare function createDefaultSessionStore(): ServerSessionStore;
@@ -1,50 +1,103 @@
1
1
  /**
2
- * In-memory session JWT manager for lit-ops-server.
2
+ * Session JWT manager for lit-ops-server.
3
3
  *
4
4
  * The server's `POST /api/auth/login` route accepts an EIP-712-signed
5
- * `DhServerLogin` envelope and returns an HS256 JWT with a 15-minute TTL.
6
- * This helper:
5
+ * `DiamondHands` envelope and returns an HS256 JWT with a 15-minute TTL. The
6
+ * same signed envelope may silently re-mint fresh JWTs until
7
+ * `issuedAt + REAUTH_WINDOW_SECONDS` (24h), so a wallet signature is only
8
+ * needed once per window. This helper:
7
9
  *
8
10
  * - caches the JWT until ~30s before expiry,
11
+ * - persists the signed envelope (+ current JWT) via a pluggable store —
12
+ * `localStorage` in browsers by default — so page reloads and expired
13
+ * JWTs recover WITHOUT another wallet popup,
9
14
  * - coalesces concurrent callers behind a single in-flight Promise so we
10
15
  * don't spam `/api/auth/login` on parallel SDK calls,
11
16
  * - exposes `getValidToken()` returning the live token and
12
17
  * `getAuthHeader()` returning a ready-to-spread `{ Authorization: ... }`
13
18
  * object.
14
19
  *
15
- * Browser-safe — no filesystem touches. The CLI mirrors this shape but
16
- * persists to `~/.diamond-hands/session.json` for cross-invocation reuse.
20
+ * Browser-safe — no filesystem touches; outside browsers the store degrades
21
+ * to per-instance memory. The CLI mirrors this shape but persists to
22
+ * `~/.diamond-hands/session.json` for cross-invocation reuse.
17
23
  */
18
24
  import type { Signer } from "ethers";
25
+ import { type ServerSessionStore } from "./server-session-store";
19
26
  export interface ServerSessionOptions {
20
27
  signer: Signer;
21
28
  serviceEndpoint: string;
22
29
  chainId: number;
30
+ /**
31
+ * Persistence for the signed login envelope + JWT. Defaults to
32
+ * `localStorage` in browsers and per-instance memory elsewhere.
33
+ */
34
+ sessionStore?: ServerSessionStore;
35
+ /** Set false to disable persistence entirely (fresh signature per instance + expiry). */
36
+ persistSession?: boolean;
37
+ /**
38
+ * Fires immediately before a wallet signature is requested — i.e. only when
39
+ * neither the cached JWT nor the persisted envelope could renew the session,
40
+ * never on the silent paths. Lets UIs show a "check your wallet" prompt.
41
+ */
42
+ onSignaturePrompt?: () => void;
23
43
  /** Override for tests. */
24
44
  now?: () => number;
25
45
  /** Override for tests. */
26
46
  fetchImpl?: typeof fetch;
27
47
  }
48
+ /** Login failure carrying the HTTP status so callers can tell rejection from outage. */
49
+ export declare class ServerLoginError extends Error {
50
+ readonly status: number;
51
+ constructor(message: string, status: number);
52
+ }
28
53
  export declare class ServerSession {
29
54
  private readonly signer;
30
55
  private readonly serviceEndpoint;
31
56
  private readonly chainId;
57
+ private readonly store;
58
+ private readonly onSignaturePrompt?;
32
59
  private readonly now;
33
60
  private readonly fetchImpl;
34
61
  private cached;
35
62
  private inFlight;
63
+ /**
64
+ * Store key for the current signer, memoized on first use so the sync
65
+ * `clear()` can drop the persisted token without an async address lookup.
66
+ */
67
+ private lastStoreKey;
36
68
  constructor(opts: ServerSessionOptions);
37
69
  /** Returns a JWT good for at least `REFRESH_LEEWAY_SECONDS` more seconds. */
38
70
  getValidToken(): Promise<string>;
39
71
  /** Convenience for fetch: spread directly into `headers`. */
40
72
  getAuthHeader(): Promise<Record<string, string>>;
41
- /** Drop the cached token (e.g. on 401 from server). Next call re-logs in. */
73
+ /**
74
+ * Drop the cached token (e.g. on 401 from server). The persisted envelope is
75
+ * kept — the next call re-mints silently — but its stored token copy is
76
+ * dropped too so a dead JWT can't be re-adopted from the store.
77
+ */
42
78
  clear(): void;
43
79
  /**
44
- * Revoke the current session JWT on lit-ops-server (`POST /api/auth/logout`).
45
- * Clears the local cache regardless of server response so the client stops
46
- * presenting the token.
80
+ * Drop the session AND the persisted re-mint envelope (wallet disconnect /
81
+ * explicit sign-out paths anything that must remove the 24h credential
82
+ * from storage).
83
+ */
84
+ clearPersisted(): Promise<void>;
85
+ /**
86
+ * Revoke the current session JWT on lit-ops-server (`POST /api/auth/logout`)
87
+ * — which also tombstones the envelope's re-mint window server-side — and
88
+ * remove the persisted credential locally. Clears the local cache regardless
89
+ * of server response so the client stops presenting the token.
47
90
  */
48
91
  logout(): Promise<void>;
49
92
  private getOrRefresh;
93
+ private login;
94
+ /**
95
+ * Load the persisted session for the current signer, validating that it
96
+ * belongs to this address/chain/endpoint and that the envelope is still
97
+ * inside the re-auth window (with a safety margin). Invalid or expired
98
+ * entries are evicted.
99
+ */
100
+ private loadPersisted;
101
+ /** Best-effort save of the envelope + live JWT for silent recovery. */
102
+ private persist;
50
103
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gvnrdao/dh-sdk",
3
- "version": "0.0.301",
3
+ "version": "0.0.303",
4
4
  "description": "TypeScript SDK for Diamond Hands Protocol - Bitcoin-backed lending with LIT Protocol PKPs",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -82,8 +82,8 @@
82
82
  },
83
83
  "sideEffects": false,
84
84
  "dependencies": {
85
- "@gvnrdao/dh-lit-actions": "^0.0.313",
86
- "@gvnrdao/dh-lit-ops": "^0.0.303",
85
+ "@gvnrdao/dh-lit-actions": "^0.0.314",
86
+ "@gvnrdao/dh-lit-ops": "^0.0.304",
87
87
  "@noble/hashes": "^1.5.0",
88
88
  "axios": "^1.17.0",
89
89
  "bech32": "^2.0.0",