@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/deployments.js
CHANGED
|
@@ -459,13 +459,13 @@ var MAINNET_DEPLOYMENT = {
|
|
|
459
459
|
block: 25472956
|
|
460
460
|
},
|
|
461
461
|
BTCSpendAuthorizer: {
|
|
462
|
-
previousImplementation: "
|
|
463
|
-
newImplementation: "
|
|
464
|
-
upgradedAt: "2026-
|
|
465
|
-
upgradedBy: "AdminModule
|
|
466
|
-
reason: "
|
|
467
|
-
executeTx: "
|
|
468
|
-
block:
|
|
462
|
+
previousImplementation: "0x40c9e8C46C65E0A57BB63da5368868A9E8Df81A5",
|
|
463
|
+
newImplementation: "0x00D7a47798E8ff169104b748131703B78f23390C",
|
|
464
|
+
upgradedAt: "2026-09-09T12:05:35.000Z",
|
|
465
|
+
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)",
|
|
466
|
+
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",
|
|
467
|
+
executeTx: "0x77cd231966c546177bd8f1a07bd4e705afd5c2dfbd110b096836401679c0c69c",
|
|
468
|
+
block: 25939703
|
|
469
469
|
},
|
|
470
470
|
PositionManager: {
|
|
471
471
|
previousImplementation: "0x600Dc0D1C2c0F94882B6Bc09172f8650047Ad5B1",
|
|
@@ -568,7 +568,7 @@ var MAINNET_DEPLOYMENT = {
|
|
|
568
568
|
ADMIN_MODULE: "0x0023B71B586795eE5fa4E83E384345C1EC8Bb946",
|
|
569
569
|
ADMIN_MODULE_IMPL: "0xb4C5C7E051A1e672F014B25CB5D55B225Aa0D512",
|
|
570
570
|
BTC_SPEND_AUTHORIZER: "0xfBbF108486fa07F1B06a811C5b66ed68DD9c508a",
|
|
571
|
-
BTC_SPEND_AUTHORIZER_IMPL: "
|
|
571
|
+
BTC_SPEND_AUTHORIZER_IMPL: "0x00D7a47798E8ff169104b748131703B78f23390C",
|
|
572
572
|
POSITION_MANAGER_VIEWS: "0x8B57954eA338c6A6bfF1457f71080e25baFF3937",
|
|
573
573
|
POSITION_MANAGER_VIEWS_IMPL: "0xE68DC096A65Ecddbc6A9b5c6e5f878e2CE143Bfe",
|
|
574
574
|
POSITION_MANAGER: "0x12A1836e56139ccaD8722B89519d84233efc224B",
|
package/dist/deployments.mjs
CHANGED
|
@@ -426,13 +426,13 @@ var MAINNET_DEPLOYMENT = {
|
|
|
426
426
|
block: 25472956
|
|
427
427
|
},
|
|
428
428
|
BTCSpendAuthorizer: {
|
|
429
|
-
previousImplementation: "
|
|
430
|
-
newImplementation: "
|
|
431
|
-
upgradedAt: "2026-
|
|
432
|
-
upgradedBy: "AdminModule
|
|
433
|
-
reason: "
|
|
434
|
-
executeTx: "
|
|
435
|
-
block:
|
|
429
|
+
previousImplementation: "0x40c9e8C46C65E0A57BB63da5368868A9E8Df81A5",
|
|
430
|
+
newImplementation: "0x00D7a47798E8ff169104b748131703B78f23390C",
|
|
431
|
+
upgradedAt: "2026-09-09T12:05:35.000Z",
|
|
432
|
+
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)",
|
|
433
|
+
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",
|
|
434
|
+
executeTx: "0x77cd231966c546177bd8f1a07bd4e705afd5c2dfbd110b096836401679c0c69c",
|
|
435
|
+
block: 25939703
|
|
436
436
|
},
|
|
437
437
|
PositionManager: {
|
|
438
438
|
previousImplementation: "0x600Dc0D1C2c0F94882B6Bc09172f8650047Ad5B1",
|
|
@@ -535,7 +535,7 @@ var MAINNET_DEPLOYMENT = {
|
|
|
535
535
|
ADMIN_MODULE: "0x0023B71B586795eE5fa4E83E384345C1EC8Bb946",
|
|
536
536
|
ADMIN_MODULE_IMPL: "0xb4C5C7E051A1e672F014B25CB5D55B225Aa0D512",
|
|
537
537
|
BTC_SPEND_AUTHORIZER: "0xfBbF108486fa07F1B06a811C5b66ed68DD9c508a",
|
|
538
|
-
BTC_SPEND_AUTHORIZER_IMPL: "
|
|
538
|
+
BTC_SPEND_AUTHORIZER_IMPL: "0x00D7a47798E8ff169104b748131703B78f23390C",
|
|
539
539
|
POSITION_MANAGER_VIEWS: "0x8B57954eA338c6A6bfF1457f71080e25baFF3937",
|
|
540
540
|
POSITION_MANAGER_VIEWS_IMPL: "0xE68DC096A65Ecddbc6A9b5c6e5f878e2CE143Bfe",
|
|
541
541
|
POSITION_MANAGER: "0x12A1836e56139ccaD8722B89519d84233efc224B",
|
|
@@ -3,6 +3,7 @@ import type { LoanData } from "../interfaces";
|
|
|
3
3
|
import type { GraphPositionWithBorrowerDTO } from "../types/graph-dtos";
|
|
4
4
|
import type { LoanEvents, LoanEventsFilter } from "../types/event-types";
|
|
5
5
|
import type { BitcoinOperations, EnrichedBitcoinBalance } from "../modules/bitcoin/bitcoin-operations.module";
|
|
6
|
+
import { type BorrowerDebtRow } from "../utils/borrower-ucd-debt-summary";
|
|
6
7
|
export interface DiamondHandsGraphConfig {
|
|
7
8
|
endpoint: string;
|
|
8
9
|
requestTimeoutMs?: number;
|
|
@@ -321,6 +322,19 @@ export declare class DiamondHandsGraph {
|
|
|
321
322
|
* Get user positions from subgraph
|
|
322
323
|
*/
|
|
323
324
|
private getUserPositionsOnly;
|
|
325
|
+
/**
|
|
326
|
+
* Raw `{ id, status, ucdDebt }` for EVERY position of a borrower: one round trip per
|
|
327
|
+
* 1000 positions, no count walk, no vault RPCs, and no `LoanData` transform (which
|
|
328
|
+
* coerces wei to a float and leaves `status` a string cast to the numeric enum).
|
|
329
|
+
*
|
|
330
|
+
* Deliberately issues the `GetUserPositions` document rather than a leaner one: the
|
|
331
|
+
* lit-ops graph proxy only passes allowlisted documents (graph-document-policy.ts),
|
|
332
|
+
* and that one is already listed, so this needs no server change. The extra fields
|
|
333
|
+
* cost bytes, not round trips.
|
|
334
|
+
*
|
|
335
|
+
* Throws past `BORROWER_DEBT_ROWS_MAX_PAGES` — never a partial set.
|
|
336
|
+
*/
|
|
337
|
+
getBorrowerDebtRows(borrower: string): Promise<BorrowerDebtRow[]>;
|
|
324
338
|
/**
|
|
325
339
|
* Get user positions, plus an accurate total count.
|
|
326
340
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ export type { DhServerLoginMessage, DhServerLoginPayload, } from './utils/eip712
|
|
|
41
41
|
export { DEFAULT_LIT_NETWORK, VALID_LIT_NETWORKS, SDK_DEFAULTS, } from './constants/chunks/sdk-config';
|
|
42
42
|
export { ALL_CONTRACTS, ALL_DEPLOYMENTS, getContractsByNetwork, getDeploymentByNetwork, LOCALHOST_CONTRACTS, SEPOLIA_CONTRACTS, } from './constants/chunks/deployment-addresses';
|
|
43
43
|
export type { DeploymentContracts, DeploymentData, DeploymentLatestEnv, } from './constants/chunks/deployment-addresses';
|
|
44
|
-
export type { CreateLoanRequest, CreateLoanResult, LoanData, LoanDataDetail, PaginatedLoansResponse, PKPValidationData, BitcoinAddresses, } from './interfaces/chunks/loan-operations.i';
|
|
44
|
+
export type { CreateLoanRequest, CreateLoanResult, LoanData, LoanDataDetail, PaginatedLoansResponse, BorrowerUcdDebtSummary, BorrowerUcdDebtStatusCount, PKPValidationData, BitcoinAddresses, } from './interfaces/chunks/loan-operations.i';
|
|
45
45
|
export type { DiamondHandsSDKConfig, SDKMode, ContractAddresses, BitcoinProviderConfig, } from './interfaces/chunks/config.i';
|
|
46
46
|
export type { PKPData, PKPCreationRequest, PKPCreationResult, PKPValidationResult, } from './interfaces/chunks/pkp-integration.i';
|
|
47
47
|
export type { AuthorizationRequest, AuthorizationResult, BTCDepositRequest, BTCDepositResult, } from './interfaces/chunks/requests.i';
|
package/dist/index.js
CHANGED
|
@@ -484,13 +484,13 @@ var init_deployment_addresses = __esm({
|
|
|
484
484
|
block: 25472956
|
|
485
485
|
},
|
|
486
486
|
BTCSpendAuthorizer: {
|
|
487
|
-
previousImplementation: "
|
|
488
|
-
newImplementation: "
|
|
489
|
-
upgradedAt: "2026-
|
|
490
|
-
upgradedBy: "AdminModule
|
|
491
|
-
reason: "
|
|
492
|
-
executeTx: "
|
|
493
|
-
block:
|
|
487
|
+
previousImplementation: "0x40c9e8C46C65E0A57BB63da5368868A9E8Df81A5",
|
|
488
|
+
newImplementation: "0x00D7a47798E8ff169104b748131703B78f23390C",
|
|
489
|
+
upgradedAt: "2026-09-09T12:05:35.000Z",
|
|
490
|
+
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)",
|
|
491
|
+
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",
|
|
492
|
+
executeTx: "0x77cd231966c546177bd8f1a07bd4e705afd5c2dfbd110b096836401679c0c69c",
|
|
493
|
+
block: 25939703
|
|
494
494
|
},
|
|
495
495
|
PositionManager: {
|
|
496
496
|
previousImplementation: "0x600Dc0D1C2c0F94882B6Bc09172f8650047Ad5B1",
|
|
@@ -593,7 +593,7 @@ var init_deployment_addresses = __esm({
|
|
|
593
593
|
ADMIN_MODULE: "0x0023B71B586795eE5fa4E83E384345C1EC8Bb946",
|
|
594
594
|
ADMIN_MODULE_IMPL: "0xb4C5C7E051A1e672F014B25CB5D55B225Aa0D512",
|
|
595
595
|
BTC_SPEND_AUTHORIZER: "0xfBbF108486fa07F1B06a811C5b66ed68DD9c508a",
|
|
596
|
-
BTC_SPEND_AUTHORIZER_IMPL: "
|
|
596
|
+
BTC_SPEND_AUTHORIZER_IMPL: "0x00D7a47798E8ff169104b748131703B78f23390C",
|
|
597
597
|
POSITION_MANAGER_VIEWS: "0x8B57954eA338c6A6bfF1457f71080e25baFF3937",
|
|
598
598
|
POSITION_MANAGER_VIEWS_IMPL: "0xE68DC096A65Ecddbc6A9b5c6e5f878e2CE143Bfe",
|
|
599
599
|
POSITION_MANAGER: "0x12A1836e56139ccaD8722B89519d84233efc224B",
|
|
@@ -6886,9 +6886,27 @@ var LOGIN_TYPES_WITH_AUDIENCE = {
|
|
|
6886
6886
|
{ name: "audience", type: "string" }
|
|
6887
6887
|
]
|
|
6888
6888
|
};
|
|
6889
|
-
|
|
6889
|
+
var LOGIN_TYPES_WITH_STATEMENT = {
|
|
6890
|
+
DiamondHands: [
|
|
6891
|
+
{ name: "statement", type: "string" },
|
|
6892
|
+
{ name: "address", type: "address" },
|
|
6893
|
+
{ name: "issuedAt", type: "uint256" },
|
|
6894
|
+
{ name: "nonce", type: "bytes32" },
|
|
6895
|
+
{ name: "audience", type: "string" }
|
|
6896
|
+
]
|
|
6897
|
+
};
|
|
6898
|
+
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.";
|
|
6899
|
+
function canonicalLoginStatement(audience) {
|
|
6900
|
+
const host = /^https?:\/\/([^/?#]+)/i.exec(audience)?.[1];
|
|
6901
|
+
if (!host)
|
|
6902
|
+
return null;
|
|
6903
|
+
return LOGIN_STATEMENT_TEMPLATE.replace("{host}", host);
|
|
6904
|
+
}
|
|
6905
|
+
async function buildSignedLoginPayload(signer, chainId, audience, reuse, withStatement = false) {
|
|
6890
6906
|
const address = await signer.getAddress();
|
|
6907
|
+
const statement = withStatement && audience ? canonicalLoginStatement(audience) : null;
|
|
6891
6908
|
const message = {
|
|
6909
|
+
...statement ? { statement } : {},
|
|
6892
6910
|
address,
|
|
6893
6911
|
issuedAt: reuse?.issuedAt ?? Math.floor(Date.now() / 1e3),
|
|
6894
6912
|
nonce: reuse?.nonce ?? (0, import_ethers4.hexlify)((0, import_ethers4.randomBytes)(32)),
|
|
@@ -6896,7 +6914,7 @@ async function buildSignedLoginPayload(signer, chainId, audience, reuse) {
|
|
|
6896
6914
|
};
|
|
6897
6915
|
const signature = await signer.signTypedData(
|
|
6898
6916
|
buildLoginDomain(chainId),
|
|
6899
|
-
audience ? LOGIN_TYPES_WITH_AUDIENCE : LOGIN_TYPES,
|
|
6917
|
+
statement ? LOGIN_TYPES_WITH_STATEMENT : audience ? LOGIN_TYPES_WITH_AUDIENCE : LOGIN_TYPES,
|
|
6900
6918
|
message
|
|
6901
6919
|
);
|
|
6902
6920
|
return { chainId, message, signature };
|
|
@@ -7069,6 +7087,8 @@ var ServerSession = class {
|
|
|
7069
7087
|
signer;
|
|
7070
7088
|
serviceEndpoint;
|
|
7071
7089
|
loginAudience;
|
|
7090
|
+
/** Whether this session's login carries the human-readable statement. */
|
|
7091
|
+
withStatement;
|
|
7072
7092
|
chainId;
|
|
7073
7093
|
store;
|
|
7074
7094
|
onSignaturePrompt;
|
|
@@ -7110,6 +7130,13 @@ var ServerSession = class {
|
|
|
7110
7130
|
/\/+$/,
|
|
7111
7131
|
""
|
|
7112
7132
|
);
|
|
7133
|
+
const explicitAudience = typeof opts.loginAudience === "string" && opts.loginAudience.length > 0;
|
|
7134
|
+
this.withStatement = opts.withStatement === true && explicitAudience;
|
|
7135
|
+
if (opts.withStatement === true && !explicitAudience) {
|
|
7136
|
+
console.warn(
|
|
7137
|
+
"[dh-sdk] withStatement ignored: it requires an explicit loginAudience, since the servers rebuild the expected sentence from the audience host"
|
|
7138
|
+
);
|
|
7139
|
+
}
|
|
7113
7140
|
this.chainId = opts.chainId;
|
|
7114
7141
|
this.store = opts.persistSession === false ? null : opts.sessionStore ?? createDefaultSessionStore();
|
|
7115
7142
|
this.onSignaturePrompt = opts.onSignaturePrompt;
|
|
@@ -7363,12 +7390,16 @@ var ServerSession = class {
|
|
|
7363
7390
|
this.signer,
|
|
7364
7391
|
this.chainId,
|
|
7365
7392
|
this.loginAudience,
|
|
7366
|
-
reuse
|
|
7393
|
+
reuse,
|
|
7394
|
+
reuse ? reuse.withStatement === true : this.withStatement
|
|
7367
7395
|
);
|
|
7368
7396
|
if (storeKey && !reuse) {
|
|
7369
7397
|
store.savePending?.(storeKey, {
|
|
7370
7398
|
issuedAt: payload.message.issuedAt,
|
|
7371
|
-
nonce: payload.message.nonce
|
|
7399
|
+
nonce: payload.message.nonce,
|
|
7400
|
+
// Recorded so a resume reproduces this digest even if the
|
|
7401
|
+
// capability flag changes before the owners finish signing.
|
|
7402
|
+
withStatement: this.withStatement
|
|
7372
7403
|
});
|
|
7373
7404
|
}
|
|
7374
7405
|
session = await this.login(payload);
|
|
@@ -10434,6 +10465,101 @@ var LoanStatus = /* @__PURE__ */ ((LoanStatus2) => {
|
|
|
10434
10465
|
|
|
10435
10466
|
// src/modules/loan/loan-query.module.ts
|
|
10436
10467
|
var import_ethers12 = require("ethers");
|
|
10468
|
+
|
|
10469
|
+
// src/constants/chunks/sdk-limits.ts
|
|
10470
|
+
var THE_GRAPH_MAX_BATCH_SIZE = 1e3;
|
|
10471
|
+
|
|
10472
|
+
// src/utils/borrower-ucd-debt-summary.ts
|
|
10473
|
+
var BORROWER_DEBT_ROWS_PAGE_SIZE = THE_GRAPH_MAX_BATCH_SIZE;
|
|
10474
|
+
var BORROWER_DEBT_ROWS_MAX_PAGES = 10;
|
|
10475
|
+
async function collectBorrowerDebtRows(fetchPage) {
|
|
10476
|
+
const pageSize = BORROWER_DEBT_ROWS_PAGE_SIZE;
|
|
10477
|
+
const rows = [];
|
|
10478
|
+
for (let page = 0; page < BORROWER_DEBT_ROWS_MAX_PAGES; page++) {
|
|
10479
|
+
const batch = await fetchPage(page * pageSize, pageSize);
|
|
10480
|
+
if (batch.length > pageSize) {
|
|
10481
|
+
throw new Error(
|
|
10482
|
+
`Borrower debt page ${page} returned ${batch.length} rows for first: ${pageSize}`
|
|
10483
|
+
);
|
|
10484
|
+
}
|
|
10485
|
+
rows.push(...batch);
|
|
10486
|
+
if (batch.length < pageSize) {
|
|
10487
|
+
return rows;
|
|
10488
|
+
}
|
|
10489
|
+
}
|
|
10490
|
+
throw new Error(
|
|
10491
|
+
`Borrower debt rows exceeded ${BORROWER_DEBT_ROWS_MAX_PAGES} pages of ${pageSize} (${rows.length} rows so far); refusing to return a partial total`
|
|
10492
|
+
);
|
|
10493
|
+
}
|
|
10494
|
+
function parseSubgraphLoanStatus(label) {
|
|
10495
|
+
if (typeof label !== "string") {
|
|
10496
|
+
throw new Error(`Subgraph loan status is not a string: ${String(label)}`);
|
|
10497
|
+
}
|
|
10498
|
+
const value = LoanStatus[label];
|
|
10499
|
+
if (typeof value !== "number") {
|
|
10500
|
+
throw new Error(`Unknown subgraph loan status label: "${label}"`);
|
|
10501
|
+
}
|
|
10502
|
+
return value;
|
|
10503
|
+
}
|
|
10504
|
+
function parseSubgraphWei(row) {
|
|
10505
|
+
const raw = row.ucdDebt || "0";
|
|
10506
|
+
if (typeof raw !== "string") {
|
|
10507
|
+
throw new Error(
|
|
10508
|
+
`ucdDebt on position ${row.id} is not a string: ${String(raw)}`
|
|
10509
|
+
);
|
|
10510
|
+
}
|
|
10511
|
+
let wei;
|
|
10512
|
+
try {
|
|
10513
|
+
wei = BigInt(raw);
|
|
10514
|
+
} catch (error) {
|
|
10515
|
+
throw new Error(
|
|
10516
|
+
`ucdDebt on position ${row.id} is not an integer wei string: "${raw}"`,
|
|
10517
|
+
{ cause: error }
|
|
10518
|
+
);
|
|
10519
|
+
}
|
|
10520
|
+
if (wei < 0n) {
|
|
10521
|
+
throw new Error(`Negative ucdDebt on position ${row.id}: ${raw}`);
|
|
10522
|
+
}
|
|
10523
|
+
return wei;
|
|
10524
|
+
}
|
|
10525
|
+
var WEI_PER_UCD = 10n ** 18n;
|
|
10526
|
+
function weiToHumanUcdString(wei) {
|
|
10527
|
+
if (wei < 0n) {
|
|
10528
|
+
throw new Error(`Cannot render negative wei: ${wei}`);
|
|
10529
|
+
}
|
|
10530
|
+
const whole = wei / WEI_PER_UCD;
|
|
10531
|
+
const frac = (wei % WEI_PER_UCD).toString().padStart(18, "0").replace(/0+$/, "");
|
|
10532
|
+
return frac.length > 0 ? `${whole}.${frac}` : `${whole}`;
|
|
10533
|
+
}
|
|
10534
|
+
var STATUS_ORDINALS = Object.values(LoanStatus).filter((v) => typeof v === "number").sort((a, b) => a - b);
|
|
10535
|
+
function buildBorrowerUcdDebtSummary(borrower, rows, fetchedAt = Date.now()) {
|
|
10536
|
+
let totalWei = 0n;
|
|
10537
|
+
const counts = /* @__PURE__ */ new Map();
|
|
10538
|
+
for (const row of rows) {
|
|
10539
|
+
totalWei += parseSubgraphWei(row);
|
|
10540
|
+
const status = parseSubgraphLoanStatus(row.status);
|
|
10541
|
+
counts.set(status, (counts.get(status) ?? 0) + 1);
|
|
10542
|
+
}
|
|
10543
|
+
const byStatus = [];
|
|
10544
|
+
for (const status of STATUS_ORDINALS) {
|
|
10545
|
+
const count = counts.get(status);
|
|
10546
|
+
if (count === void 0) {
|
|
10547
|
+
continue;
|
|
10548
|
+
}
|
|
10549
|
+
byStatus.push({ status, statusLabel: LoanStatus[status], count });
|
|
10550
|
+
}
|
|
10551
|
+
return {
|
|
10552
|
+
borrower: borrower.toLowerCase(),
|
|
10553
|
+
loanCount: rows.length,
|
|
10554
|
+
totalUcdDebt: totalWei.toString(),
|
|
10555
|
+
totalUcdDebtHuman: weiToHumanUcdString(totalWei),
|
|
10556
|
+
byStatus,
|
|
10557
|
+
source: "subgraph",
|
|
10558
|
+
fetchedAt
|
|
10559
|
+
};
|
|
10560
|
+
}
|
|
10561
|
+
|
|
10562
|
+
// src/modules/loan/loan-query.module.ts
|
|
10437
10563
|
var POSITION_CORE_ABI = [
|
|
10438
10564
|
"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))"
|
|
10439
10565
|
];
|
|
@@ -10826,6 +10952,43 @@ var LoanQuery = class {
|
|
|
10826
10952
|
async getLoansByBorrower(borrower, pagination, orderBy, orderDirection) {
|
|
10827
10953
|
return this.getLoans({ borrower, orderBy, orderDirection }, pagination);
|
|
10828
10954
|
}
|
|
10955
|
+
/**
|
|
10956
|
+
* Combined UCD debt + by-status counts for a borrower from ONE pass over the
|
|
10957
|
+
* subgraph's raw rows (`graphClient.getBorrowerDebtRows`): BigInt wei sum, strict
|
|
10958
|
+
* status labels, `source: "subgraph"`.
|
|
10959
|
+
*
|
|
10960
|
+
* INDEXED figure, not a transaction input — the subgraph lags writes. Anything that
|
|
10961
|
+
* sizes a repayment reads the chain (`getPositionDetailsView`). Fails loud: a page
|
|
10962
|
+
* failure or the page cap is a SUBGRAPH-category failure carrying the cause; there
|
|
10963
|
+
* is no partial total.
|
|
10964
|
+
*/
|
|
10965
|
+
async getBorrowerUcdDebtSummary(borrower) {
|
|
10966
|
+
const trimmed = typeof borrower === "string" ? borrower.trim() : "";
|
|
10967
|
+
if (!(0, import_ethers12.isAddress)(trimmed)) {
|
|
10968
|
+
return failure(
|
|
10969
|
+
new SDKError({
|
|
10970
|
+
message: `Borrower must be an EVM address for a UCD debt summary, got "${trimmed}"`,
|
|
10971
|
+
category: "VALIDATION" /* VALIDATION */,
|
|
10972
|
+
severity: "LOW" /* LOW */,
|
|
10973
|
+
context: { borrower: trimmed }
|
|
10974
|
+
})
|
|
10975
|
+
);
|
|
10976
|
+
}
|
|
10977
|
+
return tryCatchAsync(
|
|
10978
|
+
async () => {
|
|
10979
|
+
const rows = await this.config.graphClient.getBorrowerDebtRows(trimmed);
|
|
10980
|
+
return buildBorrowerUcdDebtSummary(trimmed, rows);
|
|
10981
|
+
},
|
|
10982
|
+
(error) => error instanceof SDKError ? error : new SDKError({
|
|
10983
|
+
message: `Borrower UCD debt summary failed for ${trimmed}: ${error instanceof Error ? error.message : String(error)}`,
|
|
10984
|
+
category: "SUBGRAPH" /* SUBGRAPH */,
|
|
10985
|
+
severity: "MEDIUM" /* MEDIUM */,
|
|
10986
|
+
originalError: error instanceof Error ? error : void 0,
|
|
10987
|
+
cause: error,
|
|
10988
|
+
context: { borrower: trimmed }
|
|
10989
|
+
})
|
|
10990
|
+
);
|
|
10991
|
+
}
|
|
10829
10992
|
/**
|
|
10830
10993
|
* Get active loans (status = ACTIVE)
|
|
10831
10994
|
*
|
|
@@ -12360,10 +12523,36 @@ var GraphClient = class {
|
|
|
12360
12523
|
}
|
|
12361
12524
|
};
|
|
12362
12525
|
|
|
12363
|
-
// src/constants/chunks/sdk-limits.ts
|
|
12364
|
-
var THE_GRAPH_MAX_BATCH_SIZE = 1e3;
|
|
12365
|
-
|
|
12366
12526
|
// src/graphs/diamond-hands.ts
|
|
12527
|
+
var GET_USER_POSITIONS_DOCUMENT = `
|
|
12528
|
+
query GetUserPositions($borrower: Bytes!, $first: Int, $skip: Int, $orderBy: Position_orderBy, $orderDirection: OrderDirection) {
|
|
12529
|
+
user(id: $borrower) {
|
|
12530
|
+
id
|
|
12531
|
+
positions(first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) {
|
|
12532
|
+
id
|
|
12533
|
+
pkpId
|
|
12534
|
+
borrower {
|
|
12535
|
+
createdAt
|
|
12536
|
+
id
|
|
12537
|
+
}
|
|
12538
|
+
# btcAmount removed - balance is now queried on-chain via lit-actions with signature validation
|
|
12539
|
+
ucdMinted
|
|
12540
|
+
ucdPaid
|
|
12541
|
+
ucdDebt
|
|
12542
|
+
collateralRatio
|
|
12543
|
+
requestedCollateralRatio
|
|
12544
|
+
selectedTerm
|
|
12545
|
+
status
|
|
12546
|
+
createdAt
|
|
12547
|
+
createdAtBlock
|
|
12548
|
+
lastUpdated
|
|
12549
|
+
originalTerm
|
|
12550
|
+
remainingDebt
|
|
12551
|
+
expiryAt
|
|
12552
|
+
}
|
|
12553
|
+
}
|
|
12554
|
+
}
|
|
12555
|
+
`;
|
|
12367
12556
|
function normalizePkpId2(pkpId) {
|
|
12368
12557
|
const raw = pkpId.startsWith("0x") ? pkpId.slice(2) : pkpId;
|
|
12369
12558
|
if (raw.length === 64 && raw.startsWith("000000000000000000000000") && raw !== "0".repeat(64)) {
|
|
@@ -12681,35 +12870,7 @@ var DiamondHandsGraph = class {
|
|
|
12681
12870
|
* Get user positions from subgraph
|
|
12682
12871
|
*/
|
|
12683
12872
|
async getUserPositionsOnly(userAddress, first, skip, orderBy, orderDirection) {
|
|
12684
|
-
const query =
|
|
12685
|
-
query GetUserPositions($borrower: Bytes!, $first: Int, $skip: Int, $orderBy: Position_orderBy, $orderDirection: OrderDirection) {
|
|
12686
|
-
user(id: $borrower) {
|
|
12687
|
-
id
|
|
12688
|
-
positions(first: $first, skip: $skip, orderBy: $orderBy, orderDirection: $orderDirection) {
|
|
12689
|
-
id
|
|
12690
|
-
pkpId
|
|
12691
|
-
borrower {
|
|
12692
|
-
createdAt
|
|
12693
|
-
id
|
|
12694
|
-
}
|
|
12695
|
-
# btcAmount removed - balance is now queried on-chain via lit-actions with signature validation
|
|
12696
|
-
ucdMinted
|
|
12697
|
-
ucdPaid
|
|
12698
|
-
ucdDebt
|
|
12699
|
-
collateralRatio
|
|
12700
|
-
requestedCollateralRatio
|
|
12701
|
-
selectedTerm
|
|
12702
|
-
status
|
|
12703
|
-
createdAt
|
|
12704
|
-
createdAtBlock
|
|
12705
|
-
lastUpdated
|
|
12706
|
-
originalTerm
|
|
12707
|
-
remainingDebt
|
|
12708
|
-
expiryAt
|
|
12709
|
-
}
|
|
12710
|
-
}
|
|
12711
|
-
}
|
|
12712
|
-
`;
|
|
12873
|
+
const query = GET_USER_POSITIONS_DOCUMENT;
|
|
12713
12874
|
const variables = {
|
|
12714
12875
|
borrower: userAddress.toLowerCase(),
|
|
12715
12876
|
first: first || void 0,
|
|
@@ -12727,6 +12888,45 @@ var DiamondHandsGraph = class {
|
|
|
12727
12888
|
}
|
|
12728
12889
|
return positions;
|
|
12729
12890
|
}
|
|
12891
|
+
/**
|
|
12892
|
+
* Raw `{ id, status, ucdDebt }` for EVERY position of a borrower: one round trip per
|
|
12893
|
+
* 1000 positions, no count walk, no vault RPCs, and no `LoanData` transform (which
|
|
12894
|
+
* coerces wei to a float and leaves `status` a string cast to the numeric enum).
|
|
12895
|
+
*
|
|
12896
|
+
* Deliberately issues the `GetUserPositions` document rather than a leaner one: the
|
|
12897
|
+
* lit-ops graph proxy only passes allowlisted documents (graph-document-policy.ts),
|
|
12898
|
+
* and that one is already listed, so this needs no server change. The extra fields
|
|
12899
|
+
* cost bytes, not round trips.
|
|
12900
|
+
*
|
|
12901
|
+
* Throws past `BORROWER_DEBT_ROWS_MAX_PAGES` — never a partial set.
|
|
12902
|
+
*/
|
|
12903
|
+
async getBorrowerDebtRows(borrower) {
|
|
12904
|
+
const borrowerId = borrower.toLowerCase();
|
|
12905
|
+
return collectBorrowerDebtRows(async (skip, first) => {
|
|
12906
|
+
const result = await this.client.execute(GET_USER_POSITIONS_DOCUMENT, {
|
|
12907
|
+
borrower: borrowerId,
|
|
12908
|
+
first,
|
|
12909
|
+
skip,
|
|
12910
|
+
orderBy: "id",
|
|
12911
|
+
orderDirection: "asc"
|
|
12912
|
+
});
|
|
12913
|
+
if (result?.user == null) {
|
|
12914
|
+
if (skip > 0) {
|
|
12915
|
+
throw new Error(
|
|
12916
|
+
`getBorrowerDebtRows: user ${borrowerId} vanished from the subgraph at skip ${skip}`
|
|
12917
|
+
);
|
|
12918
|
+
}
|
|
12919
|
+
return [];
|
|
12920
|
+
}
|
|
12921
|
+
const positions = result.user.positions;
|
|
12922
|
+
if (!Array.isArray(positions)) {
|
|
12923
|
+
throw new Error(
|
|
12924
|
+
`getBorrowerDebtRows: subgraph returned no positions array for ${borrowerId} at skip ${skip}`
|
|
12925
|
+
);
|
|
12926
|
+
}
|
|
12927
|
+
return positions.filter((p) => p != null);
|
|
12928
|
+
});
|
|
12929
|
+
}
|
|
12730
12930
|
/**
|
|
12731
12931
|
* Get user positions, plus an accurate total count.
|
|
12732
12932
|
*
|
|
@@ -19709,6 +19909,9 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
19709
19909
|
signer: config.authSigner ?? config.contractSigner,
|
|
19710
19910
|
serviceEndpoint: config.serviceEndpoint,
|
|
19711
19911
|
loginAudience: config.loginAudience,
|
|
19912
|
+
// Only meaningful alongside an audience; the servers derive the expected
|
|
19913
|
+
// sentence from its host. Defaults off so Node callers are untouched.
|
|
19914
|
+
withStatement: config.loginStatement === true,
|
|
19712
19915
|
chainId: config.chainId,
|
|
19713
19916
|
persistSession: config.sessionPersistence?.enabled,
|
|
19714
19917
|
sessionStore: config.sessionPersistence?.store,
|
|
@@ -25674,6 +25877,18 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
|
|
|
25674
25877
|
async getLoansByBorrower(borrower, pagination, orderBy, orderDirection) {
|
|
25675
25878
|
return this.loanQuery.getLoansByBorrower(borrower, pagination, orderBy, orderDirection);
|
|
25676
25879
|
}
|
|
25880
|
+
/**
|
|
25881
|
+
* Combined UCD debt (canonical wei string + display string) and by-status loan
|
|
25882
|
+
* counts for a borrower, from one pass over the subgraph. `source: "subgraph"` —
|
|
25883
|
+
* an INDEXED, informational figure that lags writes; never size a transaction
|
|
25884
|
+
* from it (repayments read the chain via `getPositionDetailsView`).
|
|
25885
|
+
*
|
|
25886
|
+
* Fails loud with a SUBGRAPH-category `SDKError` (cause attached) on any page
|
|
25887
|
+
* failure or past 10,000 positions; never returns a partial total.
|
|
25888
|
+
*/
|
|
25889
|
+
async getBorrowerUcdDebtSummary(borrower) {
|
|
25890
|
+
return this.loanQuery.getBorrowerUcdDebtSummary(borrower);
|
|
25891
|
+
}
|
|
25677
25892
|
/**
|
|
25678
25893
|
* Get all active loans
|
|
25679
25894
|
*
|