@gvnrdao/dh-sdk 0.0.328 → 0.0.330

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/dist/index.mjs CHANGED
@@ -552,6 +552,14 @@ var init_deployment_addresses = __esm({
552
552
  reason: "2026-08 agent-stack release. The mainnet PositionManagerViews at 0x9225647a was deployed as a BARE implementation (EIP-1967 slot 0x00), so upgradeToAndCall reverts UUPSUnauthorizedCallContext \u2014 it could never be upgraded. PositionManager.setViews (added in the same Execute B batch) removed the repoint blocker, so Views was replaced behind a new proxy rather than upgraded. This makes the Pashov F3 renewal debt-ceiling check REACHABLE for the first time: ExceedsMaxLoan (0x499e7985) is present in 0xE68DC096 and absent from 0x9225647a. Views holds no protocol state (8 wiring slots + roles + pause), which is what made replacement safe. The old contract is ABANDONED, not decommissioned \u2014 it stays live and callable as the rollback target for setViews. NOTE: VERSION reads 1.1.1 on both sides; verify by address.",
553
553
  executeTx: "0xf1f8d5fc13f45d7a3f932bf1a2d6e29381fdd41cd31298ad785bf7688df07fe6",
554
554
  block: 25673200
555
+ },
556
+ AgentDelegationRegistry: {
557
+ newImplementation: "0xa9fF405Edd4329fc3159a64F56B37EBD253e4928",
558
+ upgradedAt: "2026-07-31T22:03:35.000Z",
559
+ upgradedBy: "AdminModule/Safe audit-batch upgradeToAndCall; executed by 0xEba7BfAeB35ee18408b424Ee0BB45AdDf776589f in tx 0x8fed0ab7eee3aaaa3792a35b40b0ef74bd09cfb1a175934afd846bee546048d4 (block 25655710)",
560
+ reason: "Reconciled from chain \u2014 mainnet audit rollout batch (cross-ref onchain-auth/.safe-batches/mainnet-audit-execute.json)",
561
+ executeTx: "0x8fed0ab7eee3aaaa3792a35b40b0ef74bd09cfb1a175934afd846bee546048d4",
562
+ block: 25655710
555
563
  }
556
564
  },
557
565
  latestEnv: {
@@ -12037,6 +12045,169 @@ function normalizePkpId2(pkpId) {
12037
12045
  }
12038
12046
  return pkpId;
12039
12047
  }
