@gvnrdao/dh-sdk 0.0.337 → 0.0.339
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 +8 -8
- package/dist/deployments.mjs +8 -8
- package/dist/graphs/diamond-hands.d.ts +14 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +259 -44
- package/dist/index.mjs +260 -45
- package/dist/interfaces/chunks/config.i.d.ts +19 -0
- package/dist/interfaces/chunks/loan-operations.i.d.ts +34 -0
- package/dist/modules/diamond-hands-sdk.d.ts +10 -0
- package/dist/modules/loan/loan-query.module.d.ts +12 -1
- package/dist/safe-delegation.js +8 -8
- package/dist/safe-delegation.mjs +8 -8
- package/dist/sign-guard.d.ts +26 -0
- package/dist/sign-guard.js +1008 -0
- package/dist/sign-guard.mjs +941 -0
- package/dist/utils/borrower-authorization-712.d.ts +1 -1
- package/dist/utils/borrower-ucd-debt-summary.d.ts +44 -0
- package/dist/utils/eip712-login.d.ts +34 -1
- package/dist/utils/server-session-store.d.ts +15 -0
- package/dist/utils/server-session.d.ts +13 -0
- package/dist/utils/sign-guard/errors.d.ts +9 -0
- package/dist/utils/sign-guard/extend-fee-bound.d.ts +15 -0
- package/dist/utils/sign-guard/fee-ceiling.d.ts +40 -0
- package/dist/utils/sign-guard/index.d.ts +9 -0
- package/dist/utils/sign-guard/lit-ops-client.d.ts +107 -0
- package/dist/utils/sign-guard/op-envelope.d.ts +88 -0
- package/dist/utils/sign-guard/op-specs.d.ts +51 -0
- package/dist/utils/sign-guard/sign-and-broadcast.d.ts +56 -0
- package/dist/utils/sign-guard/signable-functions.d.ts +41 -0
- package/dist/utils/sign-guard/tx-validator.d.ts +148 -0
- package/package.json +26 -4
package/dist/index.mjs
CHANGED
|
@@ -490,13 +490,13 @@ var init_deployment_addresses = __esm({
|
|
|
490
490
|
block: 25472956
|
|
491
491
|
},
|
|
492
492
|
BTCSpendAuthorizer: {
|
|
493
|
-
previousImplementation: "
|
|
494
|
-
newImplementation: "
|
|
495
|
-
upgradedAt: "2026-
|
|
496
|
-
upgradedBy: "AdminModule
|
|
497
|
-
reason: "
|
|
498
|
-
executeTx: "
|
|
499
|
-
block:
|
|
493
|
+
previousImplementation: "0x40c9e8C46C65E0A57BB63da5368868A9E8Df81A5",
|
|
494
|
+
newImplementation: "0x00D7a47798E8ff169104b748131703B78f23390C",
|
|
495
|
+
upgradedAt: "2026-09-09T12:05:35.000Z",
|
|
496
|
+
upgradedBy: "AdminModule generic op 0xc0214ff38bb454864a34f6482dcd03570b3fd04171c84be2c20184afb1a2e161 (bsa-1-7-0 ceremony: proposed by seat 0x3030C0414B6d95aA9d5A57269E51a347fCBaB472 2026-09-08T11:35:23Z, 3/3 seat signatures, 24h timelock); executeGenericAdminOperation sent by 0xF20986F02420EF443AE9BE5092FB8A4975cF3aA6 in tx 0x77cd231966c546177bd8f1a07bd4e705afd5c2dfbd110b096836401679c0c69c (block 25939703)",
|
|
497
|
+
reason: "BTCSpendAuthorizer 1.6.0 -> 1.7.0: allow a full-vault sweep (targetAmount == satoshis); TargetMustLeaveFeeRoom -> TargetExceedsInputValue (PR #849; mainnet incidents 2026-09-04 and 2026-09-07). Impl deployed by 0xEba7BfAeB35ee18408b424Ee0BB45AdDf776589f at nonce 430 from source 0a7c535a1 (contracts/src identical to main 99b335f77). Cross-ref onchain-auth/.safe-batches/mainnet-bsa-1-7-0-{propose,sign,execute}.json and docs/mainnet-bsa-1-7-0-ceremony-2026-09-08.md",
|
|
498
|
+
executeTx: "0x77cd231966c546177bd8f1a07bd4e705afd5c2dfbd110b096836401679c0c69c",
|
|
499
|
+
block: 25939703
|
|
500
500
|
},
|
|
501
501
|
PositionManager: {
|
|
502
502
|
previousImplementation: "0x600Dc0D1C2c0F94882B6Bc09172f8650047Ad5B1",
|
|
@@ -599,7 +599,7 @@ var init_deployment_addresses = __esm({
|
|
|
599
599
|
ADMIN_MODULE: "0x0023B71B586795eE5fa4E83E384345C1EC8Bb946",
|
|
600
600
|
ADMIN_MODULE_IMPL: "0xb4C5C7E051A1e672F014B25CB5D55B225Aa0D512",
|
|
601
601
|
BTC_SPEND_AUTHORIZER: "0xfBbF108486fa07F1B06a811C5b66ed68DD9c508a",
|
|
602
|
-
BTC_SPEND_AUTHORIZER_IMPL: "
|
|
602
|
+
BTC_SPEND_AUTHORIZER_IMPL: "0x00D7a47798E8ff169104b748131703B78f23390C",
|
|
603
603
|
POSITION_MANAGER_VIEWS: "0x8B57954eA338c6A6bfF1457f71080e25baFF3937",
|
|
604
604
|
POSITION_MANAGER_VIEWS_IMPL: "0xE68DC096A65Ecddbc6A9b5c6e5f878e2CE143Bfe",
|
|
605
605
|
POSITION_MANAGER: "0x12A1836e56139ccaD8722B89519d84233efc224B",
|
|
@@ -6798,9 +6798,27 @@ var LOGIN_TYPES_WITH_AUDIENCE = {
|
|
|
6798
6798
|
{ name: "audience", type: "string" }
|
|
6799
6799
|
]
|
|
6800
6800
|
};
|
|
6801
|
-
|
|
6801
|
+
var LOGIN_TYPES_WITH_STATEMENT = {
|
|
6802
|
+
DiamondHands: [
|
|
6803
|
+
{ name: "statement", type: "string" },
|
|
6804
|
+
{ name: "address", type: "address" },
|
|
6805
|
+
{ name: "issuedAt", type: "uint256" },
|
|
6806
|
+
{ name: "nonce", type: "bytes32" },
|
|
6807
|
+
{ name: "audience", type: "string" }
|
|
6808
|
+
]
|
|
6809
|
+
};
|
|
6810
|
+
var LOGIN_STATEMENT_TEMPLATE = "{host} is verifying you own this wallet. Signing proves control of the address so we can show your loans and positions. It authorizes no transaction, moves no funds, and costs no gas.";
|
|
6811
|
+
function canonicalLoginStatement(audience) {
|
|
6812
|
+
const host = /^https?:\/\/([^/?#]+)/i.exec(audience)?.[1];
|
|
6813
|
+
if (!host)
|
|
6814
|
+
return null;
|
|
6815
|
+
return LOGIN_STATEMENT_TEMPLATE.replace("{host}", host);
|
|
6816
|
+
}
|
|
6817
|
+
async function buildSignedLoginPayload(signer, chainId, audience, reuse, withStatement = false) {
|
|
6802
6818
|
const address = await signer.getAddress();
|
|
6819
|
+
const statement = withStatement && audience ? canonicalLoginStatement(audience) : null;
|
|
6803
6820
|
const message = {
|
|
6821
|
+
...statement ? { statement } : {},
|
|
6804
6822
|
address,
|
|
6805
6823
|
issuedAt: reuse?.issuedAt ?? Math.floor(Date.now() / 1e3),
|
|
6806
6824
|
nonce: reuse?.nonce ?? hexlify(randomBytes(32)),
|
|
@@ -6808,7 +6826,7 @@ async function buildSignedLoginPayload(signer, chainId, audience, reuse) {
|
|
|
6808
6826
|
};
|
|
6809
6827
|
const signature = await signer.signTypedData(
|
|
6810
6828
|
buildLoginDomain(chainId),
|
|
6811
|
-
audience ? LOGIN_TYPES_WITH_AUDIENCE : LOGIN_TYPES,
|
|
6829
|
+
statement ? LOGIN_TYPES_WITH_STATEMENT : audience ? LOGIN_TYPES_WITH_AUDIENCE : LOGIN_TYPES,
|
|
6812
6830
|
message
|
|
6813
6831
|
);
|
|
6814
6832
|
return { chainId, message, signature };
|
|
@@ -6981,6 +6999,8 @@ var ServerSession = class {
|
|
|
6981
6999
|
signer;
|
|
6982
7000
|
serviceEndpoint;
|
|
6983
7001
|
loginAudience;
|
|
7002
|
+
/** Whether this session's login carries the human-readable statement. */
|
|
7003
|
+
withStatement;
|
|
6984
7004
|
chainId;
|
|
6985
7005
|
store;
|
|
6986
7006
|
onSignaturePrompt;
|
|
@@ -7022,6 +7042,13 @@ var ServerSession = class {
|
|
|
7022
7042
|
/\/+$/,
|
|
7023
7043
|
""
|
|
7024
7044
|
);
|
|
7045
|
+
const explicitAudience = typeof opts.loginAudience === "string" && opts.loginAudience.length > 0;
|
|
7046
|
+
this.withStatement = opts.withStatement === true && explicitAudience;
|
|
7047
|
+
if (opts.withStatement === true && !explicitAudience) {
|
|
7048
|
+
console.warn(
|
|
7049
|
+
"[dh-sdk] withStatement ignored: it requires an explicit loginAudience, since the servers rebuild the expected sentence from the audience host"
|
|
7050
|
+
);
|
|
7051
|
+
}
|
|
7025
7052
|
this.chainId = opts.chainId;
|
|
7026
7053
|
this.store = opts.persistSession === false ? null : opts.sessionStore ?? createDefaultSessionStore();
|
|
7027
7054
|
this.onSignaturePrompt = opts.onSignaturePrompt;
|
|
@@ -7275,12 +7302,16 @@ var ServerSession = class {
|
|
|
7275
7302
|
this.signer,
|
|
7276
7303
|
this.chainId,
|
|
7277
7304
|
this.loginAudience,
|
|
7278
|
-
reuse
|
|
7305
|
+
reuse,
|
|
7306
|
+
reuse ? reuse.withStatement === true : this.withStatement
|
|
7279
7307
|
);
|
|
7280
7308
|
if (storeKey && !reuse) {
|
|
7281
7309
|
store.savePending?.(storeKey, {
|
|
7282
7310
|
issuedAt: payload.message.issuedAt,
|
|
7283
|
-
nonce: payload.message.nonce
|
|
7311
|
+
nonce: payload.message.nonce,
|
|
7312
|
+
// Recorded so a resume reproduces this digest even if the
|
|
7313
|
+
// capability flag changes before the owners finish signing.
|
|
7314
|
+
withStatement: this.withStatement
|
|
7284
7315
|
});
|
|
7285
7316
|
}
|
|
7286
7317
|
session = await this.login(payload);
|
|
@@ -10351,7 +10382,102 @@ var LoanStatus = /* @__PURE__ */ ((LoanStatus2) => {
|
|
|
10351
10382
|
})(LoanStatus || {});
|
|
10352
10383
|
|
|
10353
10384
|
// src/modules/loan/loan-query.module.ts
|
|
10354
|
-
import { Contract as Contract4 } from "ethers";
|
|
10385
|
+
import { Contract as Contract4, isAddress as isAddress2 } from "ethers";
|
|
10386
|
+
|
|
10387
|
+
// src/constants/chunks/sdk-limits.ts
|
|
10388
|
+
var THE_GRAPH_MAX_BATCH_SIZE = 1e3;
|
|
10389
|
+
|
|
10390
|
+
// src/utils/borrower-ucd-debt-summary.ts
|
|
10391
|
+
var BORROWER_DEBT_ROWS_PAGE_SIZE = THE_GRAPH_MAX_BATCH_SIZE;
|
|
10392
|
+
var BORROWER_DEBT_ROWS_MAX_PAGES = 10;
|
|
10393
|
+
async function collectBorrowerDebtRows(fetchPage) {
|
|
10394
|
+
const pageSize = BORROWER_DEBT_ROWS_PAGE_SIZE;
|
|
10395
|
+
const rows = [];
|
|
10396
|
+
for (let page = 0; page < BORROWER_DEBT_ROWS_MAX_PAGES; page++) {
|
|
10397
|
+
const batch = await fetchPage(page * pageSize, pageSize);
|
|
10398
|
+
if (batch.length > pageSize) {
|
|
10399
|
+
throw new Error(
|
|
10400
|
+
`Borrower debt page ${page} returned ${batch.length} rows for first: ${pageSize}`
|
|
10401
|
+
);
|
|
10402
|
+
}
|
|
10403
|
+
rows.push(...batch);
|
|
10404
|
+
if (batch.length < pageSize) {
|
|
10405
|
+
return rows;
|
|
10406
|
+
}
|
|
10407
|
+
}
|
|
10408
|
+
throw new Error(
|
|
10409
|
+
`Borrower debt rows exceeded ${BORROWER_DEBT_ROWS_MAX_PAGES} pages of ${pageSize} (${rows.length} rows so far); refusing to return a partial total`
|
|
10410
|
+
);
|
|
10411
|
+
}
|
|
10412
|
+
function parseSubgraphLoanStatus(label) {
|
|
10413
|
+
if (typeof label !== "string") {
|
|
10414
|
+
throw new Error(`Subgraph loan status is not a string: ${String(label)}`);
|
|
10415
|
+
}
|
|
10416
|
+
const value = LoanStatus[label];
|
|
10417
|
+
if (typeof value !== "number") {
|
|
10418
|
+
throw new Error(`Unknown subgraph loan status label: "${label}"`);
|
|
10419
|
+
}
|
|
10420
|
+
return value;
|
|
10421
|
+
}
|
|
10422
|
+
function parseSubgraphWei(row) {
|
|
10423
|
+
const raw = row.ucdDebt || "0";
|
|
10424
|
+
if (typeof raw !== "string") {
|
|
10425
|
+
throw new Error(
|
|
10426
|
+
`ucdDebt on position ${row.id} is not a string: ${String(raw)}`
|
|
10427
|
+
);
|
|
10428
|
+
}
|
|
10429
|
+
let wei;
|
|
10430
|
+
try {
|
|
10431
|
+
wei = BigInt(raw);
|
|
10432
|
+
} catch (error) {
|
|
10433
|
+
throw new Error(
|
|
10434
|
+
`ucdDebt on position ${row.id} is not an integer wei string: "${raw}"`,
|
|
10435
|
+
{ cause: error }
|
|
10436
|
+
);
|
|
10437
|
+
}
|
|
10438
|
+
if (wei < 0n) {
|
|
10439
|
+
throw new Error(`Negative ucdDebt on position ${row.id}: ${raw}`);
|
|
10440
|
+
}
|
|
10441
|
+
return wei;
|
|
10442
|
+
}
|
|
10443
|
+
var WEI_PER_UCD = 10n ** 18n;
|
|
10444
|
+
function weiToHumanUcdString(wei) {
|
|
10445
|
+
if (wei < 0n) {
|
|
10446
|
+
throw new Error(`Cannot render negative wei: ${wei}`);
|
|
10447
|
+
}
|
|
10448
|
+
const whole = wei / WEI_PER_UCD;
|
|
10449
|
+
const frac = (wei % WEI_PER_UCD).toString().padStart(18, "0").replace(/0+$/, "");
|
|
10450
|
+
return frac.length > 0 ? `${whole}.${frac}` : `${whole}`;
|
|
10451
|
+
}
|
|
10452
|
+
var STATUS_ORDINALS = Object.values(LoanStatus).filter((v) => typeof v === "number").sort((a, b) => a - b);
|
|
10453
|
+
function buildBorrowerUcdDebtSummary(borrower, rows, fetchedAt = Date.now()) {
|
|
10454
|
+
let totalWei = 0n;
|
|
10455
|
+
const counts = /* @__PURE__ */ new Map();
|
|
10456
|
+
for (const row of rows) {
|
|
10457
|
+
totalWei += parseSubgraphWei(row);
|
|
10458
|
+
const status = parseSubgraphLoanStatus(row.status);
|
|
10459
|
+
counts.set(status, (counts.get(status) ?? 0) + 1);
|
|
10460
|
+
}
|
|
10461
|
+
const byStatus = [];
|
|
10462
|
+
for (const status of STATUS_ORDINALS) {
|
|
10463
|
+
const count = counts.get(status);
|
|
10464
|
+
if (count === void 0) {
|
|
10465
|
+
continue;
|
|
10466
|
+
}
|
|
10467
|
+
byStatus.push({ status, statusLabel: LoanStatus[status], count });
|
|
10468
|
+
}
|
|
10469
|
+
return {
|
|
10470
|
+
borrower: borrower.toLowerCase(),
|
|
10471
|
+
loanCount: rows.length,
|
|
10472
|
+
totalUcdDebt: totalWei.toString(),
|
|
10473
|
+
totalUcdDebtHuman: weiToHumanUcdString(totalWei),
|
|
10474
|
+
byStatus,
|
|
10475
|
+
source: "subgraph",
|
|
10476
|
+
fetchedAt
|
|
10477
|
+
};
|
|
10478
|
+
}
|
|
10479
|
+
|
|
10480
|
+
// src/modules/loan/loan-query.module.ts
|
|
10355
10481
|
var POSITION_CORE_ABI = [
|
|
10356
10482
|
"function getPositionDetails(bytes32) view returns (tuple(bytes32 positionId, bytes32 pkpId, uint256 ucdDebt, string vaultAddress, address borrower, uint40 createdAt, uint40 lastUpdated, uint16 selectedTerm, uint40 expiryAt, uint8 status, uint40 previousExpiryAt, uint16 totalTerm))"
|
|
10357
10483
|
];
|
|
@@ -10744,6 +10870,43 @@ var LoanQuery = class {
|
|
|
10744
10870
|
async getLoansByBorrower(borrower, pagination, orderBy, orderDirection) {
|
|
10745
10871
|
return this.getLoans({ borrower, orderBy, orderDirection }, pagination);
|
|
10746
10872
|
}
|
|
10873
|
+
/**
|
|
10874
|
+
* Combined UCD debt + by-status counts for a borrower from ONE pass over the
|
|
10875
|
+
* subgraph's raw rows (`graphClient.getBorrowerDebtRows`): BigInt wei sum, strict
|
|
10876
|
+
* status labels, `source: "subgraph"`.
|
|
10877
|
+
*
|
|
10878
|
+
* INDEXED figure, not a transaction input — the subgraph lags writes. Anything that
|
|
10879
|
+
* sizes a repayment reads the chain (`getPositionDetailsView`). Fails loud: a page
|
|
10880
|
+
* failure or the page cap is a SUBGRAPH-category failure carrying the cause; there
|
|
10881
|
+
* is no partial total.
|
|
10882
|
+
*/
|
|
10883
|
+
async getBorrowerUcdDebtSummary(borrower) {
|
|
10884
|
+
const trimmed = typeof borrower === "string" ? borrower.trim() : "";
|
|
10885
|
+
if (!isAddress2(trimmed)) {
|
|
10886
|
+
return failure(
|
|
10887
|
+
new SDKError({
|
|
10888
|
+
message: `Borrower must be an EVM address for a UCD debt summary, got "${trimmed}"`,
|
|
10889
|
+
category: "VALIDATION" /* VALIDATION */,
|
|
10890
|
+
severity: "LOW" /* LOW */,
|
|
10891
|
+
context: { borrower: trimmed }
|
|
10892
|
+
})
|
|
10893
|
+
);
|
|
10894
|
+
}
|
|
10895
|
+
return tryCatchAsync(
|
|
10896
|
+
async () => {
|
|
10897
|
+
const rows = await this.config.graphClient.getBorrowerDebtRows(trimmed);
|
|
10898
|
+
return buildBorrowerUcdDebtSummary(trimmed, rows);
|
|
10899
|
+
},
|
|
10900
|
+
(error) => error instanceof SDKError ? error : new SDKError({
|
|
10901
|
+
message: `Borrower UCD debt summary failed for ${trimmed}: ${error instanceof Error ? error.message : String(error)}`,
|
|
10902
|
+
category: "SUBGRAPH" /* SUBGRAPH */,
|
|
10903
|
+
severity: "MEDIUM" /* MEDIUM */,
|
|
10904
|
+
originalError: error instanceof Error ? error : void 0,
|
|
10905
|
+
cause: error,
|
|
10906
|
+
context: { borrower: trimmed }
|
|
10907
|
+
})
|
|
10908
|
+
);
|
|
10909
|
+
}
|
|
10747
10910
|
/**
|
|
10748
10911
|
* Get active loans (status = ACTIVE)
|
|
10749
10912
|
*
|
|
@@ -12278,10 +12441,36 @@ var GraphClient = class {
|
|
|
12278
12441
|
}
|
|
12279
12442
|
};
|
|
12280
12443
|
|
|
12281
|
-
// src/constants/chunks/sdk-limits.ts
|
|
12282
|
-
var THE_GRAPH_MAX_BATCH_SIZE = 1e3;
|
|
12283
|
-
|
|
12284
12444
|
// src/graphs/diamond-hands.ts
|
|
12445
|
+
var GET_USER_POSITIONS_DOCUMENT = `
|
|
12446
|
+
query GetUserPositions($borrower: Bytes!, $first: Int, $skip: Int, $orderBy: Position_orderBy, $orderDirection: OrderDirection) {
|
|
12447
|
+
user(id: $borrower) {
|
|
12448
|
+
id
|
|
12449
|
+
positions(first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) {
|
|
12450
|
+
id
|
|
12451
|
+
pkpId
|
|
12452
|
+
borrower {
|
|
12453
|
+
createdAt
|
|
12454
|
+
id
|
|
12455
|
+
}
|
|
12456
|
+
# btcAmount removed - balance is now queried on-chain via lit-actions with signature validation
|
|
12457
|
+
ucdMinted
|
|
12458
|
+
ucdPaid
|
|
12459
|
+
ucdDebt
|
|
12460
|
+
collateralRatio
|
|
12461
|
+
requestedCollateralRatio
|
|
12462
|
+
selectedTerm
|
|
12463
|
+
status
|
|
12464
|
+
createdAt
|
|
12465
|
+
createdAtBlock
|
|
12466
|
+
lastUpdated
|
|
12467
|
+
originalTerm
|
|
12468
|
+
remainingDebt
|
|
12469
|
+
expiryAt
|
|
12470
|
+
}
|
|
12471
|
+
}
|
|
12472
|
+
}
|
|
12473
|
+
`;
|
|
12285
12474
|
function normalizePkpId2(pkpId) {
|
|
12286
12475
|
const raw = pkpId.startsWith("0x") ? pkpId.slice(2) : pkpId;
|
|
12287
12476
|
if (raw.length === 64 && raw.startsWith("000000000000000000000000") && raw !== "0".repeat(64)) {
|
|
@@ -12599,35 +12788,7 @@ var DiamondHandsGraph = class {
|
|
|
12599
12788
|
* Get user positions from subgraph
|
|
12600
12789
|
*/
|
|
12601
12790
|
async getUserPositionsOnly(userAddress, first, skip, orderBy, orderDirection) {
|
|
12602
|
-
const query =
|
|
12603
|
-
query GetUserPositions($borrower: Bytes!, $first: Int, $skip: Int, $orderBy: Position_orderBy, $orderDirection: OrderDirection) {
|
|
12604
|
-
user(id: $borrower) {
|
|
12605
|
-
id
|
|
12606
|
-
positions(first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) {
|
|
12607
|
-
id
|
|
12608
|
-
pkpId
|
|
12609
|
-
borrower {
|
|
12610
|
-
createdAt
|
|
12611
|
-
id
|
|
12612
|
-
}
|
|
12613
|
-
# btcAmount removed - balance is now queried on-chain via lit-actions with signature validation
|
|
12614
|
-
ucdMinted
|
|
12615
|
-
ucdPaid
|
|
12616
|
-
ucdDebt
|
|
12617
|
-
collateralRatio
|
|
12618
|
-
requestedCollateralRatio
|
|
12619
|
-
selectedTerm
|
|
12620
|
-
status
|
|
12621
|
-
createdAt
|
|
12622
|
-
createdAtBlock
|
|
12623
|
-
lastUpdated
|
|
12624
|
-
originalTerm
|
|
12625
|
-
remainingDebt
|
|
12626
|
-
expiryAt
|
|
12627
|
-
}
|
|
12628
|
-
}
|
|
12629
|
-
}
|
|
12630
|
-
`;
|
|
12791
|
+
const query = GET_USER_POSITIONS_DOCUMENT;
|
|
12631
12792
|
const variables = {
|
|
12632
12793
|
borrower: userAddress.toLowerCase(),
|
|
12633
12794
|
first: first || void 0,
|
|
@@ -12645,6 +12806,45 @@ var DiamondHandsGraph = class {
|
|
|
12645
12806
|
}
|
|
12646
12807
|
return positions;
|
|
12647
12808
|
}
|
|
12809
|
+
/**
|
|
12810
|
+
* Raw `{ id, status, ucdDebt }` for EVERY position of a borrower: one round trip per
|
|
12811
|
+
* 1000 positions, no count walk, no vault RPCs, and no `LoanData` transform (which
|
|
12812
|
+
* coerces wei to a float and leaves `status` a string cast to the numeric enum).
|
|
12813
|
+
*
|
|
12814
|
+
* Deliberately issues the `GetUserPositions` document rather than a leaner one: the
|
|
12815
|
+
* lit-ops graph proxy only passes allowlisted documents (graph-document-policy.ts),
|
|
12816
|
+
* and that one is already listed, so this needs no server change. The extra fields
|
|
12817
|
+
* cost bytes, not round trips.
|
|
12818
|
+
*
|
|
12819
|
+
* Throws past `BORROWER_DEBT_ROWS_MAX_PAGES` — never a partial set.
|
|
12820
|
+
*/
|
|
12821
|
+
async getBorrowerDebtRows(borrower) {
|
|
12822
|
+
const borrowerId = borrower.toLowerCase();
|
|
12823
|
+
return collectBorrowerDebtRows(async (skip, first) => {
|
|
12824
|
+
const result = await this.client.execute(GET_USER_POSITIONS_DOCUMENT, {
|
|
12825
|
+
borrower: borrowerId,
|
|
12826
|
+
first,
|
|
12827
|
+
skip,
|
|
12828
|
+
orderBy: "id",
|
|
12829
|
+
orderDirection: "asc"
|
|
12830
|
+
});
|
|
12831
|
+
if (result?.user == null) {
|
|
12832
|
+
if (skip > 0) {
|
|
12833
|
+
throw new Error(
|
|
12834
|
+
`getBorrowerDebtRows: user ${borrowerId} vanished from the subgraph at skip ${skip}`
|
|
12835
|
+
);
|
|
12836
|
+
}
|
|
12837
|
+
return [];
|
|
12838
|
+
}
|
|
12839
|
+
const positions = result.user.positions;
|
|
12840
|
+
if (!Array.isArray(positions)) {
|
|
12841
|
+
throw new Error(
|
|
12842
|
+
`getBorrowerDebtRows: subgraph returned no positions array for ${borrowerId} at skip ${skip}`
|
|
12843
|
+
);
|
|
12844
|
+
}
|
|
12845
|
+
return positions.filter((p) => p != null);
|
|
12846
|
+
});
|
|
12847
|
+
}
|
|
12648
12848
|
/**
|
|
12649
12849
|
* Get user positions, plus an accurate total count.
|
|
12650
12850
|
*
|
|
@@ -19635,6 +19835,9 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
19635
19835
|
signer: config.authSigner ?? config.contractSigner,
|
|
19636
19836
|
serviceEndpoint: config.serviceEndpoint,
|
|
19637
19837
|
loginAudience: config.loginAudience,
|
|
19838
|
+
// Only meaningful alongside an audience; the servers derive the expected
|
|
19839
|
+
// sentence from its host. Defaults off so Node callers are untouched.
|
|
19840
|
+
withStatement: config.loginStatement === true,
|
|
19638
19841
|
chainId: config.chainId,
|
|
19639
19842
|
persistSession: config.sessionPersistence?.enabled,
|
|
19640
19843
|
sessionStore: config.sessionPersistence?.store,
|
|
@@ -25600,6 +25803,18 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
25600
25803
|
async getLoansByBorrower(borrower, pagination, orderBy, orderDirection) {
|
|
25601
25804
|
return this.loanQuery.getLoansByBorrower(borrower, pagination, orderBy, orderDirection);
|
|
25602
25805
|
}
|
|
25806
|
+
/**
|
|
25807
|
+
* Combined UCD debt (canonical wei string + display string) and by-status loan
|
|
25808
|
+
* counts for a borrower, from one pass over the subgraph. `source: "subgraph"` —
|
|
25809
|
+
* an INDEXED, informational figure that lags writes; never size a transaction
|
|
25810
|
+
* from it (repayments read the chain via `getPositionDetailsView`).
|
|
25811
|
+
*
|
|
25812
|
+
* Fails loud with a SUBGRAPH-category `SDKError` (cause attached) on any page
|
|
25813
|
+
* failure or past 10,000 positions; never returns a partial total.
|
|
25814
|
+
*/
|
|
25815
|
+
async getBorrowerUcdDebtSummary(borrower) {
|
|
25816
|
+
return this.loanQuery.getBorrowerUcdDebtSummary(borrower);
|
|
25817
|
+
}
|
|
25603
25818
|
/**
|
|
25604
25819
|
* Get all active loans
|
|
25605
25820
|
*
|
|
@@ -97,6 +97,25 @@ interface BaseSDKConfig {
|
|
|
97
97
|
* endpoint they call.
|
|
98
98
|
*/
|
|
99
99
|
loginAudience?: string;
|
|
100
|
+
/**
|
|
101
|
+
* Add the human-readable `statement` to the server-session login — the sentence
|
|
102
|
+
* a wallet renders instead of four opaque fields.
|
|
103
|
+
*
|
|
104
|
+
* OFF by default, and the default is load-bearing. `statement` changes the
|
|
105
|
+
* EIP-712 digest, and lit-ops-server chooses its field set by presence alone, so
|
|
106
|
+
* a client that sends one to a server which predates it has EVERY login
|
|
107
|
+
* rejected. Node callers (CLI, MCP, cr-monitor) have no signing UI to improve
|
|
108
|
+
* and should leave it off entirely.
|
|
109
|
+
*
|
|
110
|
+
* A browser sets this from the same capability flag that gates its own login
|
|
111
|
+
* statement (`LOGIN_STATEMENT_ENABLED`, served via `/v1/init`), which is what
|
|
112
|
+
* keeps the two in step: the flag asserts the server accepts statements, and
|
|
113
|
+
* both logins believe it or neither does.
|
|
114
|
+
*
|
|
115
|
+
* Ignored without `loginAudience` — the servers rebuild the expected sentence
|
|
116
|
+
* from the audience host, so a statement they cannot rebuild is one they reject.
|
|
117
|
+
*/
|
|
118
|
+
loginStatement?: boolean;
|
|
100
119
|
/**
|
|
101
120
|
* Fires immediately before the server-session login requests a wallet
|
|
102
121
|
* signature — never on the silent re-mint paths. Lets UIs show a
|
|
@@ -78,6 +78,40 @@ export interface PaginatedLoansResponse {
|
|
|
78
78
|
maxRows: number;
|
|
79
79
|
totalLoans: number;
|
|
80
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Per-status row in {@link BorrowerUcdDebtSummary.byStatus}.
|
|
83
|
+
* Only statuses with count > 0 appear; order follows {@link LoanStatus} ordinals.
|
|
84
|
+
*/
|
|
85
|
+
export interface BorrowerUcdDebtStatusCount {
|
|
86
|
+
status: LoanStatus;
|
|
87
|
+
/** Enum key, e.g. "ACTIVE", "PENDING_DEPOSIT". */
|
|
88
|
+
statusLabel: string;
|
|
89
|
+
count: number;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Aggregated UCD debt + status mix for one borrower, from ONE pass over the
|
|
93
|
+
* subgraph's raw position rows — shared by Butler, MCP, CLI.
|
|
94
|
+
*
|
|
95
|
+
* This is the INDEXED view (the subgraph lags writes). It is informational and
|
|
96
|
+
* must never size a transaction; repayments read the chain
|
|
97
|
+
* (`getPositionDetailsView`).
|
|
98
|
+
*/
|
|
99
|
+
export interface BorrowerUcdDebtSummary {
|
|
100
|
+
/** Lower-cased. */
|
|
101
|
+
borrower: string;
|
|
102
|
+
/** Positions included in the sum — every status, all pages. */
|
|
103
|
+
loanCount: number;
|
|
104
|
+
/** Canonical total debt in wei (18 decimals), as a decimal string (BigInt sum). */
|
|
105
|
+
totalUcdDebt: string;
|
|
106
|
+
/** Display only: 18-decimal rendering with trailing zeros trimmed (e.g. "21.93"). */
|
|
107
|
+
totalUcdDebtHuman: string;
|
|
108
|
+
/** Only statuses with count > 0, ordered by {@link LoanStatus} ordinal. */
|
|
109
|
+
byStatus: BorrowerUcdDebtStatusCount[];
|
|
110
|
+
/** Where the figures came from. Reserved for a future on-chain cross-check ("chain"). */
|
|
111
|
+
source: "subgraph";
|
|
112
|
+
/** ms epoch, set by the SDK when the last page landed. */
|
|
113
|
+
fetchedAt: number;
|
|
114
|
+
}
|
|
81
115
|
/**
|
|
82
116
|
* Detailed Loan Data Interface (used by getLoanById / PKP)
|
|
83
117
|
*/
|
|
@@ -893,6 +893,16 @@ export declare class DiamondHandsSDK {
|
|
|
893
893
|
page: number;
|
|
894
894
|
pageSize: number;
|
|
895
895
|
}, orderBy?: "createdAt" | "lastUpdatedAt" | "ucdDebt", orderDirection?: "asc" | "desc"): Promise<Result<import("../interfaces/chunks/loan-operations.i").PaginatedLoansResponse, SDKError>>;
|
|
896
|
+
/**
|
|
897
|
+
* Combined UCD debt (canonical wei string + display string) and by-status loan
|
|
898
|
+
* counts for a borrower, from one pass over the subgraph. `source: "subgraph"` —
|
|
899
|
+
* an INDEXED, informational figure that lags writes; never size a transaction
|
|
900
|
+
* from it (repayments read the chain via `getPositionDetailsView`).
|
|
901
|
+
*
|
|
902
|
+
* Fails loud with a SUBGRAPH-category `SDKError` (cause attached) on any page
|
|
903
|
+
* failure or past 10,000 positions; never returns a partial total.
|
|
904
|
+
*/
|
|
905
|
+
getBorrowerUcdDebtSummary(borrower: string): Promise<Result<import("../interfaces/chunks/loan-operations.i").BorrowerUcdDebtSummary, SDKError>>;
|
|
896
906
|
/**
|
|
897
907
|
* Get all active loans
|
|
898
908
|
*
|
|
@@ -13,7 +13,7 @@ import { Result } from "../../types/result";
|
|
|
13
13
|
import { SDKError } from "../../utils/error-handler";
|
|
14
14
|
import type { BitcoinOperations } from "../bitcoin/bitcoin-operations.module";
|
|
15
15
|
import type { Cache } from "../cache/cache-manager.module";
|
|
16
|
-
import type { LoanData, LoanDataDetail, PaginatedLoansResponse } from "../../interfaces/chunks/loan-operations.i";
|
|
16
|
+
import type { BorrowerUcdDebtSummary, LoanData, LoanDataDetail, PaginatedLoansResponse } from "../../interfaces/chunks/loan-operations.i";
|
|
17
17
|
import type { LoanEvents, LoanEventsFilter } from "../../types/event-types";
|
|
18
18
|
import { DiamondHandsGraphClient } from "@graphs/diamond-hands";
|
|
19
19
|
import { type Provider } from "ethers";
|
|
@@ -141,6 +141,17 @@ export declare class LoanQuery {
|
|
|
141
141
|
* @returns Paginated loans for borrower
|
|
142
142
|
*/
|
|
143
143
|
getLoansByBorrower(borrower: string, pagination?: PaginationParams, orderBy?: "createdAt" | "lastUpdatedAt" | "ucdDebt", orderDirection?: "asc" | "desc"): Promise<Result<PaginatedLoansResponse, SDKError>>;
|
|
144
|
+
/**
|
|
145
|
+
* Combined UCD debt + by-status counts for a borrower from ONE pass over the
|
|
146
|
+
* subgraph's raw rows (`graphClient.getBorrowerDebtRows`): BigInt wei sum, strict
|
|
147
|
+
* status labels, `source: "subgraph"`.
|
|
148
|
+
*
|
|
149
|
+
* INDEXED figure, not a transaction input — the subgraph lags writes. Anything that
|
|
150
|
+
* sizes a repayment reads the chain (`getPositionDetailsView`). Fails loud: a page
|
|
151
|
+
* failure or the page cap is a SUBGRAPH-category failure carrying the cause; there
|
|
152
|
+
* is no partial total.
|
|
153
|
+
*/
|
|
154
|
+
getBorrowerUcdDebtSummary(borrower: string): Promise<Result<BorrowerUcdDebtSummary, SDKError>>;
|
|
144
155
|
/**
|
|
145
156
|
* Get active loans (status = ACTIVE)
|
|
146
157
|
*
|
package/dist/safe-delegation.js
CHANGED
|
@@ -425,13 +425,13 @@ var MAINNET_DEPLOYMENT = {
|
|
|
425
425
|
block: 25472956
|
|
426
426
|
},
|
|
427
427
|
BTCSpendAuthorizer: {
|
|
428
|
-
previousImplementation: "
|
|
429
|
-
newImplementation: "
|
|
430
|
-
upgradedAt: "2026-
|
|
431
|
-
upgradedBy: "AdminModule
|
|
432
|
-
reason: "
|
|
433
|
-
executeTx: "
|
|
434
|
-
block:
|
|
428
|
+
previousImplementation: "0x40c9e8C46C65E0A57BB63da5368868A9E8Df81A5",
|
|
429
|
+
newImplementation: "0x00D7a47798E8ff169104b748131703B78f23390C",
|
|
430
|
+
upgradedAt: "2026-09-09T12:05:35.000Z",
|
|
431
|
+
upgradedBy: "AdminModule generic op 0xc0214ff38bb454864a34f6482dcd03570b3fd04171c84be2c20184afb1a2e161 (bsa-1-7-0 ceremony: proposed by seat 0x3030C0414B6d95aA9d5A57269E51a347fCBaB472 2026-09-08T11:35:23Z, 3/3 seat signatures, 24h timelock); executeGenericAdminOperation sent by 0xF20986F02420EF443AE9BE5092FB8A4975cF3aA6 in tx 0x77cd231966c546177bd8f1a07bd4e705afd5c2dfbd110b096836401679c0c69c (block 25939703)",
|
|
432
|
+
reason: "BTCSpendAuthorizer 1.6.0 -> 1.7.0: allow a full-vault sweep (targetAmount == satoshis); TargetMustLeaveFeeRoom -> TargetExceedsInputValue (PR #849; mainnet incidents 2026-09-04 and 2026-09-07). Impl deployed by 0xEba7BfAeB35ee18408b424Ee0BB45AdDf776589f at nonce 430 from source 0a7c535a1 (contracts/src identical to main 99b335f77). Cross-ref onchain-auth/.safe-batches/mainnet-bsa-1-7-0-{propose,sign,execute}.json and docs/mainnet-bsa-1-7-0-ceremony-2026-09-08.md",
|
|
433
|
+
executeTx: "0x77cd231966c546177bd8f1a07bd4e705afd5c2dfbd110b096836401679c0c69c",
|
|
434
|
+
block: 25939703
|
|
435
435
|
},
|
|
436
436
|
PositionManager: {
|
|
437
437
|
previousImplementation: "0x600Dc0D1C2c0F94882B6Bc09172f8650047Ad5B1",
|
|
@@ -534,7 +534,7 @@ var MAINNET_DEPLOYMENT = {
|
|
|
534
534
|
ADMIN_MODULE: "0x0023B71B586795eE5fa4E83E384345C1EC8Bb946",
|
|
535
535
|
ADMIN_MODULE_IMPL: "0xb4C5C7E051A1e672F014B25CB5D55B225Aa0D512",
|
|
536
536
|
BTC_SPEND_AUTHORIZER: "0xfBbF108486fa07F1B06a811C5b66ed68DD9c508a",
|
|
537
|
-
BTC_SPEND_AUTHORIZER_IMPL: "
|
|
537
|
+
BTC_SPEND_AUTHORIZER_IMPL: "0x00D7a47798E8ff169104b748131703B78f23390C",
|
|
538
538
|
POSITION_MANAGER_VIEWS: "0x8B57954eA338c6A6bfF1457f71080e25baFF3937",
|
|
539
539
|
POSITION_MANAGER_VIEWS_IMPL: "0xE68DC096A65Ecddbc6A9b5c6e5f878e2CE143Bfe",
|
|
540
540
|
POSITION_MANAGER: "0x12A1836e56139ccaD8722B89519d84233efc224B",
|
package/dist/safe-delegation.mjs
CHANGED
|
@@ -398,13 +398,13 @@ var MAINNET_DEPLOYMENT = {
|
|
|
398
398
|
block: 25472956
|
|
399
399
|
},
|
|
400
400
|
BTCSpendAuthorizer: {
|
|
401
|
-
previousImplementation: "
|
|
402
|
-
newImplementation: "
|
|
403
|
-
upgradedAt: "2026-
|
|
404
|
-
upgradedBy: "AdminModule
|
|
405
|
-
reason: "
|
|
406
|
-
executeTx: "
|
|
407
|
-
block:
|
|
401
|
+
previousImplementation: "0x40c9e8C46C65E0A57BB63da5368868A9E8Df81A5",
|
|
402
|
+
newImplementation: "0x00D7a47798E8ff169104b748131703B78f23390C",
|
|
403
|
+
upgradedAt: "2026-09-09T12:05:35.000Z",
|
|
404
|
+
upgradedBy: "AdminModule generic op 0xc0214ff38bb454864a34f6482dcd03570b3fd04171c84be2c20184afb1a2e161 (bsa-1-7-0 ceremony: proposed by seat 0x3030C0414B6d95aA9d5A57269E51a347fCBaB472 2026-09-08T11:35:23Z, 3/3 seat signatures, 24h timelock); executeGenericAdminOperation sent by 0xF20986F02420EF443AE9BE5092FB8A4975cF3aA6 in tx 0x77cd231966c546177bd8f1a07bd4e705afd5c2dfbd110b096836401679c0c69c (block 25939703)",
|
|
405
|
+
reason: "BTCSpendAuthorizer 1.6.0 -> 1.7.0: allow a full-vault sweep (targetAmount == satoshis); TargetMustLeaveFeeRoom -> TargetExceedsInputValue (PR #849; mainnet incidents 2026-09-04 and 2026-09-07). Impl deployed by 0xEba7BfAeB35ee18408b424Ee0BB45AdDf776589f at nonce 430 from source 0a7c535a1 (contracts/src identical to main 99b335f77). Cross-ref onchain-auth/.safe-batches/mainnet-bsa-1-7-0-{propose,sign,execute}.json and docs/mainnet-bsa-1-7-0-ceremony-2026-09-08.md",
|
|
406
|
+
executeTx: "0x77cd231966c546177bd8f1a07bd4e705afd5c2dfbd110b096836401679c0c69c",
|
|
407
|
+
block: 25939703
|
|
408
408
|
},
|
|
409
409
|
PositionManager: {
|
|
410
410
|
previousImplementation: "0x600Dc0D1C2c0F94882B6Bc09172f8650047Ad5B1",
|
|
@@ -507,7 +507,7 @@ var MAINNET_DEPLOYMENT = {
|
|
|
507
507
|
ADMIN_MODULE: "0x0023B71B586795eE5fa4E83E384345C1EC8Bb946",
|
|
508
508
|
ADMIN_MODULE_IMPL: "0xb4C5C7E051A1e672F014B25CB5D55B225Aa0D512",
|
|
509
509
|
BTC_SPEND_AUTHORIZER: "0xfBbF108486fa07F1B06a811C5b66ed68DD9c508a",
|
|
510
|
-
BTC_SPEND_AUTHORIZER_IMPL: "
|
|
510
|
+
BTC_SPEND_AUTHORIZER_IMPL: "0x00D7a47798E8ff169104b748131703B78f23390C",
|
|
511
511
|
POSITION_MANAGER_VIEWS: "0x8B57954eA338c6A6bfF1457f71080e25baFF3937",
|
|
512
512
|
POSITION_MANAGER_VIEWS_IMPL: "0xE68DC096A65Ecddbc6A9b5c6e5f878e2CE143Bfe",
|
|
513
513
|
POSITION_MANAGER: "0x12A1836e56139ccaD8722B89519d84233efc224B",
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@gvnrdao/dh-sdk/sign-guard` — the client-side guard between an untrusted
|
|
3
|
+
* lit-ops-server's bytes and a borrower's key.
|
|
4
|
+
*
|
|
5
|
+
* A SUBPATH rather than a main-index re-export, on purpose:
|
|
6
|
+
*
|
|
7
|
+
* - it is consumed by the CLI (CJS, node) and the MCP (bundled) and must not
|
|
8
|
+
* drag the whole SDK or `@gvnrdao/dh-lit-actions` into either; the files
|
|
9
|
+
* under `utils/sign-guard/` import only `ethers` and three pure SDK
|
|
10
|
+
* modules, enforced by `scripts/check-sign-guard-imports.mjs`;
|
|
11
|
+
* - `TxValidationError` is exported from exactly one entry so `instanceof`
|
|
12
|
+
* cannot split across module realms;
|
|
13
|
+
* - the frontend shadows the bare `@gvnrdao/dh-sdk` specifier with an ambient
|
|
14
|
+
* module; subpaths escape the shim and get the package's own types.
|
|
15
|
+
*
|
|
16
|
+
* What is here: the signable-function whitelist and its decoder, the
|
|
17
|
+
* unsigned-tx validator with its fee ceiling, the quantum/nonce helpers, the
|
|
18
|
+
* operation-envelope stamper, the three delegated-op specs, the lit-ops
|
|
19
|
+
* transport with envelope narrowing, the validate → banner → wrap → sign →
|
|
20
|
+
* broadcast pipeline, and the H-3 extension-fee bound.
|
|
21
|
+
*
|
|
22
|
+
* What is deliberately NOT here: how a consumer prompts, prints, stores a
|
|
23
|
+
* session JWT, reads its fee-ceiling override, or floors priority fees. Those
|
|
24
|
+
* are consumer policy and stay consumer-side.
|
|
25
|
+
*/
|
|
26
|
+
export * from "./utils/sign-guard";
|