@gvnrdao/dh-sdk 0.0.327 → 0.0.329

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.js CHANGED
@@ -546,6 +546,14 @@ var init_deployment_addresses = __esm({
546
546
  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.",
547
547
  executeTx: "0xf1f8d5fc13f45d7a3f932bf1a2d6e29381fdd41cd31298ad785bf7688df07fe6",
548
548
  block: 25673200
549
+ },
550
+ AgentDelegationRegistry: {
551
+ newImplementation: "0xa9fF405Edd4329fc3159a64F56B37EBD253e4928",
552
+ upgradedAt: "2026-07-31T22:03:35.000Z",
553
+ upgradedBy: "AdminModule/Safe audit-batch upgradeToAndCall; executed by 0xEba7BfAeB35ee18408b424Ee0BB45AdDf776589f in tx 0x8fed0ab7eee3aaaa3792a35b40b0ef74bd09cfb1a175934afd846bee546048d4 (block 25655710)",
554
+ reason: "Reconciled from chain \u2014 mainnet audit rollout batch (cross-ref onchain-auth/.safe-batches/mainnet-audit-execute.json)",
555
+ executeTx: "0x8fed0ab7eee3aaaa3792a35b40b0ef74bd09cfb1a175934afd846bee546048d4",
556
+ block: 25655710
549
557
  }
550
558
  },
551
559
  latestEnv: {
@@ -678,9 +686,9 @@ var init_deployment_addresses = __esm({
678
686
  }
679
687
  });
680
688
 