12048
+ function hasLoanEventsFilter(filter) {
12049
+ if (!filter)
12050
+ return false;
12051
+ return filter.eventTypes !== void 0 || filter.fromTimestamp !== void 0 || filter.toTimestamp !== void 0 || filter.fromBlock !== void 0 || filter.toBlock !== void 0 || filter.limit !== void 0 || filter.orderDirection !== void 0;
12052
+ }
12053
+ var WALLET_LOAN_EVENTS_QUERY = `
12054
+ query GetWalletLoanEvents($borrower: Bytes!, $first: Int!, $limit: Int!, $orderDirection: OrderDirection!) {
12055
+ positions(where: { borrower: $borrower }, first: $first, orderBy: id) {
12056
+ id
12057
+ payments(first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12058
+ id
12059
+ payer { id }
12060
+ amount
12061
+ type
12062
+ transactionHash
12063
+ timestamp
12064
+ blockNumber
12065
+ logIndex
12066
+ newDebt
12067
+ newCollateralRatio
12068
+ }
12069
+ statusUpdates(first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12070
+ id
12071
+ oldStatus
12072
+ newStatus
12073
+ reason
12074
+ timestamp
12075
+ blockNumber
12076
+ transactionHash
12077
+ }
12078
+ liquidation {
12079
+ id
12080
+ liquidator
12081
+ liquidatedAmount
12082
+ collateralSeized
12083
+ transactionHash
12084
+ timestamp
12085
+ blockNumber
12086
+ commitHash
12087
+ commitTimestamp
12088
+ revealDelay
12089
+ }
12090
+ withdrawals(first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12091
+ id
12092
+ withdrawalAmount
12093
+ withdrawalAddress
12094
+ networkFee
12095
+ timestamp
12096
+ blockNumber
12097
+ transactionHash
12098
+ logIndex
12099
+ }
12100
+ renewals(first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12101
+ id
12102
+ extensionTerm
12103
+ extensionFee
12104
+ newExpiryDate
12105
+ timestamp
12106
+ blockNumber
12107
+ transactionHash
12108
+ logIndex
12109
+ }
12110
+ operationFailures(first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12111
+ id
12112
+ operationType
12113
+ reason
12114
+ errorCode
12115
+ amount
12116
+ timestamp
12117
+ blockNumber
12118
+ transactionHash
12119
+ logIndex
12120
+ }
12121
+ mintRequests(first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12122
+ id
12123
+ pkpId
12124
+ borrower { id }
12125
+ mintAmount
12126
+ originationFee
12127
+ newDebt
12128
+ newCollateralRatio
12129
+ btcPrice
12130
+ quantumTimestamp
12131
+ opId
12132
+ timestamp
12133
+ blockNumber
12134
+ transactionHash
12135
+ logIndex
12136
+ }
12137
+ burnRequests(first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12138
+ id
12139
+ pkpId
12140
+ borrower { id }
12141
+ burnAmount
12142
+ remainingDebt
12143
+ newCollateralRatio
12144
+ opId
12145
+ timestamp
12146
+ blockNumber
12147
+ transactionHash
12148
+ logIndex
12149
+ }
12150
+ mintingFees(first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12151
+ id
12152
+ borrower { id }
12153
+ feeAmount
12154
+ mintAmount
12155
+ newDebt
12156
+ btcPrice
12157
+ quantumTimestamp
12158
+ timestamp
12159
+ blockNumber
12160
+ transactionHash
12161
+ logIndex
12162
+ }
12163
+ renewalFees(first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12164
+ id
12165
+ borrower { id }
12166
+ feeAmount
12167
+ extensionTerm
12168
+ newExpiryDate
12169
+ timestamp
12170
+ blockNumber
12171
+ transactionHash
12172
+ logIndex
12173
+ }
12174
+ positionCreatedEvents(first: 1, orderBy: timestamp, orderDirection: asc) {
12175
+ id
12176
+ pkpId
12177
+ borrower { id }
12178
+ requestedCollateralRatio
12179
+ selectedTerm
12180
+ expiryAt
12181
+ timestamp
12182
+ blockNumber
12183
+ transactionHash
12184
+ }
12185
+ }
12186
+ ucdmintEvents(where: { position_: { borrower: $borrower } }, first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12187
+ id
12188
+ position { id }
12189
+ amount
12190
+ proof
12191
+ minter { id }
12192
+ isCommunityMint
12193
+ timestamp
12194
+ blockNumber
12195
+ transactionHash
12196
+ }
12197
+ collateralEvents(where: { position_: { borrower: $borrower } }, first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12198
+ id
12199
+ position { id }
12200
+ eventType
12201
+ networkFee
12202
+ remainingCollateral
12203
+ collateralRatio
12204
+ timestamp
12205
+ blockNumber
12206
+ transactionHash
12207
+ logIndex
12208
+ }
12209
+ }
12210
+ `;
12040
12211
  var DiamondHandsGraph = class {
12041
12212
  client;
12042
12213
  defaultPageSize;
@@ -12142,7 +12313,7 @@ var DiamondHandsGraph = class {
12142
12313
  /**
12143
12314
  * Get user positions from subgraph
12144
12315
  */
12145
- async getUserPositions(userAddress, first, skip, orderBy, orderDirection) {
12316
+ async getUserPositionsOnly(userAddress, first, skip, orderBy, orderDirection) {
12146
12317
  const query = `
12147
12318
  query GetUserPositions($borrower: Bytes!, $first: Int, $skip: Int, $orderBy: Position_orderBy, $orderDirection: OrderDirection) {
12148
12319
  user(id: $borrower) {
@@ -12183,17 +12354,28 @@ var DiamondHandsGraph = class {
12183
12354
  query,
12184
12355
  variables
12185
12356
  );
12186
- const total = await this.getTotalCountPaginated("{ borrower: $borrower }", {
12187
- borrower: userAddress.toLowerCase()
12188
- });
12189
12357
  let positions = [];
12190
12358
  if (result && result.user && Array.isArray(result.user.positions)) {
12191
12359
  positions = result.user.positions.filter((p) => p != null).map((p) => this.transformGraphPositionToLoanData(p));
12192
12360
  }
12193
- return {
12194
- positions,
12195
- total
12196
- };
12361
+ return positions;
12362
+ }
12363
+ /**
12364
+ * Get user positions, plus an accurate total count.
12365
+ *
12366
+ * The count is a SECOND round trip (`getTotalCountPaginated`, which pages to handle
12367
+ * >1000 items). Callers that only need the rows should use `getUserPositionsOnly` and
12368
+ * skip it — the event paths did not, and paid for a count they immediately discarded on
12369
+ * every activity refresh.
12370
+ */
12371
+ async getUserPositions(userAddress, first, skip, orderBy, orderDirection) {
12372
+ const [positions, total] = await Promise.all([
12373
+ this.getUserPositionsOnly(userAddress, first, skip, orderBy, orderDirection),
12374
+ this.getTotalCountPaginated("{ borrower: $borrower }", {
12375
+ borrower: userAddress.toLowerCase()
12376
+ })
12377
+ ]);
12378
+ return { positions, total };
12197
12379
  }
12198
12380
  /**
12199
12381
  * Get user positions with enriched Bitcoin balance data
@@ -13284,6 +13466,16 @@ var DiamondHandsGraph = class {
13284
13466
  orderDirection
13285
13467
  };
13286
13468
  const result = await this.client.execute(query, variables);
13469
+ return this.assembleLoanEvents(positionId, result);
13470
+ }
13471
+ /**
13472
+ * Map one position's raw event payload into `LoanEvents`.
13473
+ *
13474
+ * Extracted verbatim from getLoanEvents so the wallet-collapsed path produces
13475
+ * byte-identical objects by construction rather than by a second implementation
13476
+ * that has to be kept in step by hand.
13477
+ */
13478
+ assembleLoanEvents(positionId, result) {
13287
13479
  const rawCreated = result.positionCreatedEvents?.[0];
13288
13480
  const positionCreated = rawCreated ? {
13289
13481
  id: rawCreated.id,
@@ -13488,7 +13680,22 @@ var DiamondHandsGraph = class {
13488
13680
  * @returns One LoanEvents object per position; empty array if the wallet has none.
13489
13681
  */
13490
13682
  async getWalletLoanEvents(userAddress, filter) {
13491
- const { positions } = await this.getUserPositions(userAddress);
13683
+ if (!hasLoanEventsFilter(filter)) {
13684
+ return this.getWalletLoanEventsCollapsed(userAddress);
13685
+ }
13686
+ return this.getWalletLoanEventsFanout(userAddress, filter);
13687
+ }
13688
+ /**
13689
+ * The per-position path: enumerate the wallet's positions, then one `GetLoanEvents` each.
13690
+ *
13691
+ * Used for filtered calls, and as the completeness fallback when the collapsed document
13692
+ * hits a page limit. Costs `1 + N` requests, which is exactly why it is not the default.
13693
+ */
13694
+ async getWalletLoanEventsFanout(userAddress, filter) {
13695
+ const positions = await this.getUserPositionsOnly(
13696
+ userAddress,
13697
+ THE_GRAPH_MAX_BATCH_SIZE
13698
+ );
13492
13699
  const positionIds = positions.map((p) => p?.id != null ? String(p.id) : "").filter((id) => id.length > 0);
13493
13700
  if (positionIds.length === 0)
13494
13701
  return [];
@@ -13503,6 +13710,81 @@ var DiamondHandsGraph = class {
13503
13710
  }
13504
13711
  return results;
13505
13712
  }
13713
+ /**
13714
+ * The whole wallet's event history in ONE request.
13715
+ *
13716
+ * Replaces `1 + N` round trips (one position enumeration plus one per position) with a
13717
+ * single document. Measured on Sepolia: a wallet holding 44 positions cost 46 requests
13718
+ * per activity refresh against a per-wallet budget of 30/min — this makes it 1.
13719
+ *
13720
+ * Three subgraph facts make it possible, all verified against the deployed schema rather
13721
+ * than assumed:
13722
+ * - ten event collections are `@derivedFrom` on Position, so they nest under a
13723
+ * borrower-filtered `positions(...)`;
13724
+ * - `positionCreatedEvents` is ALSO derived on Position — `getLoanEvents` queries it
13725
+ * top-level, which it never needed to;
13726
+ * - `ucdmintEvents` / `collateralEvents` are not derived and carry no `borrower`
13727
+ * field, but graph-node's nested `position_: { borrower }` filter reaches them.
13728
+ * Without that last one this would be two requests, not one.
13729
+ */
13730
+ async getWalletLoanEventsCollapsed(userAddress) {
13731
+ const borrower = userAddress.toLowerCase();
13732
+ const pageSize = THE_GRAPH_MAX_BATCH_SIZE;
13733
+ const result = await this.client.execute(
13734
+ WALLET_LOAN_EVENTS_QUERY,
13735
+ {
13736
+ borrower,
13737
+ first: pageSize,
13738
+ limit: pageSize,
13739
+ orderDirection: "desc"
13740
+ }
13741
+ );
13742
+ const positions = result.positions || [];
13743
+ const ucdmintEvents = result.ucdmintEvents || [];
13744
+ const collateralEvents = result.collateralEvents || [];
13745
+ const truncated = positions.length >= pageSize || ucdmintEvents.length >= pageSize || collateralEvents.length >= pageSize;
13746
+ if (truncated) {
13747
+ log.warn(
13748
+ "[DiamondHandsGraph] wallet event page limit reached \u2014 falling back to the per-position path to avoid returning a partial history",
13749
+ {
13750
+ borrower,
13751
+ pageSize,
13752
+ positions: positions.length,
13753
+ ucdmintEvents: ucdmintEvents.length,
13754
+ collateralEvents: collateralEvents.length
13755
+ }
13756
+ );
13757
+ return this.getWalletLoanEventsFanout(userAddress);
13758
+ }
13759
+ if (positions.length === 0)
13760
+ return [];
13761
+ const mintsByPosition = /* @__PURE__ */ new Map();
13762
+ for (const m of result.ucdmintEvents || []) {
13763
+ const key = m.position?.id;
13764
+ if (!key)
13765
+ continue;
13766
+ const bucket = mintsByPosition.get(key) || [];
13767
+ bucket.push(m);
13768
+ mintsByPosition.set(key, bucket);
13769
+ }
13770
+ const collateralByPosition = /* @__PURE__ */ new Map();
13771
+ for (const c of result.collateralEvents || []) {
13772
+ const key = c.position?.id;
13773
+ if (!key)
13774
+ continue;
13775
+ const bucket = collateralByPosition.get(key) || [];
13776
+ bucket.push(c);
13777
+ collateralByPosition.set(key, bucket);
13778
+ }
13779
+ return positions.map(
13780
+ (p) => this.assembleLoanEvents(p.id, {
13781
+ position: p,
13782
+ positionCreatedEvents: p.positionCreatedEvents || [],
13783
+ ucdmintEvents: mintsByPosition.get(p.id) || [],
13784
+ collateralEvents: collateralByPosition.get(p.id) || []
13785
+ })
13786
+ );
13787
+ }
13506
13788
  // ============================================================================
13507
13789
  // Protocol-wide Events
13508
13790
  // ============================================================================
@@ -18421,16 +18703,6 @@ var import_dh_lit_actions2 = __toESM(require_pkg_src());
18421
18703
  import { LitOps, broadcastSignedBitcoinTransaction } from "@gvnrdao/dh-lit-ops";
18422
18704
 
18423
18705
  // src/utils/telegram-messaging.utils.ts
18424
- var TelegramBot = null;
18425
- try {
18426
- if (typeof window === "undefined" && typeof global !== "undefined") {
18427
- TelegramBot = __require("node-telegram-bot-api");
18428
- }
18429
- } catch (error) {
18430
- log.warn("Telegram bot API not available", {
18431
- error: error instanceof Error ? error.message : String(error)
18432
- });
18433
- }
18434
18706
  function formatMintMessage(mintAmount, loanId, txHash) {
18435
18707
  return `\u2705 DH SDK: ${mintAmount} UCD was minted for loan id: ${loanId} Tx: https://sepolia.etherscan.io/tx/${txHash}`;
18436
18708
  }
@@ -487,6 +487,14 @@ var MAINNET_DEPLOYMENT = {
487
487
  reason: "2026-08 agent-stack release. The mainnet PositionManagerViews at 0x9225647a was deployed as a BARE implementation (EIP-1967 slot 0x00), so upgradeToAndCall reverts UUPSUnauthorizedCallContext \u2014 it could never be upgraded. PositionManager.setViews (added in the same Execute B batch) removed the repoint blocker, so Views was replaced behind a new proxy rather than upgraded. This makes the Pashov F3 renewal debt-ceiling check REACHABLE for the first time: ExceedsMaxLoan (0x499e7985) is present in 0xE68DC096 and absent from 0x9225647a. Views holds no protocol state (8 wiring slots + roles + pause), which is what made replacement safe. The old contract is ABANDONED, not decommissioned \u2014 it stays live and callable as the rollback target for setViews. NOTE: VERSION reads 1.1.1 on both sides; verify by address.",
488
488
  executeTx: "0xf1f8d5fc13f45d7a3f932bf1a2d6e29381fdd41cd31298ad785bf7688df07fe6",
489
489
  block: 25673200
490
+ },
491
+ AgentDelegationRegistry: {
492
+ newImplementation: "0xa9fF405Edd4329fc3159a64F56B37EBD253e4928",
493
+ upgradedAt: "2026-07-31T22:03:35.000Z",
494
+ upgradedBy: "AdminModule/Safe audit-batch upgradeToAndCall; executed by 0xEba7BfAeB35ee18408b424Ee0BB45AdDf776589f in tx 0x8fed0ab7eee3aaaa3792a35b40b0ef74bd09cfb1a175934afd846bee546048d4 (block 25655710)",
495
+ reason: "Reconciled from chain \u2014 mainnet audit rollout batch (cross-ref onchain-auth/.safe-batches/mainnet-audit-execute.json)",
496
+ executeTx: "0x8fed0ab7eee3aaaa3792a35b40b0ef74bd09cfb1a175934afd846bee546048d4",
497
+ block: 25655710
490
498
  }
491
499
  },
492
500
  latestEnv: {
@@ -460,6 +460,14 @@ var MAINNET_DEPLOYMENT = {
460
460
  reason: "2026-08 agent-stack release. The mainnet PositionManagerViews at 0x9225647a was deployed as a BARE implementation (EIP-1967 slot 0x00), so upgradeToAndCall reverts UUPSUnauthorizedCallContext \u2014 it could never be upgraded. PositionManager.setViews (added in the same Execute B batch) removed the repoint blocker, so Views was replaced behind a new proxy rather than upgraded. This makes the Pashov F3 renewal debt-ceiling check REACHABLE for the first time: ExceedsMaxLoan (0x499e7985) is present in 0xE68DC096 and absent from 0x9225647a. Views holds no protocol state (8 wiring slots + roles + pause), which is what made replacement safe. The old contract is ABANDONED, not decommissioned \u2014 it stays live and callable as the rollback target for setViews. NOTE: VERSION reads 1.1.1 on both sides; verify by address.",
461
461
  executeTx: "0xf1f8d5fc13f45d7a3f932bf1a2d6e29381fdd41cd31298ad785bf7688df07fe6",
462
462
  block: 25673200
463
+ },
464
+ AgentDelegationRegistry: {
465
+ newImplementation: "0xa9fF405Edd4329fc3159a64F56B37EBD253e4928",
466
+ upgradedAt: "2026-07-31T22:03:35.000Z",
467
+ upgradedBy: "AdminModule/Safe audit-batch upgradeToAndCall; executed by 0xEba7BfAeB35ee18408b424Ee0BB45AdDf776589f in tx 0x8fed0ab7eee3aaaa3792a35b40b0ef74bd09cfb1a175934afd846bee546048d4 (block 25655710)",
468
+ reason: "Reconciled from chain \u2014 mainnet audit rollout batch (cross-ref onchain-auth/.safe-batches/mainnet-audit-execute.json)",
469
+ executeTx: "0x8fed0ab7eee3aaaa3792a35b40b0ef74bd09cfb1a175934afd846bee546048d4",
470
+ block: 25655710
463
471
  }
464
472
  },
465
473
  latestEnv: {
@@ -1,18 +1,18 @@
1
1
  /**
2
- * Telegram Messaging Utilities
2
+ * Notification Message Formatters
3
3
  *
4
- * Provides functionality to send messages to Telegram chats using the node-telegram-bot-api package
5
- */
6
- /**
7
- * Send a message to a Telegram chat
4
+ * Pure, dependency-free formatters for the protocol lifecycle messages the SDK
5
+ * emits. They are pure string builders: they choose no transport and perform no
6
+ * I/O.
8
7
  *
9
- * @param chatId - Telegram chat ID
10
- * @param chatToken - Telegram bot token
11
- * @param message - Message to send
12
- * @param threadId - Optional thread ID for message threads in groups
13
- * @returns Promise that resolves when message is sent, or rejects on error
8
+ * Dispatch lives elsewhere by design. `DiamondHandsSDK._relayNotification`
9
+ * hands the formatted string to the consumer's `notificationHandler` and, in
10
+ * `service` mode, POSTs it to `lit-ops-server` `/api/lit/notifications/send`;
11
+ * the server owns the bot credentials and talks to `api.telegram.org` directly
12
+ * (`lit-ops-server/src/services/notification.service.ts`). Keeping the token
13
+ * server-side is why no `sendTelegramMessage` belongs here — an in-SDK sender
14
+ * would need the bot token in every consumer's process.
14
15
  */
15
- export declare function sendTelegramMessage(chatId: string, chatToken: string, message: string, threadId?: number): Promise<void>;
16
16
  /**
17
17
  * Format a mint notification message
18
18
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gvnrdao/dh-sdk",
3
- "version": "0.0.328",
3
+ "version": "0.0.330",
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",
@@ -88,7 +88,7 @@
88
88
  "sideEffects": false,
89
89
  "dependencies": {
90
90
  "@gvnrdao/dh-lit-actions": "^0.0.321",
91
- "@gvnrdao/dh-lit-ops": "^0.0.313",
91
+ "@gvnrdao/dh-lit-ops": "^0.0.314",
92
92
  "@noble/hashes": "^1.5.0",
93
93
  "axios": "^1.17.0",
94
94
  "bech32": "^2.0.0",
@@ -106,7 +106,6 @@
106
106
  "@types/crypto-js": "^4.2.2",
107
107
  "@types/jest": "^29.0.0",
108
108
  "@types/node": "^20.0.0",
109
- "@types/node-telegram-bot-api": "^0.64.12",
110
109
  "@typescript-eslint/eslint-plugin": "^6.0.0",
111
110
  "@typescript-eslint/parser": "^6.0.0",
112
111
  "babel-jest": "^29.0.0",
@@ -132,8 +131,5 @@
132
131
  "bugs": {
133
132
  "url": "https://github.com/gvnrdao/diamond-hands/issues"
134
133
  },
135
- "homepage": "https://diamond-hands-protocol.com",
136
- "optionalDependencies": {
137
- "node-telegram-bot-api": "^0.67.0"
138
- }
134
+ "homepage": "https://diamond-hands-protocol.com"
139
135
  }