@gvnrdao/dh-sdk 0.0.300 → 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.
- package/browser/dist/browser.js +1 -1
- package/dist/constants/chunks/deployment-addresses.d.ts +2 -0
- package/dist/constants/chunks/network-configs.d.ts +2 -0
- package/dist/deployments.js +137 -30
- package/dist/deployments.mjs +137 -30
- package/dist/graphs/diamond-hands.d.ts +18 -0
- package/dist/index.js +1839 -929
- package/dist/index.mjs +1747 -837
- package/dist/interfaces/chunks/config.i.d.ts +22 -0
- package/dist/modules/diamond-hands-sdk.d.ts +41 -0
- package/dist/modules/loan/loan-query.module.d.ts +23 -0
- package/dist/types/authorization-params.d.ts +0 -2
- package/dist/utils/btc-withdrawal-message.d.ts +31 -0
- package/dist/utils/chunks/eip1559-broadcast.utils.d.ts +15 -24
- package/dist/utils/quantum-revert.utils.d.ts +9 -0
- package/dist/utils/quantum-submission.utils.d.ts +93 -0
- package/dist/utils/server-session-store.d.ts +56 -0
- package/dist/utils/server-session.d.ts +62 -9
- package/package.json +3 -3
|
@@ -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>;
|
|
@@ -4,27 +4,24 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { type Provider, type Signer, type TransactionResponse } from "ethers";
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* ~1.5-2x-headroom convention -- a smaller margin than those because this
|
|
21
|
-
* ceiling is a rarely-hit fallback/cap, not the everyday operating value, so
|
|
22
|
-
* a little extra buffer for cold-storage variance is cheap insurance.
|
|
7
|
+
* Fixed-gas ceiling for mintUCD's broadcast. Since the gold-standard refactor
|
|
8
|
+
* (2026-07-08) this IS the everyday submitted gasLimit: mintUCD no longer runs
|
|
9
|
+
* eth_estimateGas (its round-trip latency re-introduced the quantum-boundary
|
|
10
|
+
* race the submission gate exists to prevent), matching makePayment /
|
|
11
|
+
* withdrawBTC / extendPosition. Grounded in 16 real mint transactions
|
|
12
|
+
* (1 mainnet, 15 recent Sepolia, 2026-07-02): the estimate-derived gasLimit
|
|
13
|
+
* actually submitted ranged 388,620-886,988, and those limits all succeeded —
|
|
14
|
+
* so 886,988 is a KNOWN-SUFFICIENT limit, not a bare gasUsed figure.
|
|
15
|
+
* 1,300,000 gives ~1.47x headroom over it, matching MAKE_PAYMENT_GAS_CEILING's
|
|
16
|
+
* ~1.5x convention. Deliberately NOT higher: wallets (MetaMask) reject a tx
|
|
17
|
+
* when balance < gasLimit × maxFeePerGas, so an oversized fixed ceiling
|
|
18
|
+
* (previously 2M as a rarely-hit cap) would block low-ETH users from an
|
|
19
|
+
* affordable mint.
|
|
23
20
|
*/
|
|
24
21
|
export declare const MINT_UCD_GAS_CEILING: bigint;
|
|
25
22
|
/**
|
|
26
|
-
* Fixed-gas ceiling for
|
|
27
|
-
*
|
|
23
|
+
* Fixed-gas ceiling for withdrawBTC's broadcast (the everyday submitted
|
|
24
|
+
* gasLimit — no estimateGas runs on this path). The on-chain flow now spans PositionManager →
|
|
28
25
|
* CollateralManager → LoanOperationsManager → BTCSpendAuthorizer plus the audit
|
|
29
26
|
* #3 Chainlink feed-staleness gate and the BitcoinWithdrawalAddressRegistry
|
|
30
27
|
* allowlist STATICCALL; observed end-to-end cost is ~1.0–1.3M gas, so the prior
|
|
@@ -89,12 +86,6 @@ export declare const LIQUIDATION_COMMIT_GAS_CEILING: bigint;
|
|
|
89
86
|
* provisional until re-measured against a real liquidation.
|
|
90
87
|
*/
|
|
91
88
|
export declare const LIQUIDATION_REVEAL_GAS_CEILING: bigint;
|
|
92
|
-
/** Headroom on eth_estimateGas (25%). */
|
|
93
|
-
export declare const MINT_UCD_ESTIMATE_MARGIN_BPS = 2500;
|
|
94
|
-
/**
|
|
95
|
-
* Returns gas limit with margin, capped, or null if estimateGas reverts/fails.
|
|
96
|
-
*/
|
|
97
|
-
export declare function estimateContractCallGasWithMargin(provider: Provider, from: string, to: string, data: string, marginBps: number, ceiling: bigint): Promise<bigint | null>;
|
|
98
89
|
export declare function resolveEip1559FeeFields(provider: Provider): Promise<{
|
|
99
90
|
maxFeePerGas: bigint;
|
|
100
91
|
maxPriorityFeePerGas: bigint;
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
*/
|
|
11
11
|
/** `DeadZoneViolation()` selector — the stale-latest-block artifact we tolerate. */
|
|
12
12
|
export declare const DEAD_ZONE_VIOLATION_SELECTOR = "0xbe4b82c1";
|
|
13
|
+
/** Solidity `Error(string)` (require/revert with a message). */
|
|
14
|
+
export declare const ERROR_STRING_SELECTOR = "0x08c379a0";
|
|
15
|
+
/** Solidity `Panic(uint256)` (assert failures, overflow, div-by-zero, …). */
|
|
16
|
+
export declare const PANIC_SELECTOR = "0x4e487b71";
|
|
13
17
|
/**
|
|
14
18
|
* Union of the custom-error selectors the mint/payment/quantum paths decode. Keep in
|
|
15
19
|
* sync with OperationAuthorizationRegistry / PositionManager / LoanOperationsManager.
|
|
@@ -26,6 +30,11 @@ export interface DecodedRevert {
|
|
|
26
30
|
/**
|
|
27
31
|
* Extract the revert data / custom-error selector / known name from an ethers error,
|
|
28
32
|
* probing every provider nesting (JsonRpc, Browser/MetaMask, wrapped `error.error`).
|
|
33
|
+
*
|
|
34
|
+
* Besides the custom-error selector map, decodes the two standard Solidity revert
|
|
35
|
+
* shapes so require-message reverts stay actionable when call sites rely solely on
|
|
36
|
+
* this decoder: `Error(string)` yields `name = 'Error("<message>")'` and
|
|
37
|
+
* `Panic(uint256)` yields `name = "Panic(0x<code>)"`.
|
|
29
38
|
*/
|
|
30
39
|
export declare function decodeQuantumRevert(e: any): DecodedRevert;
|
|
31
40
|
/** True iff the error decodes to `DeadZoneViolation()` from any provider nesting. */
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared submission-time safety for quantum-signed broadcasts
|
|
3
|
+
* (makePayment / mintUCD / withdrawBTC / extendPosition).
|
|
4
|
+
*
|
|
5
|
+
* One helper owns the dead-zone gate AND the pre-send simulate/re-simulate loop
|
|
6
|
+
* that used to exist as three subtly different inline copies in
|
|
7
|
+
* diamond-hands-sdk.ts. Callers encode calldata, call
|
|
8
|
+
* `assertSafeQuantumSubmission`, then broadcast ONCE with a fixed gas ceiling
|
|
9
|
+
* (`sendEip1559Transaction`) — never `estimateGas`, whose latency re-introduces
|
|
10
|
+
* the boundary race the gate exists to prevent, and never a re-broadcast.
|
|
11
|
+
*
|
|
12
|
+
* See docs/sdk-quantum-goldstandard-refactor-plan-2026-07-08.md.
|
|
13
|
+
*/
|
|
14
|
+
/** Default cap on dead-zone-triggered re-simulations (mirrors old makePayment). */
|
|
15
|
+
export declare const MAX_DEADZONE_RESIMULATIONS = 3;
|
|
16
|
+
/**
|
|
17
|
+
* Thrown when the pre-send simulation proves the transaction would revert.
|
|
18
|
+
* Carries everything a caller needs to enrich the failure without re-probing
|
|
19
|
+
* the provider error itself:
|
|
20
|
+
*
|
|
21
|
+
* - `errorName` — decoded name, e.g. `"QuantumExpired()"` or `Error("msg")`
|
|
22
|
+
* (named to avoid shadowing `Error.name`).
|
|
23
|
+
* - `selector` — 4-byte selector, e.g. `"0x131d9a21"`.
|
|
24
|
+
* - `data` — FULL revert data (mintUCD decodes
|
|
25
|
+
* `DebtUpdateVerificationFailedDetailed` args from it).
|
|
26
|
+
* - `cause` — the original provider error (`reason` / `shortMessage`
|
|
27
|
+
* fallbacks, empty-data detection → `diagnoseMintUCD`).
|
|
28
|
+
*
|
|
29
|
+
* Only thrown for DECODABLE reverts (revert data present). Transport-level
|
|
30
|
+
* `eth_call` failures never raise this — see `assertSafeQuantumSubmission`.
|
|
31
|
+
*/
|
|
32
|
+
export declare class QuantumRevertError extends Error {
|
|
33
|
+
readonly errorName: string | null;
|
|
34
|
+
readonly selector: string | null;
|
|
35
|
+
readonly data: string | null;
|
|
36
|
+
readonly cause: unknown;
|
|
37
|
+
constructor(errorName: string | null, selector: string | null, data: string | null, cause: unknown);
|
|
38
|
+
}
|
|
39
|
+
export interface SafeQuantumSubmissionOpts {
|
|
40
|
+
/** Provider used for the `eth_call` simulation (only `.call` is needed). */
|
|
41
|
+
provider: {
|
|
42
|
+
call(tx: {
|
|
43
|
+
to: string;
|
|
44
|
+
from: string;
|
|
45
|
+
data: string;
|
|
46
|
+
}): Promise<string>;
|
|
47
|
+
};
|
|
48
|
+
/** Target contract address. */
|
|
49
|
+
to: string;
|
|
50
|
+
/** Sender address — `eth_call` must run as the real sender (auth checks). */
|
|
51
|
+
from: string;
|
|
52
|
+
/** Encoded calldata; broadcast MUST send these exact bytes. */
|
|
53
|
+
data: string;
|
|
54
|
+
/** The quantum timestamp embedded in the LIT-signed payload. */
|
|
55
|
+
quantumTimestamp: number;
|
|
56
|
+
/** Cap on dead-zone re-simulations (default {@link MAX_DEADZONE_RESIMULATIONS}). */
|
|
57
|
+
maxResimulations?: number;
|
|
58
|
+
/** Clock seam (whole seconds). Drives BOTH the gate and the artifact test. */
|
|
59
|
+
now?: () => number;
|
|
60
|
+
/** Sleep seam, forwarded to the gate. */
|
|
61
|
+
sleep?: (ms: number) => Promise<void>;
|
|
62
|
+
/** Debug sink; called with human-readable progress lines. */
|
|
63
|
+
onDebug?: (msg: string) => void;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Gate + pre-send simulation loop. Resolves when it is safe to broadcast;
|
|
67
|
+
* throws {@link QuantumRevertError} on a real (non-artifact) decodable revert.
|
|
68
|
+
* Never broadcasts anything itself.
|
|
69
|
+
*
|
|
70
|
+
* Sequence:
|
|
71
|
+
* 1. **Gate** (`awaitSafeSubmissionWindow`): a NEXT-quantum signature mined in
|
|
72
|
+
* the last DEAD_ZONE_SECONDS of the current quantum reverts
|
|
73
|
+
* `DeadZoneViolation()` on-chain, so defer the send across the boundary
|
|
74
|
+
* when inclusion could land there.
|
|
75
|
+
* 2. **Simulate** (`eth_call`) and route the outcome:
|
|
76
|
+
* - success → safe to broadcast.
|
|
77
|
+
* - `DeadZoneViolation()` → `eth_call` runs against the LATEST block's
|
|
78
|
+
* (slightly stale) timestamp while the on-chain rule uses MINE time. If
|
|
79
|
+
* the signature is already the CURRENT quantum in real time it can never
|
|
80
|
+
* be dead-zoned on-chain — stale-block artifact, proceed. Otherwise the
|
|
81
|
+
* signature is still NEXT near the boundary: re-gate across it and
|
|
82
|
+
* re-simulate (bounded by `maxResimulations`).
|
|
83
|
+
* - any other DECODABLE revert → throw `QuantumRevertError` (fail fast: no
|
|
84
|
+
* broadcast, no gas burned on a doomed tx).
|
|
85
|
+
* - UNDECODABLE failure (no revert data: RPC timeout, rate limit, provider
|
|
86
|
+
* quirk) → log and proceed. The fixed-ceiling broadcast degrades to the
|
|
87
|
+
* pre-refactor blind-broadcast behavior instead of letting a flaky RPC
|
|
88
|
+
* block a valid money-path operation.
|
|
89
|
+
* 3. **Final re-gate**: the sim loop adds RPC round-trips after step 1; this
|
|
90
|
+
* closes the drift with pure clock math (no RPC). If it waits, the
|
|
91
|
+
* signature is CURRENT afterward and needs no further simulation.
|
|
92
|
+
*/
|
|
93
|
+
export declare function assertSafeQuantumSubmission(opts: SafeQuantumSubmissionOpts): Promise<void>;
|
|
@@ -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
|
-
*
|
|
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
|
-
* `
|
|
6
|
-
*
|
|
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
|
|
16
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
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.
|
|
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.
|
|
86
|
-
"@gvnrdao/dh-lit-ops": "^0.0.
|
|
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",
|