@gvnrdao/dh-sdk 0.0.303 → 0.0.305
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/deployments.js +6 -2
- package/dist/deployments.mjs +6 -2
- package/dist/index.js +216 -58
- package/dist/index.mjs +216 -58
- package/dist/interfaces/chunks/loan-operations.i.d.ts +10 -0
- package/dist/modules/diamond-hands-sdk.d.ts +28 -4
- package/dist/utils/server-session.d.ts +31 -2
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -444,9 +444,13 @@ var init_deployment_addresses = __esm({
|
|
|
444
444
|
reason: "Validator gas fix (#420): BitcoinAddressValidator packed-table lookups; VERSION 1.0.0 -> 1.0.1"
|
|
445
445
|
},
|
|
446
446
|
UCDController: {
|
|
447
|
+
previousImplementation: "0x16f94AcC78cE87bEeBCfF90552091B3EfC071E48",
|
|
447
448
|
newImplementation: "0x28B8cAB89D89B3F83581FD2290de2Ce84535bd0A",
|
|
448
|
-
|
|
449
|
-
|
|
449
|
+
upgradedAt: "2026-07-06T10:41:59.000Z",
|
|
450
|
+
upgradedBy: "AdminModule/Safe audit-batch upgradeToAndCall; executed by 0xEba7BfAeB35ee18408b424Ee0BB45AdDf776589f in tx 0xf6609d3fa43b460d953c85d8d7265b59bf177723042db79a6a700571bad777b7 (block 25472962)",
|
|
451
|
+
reason: "Reconciled from chain \u2014 mainnet audit rollout batch (cross-ref onchain-auth/.safe-batches/mainnet-audit-execute.json)",
|
|
452
|
+
executeTx: "0xf6609d3fa43b460d953c85d8d7265b59bf177723042db79a6a700571bad777b7",
|
|
453
|
+
block: 25472962
|
|
450
454
|
},
|
|
451
455
|
PositionManagerCoreModule: {
|
|
452
456
|
previousImplementation: "0x6C8b0FB93F8d0239254aefaC24d4264cA2F8AAeB",
|
|
@@ -1293,7 +1297,7 @@ var require_pkg_src = __commonJS({
|
|
|
1293
1297
|
description: "Production Pkp Validator",
|
|
1294
1298
|
version: "1.0.0",
|
|
1295
1299
|
deployed: true,
|
|
1296
|
-
deployedAt:
|
|
1300
|
+
deployedAt: 1784569687492,
|
|
1297
1301
|
size: 4140,
|
|
1298
1302
|
hash: "4c0d65bf1f33cdac22a78fd8c4764319cc3fe00650e0fe7fa8a6090d9a318a7b",
|
|
1299
1303
|
validatorWalletAddress: "0x82db9f515957aa380b596c6ea2bd30cef181f236",
|
|
@@ -1370,17 +1374,17 @@ var require_pkg_src = __commonJS({
|
|
|
1370
1374
|
}
|
|
1371
1375
|
},
|
|
1372
1376
|
btcWithdrawal: {
|
|
1373
|
-
cid: "
|
|
1377
|
+
cid: "QmexvpfHPacjsVcqNHHLyJhSnnAPLgbWmWd9N3NDLDvYds",
|
|
1374
1378
|
authorizedCidHex: cidToHex(
|
|
1375
|
-
"
|
|
1379
|
+
"QmexvpfHPacjsVcqNHHLyJhSnnAPLgbWmWd9N3NDLDvYds"
|
|
1376
1380
|
),
|
|
1377
1381
|
name: "Btc Withdrawal",
|
|
1378
1382
|
description: "Production Btc Withdrawal",
|
|
1379
|
-
version: "1.2.
|
|
1383
|
+
version: "1.2.3",
|
|
1380
1384
|
deployed: true,
|
|
1381
|
-
deployedAt:
|
|
1382
|
-
size:
|
|
1383
|
-
hash: "
|
|
1385
|
+
deployedAt: 1784585746901,
|
|
1386
|
+
size: 92027,
|
|
1387
|
+
hash: "7b4f4eab1f0c08b8b5b1c9cdb1064dd403ec13f57765919f62dec67fb8ba3c69",
|
|
1384
1388
|
validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
|
|
1385
1389
|
pkp: {
|
|
1386
1390
|
publicKey: "0x043616787c5432415c24378c4ef48de2bcd6bb7b575b837e3cff09171802662da7105d79586c7659677a0ecbaddac4cce06cb2a11f69a16fa0c4d7002ac7d51a4d",
|
|
@@ -3752,12 +3756,23 @@ ${auth.clientId}`;
|
|
|
3752
3756
|
if (!data.vout || !Array.isArray(data.vout)) {
|
|
3753
3757
|
return null;
|
|
3754
3758
|
}
|
|
3755
|
-
return data.vout.map((output, index) =>
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3759
|
+
return data.vout.map((output, index) => {
|
|
3760
|
+
if (output.value === void 0) {
|
|
3761
|
+
throw new Error(
|
|
3762
|
+
`getTransactionOutputs(${txid}): output ${index} is missing its value \u2014 malformed provider response, refusing to return outputs that cannot be value-verified`
|
|
3763
|
+
);
|
|
3764
|
+
}
|
|
3765
|
+
assertSaneSatoshis(
|
|
3766
|
+
output.value,
|
|
3767
|
+
`getTransactionOutputs(${txid}) output ${index}`
|
|
3768
|
+
);
|
|
3769
|
+
return {
|
|
3770
|
+
index,
|
|
3771
|
+
scriptpubkey: output.scriptpubkey,
|
|
3772
|
+
scriptpubkey_address: output.scriptpubkey_address,
|
|
3773
|
+
value: output.value
|
|
3774
|
+
};
|
|
3775
|
+
});
|
|
3761
3776
|
} catch (error) {
|
|
3762
3777
|
clearTimeout(timeoutId);
|
|
3763
3778
|
if (isAbortError(error)) {
|
|
@@ -6526,6 +6541,14 @@ var ServerSession = class {
|
|
|
6526
6541
|
fetchImpl;
|
|
6527
6542
|
cached = null;
|
|
6528
6543
|
inFlight = null;
|
|
6544
|
+
/**
|
|
6545
|
+
* Bumped by `clearPersisted()` (logout / disconnect). An in-flight
|
|
6546
|
+
* `getOrRefresh()` captures this at start and refuses to write its freshly
|
|
6547
|
+
* minted token back to `this.cached`/the store if the value changed
|
|
6548
|
+
* meanwhile — so a refresh racing a sign-out can't silently "un-clear" the
|
|
6549
|
+
* credential it was told to drop.
|
|
6550
|
+
*/
|
|
6551
|
+
generation = 0;
|
|
6529
6552
|
/**
|
|
6530
6553
|
* Store key for the current signer, memoized on first use so the sync
|
|
6531
6554
|
* `clear()` can drop the persisted token without an async address lookup.
|
|
@@ -6541,8 +6564,22 @@ var ServerSession = class {
|
|
|
6541
6564
|
this.now = opts.now ?? (() => Math.floor(Date.now() / 1e3));
|
|
6542
6565
|
this.fetchImpl = opts.fetchImpl ?? ((...a) => fetch(...a));
|
|
6543
6566
|
}
|
|
6544
|
-
/**
|
|
6545
|
-
|
|
6567
|
+
/**
|
|
6568
|
+
* Returns a JWT good for at least `REFRESH_LEEWAY_SECONDS` more seconds.
|
|
6569
|
+
* Pass `{ silent: true }` to forbid a fresh wallet signature: it resolves ONLY
|
|
6570
|
+
* from a live cached token or the persisted re-mint envelope, never prompting
|
|
6571
|
+
* and never joining a prompt-capable in-flight refresh (used by `logout()`,
|
|
6572
|
+
* which must never pop the wallet during sign-out). Rejects when only a fresh
|
|
6573
|
+
* signature could produce a token.
|
|
6574
|
+
*/
|
|
6575
|
+
async getValidToken(opts) {
|
|
6576
|
+
if (opts?.silent) {
|
|
6577
|
+
const session2 = await this.tryResolveSilently(this.generation);
|
|
6578
|
+
if (!session2) {
|
|
6579
|
+
throw new Error("ServerSession: no existing session to refresh");
|
|
6580
|
+
}
|
|
6581
|
+
return session2.token;
|
|
6582
|
+
}
|
|
6546
6583
|
const session = await this.getOrRefresh();
|
|
6547
6584
|
return session.token;
|
|
6548
6585
|
}
|
|
@@ -6575,20 +6612,24 @@ var ServerSession = class {
|
|
|
6575
6612
|
* from storage).
|
|
6576
6613
|
*/
|
|
6577
6614
|
async clearPersisted() {
|
|
6615
|
+
this.generation += 1;
|
|
6578
6616
|
this.cached = null;
|
|
6579
6617
|
this.inFlight = null;
|
|
6580
6618
|
if (!this.store)
|
|
6581
6619
|
return;
|
|
6582
|
-
if (this.lastStoreKey) {
|
|
6583
|
-
this.store.clear(this.lastStoreKey);
|
|
6584
|
-
return;
|
|
6585
|
-
}
|
|
6586
6620
|
try {
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6621
|
+
if (this.lastStoreKey) {
|
|
6622
|
+
this.store.clear(this.lastStoreKey);
|
|
6623
|
+
} else {
|
|
6624
|
+
const address = (await this.signer.getAddress()).toLowerCase();
|
|
6625
|
+
this.store.clear(
|
|
6626
|
+
sessionStoreKey(address, this.chainId, this.serviceEndpoint)
|
|
6627
|
+
);
|
|
6628
|
+
}
|
|
6591
6629
|
} catch {
|
|
6630
|
+
} finally {
|
|
6631
|
+
this.generation += 1;
|
|
6632
|
+
this.cached = null;
|
|
6592
6633
|
}
|
|
6593
6634
|
}
|
|
6594
6635
|
/**
|
|
@@ -6598,7 +6639,8 @@ var ServerSession = class {
|
|
|
6598
6639
|
* of server response so the client stops presenting the token.
|
|
6599
6640
|
*/
|
|
6600
6641
|
async logout() {
|
|
6601
|
-
const
|
|
6642
|
+
const cached = this.cached;
|
|
6643
|
+
const token = cached && cached.expiresAt > this.now() ? cached.token : await this.getValidToken({ silent: true }).catch(() => null);
|
|
6602
6644
|
await this.clearPersisted();
|
|
6603
6645
|
if (!token)
|
|
6604
6646
|
return;
|
|
@@ -6613,6 +6655,50 @@ var ServerSession = class {
|
|
|
6613
6655
|
);
|
|
6614
6656
|
}
|
|
6615
6657
|
}
|
|
6658
|
+
/**
|
|
6659
|
+
* Resolve a session WITHOUT ever prompting for a wallet signature: a live
|
|
6660
|
+
* cached token, else the persisted JWT (adopted if still fresh, otherwise
|
|
6661
|
+
* silently re-minted from the envelope). Returns `null` only when a fresh
|
|
6662
|
+
* wallet signature would be required (dead/absent envelope). Never touches
|
|
6663
|
+
* `this.inFlight`, so a silent caller (logout) can resolve independently
|
|
6664
|
+
* instead of being dragged into a prompt-capable refresh already in flight.
|
|
6665
|
+
*
|
|
6666
|
+
* `gen` is the caller's clear-generation snapshot: writes to `this.cached`
|
|
6667
|
+
* and the store are skipped if a `clearPersisted()` landed meanwhile.
|
|
6668
|
+
*/
|
|
6669
|
+
async tryResolveSilently(gen) {
|
|
6670
|
+
const nowSec = this.now();
|
|
6671
|
+
if (this.cached && this.cached.expiresAt - REFRESH_LEEWAY_SECONDS > nowSec) {
|
|
6672
|
+
return this.cached;
|
|
6673
|
+
}
|
|
6674
|
+
const persisted = await this.loadPersisted();
|
|
6675
|
+
if (!persisted)
|
|
6676
|
+
return null;
|
|
6677
|
+
if (persisted.token && typeof persisted.tokenExpiresAt === "number" && persisted.tokenExpiresAt - REFRESH_LEEWAY_SECONDS > this.now()) {
|
|
6678
|
+
const adopted = {
|
|
6679
|
+
token: persisted.token,
|
|
6680
|
+
expiresAt: persisted.tokenExpiresAt
|
|
6681
|
+
};
|
|
6682
|
+
if (gen === this.generation)
|
|
6683
|
+
this.cached = adopted;
|
|
6684
|
+
return adopted;
|
|
6685
|
+
}
|
|
6686
|
+
try {
|
|
6687
|
+
const session = await this.login(persisted.payload);
|
|
6688
|
+
if (gen === this.generation) {
|
|
6689
|
+
this.persist(persisted.payload, session);
|
|
6690
|
+
this.cached = session;
|
|
6691
|
+
}
|
|
6692
|
+
return session;
|
|
6693
|
+
} catch (error) {
|
|
6694
|
+
if (!isLoginRejection(error))
|
|
6695
|
+
throw error;
|
|
6696
|
+
if (gen === this.generation && this.store && this.lastStoreKey) {
|
|
6697
|
+
this.store.clear(this.lastStoreKey);
|
|
6698
|
+
}
|
|
6699
|
+
return null;
|
|
6700
|
+
}
|
|
6701
|
+
}
|
|
6616
6702
|
async getOrRefresh() {
|
|
6617
6703
|
const nowSec = this.now();
|
|
6618
6704
|
if (this.cached && this.cached.expiresAt - REFRESH_LEEWAY_SECONDS > nowSec) {
|
|
@@ -6620,30 +6706,13 @@ var ServerSession = class {
|
|
|
6620
6706
|
}
|
|
6621
6707
|
if (this.inFlight)
|
|
6622
6708
|
return this.inFlight;
|
|
6709
|
+
const gen = this.generation;
|
|
6710
|
+
const notCleared = () => gen === this.generation;
|
|
6623
6711
|
this.inFlight = (async () => {
|
|
6624
6712
|
try {
|
|
6625
|
-
const
|
|
6626
|
-
if (
|
|
6627
|
-
|
|
6628
|
-
this.cached = {
|
|
6629
|
-
token: persisted.token,
|
|
6630
|
-
expiresAt: persisted.tokenExpiresAt
|
|
6631
|
-
};
|
|
6632
|
-
return this.cached;
|
|
6633
|
-
}
|
|
6634
|
-
try {
|
|
6635
|
-
const session2 = await this.login(persisted.payload);
|
|
6636
|
-
this.persist(persisted.payload, session2);
|
|
6637
|
-
this.cached = session2;
|
|
6638
|
-
return session2;
|
|
6639
|
-
} catch (error) {
|
|
6640
|
-
if (!isLoginRejection(error))
|
|
6641
|
-
throw error;
|
|
6642
|
-
if (this.store && this.lastStoreKey) {
|
|
6643
|
-
this.store.clear(this.lastStoreKey);
|
|
6644
|
-
}
|
|
6645
|
-
}
|
|
6646
|
-
}
|
|
6713
|
+
const existing = await this.tryResolveSilently(gen);
|
|
6714
|
+
if (existing)
|
|
6715
|
+
return existing;
|
|
6647
6716
|
this.onSignaturePrompt?.();
|
|
6648
6717
|
const payload = await buildSignedLoginPayload(
|
|
6649
6718
|
this.signer,
|
|
@@ -6651,11 +6720,14 @@ var ServerSession = class {
|
|
|
6651
6720
|
this.serviceEndpoint
|
|
6652
6721
|
);
|
|
6653
6722
|
const session = await this.login(payload);
|
|
6654
|
-
|
|
6655
|
-
|
|
6723
|
+
if (notCleared()) {
|
|
6724
|
+
this.persist(payload, session);
|
|
6725
|
+
this.cached = session;
|
|
6726
|
+
}
|
|
6656
6727
|
return session;
|
|
6657
6728
|
} finally {
|
|
6658
|
-
|
|
6729
|
+
if (notCleared())
|
|
6730
|
+
this.inFlight = null;
|
|
6659
6731
|
}
|
|
6660
6732
|
})();
|
|
6661
6733
|
return this.inFlight;
|
|
@@ -16032,13 +16104,25 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
16032
16104
|
return this.serverSession.getAuthHeader();
|
|
16033
16105
|
}
|
|
16034
16106
|
/**
|
|
16035
|
-
*
|
|
16036
|
-
*
|
|
16037
|
-
*
|
|
16038
|
-
*
|
|
16107
|
+
* Sign out of the lit-ops-server session for the current auth signer. Call on
|
|
16108
|
+
* wallet disconnect or explicit sign-out so the credential does not outlive
|
|
16109
|
+
* the wallet connection. No-op in standalone mode.
|
|
16110
|
+
*
|
|
16111
|
+
* Attempts a server-side revocation first (`ServerSession.logout()` — which
|
|
16112
|
+
* tombstones the envelope's 24h re-mint window and invalidates the JWT), then
|
|
16113
|
+
* always removes the local credential. If the revocation call fails (offline,
|
|
16114
|
+
* server down, wallet already disconnected) we still clear locally so the
|
|
16115
|
+
* session cannot silently re-mint after sign-out; `logout()` is best-effort
|
|
16116
|
+
* and never prompts the wallet.
|
|
16039
16117
|
*/
|
|
16040
16118
|
async clearServerSession() {
|
|
16041
|
-
|
|
16119
|
+
if (!this.serverSession)
|
|
16120
|
+
return;
|
|
16121
|
+
try {
|
|
16122
|
+
await this.serverSession.logout();
|
|
16123
|
+
} catch {
|
|
16124
|
+
await this.serverSession.clearPersisted();
|
|
16125
|
+
}
|
|
16042
16126
|
}
|
|
16043
16127
|
/**
|
|
16044
16128
|
* Audit H-9: invalidate the LoanQuery cache so subsequent reads return
|
|
@@ -20613,7 +20697,9 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
20613
20697
|
gasUsed: receipt.gasUsed?.toString() || "unknown",
|
|
20614
20698
|
utxoTxid: validationResponse.utxoTxid,
|
|
20615
20699
|
utxoVout: validationResponse.utxoVout,
|
|
20616
|
-
utxoSatoshis: validationResponse.utxoSatoshis
|
|
20700
|
+
utxoSatoshis: validationResponse.utxoSatoshis,
|
|
20701
|
+
// Phase B: full consensus-validated spend set (absent on older actions).
|
|
20702
|
+
confirmedUtxos: validationResponse.confirmedUtxos
|
|
20617
20703
|
};
|
|
20618
20704
|
} catch (error) {
|
|
20619
20705
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
@@ -20643,6 +20729,42 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
20643
20729
|
};
|
|
20644
20730
|
}
|
|
20645
20731
|
}
|
|
20732
|
+
/**
|
|
20733
|
+
* TEMPORARY (multi-UTXO withdrawal fix, Phase A): fetch the vault's confirmed
|
|
20734
|
+
* UTXO set from lit-ops-server (`GET /api/lit/vault-utxos`) so the caller can
|
|
20735
|
+
* assemble and sign the FULL input set for a multi-UTXO consolidation
|
|
20736
|
+
* withdrawal. Phase 1 only returns ONE representative UTXO, so a vault whose
|
|
20737
|
+
* balance is spread across several UTXOs otherwise fails the Phase-2 signer
|
|
20738
|
+
* with "Insufficient UTXO value ... across 1 input(s)".
|
|
20739
|
+
*
|
|
20740
|
+
* Returns `[]` on any failure (non-service mode, network error, bad shape) so
|
|
20741
|
+
* callers transparently fall back to the single authorized UTXO. Superseded
|
|
20742
|
+
* by the btc-withdrawal Lit Action returning its own confirmedUTXOs (Phase B).
|
|
20743
|
+
*/
|
|
20744
|
+
async fetchConfirmedVaultUtxos(positionId) {
|
|
20745
|
+
if (this.config.mode !== "service" || !this.config.serviceEndpoint)
|
|
20746
|
+
return [];
|
|
20747
|
+
try {
|
|
20748
|
+
const pid = positionId.startsWith("0x") ? positionId : `0x${positionId}`;
|
|
20749
|
+
const resp = await fetch(
|
|
20750
|
+
`${this.config.serviceEndpoint}/api/lit/vault-utxos?positionId=${encodeURIComponent(pid)}`,
|
|
20751
|
+
{ method: "GET", headers: { ...await this.getAuthHeader() } }
|
|
20752
|
+
);
|
|
20753
|
+
if (!resp.ok)
|
|
20754
|
+
return [];
|
|
20755
|
+
const json = await resp.json();
|
|
20756
|
+
if (!json.success || !Array.isArray(json.data?.utxos))
|
|
20757
|
+
return [];
|
|
20758
|
+
return json.data.utxos.map((u) => ({
|
|
20759
|
+
txid: String(u.txid),
|
|
20760
|
+
vout: Number(u.vout),
|
|
20761
|
+
value: Number(u.value),
|
|
20762
|
+
confirmations: Number(u.confirmations ?? 0)
|
|
20763
|
+
}));
|
|
20764
|
+
} catch {
|
|
20765
|
+
return [];
|
|
20766
|
+
}
|
|
20767
|
+
}
|
|
20646
20768
|
/**
|
|
20647
20769
|
* Execute Bitcoin withdrawal (Phase 2)
|
|
20648
20770
|
*
|
|
@@ -21259,13 +21381,49 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
21259
21381
|
if (this.config.debug) {
|
|
21260
21382
|
log.info("\u{1F50D} Found vault UTXO for execution", { utxoIdentifier });
|
|
21261
21383
|
}
|
|
21384
|
+
let executionUtxos;
|
|
21385
|
+
const gatheredUtxos = authResult.confirmedUtxos && authResult.confirmedUtxos.length > 0 ? authResult.confirmedUtxos.map((u) => ({
|
|
21386
|
+
txid: u.txid,
|
|
21387
|
+
vout: Number(u.vout),
|
|
21388
|
+
value: Number(u.value),
|
|
21389
|
+
confirmations: 0
|
|
21390
|
+
})) : await this.fetchConfirmedVaultUtxos(positionId);
|
|
21391
|
+
if (gatheredUtxos.length > 0) {
|
|
21392
|
+
const normTxid = (t) => String(t).toLowerCase().replace(/^0x/, "");
|
|
21393
|
+
const byOutpoint = /* @__PURE__ */ new Map();
|
|
21394
|
+
for (const u of gatheredUtxos) {
|
|
21395
|
+
const txid = normTxid(u.txid);
|
|
21396
|
+
byOutpoint.set(`${txid}:${u.vout}`, {
|
|
21397
|
+
txid,
|
|
21398
|
+
vout: Number(u.vout),
|
|
21399
|
+
value: Number(u.value)
|
|
21400
|
+
});
|
|
21401
|
+
}
|
|
21402
|
+
const repKey = `${normTxid(authResult.utxoTxid)}:${authResult.utxoVout}`;
|
|
21403
|
+
if (!byOutpoint.has(repKey)) {
|
|
21404
|
+
byOutpoint.set(repKey, {
|
|
21405
|
+
txid: normTxid(authResult.utxoTxid),
|
|
21406
|
+
vout: Number(authResult.utxoVout),
|
|
21407
|
+
value: Number(authResult.utxoSatoshis)
|
|
21408
|
+
});
|
|
21409
|
+
}
|
|
21410
|
+
if (byOutpoint.size > 1) {
|
|
21411
|
+
executionUtxos = Array.from(byOutpoint.values());
|
|
21412
|
+
if (this.config.debug) {
|
|
21413
|
+
log.info("\u{1F9E9} Multi-UTXO withdrawal: signing full input set", {
|
|
21414
|
+
inputs: executionUtxos.length
|
|
21415
|
+
});
|
|
21416
|
+
}
|
|
21417
|
+
}
|
|
21418
|
+
}
|
|
21262
21419
|
const executionResult = await this.executeBTCWithdrawal({
|
|
21263
21420
|
positionId,
|
|
21264
21421
|
utxoIdentifier,
|
|
21265
21422
|
utxoSatoshis: Number(authResult.utxoSatoshis),
|
|
21266
21423
|
networkFee,
|
|
21267
21424
|
destination: withdrawalAddress,
|
|
21268
|
-
targetAmount: withdrawalAmount
|
|
21425
|
+
targetAmount: withdrawalAmount,
|
|
21426
|
+
...executionUtxos ? { utxos: executionUtxos } : {}
|
|
21269
21427
|
});
|
|
21270
21428
|
if (this.config.debug) {
|
|
21271
21429
|
log.info("\u2705 Phase 2 complete: Bitcoin transaction executed", {
|
|
@@ -233,6 +233,16 @@ export interface BTCWithdrawalResult {
|
|
|
233
233
|
utxoVout?: number;
|
|
234
234
|
/** Full satoshi value of the authorized UTXO; needed by Phase 2 broadcast. */
|
|
235
235
|
utxoSatoshis?: string;
|
|
236
|
+
/**
|
|
237
|
+
* Phase B: the FULL consensus-validated confirmed UTXO set the Lit Action
|
|
238
|
+
* spends, so a multi-UTXO consolidation withdrawal can sign the whole set
|
|
239
|
+
* without the temporary /vault-utxos endpoint. Absent on older Lit Actions.
|
|
240
|
+
*/
|
|
241
|
+
confirmedUtxos?: Array<{
|
|
242
|
+
txid: string;
|
|
243
|
+
vout: number;
|
|
244
|
+
value: string;
|
|
245
|
+
}>;
|
|
236
246
|
error?: string;
|
|
237
247
|
}
|
|
238
248
|
/**
|
|
@@ -112,10 +112,16 @@ export declare class DiamondHandsSDK {
|
|
|
112
112
|
*/
|
|
113
113
|
private getAuthHeader;
|
|
114
114
|
/**
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
115
|
+
* Sign out of the lit-ops-server session for the current auth signer. Call on
|
|
116
|
+
* wallet disconnect or explicit sign-out so the credential does not outlive
|
|
117
|
+
* the wallet connection. No-op in standalone mode.
|
|
118
|
+
*
|
|
119
|
+
* Attempts a server-side revocation first (`ServerSession.logout()` — which
|
|
120
|
+
* tombstones the envelope's 24h re-mint window and invalidates the JWT), then
|
|
121
|
+
* always removes the local credential. If the revocation call fails (offline,
|
|
122
|
+
* server down, wallet already disconnected) we still clear locally so the
|
|
123
|
+
* session cannot silently re-mint after sign-out; `logout()` is best-effort
|
|
124
|
+
* and never prompts the wallet.
|
|
119
125
|
*/
|
|
120
126
|
clearServerSession(): Promise<void>;
|
|
121
127
|
/**
|
|
@@ -442,6 +448,24 @@ export declare class DiamondHandsSDK {
|
|
|
442
448
|
* @returns Withdrawal result with transaction details
|
|
443
449
|
*/
|
|
444
450
|
withdrawBTC(positionId: string, withdrawalAddress: string, withdrawalAmount: number): Promise<BTCWithdrawalResult>;
|
|
451
|
+
/**
|
|
452
|
+
* TEMPORARY (multi-UTXO withdrawal fix, Phase A): fetch the vault's confirmed
|
|
453
|
+
* UTXO set from lit-ops-server (`GET /api/lit/vault-utxos`) so the caller can
|
|
454
|
+
* assemble and sign the FULL input set for a multi-UTXO consolidation
|
|
455
|
+
* withdrawal. Phase 1 only returns ONE representative UTXO, so a vault whose
|
|
456
|
+
* balance is spread across several UTXOs otherwise fails the Phase-2 signer
|
|
457
|
+
* with "Insufficient UTXO value ... across 1 input(s)".
|
|
458
|
+
*
|
|
459
|
+
* Returns `[]` on any failure (non-service mode, network error, bad shape) so
|
|
460
|
+
* callers transparently fall back to the single authorized UTXO. Superseded
|
|
461
|
+
* by the btc-withdrawal Lit Action returning its own confirmedUTXOs (Phase B).
|
|
462
|
+
*/
|
|
463
|
+
fetchConfirmedVaultUtxos(positionId: string): Promise<Array<{
|
|
464
|
+
txid: string;
|
|
465
|
+
vout: number;
|
|
466
|
+
value: number;
|
|
467
|
+
confirmations: number;
|
|
468
|
+
}>>;
|
|
445
469
|
/**
|
|
446
470
|
* Execute Bitcoin withdrawal (Phase 2)
|
|
447
471
|
*
|
|
@@ -60,14 +60,31 @@ export declare class ServerSession {
|
|
|
60
60
|
private readonly fetchImpl;
|
|
61
61
|
private cached;
|
|
62
62
|
private inFlight;
|
|
63
|
+
/**
|
|
64
|
+
* Bumped by `clearPersisted()` (logout / disconnect). An in-flight
|
|
65
|
+
* `getOrRefresh()` captures this at start and refuses to write its freshly
|
|
66
|
+
* minted token back to `this.cached`/the store if the value changed
|
|
67
|
+
* meanwhile — so a refresh racing a sign-out can't silently "un-clear" the
|
|
68
|
+
* credential it was told to drop.
|
|
69
|
+
*/
|
|
70
|
+
private generation;
|
|
63
71
|
/**
|
|
64
72
|
* Store key for the current signer, memoized on first use so the sync
|
|
65
73
|
* `clear()` can drop the persisted token without an async address lookup.
|
|
66
74
|
*/
|
|
67
75
|
private lastStoreKey;
|
|
68
76
|
constructor(opts: ServerSessionOptions);
|
|
69
|
-
/**
|
|
70
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Returns a JWT good for at least `REFRESH_LEEWAY_SECONDS` more seconds.
|
|
79
|
+
* Pass `{ silent: true }` to forbid a fresh wallet signature: it resolves ONLY
|
|
80
|
+
* from a live cached token or the persisted re-mint envelope, never prompting
|
|
81
|
+
* and never joining a prompt-capable in-flight refresh (used by `logout()`,
|
|
82
|
+
* which must never pop the wallet during sign-out). Rejects when only a fresh
|
|
83
|
+
* signature could produce a token.
|
|
84
|
+
*/
|
|
85
|
+
getValidToken(opts?: {
|
|
86
|
+
silent?: boolean;
|
|
87
|
+
}): Promise<string>;
|
|
71
88
|
/** Convenience for fetch: spread directly into `headers`. */
|
|
72
89
|
getAuthHeader(): Promise<Record<string, string>>;
|
|
73
90
|
/**
|
|
@@ -89,6 +106,18 @@ export declare class ServerSession {
|
|
|
89
106
|
* of server response so the client stops presenting the token.
|
|
90
107
|
*/
|
|
91
108
|
logout(): Promise<void>;
|
|
109
|
+
/**
|
|
110
|
+
* Resolve a session WITHOUT ever prompting for a wallet signature: a live
|
|
111
|
+
* cached token, else the persisted JWT (adopted if still fresh, otherwise
|
|
112
|
+
* silently re-minted from the envelope). Returns `null` only when a fresh
|
|
113
|
+
* wallet signature would be required (dead/absent envelope). Never touches
|
|
114
|
+
* `this.inFlight`, so a silent caller (logout) can resolve independently
|
|
115
|
+
* instead of being dragged into a prompt-capable refresh already in flight.
|
|
116
|
+
*
|
|
117
|
+
* `gen` is the caller's clear-generation snapshot: writes to `this.cached`
|
|
118
|
+
* and the store are skipped if a `clearPersisted()` landed meanwhile.
|
|
119
|
+
*/
|
|
120
|
+
private tryResolveSilently;
|
|
92
121
|
private getOrRefresh;
|
|
93
122
|
private login;
|
|
94
123
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gvnrdao/dh-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.305",
|
|
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.315",
|
|
86
|
+
"@gvnrdao/dh-lit-ops": "^0.0.305",
|
|
87
87
|
"@noble/hashes": "^1.5.0",
|
|
88
88
|
"axios": "^1.17.0",
|
|
89
89
|
"bech32": "^2.0.0",
|