681
- // node_modules/@gvnrdao/dh-lit-actions/pkg-dist/pkg-src/index.js
689
+ // ../../../../../../../../Users/rusty/Development/gvnr/diamond-hands/sdk/node_modules/@gvnrdao/dh-lit-actions/pkg-dist/pkg-src/index.js
682
690
  var require_pkg_src = __commonJS({
683
- "node_modules/@gvnrdao/dh-lit-actions/pkg-dist/pkg-src/index.js"(exports, module2) {
691
+ "../../../../../../../../Users/rusty/Development/gvnr/diamond-hands/sdk/node_modules/@gvnrdao/dh-lit-actions/pkg-dist/pkg-src/index.js"(exports, module2) {
684
692
  "use strict";
685
693
  var __defProp2 = Object.defineProperty;
686
694
  var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
@@ -1363,7 +1371,7 @@ var require_pkg_src = __commonJS({
1363
1371
  description: "Production Btc Transaction Signer",
1364
1372
  version: "1.1.1",
1365
1373
  deployed: true,
1366
- deployedAt: 1786113907707,
1374
+ deployedAt: 1786465606156,
1367
1375
  size: 67999,
1368
1376
  hash: "83f3644c68819215e05ba8db3225571354a009458fc1437f606df6cb9142d3f9"
1369
1377
  },
@@ -1376,7 +1384,7 @@ var require_pkg_src = __commonJS({
1376
1384
  description: "Production Ucd Mint Validator",
1377
1385
  version: "1.2.1",
1378
1386
  deployed: true,
1379
- deployedAt: 1786113908489,
1387
+ deployedAt: 1786465627201,
1380
1388
  size: 82739,
1381
1389
  hash: "2bdd9aebdaad58a43a4731d5b7284f2531b7a9caaf8caf5719d891c4ed33cb2e",
1382
1390
  validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
@@ -1394,7 +1402,7 @@ var require_pkg_src = __commonJS({
1394
1402
  description: "Production Process Payment Validator",
1395
1403
  version: "1.2.1",
1396
1404
  deployed: true,
1397
- deployedAt: 1786113909138,
1405
+ deployedAt: 1786465654796,
1398
1406
  size: 78736,
1399
1407
  hash: "17191ecc9c009e0fa5ab52c876330beb2a9a75d30cccb92e7c0c04f169df088b",
1400
1408
  validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
@@ -1412,7 +1420,7 @@ var require_pkg_src = __commonJS({
1412
1420
  description: "Production Extend Position Validator",
1413
1421
  version: "1.2.1",
1414
1422
  deployed: true,
1415
- deployedAt: 1786113909872,
1423
+ deployedAt: 1786465681874,
1416
1424
  size: 76515,
1417
1425
  hash: "1d4e085335af80f194a663a577876902c83015177e95906a0b461fa93bd5da08",
1418
1426
  validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
@@ -1422,21 +1430,17 @@ var require_pkg_src = __commonJS({
1422
1430
  }
1423
1431
  },
1424
1432
  btcWithdrawal: {
1425
- cid: "QmZuR3qSBajutXfRd91ffpyFrRMgmaMh7fDEHRiQQF8Ljp",
1433
+ cid: "QmRYVoTTDxk2McvBqxSm9M7YcaBGdTqjnZGPX4Y191DaZh",
1426
1434
  authorizedCidHex: cidToHex(
1427
- "QmZuR3qSBajutXfRd91ffpyFrRMgmaMh7fDEHRiQQF8Ljp"
1435
+ "QmRYVoTTDxk2McvBqxSm9M7YcaBGdTqjnZGPX4Y191DaZh"
1428
1436
  ),
1429
1437
  name: "Btc Withdrawal",
1430
1438
  description: "Production Btc Withdrawal",
1431
- // Monotonic per action, NOT a shared release train — the EIP-1271 rotation
1432
- // stamped its cohort 1.2.1, but this action was already at 1.2.3 from the vout
1433
- // canonicalization fix. Reusing the cohort stamp made a newer CID read as older
1434
- // than the one it replaced, which a rotation ceremony would treat as a rollback.
1435
- version: "1.2.4",
1439
+ version: "1.2.7",
1436
1440
  deployed: true,
1437
- deployedAt: 1786113910531,
1438
- size: 95313,
1439
- hash: "ce12f11e3b5469953bc919586e4f3b8b5d594224b58b799dd9a3b60e3749559c",
1441
+ deployedAt: 1786446046614,
1442
+ size: 97456,
1443
+ hash: "3cae8250861832f91ee3c573abeb50ff27b9acd34148176071cbfbabb359fc91",
1440
1444
  validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
1441
1445
  pkp: {
1442
1446
  publicKey: "0x043616787c5432415c24378c4ef48de2bcd6bb7b575b837e3cff09171802662da7105d79586c7659677a0ecbaddac4cce06cb2a11f69a16fa0c4d7002ac7d51a4d",
@@ -1548,19 +1552,17 @@ var require_pkg_src = __commonJS({
1548
1552
  hash: "03ee5be239c91c735d2c95db89a0d1b77e602cfc18af8eb64b3281fddcd90a72"
1549
1553
  },
1550
1554
  agentModuleExecutor: {
1551
- // Bound to the isolated Sepolia agent group 7 ("DH-Agent-Sepolia"), which
1552
- // holds this action and no PKPs until an agent is minted into it. NOT yet
1553
- // bound on mainnet — the Base `DH-Agent-Mainnet` group is created at rollout.
1554
- cid: "QmVgjGXUuxoGf8XyddW8a7btaazT9yEotpKoBgb65oPvcF",
1555
+ cid: "QmdhC18wF7PFGMQ8aThdPsEVSpuDewA27dFvbA3bG6kVDQ",
1555
1556
  authorizedCidHex: cidToHex(
1556
- "QmVgjGXUuxoGf8XyddW8a7btaazT9yEotpKoBgb65oPvcF"
1557
+ "QmdhC18wF7PFGMQ8aThdPsEVSpuDewA27dFvbA3bG6kVDQ"
1557
1558
  ),
1558
1559
  name: "Agent Module Executor",
1559
- description: "Capability gate for multi-sig Safe agent delegation: binds the agent PKP to the AgentDelegationRegistry record before signing (authorize | execute modes)",
1560
- version: "1.0.0",
1560
+ description: "Production Agent Module Executor",
1561
+ version: "1.1.1",
1561
1562
  deployed: true,
1562
- size: 11785,
1563
- hash: "fa294e164273c2158f9ef11a0ccfd735c343c0e3759fcb4b37e0f8a5d1ab0d29"
1563
+ deployedAt: 1786446108348,
1564
+ size: 11655,
1565
+ hash: "d4e2149f843d6c2699fc61114d54b268ba4ed2c46ebdaee1145e0f101ec2a1ec"
1564
1566
  },
1565
1567
  staleSpendRecoveryValidator: {
1566
1568
  cid: "QmXQWT8V4wjuvH8BbC7xrkYxsVu5AKfvfWcS7jnYfrexYm",
@@ -1589,7 +1591,7 @@ var require_pkg_src = __commonJS({
1589
1591
  description: "Production Btc Utxo Invalidator",
1590
1592
  version: "1.0.0",
1591
1593
  deployed: true,
1592
- deployedAt: 1786113911948,
1594
+ deployedAt: 1786465704240,
1593
1595
  size: 62553,
1594
1596
  hash: "1dcea2b49e545c423195be22e072dc5e3039ed1c95a8490b23505956029f5b33"
1595
1597
  }
@@ -12126,6 +12128,169 @@ function normalizePkpId2(pkpId) {
12126
12128
  }
12127
12129
  return pkpId;
12128
12130
  }
12131
+ function hasLoanEventsFilter(filter) {
12132
+ if (!filter)
12133
+ return false;
12134
+ 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;
12135
+ }
12136
+ var WALLET_LOAN_EVENTS_QUERY = `
12137
+ query GetWalletLoanEvents($borrower: Bytes!, $first: Int!, $limit: Int!, $orderDirection: OrderDirection!) {
12138
+ positions(where: { borrower: $borrower }, first: $first, orderBy: id) {
12139
+ id
12140
+ payments(first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12141
+ id
12142
+ payer { id }
12143
+ amount
12144
+ type
12145
+ transactionHash
12146
+ timestamp
12147
+ blockNumber
12148
+ logIndex
12149
+ newDebt
12150
+ newCollateralRatio
12151
+ }
12152
+ statusUpdates(first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12153
+ id
12154
+ oldStatus
12155
+ newStatus
12156
+ reason
12157
+ timestamp
12158
+ blockNumber
12159
+ transactionHash
12160
+ }
12161
+ liquidation {
12162
+ id
12163
+ liquidator
12164
+ liquidatedAmount
12165
+ collateralSeized
12166
+ transactionHash
12167
+ timestamp
12168
+ blockNumber
12169
+ commitHash
12170
+ commitTimestamp
12171
+ revealDelay
12172
+ }
12173
+ withdrawals(first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12174
+ id
12175
+ withdrawalAmount
12176
+ withdrawalAddress
12177
+ networkFee
12178
+ timestamp
12179
+ blockNumber
12180
+ transactionHash
12181
+ logIndex
12182
+ }
12183
+ renewals(first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12184
+ id
12185
+ extensionTerm
12186
+ extensionFee
12187
+ newExpiryDate
12188
+ timestamp
12189
+ blockNumber
12190
+ transactionHash
12191
+ logIndex
12192
+ }
12193
+ operationFailures(first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12194
+ id
12195
+ operationType
12196
+ reason
12197
+ errorCode
12198
+ amount
12199
+ timestamp
12200
+ blockNumber
12201
+ transactionHash
12202
+ logIndex
12203
+ }
12204
+ mintRequests(first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12205
+ id
12206
+ pkpId
12207
+ borrower { id }
12208
+ mintAmount
12209
+ originationFee
12210
+ newDebt
12211
+ newCollateralRatio
12212
+ btcPrice
12213
+ quantumTimestamp
12214
+ opId
12215
+ timestamp
12216
+ blockNumber
12217
+ transactionHash
12218
+ logIndex
12219
+ }
12220
+ burnRequests(first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12221
+ id
12222
+ pkpId
12223
+ borrower { id }
12224
+ burnAmount
12225
+ remainingDebt
12226
+ newCollateralRatio
12227
+ opId
12228
+ timestamp
12229
+ blockNumber
12230
+ transactionHash
12231
+ logIndex
12232
+ }
12233
+ mintingFees(first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12234
+ id
12235
+ borrower { id }
12236
+ feeAmount
12237
+ mintAmount
12238
+ newDebt
12239
+ btcPrice
12240
+ quantumTimestamp
12241
+ timestamp
12242
+ blockNumber
12243
+ transactionHash
12244
+ logIndex
12245
+ }
12246
+ renewalFees(first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12247
+ id
12248
+ borrower { id }
12249
+ feeAmount
12250
+ extensionTerm
12251
+ newExpiryDate
12252
+ timestamp
12253
+ blockNumber
12254
+ transactionHash
12255
+ logIndex
12256
+ }
12257
+ positionCreatedEvents(first: 1, orderBy: timestamp, orderDirection: asc) {
12258
+ id
12259
+ pkpId
12260
+ borrower { id }
12261
+ requestedCollateralRatio
12262
+ selectedTerm
12263
+ expiryAt
12264
+ timestamp
12265
+ blockNumber
12266
+ transactionHash
12267
+ }
12268
+ }
12269
+ ucdmintEvents(where: { position_: { borrower: $borrower } }, first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12270
+ id
12271
+ position { id }
12272
+ amount
12273
+ proof
12274
+ minter { id }
12275
+ isCommunityMint
12276
+ timestamp
12277
+ blockNumber
12278
+ transactionHash
12279
+ }
12280
+ collateralEvents(where: { position_: { borrower: $borrower } }, first: $limit, orderBy: timestamp, orderDirection: $orderDirection) {
12281
+ id
12282
+ position { id }
12283
+ eventType
12284
+ networkFee
12285
+ remainingCollateral
12286
+ collateralRatio
12287
+ timestamp
12288
+ blockNumber
12289
+ transactionHash
12290
+ logIndex
12291
+ }
12292
+ }
12293
+ `;
12129
12294
  var DiamondHandsGraph = class {
12130
12295
  client;
12131
12296
  defaultPageSize;
@@ -12231,7 +12396,7 @@ var DiamondHandsGraph = class {
12231
12396
  /**
12232
12397
  * Get user positions from subgraph
12233
12398
  */
12234
- async getUserPositions(userAddress, first, skip, orderBy, orderDirection) {
12399
+ async getUserPositionsOnly(userAddress, first, skip, orderBy, orderDirection) {
12235
12400
  const query = `
12236
12401
  query GetUserPositions($borrower: Bytes!, $first: Int, $skip: Int, $orderBy: Position_orderBy, $orderDirection: OrderDirection) {
12237
12402
  user(id: $borrower) {
@@ -12272,17 +12437,28 @@ var DiamondHandsGraph = class {
12272
12437
  query,
12273
12438
  variables
12274
12439
  );
12275
- const total = await this.getTotalCountPaginated("{ borrower: $borrower }", {
12276
- borrower: userAddress.toLowerCase()
12277
- });
12278
12440
  let positions = [];
12279
12441
  if (result && result.user && Array.isArray(result.user.positions)) {
12280
12442
  positions = result.user.positions.filter((p) => p != null).map((p) => this.transformGraphPositionToLoanData(p));
12281
12443
  }
12282
- return {
12283
- positions,
12284
- total
12285
- };
12444
+ return positions;
12445
+ }
12446
+ /**
12447
+ * Get user positions, plus an accurate total count.
12448
+ *
12449
+ * The count is a SECOND round trip (`getTotalCountPaginated`, which pages to handle
12450
+ * >1000 items). Callers that only need the rows should use `getUserPositionsOnly` and
12451
+ * skip it — the event paths did not, and paid for a count they immediately discarded on
12452
+ * every activity refresh.
12453
+ */
12454
+ async getUserPositions(userAddress, first, skip, orderBy, orderDirection) {
12455
+ const [positions, total] = await Promise.all([
12456
+ this.getUserPositionsOnly(userAddress, first, skip, orderBy, orderDirection),
12457
+ this.getTotalCountPaginated("{ borrower: $borrower }", {
12458
+ borrower: userAddress.toLowerCase()
12459
+ })
12460
+ ]);
12461
+ return { positions, total };
12286
12462
  }
12287
12463
  /**
12288
12464
  * Get user positions with enriched Bitcoin balance data
@@ -13373,6 +13549,16 @@ var DiamondHandsGraph = class {
13373
13549
  orderDirection
13374
13550
  };
13375
13551
  const result = await this.client.execute(query, variables);
13552
+ return this.assembleLoanEvents(positionId, result);
13553
+ }
13554
+ /**
13555
+ * Map one position's raw event payload into `LoanEvents`.
13556
+ *
13557
+ * Extracted verbatim from getLoanEvents so the wallet-collapsed path produces
13558
+ * byte-identical objects by construction rather than by a second implementation
13559
+ * that has to be kept in step by hand.
13560
+ */
13561
+ assembleLoanEvents(positionId, result) {
13376
13562
  const rawCreated = result.positionCreatedEvents?.[0];
13377
13563
  const positionCreated = rawCreated ? {
13378
13564
  id: rawCreated.id,
@@ -13577,7 +13763,22 @@ var DiamondHandsGraph = class {
13577
13763
  * @returns One LoanEvents object per position; empty array if the wallet has none.
13578
13764
  */
13579
13765
  async getWalletLoanEvents(userAddress, filter) {
13580
- const { positions } = await this.getUserPositions(userAddress);
13766
+ if (!hasLoanEventsFilter(filter)) {
13767
+ return this.getWalletLoanEventsCollapsed(userAddress);
13768
+ }
13769
+ return this.getWalletLoanEventsFanout(userAddress, filter);
13770
+ }
13771
+ /**
13772
+ * The per-position path: enumerate the wallet's positions, then one `GetLoanEvents` each.
13773
+ *
13774
+ * Used for filtered calls, and as the completeness fallback when the collapsed document
13775
+ * hits a page limit. Costs `1 + N` requests, which is exactly why it is not the default.
13776
+ */
13777
+ async getWalletLoanEventsFanout(userAddress, filter) {
13778
+ const positions = await this.getUserPositionsOnly(
13779
+ userAddress,
13780
+ THE_GRAPH_MAX_BATCH_SIZE
13781
+ );
13581
13782
  const positionIds = positions.map((p) => p?.id != null ? String(p.id) : "").filter((id) => id.length > 0);
13582
13783
  if (positionIds.length === 0)
13583
13784
  return [];
@@ -13592,6 +13793,81 @@ var DiamondHandsGraph = class {
13592
13793
  }
13593
13794
  return results;
13594
13795
  }
13796
+ /**
13797
+ * The whole wallet's event history in ONE request.
13798
+ *
13799
+ * Replaces `1 + N` round trips (one position enumeration plus one per position) with a
13800
+ * single document. Measured on Sepolia: a wallet holding 44 positions cost 46 requests
13801
+ * per activity refresh against a per-wallet budget of 30/min — this makes it 1.
13802
+ *
13803
+ * Three subgraph facts make it possible, all verified against the deployed schema rather
13804
+ * than assumed:
13805
+ * - ten event collections are `@derivedFrom` on Position, so they nest under a
13806
+ * borrower-filtered `positions(...)`;
13807
+ * - `positionCreatedEvents` is ALSO derived on Position — `getLoanEvents` queries it
13808
+ * top-level, which it never needed to;
13809
+ * - `ucdmintEvents` / `collateralEvents` are not derived and carry no `borrower`
13810
+ * field, but graph-node's nested `position_: { borrower }` filter reaches them.
13811
+ * Without that last one this would be two requests, not one.
13812
+ */
13813
+ async getWalletLoanEventsCollapsed(userAddress) {
13814
+ const borrower = userAddress.toLowerCase();
13815
+ const pageSize = THE_GRAPH_MAX_BATCH_SIZE;
13816
+ const result = await this.client.execute(
13817
+ WALLET_LOAN_EVENTS_QUERY,
13818
+ {
13819
+ borrower,
13820
+ first: pageSize,
13821
+ limit: pageSize,
13822
+ orderDirection: "desc"
13823
+ }
13824
+ );
13825
+ const positions = result.positions || [];
13826
+ const ucdmintEvents = result.ucdmintEvents || [];
13827
+ const collateralEvents = result.collateralEvents || [];
13828
+ const truncated = positions.length >= pageSize || ucdmintEvents.length >= pageSize || collateralEvents.length >= pageSize;
13829
+ if (truncated) {
13830
+ log.warn(
13831
+ "[DiamondHandsGraph] wallet event page limit reached \u2014 falling back to the per-position path to avoid returning a partial history",
13832
+ {
13833
+ borrower,
13834
+ pageSize,
13835
+ positions: positions.length,
13836
+ ucdmintEvents: ucdmintEvents.length,
13837
+ collateralEvents: collateralEvents.length
13838
+ }
13839
+ );
13840
+ return this.getWalletLoanEventsFanout(userAddress);
13841
+ }
13842
+ if (positions.length === 0)
13843
+ return [];
13844
+ const mintsByPosition = /* @__PURE__ */ new Map();
13845
+ for (const m of result.ucdmintEvents || []) {
13846
+ const key = m.position?.id;
13847
+ if (!key)
13848
+ continue;
13849
+ const bucket = mintsByPosition.get(key) || [];
13850
+ bucket.push(m);
13851
+ mintsByPosition.set(key, bucket);
13852
+ }
13853
+ const collateralByPosition = /* @__PURE__ */ new Map();
13854
+ for (const c of result.collateralEvents || []) {
13855
+ const key = c.position?.id;
13856
+ if (!key)
13857
+ continue;
13858
+ const bucket = collateralByPosition.get(key) || [];
13859
+ bucket.push(c);
13860
+ collateralByPosition.set(key, bucket);
13861
+ }
13862
+ return positions.map(
13863
+ (p) => this.assembleLoanEvents(p.id, {
13864
+ position: p,
13865
+ positionCreatedEvents: p.positionCreatedEvents || [],
13866
+ ucdmintEvents: mintsByPosition.get(p.id) || [],
13867
+ collateralEvents: collateralByPosition.get(p.id) || []
13868
+ })
13869
+ );
13870
+ }
13595
13871
  // ============================================================================
13596
13872
  // Protocol-wide Events
13597
13873
  // ============================================================================
@@ -18502,16 +18778,6 @@ var import_dh_lit_ops = require("@gvnrdao/dh-lit-ops");
18502
18778
  var import_dh_lit_actions2 = __toESM(require_pkg_src());
18503
18779
 
18504
18780
  // src/utils/telegram-messaging.utils.ts
18505
- var TelegramBot = null;
18506
- try {
18507
- if (typeof window === "undefined" && typeof global !== "undefined") {
18508
- TelegramBot = require("node-telegram-bot-api");
18509
- }
18510
- } catch (error) {
18511
- log.warn("Telegram bot API not available", {
18512
- error: error instanceof Error ? error.message : String(error)
18513
- });
18514
- }
18515
18781
  function formatMintMessage(mintAmount, loanId, txHash) {
18516
18782
  return `\u2705 DH SDK: ${mintAmount} UCD was minted for loan id: ${loanId} Tx: https://sepolia.etherscan.io/tx/${txHash}`;
18517
18783
  }
@@ -24511,6 +24777,13 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
24511
24777
  error: "ethRpcUrl required for recoverStaleSpend \u2014 set DiamondHandsSDKConfig.ethRpcUrl or pass rpcUrl"
24512
24778
  };
24513
24779
  }
24780
+ if (!this.config.contractSigner) {
24781
+ return {
24782
+ success: false,
24783
+ failedStep: "config",
24784
+ error: "Contract signer not configured \u2014 recoverStaleSpend submits its own recovery transaction in direct mode. Set DiamondHandsSDKConfig.contractSigner, or use service mode, where the server relays and pays for it."
24785
+ };
24786
+ }
24514
24787
  const sdkBitcoinProviderUrl = this.config.bitcoinProviders?.[0]?.url;
24515
24788
  attRes = await this.litOps.requestStaleSpendRecoveryAttestation({
24516
24789
  chainId,