@gvnrdao/dh-sdk 0.0.323 → 0.0.327

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
@@ -354,7 +354,7 @@ var init_deployment_addresses = __esm({
354
354
  CARRY_AGENT_REGISTRY: "0x5595CB538D44E6Cf179D20364fD631D0477e69f7",
355
355
  CARRY_AGENT_REGISTRY_IMPL: "0x1b11a3D5e46F3672E643c997f0d569DFf0b64a01",
356
356
  AGENT_DELEGATION_REGISTRY: "0x6AE7fc6bE0925126b9D25b1B7Ce5AD5d127ec025",
357
- AGENT_DELEGATION_REGISTRY_IMPL: "0x80C47350274513688e348D02506477A96B4afCCb",
357
+ AGENT_DELEGATION_REGISTRY_IMPL: "0xE931E4b9ceF2A48Ac7D57192da4B523756cFC310",
358
358
  DH_AGENT_DELEGATE: "0xa5A14489ae883960df6E1463A41a47aBE9Ecc5AF"
359
359
  }
360
360
  };
@@ -624,7 +624,7 @@ var init_deployment_addresses = __esm({
624
624
  LOCALHOST_DEPLOYMENT = {
625
625
  network: "localhost",
626
626
  chainId: 1337,
627
- timestamp: "2026-07-23T17:13:42.777Z",
627
+ timestamp: "2026-07-20T19:45:58.514Z",
628
628
  deployer: "",
629
629
  contracts: {
630
630
  MessageHashBuilder: "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9",
@@ -644,30 +644,6 @@ var init_deployment_addresses = __esm({
644
644
  PositionManager: "0x851356ae760d987E095750cCeb3bC6014560891C",
645
645
  OperationAuthorizationRegistry: "0xc96304e3c037f81dA488ed9dEa1D8F2a48278a75",
646
646
  PKPValidation: "0xD0141E899a65C95a556fE2B27e5982A6DE7fDD7A"
647
- },
648
- latestEnv: {
649
- UCD_TOKEN: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
650
- UCD_CONTROLLER: "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9",
651
- POSITION_MANAGER: "0x851356ae760d987E095750cCeb3bC6014560891C",
652
- PKP_VALIDATION_REGISTRY: "0xD0141E899a65C95a556fE2B27e5982A6DE7fDD7A",
653
- PKP_VALIDATION_CID_V1: "0x12203af2054b0ab6e18d898ddfc7985593439fb82d22336133387c1169f98d3702ab",
654
- PKP_VALIDATION_CID_V3: "0x12203af2054b0ab6e18d898ddfc7985593439fb82d22336133387c1169f98d3702ab",
655
- PKP_ETH_ADDRESS: "0x406f6a9855de2f5577b1525f0600461f02859bd0",
656
- OPERATION_AUTHORIZATION_REGISTRY: "0xc96304e3c037f81dA488ed9dEa1D8F2a48278a75",
657
- UCD_MINT_VALIDATOR_ADDRESS: "0x406f6a9855de2f5577b1525f0600461f02859bd0",
658
- UCD_MINT_VALIDATOR_VERSION: 1,
659
- BTC_WITHDRAWAL_VALIDATOR_ADDRESS: "0x406f6a9855de2f5577b1525f0600461f02859bd0",
660
- BTC_WITHDRAWAL_VALIDATOR_VERSION: 3,
661
- UPDATE_BALANCE_VALIDATOR_ADDRESS: "0x406f6a9855de2f5577b1525f0600461f02859bd0",
662
- UPDATE_BALANCE_VALIDATOR_VERSION: 1,
663
- PROCESS_PAYMENT_VALIDATOR_ADDRESS: "0x406f6a9855de2f5577b1525f0600461f02859bd0",
664
- PROCESS_PAYMENT_VALIDATOR_VERSION: 1,
665
- EXTEND_POSITION_VALIDATOR_ADDRESS: "0x406f6a9855de2f5577b1525f0600461f02859bd0",
666
- EXTEND_POSITION_VALIDATOR_VERSION: 1,
667
- POSITION_MANAGER_CORE_MODULE: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e",
668
- LOAN_OPERATIONS_MANAGER_MODULE: "0x68B1D87F95878fE05B998F19b66F4baba5De1aed",
669
- TERM_MANAGER_MODULE: "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82",
670
- COMMUNITY_MANAGER_MODULE: ""
671
647
  }
672
648
  };
673
649
  LOCALHOST_CONTRACTS = {
@@ -1379,30 +1355,30 @@ var require_pkg_src = __commonJS({
1379
1355
  }
1380
1356
  },
1381
1357
  btcTransactionSigner: {
1382
- cid: "QmPncBUbxN9giWntJcvBMFW9GDAgA3DwgAkUAHxSsY67ic",
1358
+ cid: "QmakWzTce8NH1Jkgv1xwg83Bg9WmF2UG1Mq76pDYDSwwUp",
1383
1359
  authorizedCidHex: cidToHex(
1384
- "QmPncBUbxN9giWntJcvBMFW9GDAgA3DwgAkUAHxSsY67ic"
1360
+ "QmakWzTce8NH1Jkgv1xwg83Bg9WmF2UG1Mq76pDYDSwwUp"
1385
1361
  ),
1386
1362
  name: "Btc Transaction Signer",
1387
1363
  description: "Production Btc Transaction Signer",
1388
1364
  version: "1.1.1",
1389
1365
  deployed: true,
1390
- deployedAt: 1785226367707,
1391
- size: 66624,
1392
- hash: "9e0d9909519d5133bab03a9491566870b08d8b42c80e2d768bd24f02946cc781"
1366
+ deployedAt: 1786113907707,
1367
+ size: 67999,
1368
+ hash: "83f3644c68819215e05ba8db3225571354a009458fc1437f606df6cb9142d3f9"
1393
1369
  },
1394
1370
  ucdMintValidator: {
1395
- cid: "QmPRLCfseSWCAS4PpvQWt9aEd6rXqWYG4JyGTn26KYk6xc",
1371
+ cid: "Qmd8jgKRceLvf5aoTtwdXG2JqMoBK5cRJwrvEtQkgvbFbx",
1396
1372
  authorizedCidHex: cidToHex(
1397
- "QmPRLCfseSWCAS4PpvQWt9aEd6rXqWYG4JyGTn26KYk6xc"
1373
+ "Qmd8jgKRceLvf5aoTtwdXG2JqMoBK5cRJwrvEtQkgvbFbx"
1398
1374
  ),
1399
1375
  name: "Ucd Mint Validator",
1400
1376
  description: "Production Ucd Mint Validator",
1401
1377
  version: "1.2.1",
1402
1378
  deployed: true,
1403
- deployedAt: 1785226381631,
1404
- size: 81658,
1405
- hash: "d8ea21622885c3d26fdb64e2ba9d2f9460753a5cd7934b2928858899537443eb",
1379
+ deployedAt: 1786113908489,
1380
+ size: 82739,
1381
+ hash: "2bdd9aebdaad58a43a4731d5b7284f2531b7a9caaf8caf5719d891c4ed33cb2e",
1406
1382
  validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
1407
1383
  pkp: {
1408
1384
  publicKey: "0x041ab17cd91fc5c0b761eea6092d032807561b621b82c488826776e04a9158d61ba64d809a4729f0501289d980732cb06d8dfd06999dd9c8efd2f495dad78b31bb",
@@ -1410,17 +1386,17 @@ var require_pkg_src = __commonJS({
1410
1386
  }
1411
1387
  },
1412
1388
  processPaymentValidator: {
1413
- cid: "QmNUzmHQHsaKThGkfZiD3xZrN64qZfn4P6J4MqWgvkeEsX",
1389
+ cid: "QmNXVdysXDhCaQ1uNE2Vgjbuzy6yUEnnVgHzmDfNKXMU6z",
1414
1390
  authorizedCidHex: cidToHex(
1415
- "QmNUzmHQHsaKThGkfZiD3xZrN64qZfn4P6J4MqWgvkeEsX"
1391
+ "QmNXVdysXDhCaQ1uNE2Vgjbuzy6yUEnnVgHzmDfNKXMU6z"
1416
1392
  ),
1417
1393
  name: "Process Payment Validator",
1418
1394
  description: "Production Process Payment Validator",
1419
1395
  version: "1.2.1",
1420
1396
  deployed: true,
1421
- deployedAt: 1785226385509,
1422
- size: 77655,
1423
- hash: "3c2af3c3e62e8e606d35f81fd7e6f4943f8516a9bef74f4150c8c1767cbd316f",
1397
+ deployedAt: 1786113909138,
1398
+ size: 78736,
1399
+ hash: "17191ecc9c009e0fa5ab52c876330beb2a9a75d30cccb92e7c0c04f169df088b",
1424
1400
  validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
1425
1401
  pkp: {
1426
1402
  publicKey: "0x04cfccb9b6c7addc79469c0e9fafebef4c70801302c62cdb26b610723a9b8527d23f98a40158c7340d83fd0e62d02b5ec74c12f7a86d98e8c6973b4811110f19ad",
@@ -1428,17 +1404,17 @@ var require_pkg_src = __commonJS({
1428
1404
  }
1429
1405
  },
1430
1406
  extendPositionValidator: {
1431
- cid: "QmY2t41w1maaoDmdRPx6wTfciHPQAydSpLSKwKVFSS2DPM",
1407
+ cid: "QmYwBsyrhGfHWyRKx549G4tKHxsBLAuRUHcecigeUCkFav",
1432
1408
  authorizedCidHex: cidToHex(
1433
- "QmY2t41w1maaoDmdRPx6wTfciHPQAydSpLSKwKVFSS2DPM"
1409
+ "QmYwBsyrhGfHWyRKx549G4tKHxsBLAuRUHcecigeUCkFav"
1434
1410
  ),
1435
1411
  name: "Extend Position Validator",
1436
1412
  description: "Production Extend Position Validator",
1437
1413
  version: "1.2.1",
1438
1414
  deployed: true,
1439
- deployedAt: 1785226398291,
1440
- size: 75434,
1441
- hash: "ba55532fbeaf6e65d180b869fd69ee06393d574daa0ace524a9e831d846539a5",
1415
+ deployedAt: 1786113909872,
1416
+ size: 76515,
1417
+ hash: "1d4e085335af80f194a663a577876902c83015177e95906a0b461fa93bd5da08",
1442
1418
  validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
1443
1419
  pkp: {
1444
1420
  publicKey: "0x04e52338f6a0c3362800f1a94c4c8f08be5030dde0fa217388c53d1c1a4790f4443c9f2886b2d2d8f4b75a9a156e9b028011b1a1cf7648da0e556aa980ec692836",
@@ -1446,9 +1422,9 @@ var require_pkg_src = __commonJS({
1446
1422
  }
1447
1423
  },
1448
1424
  btcWithdrawal: {
1449
- cid: "QmcBg8vPXsCmiKDk28bJVTqDpTrwCzSYAGu5V6ABVVPuK5",
1425
+ cid: "QmZuR3qSBajutXfRd91ffpyFrRMgmaMh7fDEHRiQQF8Ljp",
1450
1426
  authorizedCidHex: cidToHex(
1451
- "QmcBg8vPXsCmiKDk28bJVTqDpTrwCzSYAGu5V6ABVVPuK5"
1427
+ "QmZuR3qSBajutXfRd91ffpyFrRMgmaMh7fDEHRiQQF8Ljp"
1452
1428
  ),
1453
1429
  name: "Btc Withdrawal",
1454
1430
  description: "Production Btc Withdrawal",
@@ -1458,9 +1434,9 @@ var require_pkg_src = __commonJS({
1458
1434
  // than the one it replaced, which a rotation ceremony would treat as a rollback.
1459
1435
  version: "1.2.4",
1460
1436
  deployed: true,
1461
- deployedAt: 1785226412397,
1462
- size: 92754,
1463
- hash: "d3a8ec62e0b1e6d0566a154a884bf7ee4e25ef6dc2dd84fde98211d25c1dede5",
1437
+ deployedAt: 1786113910531,
1438
+ size: 95313,
1439
+ hash: "ce12f11e3b5469953bc919586e4f3b8b5d594224b58b799dd9a3b60e3749559c",
1464
1440
  validatorWalletAddress: "0x82DB9F515957aA380B596C6eA2Bd30CEF181f236",
1465
1441
  pkp: {
1466
1442
  publicKey: "0x043616787c5432415c24378c4ef48de2bcd6bb7b575b837e3cff09171802662da7105d79586c7659677a0ecbaddac4cce06cb2a11f69a16fa0c4d7002ac7d51a4d",
@@ -1571,6 +1547,21 @@ var require_pkg_src = __commonJS({
1571
1547
  size: 1046,
1572
1548
  hash: "03ee5be239c91c735d2c95db89a0d1b77e602cfc18af8eb64b3281fddcd90a72"
1573
1549
  },
1550
+ 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
+ authorizedCidHex: cidToHex(
1556
+ "QmVgjGXUuxoGf8XyddW8a7btaazT9yEotpKoBgb65oPvcF"
1557
+ ),
1558
+ 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",
1561
+ deployed: true,
1562
+ size: 11785,
1563
+ hash: "fa294e164273c2158f9ef11a0ccfd735c343c0e3759fcb4b37e0f8a5d1ab0d29"
1564
+ },
1574
1565
  staleSpendRecoveryValidator: {
1575
1566
  cid: "QmXQWT8V4wjuvH8BbC7xrkYxsVu5AKfvfWcS7jnYfrexYm",
1576
1567
  authorizedCidHex: cidToHex(
@@ -1590,17 +1581,17 @@ var require_pkg_src = __commonJS({
1590
1581
  }
1591
1582
  },
1592
1583
  btcUtxoInvalidator: {
1593
- cid: "QmZE422cZsEjYN8pE6AmpkgFoZCK4UAfdDuzoVRRsMJbwm",
1584
+ cid: "Qmf4Ze8yhnQhHbnX5RPgJrC7Jw1aA3gbpfpd52kFg7fd1m",
1594
1585
  authorizedCidHex: cidToHex(
1595
- "QmZE422cZsEjYN8pE6AmpkgFoZCK4UAfdDuzoVRRsMJbwm"
1586
+ "Qmf4Ze8yhnQhHbnX5RPgJrC7Jw1aA3gbpfpd52kFg7fd1m"
1596
1587
  ),
1597
1588
  name: "Btc Utxo Invalidator",
1598
1589
  description: "Production Btc Utxo Invalidator",
1599
1590
  version: "1.0.0",
1600
1591
  deployed: true,
1601
- deployedAt: 1785226423791,
1602
- size: 61472,
1603
- hash: "75eca528d8ac30ae8f21caaae206ccab162a83a048d622868e60f645f0c475e2"
1592
+ deployedAt: 1786113911948,
1593
+ size: 62553,
1594
+ hash: "1dcea2b49e545c423195be22e072dc5e3039ed1c95a8490b23505956029f5b33"
1604
1595
  }
1605
1596
  };
1606
1597
  function getDeploymentsForNetwork(network) {
@@ -5507,8 +5498,14 @@ function getMainnetConfig() {
5507
5498
  agentModuleFactory: MAINNET_CONTRACTS.AgentModuleFactory || ""
5508
5499
  },
5509
5500
  subgraphs: {
5501
+ // KNOWN-WRONG placeholder (see GOAL-PLAN step 2 / dogfood F-CLI-4): this is a
5502
+ // SEPOLIA subgraph id in a mainnet (chainId 1) config, and this gateway host
5503
+ // needs an Authorization key the SDK does not (and must not) hold — so the
5504
+ // URL is doubly unusable. The `CCTPsd…` path segment is the subgraph's
5505
+ // PUBLIC ID, not a credential. The fix is a chain-correct SERVER-side proxy
5506
+ // (see mcp/docs/SUBGRAPH-PROXY-PLAN.md) — same rule as rpcUrls above:
5507
+ // never ship an API-keyed URL in a client config.
5510
5508
  diamondHandsUrl: "https://gateway-arbitrum.network.thegraph.com/api/subgraphs/id/CCTPsdYqco2jChDLLBQTbdJWwoukVoMt1cXeR9ti6r9A"
5511
- // Published Sepolia subgraph on Arbitrum One
5512
5509
  },
5513
5510
  litNetwork: "chipotle",
5514
5511
  debug: false
@@ -6253,7 +6250,10 @@ var import_ethers2 = require("ethers");
6253
6250
  // src/utils/quantum-timing.ts
6254
6251
  var QUANTUM_WINDOW_SECONDS = 60;
6255
6252
  var DEAD_ZONE_SECONDS = 8;
6256
- var INCLUSION_LATENCY_BUDGET = 16;
6253
+ var SLOT_SECONDS = 12;
6254
+ var INCLUSION_SLOT_TOLERANCE = 3;
6255
+ var INCLUSION_LATENCY_BUDGET = SLOT_SECONDS * INCLUSION_SLOT_TOLERANCE;
6256
+ var MAX_GATE_POLLS = 12;
6257
6257
  var POST_BOUNDARY_SKEW_MARGIN = 3;
6258
6258
  function calculateNextQuantumTimestamp() {
6259
6259
  const now = Math.floor(Date.now() / 1e3);
@@ -6286,8 +6286,17 @@ function validateQuantumTiming(signedTimestamp, _bufferSeconds = 30) {
6286
6286
  async function awaitSafeSubmissionWindow(quantumTimestamp, opts = {}) {
6287
6287
  const now = opts.now ?? (() => Math.floor(Date.now() / 1e3));
6288
6288
  const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
6289
- const nowSec = now();
6290
6289
  const signatureQuantum = Math.floor(quantumTimestamp / QUANTUM_WINDOW_SECONDS) * QUANTUM_WINDOW_SECONDS;
6290
+ if (opts.chainNow) {
6291
+ const chainResult = await awaitSafeSubmissionWindowOnChain(
6292
+ signatureQuantum,
6293
+ opts.chainNow,
6294
+ sleep
6295
+ );
6296
+ if (chainResult)
6297
+ return chainResult;
6298
+ }
6299
+ const nowSec = now();
6291
6300
  const currentQuantum = Math.floor(nowSec / QUANTUM_WINDOW_SECONDS) * QUANTUM_WINDOW_SECONDS;
6292
6301
  const secsToBoundary = QUANTUM_WINDOW_SECONDS - nowSec % QUANTUM_WINDOW_SECONDS;
6293
6302
  const isNextQuantumSig = signatureQuantum === currentQuantum + QUANTUM_WINDOW_SECONDS;
@@ -6304,6 +6313,44 @@ async function awaitSafeSubmissionWindow(quantumTimestamp, opts = {}) {
6304
6313
  }
6305
6314
  return { waited: false, waitedSeconds: 0 };
6306
6315
  }
6316
+ async function awaitSafeSubmissionWindowOnChain(signatureQuantum, chainNow, sleep) {
6317
+ let waitedSeconds = 0;
6318
+ for (let poll = 0; poll < MAX_GATE_POLLS; poll++) {
6319
+ let head;
6320
+ try {
6321
+ head = await chainNow();
6322
+ } catch {
6323
+ head = null;
6324
+ }
6325
+ if (head === null || !Number.isFinite(head)) {
6326
+ return waitedSeconds > 0 ? { waited: true, waitedSeconds } : null;
6327
+ }
6328
+ const chainQuantum = Math.floor(head / QUANTUM_WINDOW_SECONDS) * QUANTUM_WINDOW_SECONDS;
6329
+ if (signatureQuantum <= chainQuantum) {
6330
+ return { waited: waitedSeconds > 0, waitedSeconds };
6331
+ }
6332
+ const earliestMine = head + SLOT_SECONDS;
6333
+ const latestMine = head + SLOT_SECONDS * INCLUSION_SLOT_TOLERANCE;
6334
+ const safeBeforeDeadZone = latestMine < signatureQuantum - DEAD_ZONE_SECONDS;
6335
+ const safeAfterBoundary = earliestMine >= signatureQuantum;
6336
+ if (safeBeforeDeadZone || safeAfterBoundary) {
6337
+ return { waited: waitedSeconds > 0, waitedSeconds };
6338
+ }
6339
+ const sleepSeconds = Math.max(
6340
+ 1,
6341
+ Math.min(SLOT_SECONDS, signatureQuantum - head)
6342
+ );
6343
+ log.debug(
6344
+ `[Quantum Timing] \u23F3 Deferring send ${sleepSeconds}s to clear the quantum dead zone (sig quantum ${signatureQuantum}, chain head ${head}, insured mine window [${earliestMine}, ${latestMine}])`
6345
+ );
6346
+ await sleep(sleepSeconds * 1e3);
6347
+ waitedSeconds += sleepSeconds;
6348
+ }
6349
+ log.debug(
6350
+ `[Quantum Timing] \u26A0\uFE0F Dead-zone gate exhausted ${MAX_GATE_POLLS} chain polls (sig quantum ${signatureQuantum}) \u2014 proceeding`
6351
+ );
6352
+ return { waited: waitedSeconds > 0, waitedSeconds };
6353
+ }
6307
6354
 
6308
6355
  // src/utils/mint-authorization.utils.ts
6309
6356
  var PKP_NFT_ABI = [
@@ -6350,7 +6397,7 @@ async function generateMintAuthorization(positionId, amount, chainId, signerOrPr
6350
6397
  signature
6351
6398
  };
6352
6399
  }
6353
- async function generatePaymentAuthorization(positionId, amount, chainId, signer) {
6400
+ async function generatePaymentAuthorization(positionId, amount, chainId, signerOrPrecomputed) {
6354
6401
  if (typeof positionId !== "string") {
6355
6402
  throw new Error(`positionId must be a string, got ${typeof positionId}`);
6356
6403
  }
@@ -6360,6 +6407,18 @@ async function generatePaymentAuthorization(positionId, amount, chainId, signer)
6360
6407
  if (typeof chainId !== "number") {
6361
6408
  throw new Error(`chainId must be a number, got ${typeof chainId}`);
6362
6409
  }
6410
+ if (typeof signerOrPrecomputed === "object" && "signature" in signerOrPrecomputed && !("signMessage" in signerOrPrecomputed)) {
6411
+ const { timestamp: timestamp2, signature: signature2 } = signerOrPrecomputed;
6412
+ return {
6413
+ positionId,
6414
+ timestamp: timestamp2,
6415
+ chainId,
6416
+ amount: amount.toString(),
6417
+ action: "make-payment",
6418
+ signature: signature2
6419
+ };
6420
+ }
6421
+ const signer = signerOrPrecomputed;
6363
6422
  const timestamp = await calculateNextQuantumTimestamp();
6364
6423
  if (typeof timestamp !== "number") {
6365
6424
  throw new Error(`timestamp must be a number, got ${typeof timestamp}`);
@@ -6601,6 +6660,26 @@ async function generateExtendAuthorization(positionId, newTerm, chainId, signerO
6601
6660
  };
6602
6661
  }
6603
6662
 
6663
+ // src/utils/authorization-provider.utils.ts
6664
+ async function resolveAuthorizationInput(provider, signer, ctx) {
6665
+ if (!provider)
6666
+ return signer;
6667
+ const result = await provider(ctx);
6668
+ if (!result || typeof result.signature !== "string" || result.signature.length === 0) {
6669
+ throw new Error(
6670
+ "authorizationProvider returned no signature \u2014 refusing to fall back to the connected signer, which would prompt for the owner quorum this provider exists to avoid"
6671
+ );
6672
+ }
6673
+ if (!Number.isInteger(result.timestamp)) {
6674
+ throw new Error(
6675
+ `authorizationProvider returned a non-integer timestamp (${String(
6676
+ result.timestamp
6677
+ )}) \u2014 the protocol call must carry the same value the envelope was signed over`
6678
+ );
6679
+ }
6680
+ return { timestamp: result.timestamp, signature: result.signature };
6681
+ }
6682
+
6604
6683
  // src/utils/eip712-login.ts
6605
6684
  var import_ethers4 = require("ethers");
6606
6685
  function buildLoginDomain(chainId) {
@@ -7260,6 +7339,20 @@ function decodeQuantumRevert(e) {
7260
7339
  }
7261
7340
  return { data, selector, name };
7262
7341
  }
7342
+ var RETRYABLE_QUANTUM_ERROR_NAMES = [
7343
+ "DeadZoneViolation",
7344
+ "QuantumOutsideWindow",
7345
+ "QuantumAlreadyUsed",
7346
+ "QuantumExpired"
7347
+ ];
7348
+ function isRetryableQuantumFailure(message) {
7349
+ if (!message)
7350
+ return false;
7351
+ if (message.includes("Transaction timeout") || message.includes("Transaction confirmation failed") || message.includes("Transaction receipt not found")) {
7352
+ return false;
7353
+ }
7354
+ return RETRYABLE_QUANTUM_ERROR_NAMES.some((name) => message.includes(name)) || message.includes("Transaction reverted");
7355
+ }
7263
7356
 
7264
7357
  // src/utils/quantum-submission.utils.ts
7265
7358
  var MAX_DEADZONE_RESIMULATIONS = 3;
@@ -7281,9 +7374,14 @@ async function assertSafeQuantumSubmission(opts) {
7281
7374
  const now = opts.now ?? (() => Math.floor(Date.now() / 1e3));
7282
7375
  const onDebug = opts.onDebug ?? (() => {
7283
7376
  });
7377
+ const chainNow = opts.chainNow ?? (typeof provider.getBlock === "function" ? async () => {
7378
+ const block = await provider.getBlock("latest");
7379
+ return typeof block?.timestamp === "number" ? block.timestamp : null;
7380
+ } : void 0);
7284
7381
  const gateOpts = {
7285
7382
  now,
7286
- ...opts.sleep ? { sleep: opts.sleep } : {}
7383
+ ...opts.sleep ? { sleep: opts.sleep } : {},
7384
+ ...chainNow ? { chainNow } : {}
7287
7385
  };
7288
7386
  await awaitSafeSubmissionWindow(quantumTimestamp, gateOpts);
7289
7387
  const SAME_BLOCK_INTERACTION_SELECTOR = "0x3b53d1db";
@@ -19993,11 +20091,20 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
19993
20091
  {}
19994
20092
  );
19995
20093
  }
20094
+ const mintAuthInput = await resolveAuthorizationInput(
20095
+ request.authorizationProvider,
20096
+ signer2,
20097
+ {
20098
+ positionId: request.positionId,
20099
+ timestamp: calculateNextQuantumTimestamp(),
20100
+ amount: requestAmountWei.toString()
20101
+ }
20102
+ );
19996
20103
  const auth = await generateMintAuthorization(
19997
20104
  request.positionId,
19998
20105
  BigInt(requestAmountWei.toString()),
19999
20106
  chainId,
20000
- signer2
20107
+ mintAuthInput
20001
20108
  );
20002
20109
  if (this.config.debug) {
20003
20110
  log.info(` Authorization generated (attempt ${attempt})`, {});
@@ -21588,7 +21695,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
21588
21695
  * @param _selectedTerm - Extension term in months
21589
21696
  * @returns Transaction receipt
21590
21697
  */
21591
- async extendPosition(_positionId, _selectedTerm, _rpcUrl, _bitcoinRpcUrl) {
21698
+ async extendPosition(_positionId, _selectedTerm, _rpcUrl, _bitcoinRpcUrl, _authorizationProvider) {
21592
21699
  this.ensureInitialized();
21593
21700
  if (typeof _positionId !== "string") {
21594
21701
  throw new Error(`positionId must be a string, got ${typeof _positionId}`);
@@ -21682,11 +21789,23 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
21682
21789
  }
21683
21790
  }
21684
21791
  const signer = this.getSignerOrThrow();
21792
+ const extendAuthInput = await resolveAuthorizationInput(
21793
+ _authorizationProvider,
21794
+ signer,
21795
+ {
21796
+ positionId: _positionId,
21797
+ timestamp: calculateNextQuantumTimestamp(),
21798
+ // The extend envelope binds the TERM, not an amount. Carried as "0" so
21799
+ // the context shape stays uniform across the four operations.
21800
+ amount: "0",
21801
+ selectedTerm: _selectedTerm
21802
+ }
21803
+ );
21685
21804
  const auth = await generateExtendAuthorization(
21686
21805
  _positionId,
21687
21806
  _selectedTerm,
21688
21807
  chainId,
21689
- signer
21808
+ extendAuthInput
21690
21809
  );
21691
21810
  if (this.config.debug) {
21692
21811
  log.info(" Extend authorization generated", {
@@ -21942,7 +22061,8 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
21942
22061
  positionId,
21943
22062
  selectedTerm,
21944
22063
  request.rpcUrl,
21945
- request.customBitcoinRpcUrl
22064
+ request.customBitcoinRpcUrl,
22065
+ request.authorizationProvider
21946
22066
  );
21947
22067
  let newExpiryAt;
21948
22068
  try {
@@ -22293,10 +22413,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
22293
22413
  isRetryablePaymentQuantumFailure(result) {
22294
22414
  if (result.success || !result.error)
22295
22415
  return false;
22296
- const e = result.error;
22297
- if (e.includes("Transaction timeout"))
22298
- return false;
22299
- return e.includes("DeadZoneViolation") || e.includes("QuantumOutsideWindow") || e.includes("QuantumAlreadyUsed") || e.includes("QuantumExpired") || e === "Transaction reverted";
22416
+ return isRetryableQuantumFailure(result.error);
22300
22417
  }
22301
22418
  /**
22302
22419
  * One payment attempt: user auth → Lit Action authorization → dead-zone gate →
@@ -22473,11 +22590,20 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
22473
22590
  }
22474
22591
  let auth;
22475
22592
  try {
22593
+ const paymentAuthInput = await resolveAuthorizationInput(
22594
+ request.authorizationProvider,
22595
+ signer,
22596
+ {
22597
+ positionId: request.positionId,
22598
+ timestamp: calculateNextQuantumTimestamp(),
22599
+ amount: paymentAmountWei.toString()
22600
+ }
22601
+ );
22476
22602
  auth = await generatePaymentAuthorization(
22477
22603
  request.positionId,
22478
22604
  paymentAmountWei,
22479
22605
  chainId,
22480
- signer
22606
+ paymentAuthInput
22481
22607
  );
22482
22608
  } catch (authError) {
22483
22609
  if (authError.message && authError.message.includes("BigNumber")) {
@@ -22935,6 +23061,37 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
22935
23061
  }
22936
23062
  return this.withdrawalAddressModule.getEntries(target);
22937
23063
  }
23064
+ /**
23065
+ * Recover the revert reason of an already-mined, already-reverted transaction.
23066
+ *
23067
+ * A status-0 receipt carries no revert data, and ethers does not re-fetch it, so
23068
+ * the reason has to be recovered by replaying the exact calldata as an `eth_call`
23069
+ * pinned to the block that mined it — the only block whose `block.timestamp`
23070
+ * reproduces a timing-dependent revert such as `DeadZoneViolation()`.
23071
+ *
23072
+ * Caveat: the replay runs against end-of-block state rather than the state at the
23073
+ * transaction's own index, so for a state-dependent revert the recovered name is
23074
+ * diagnostic, not proof. Timing reverts (the case this exists for) are unaffected —
23075
+ * they depend only on the block timestamp.
23076
+ *
23077
+ * @returns the decoded error name, its raw selector if unknown, or `null` when the
23078
+ * replay yields no revert data at all (never throws — a failed diagnosis
23079
+ * must not mask the underlying failure).
23080
+ */
23081
+ async decodeMinedRevert(params) {
23082
+ try {
23083
+ await this.getProviderOrThrow().call({
23084
+ to: params.to,
23085
+ from: params.from,
23086
+ data: params.data,
23087
+ blockTag: params.blockNumber
23088
+ });
23089
+ return null;
23090
+ } catch (replayError) {
23091
+ const decoded = decodeQuantumRevert(replayError);
23092
+ return decoded.name ?? decoded.selector ?? null;
23093
+ }
23094
+ }
22938
23095
  /**
22939
23096
  * Withdraw Bitcoin from a position
22940
23097
  *
@@ -22958,7 +23115,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
22958
23115
  * @param customBitcoinRpcUrl - Optional custom Bitcoin RPC URL (for local testing)
22959
23116
  * @returns Withdrawal result with transaction details
22960
23117
  */
22961
- async withdrawBTC(positionId, withdrawalAddress, withdrawalAmount) {
23118
+ async withdrawBTC(positionId, withdrawalAddress, withdrawalAmount, options) {
22962
23119
  this.ensureInitialized();
22963
23120
  try {
22964
23121
  if (this.config.debug) {
@@ -23173,11 +23330,21 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
23173
23330
  {}
23174
23331
  );
23175
23332
  }
23333
+ const withdrawAuthInput = await resolveAuthorizationInput(
23334
+ options?.authorizationProvider,
23335
+ signer,
23336
+ {
23337
+ positionId,
23338
+ timestamp: calculateNextQuantumTimestamp(),
23339
+ amount: BigInt(withdrawalAmount).toString(),
23340
+ destinationAddress: withdrawalAddress
23341
+ }
23342
+ );
23176
23343
  const auth = await generateWithdrawAuthorization(
23177
23344
  positionId,
23178
23345
  BigInt(withdrawalAmount),
23179
23346
  chainId,
23180
- signer,
23347
+ withdrawAuthInput,
23181
23348
  withdrawalAddress
23182
23349
  // Pass destination address for message hash
23183
23350
  );
@@ -23508,9 +23675,23 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
23508
23675
  txHash: tx.hash
23509
23676
  });
23510
23677
  }
23678
+ const withdrawProvider = this.getProviderOrThrow();
23679
+ const withdrawTimeoutMs = this.config.transactionTimeoutMs || 3e5;
23511
23680
  let receipt;
23512
23681
  try {
23513
- receipt = await tx.wait();
23682
+ receipt = await Promise.race([
23683
+ withdrawProvider.waitForTransaction(tx.hash, 1),
23684
+ new Promise(
23685
+ (_, reject) => setTimeout(
23686
+ () => reject(
23687
+ new Error(
23688
+ `Transaction timeout after ${withdrawTimeoutMs}ms. Hash: ${tx.hash}`
23689
+ )
23690
+ ),
23691
+ withdrawTimeoutMs
23692
+ )
23693
+ )
23694
+ ]);
23514
23695
  } catch (waitError) {
23515
23696
  const waitErrorMsg = waitError instanceof Error ? waitError.message : String(waitError);
23516
23697
  if (this.config.debug) {
@@ -23527,7 +23708,26 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
23527
23708
  if (!receipt) {
23528
23709
  return {
23529
23710
  success: false,
23530
- error: `Transaction confirmation returned no receipt. Hash: ${tx.hash}`
23711
+ error: `Transaction receipt not found for hash: ${tx.hash}`
23712
+ };
23713
+ }
23714
+ if (receipt.status === 0) {
23715
+ const revertName = await this.decodeMinedRevert({
23716
+ to: positionManagerAddress,
23717
+ from: await withdrawSigner.getAddress(),
23718
+ data: withdrawCalldata,
23719
+ blockNumber: receipt.blockNumber
23720
+ });
23721
+ if (this.config.debug) {
23722
+ log.error("\u274C withdrawBTC reverted on-chain", {
23723
+ txHash: receipt.hash,
23724
+ blockNumber: receipt.blockNumber,
23725
+ revert: revertName ?? "undecodable"
23726
+ });
23727
+ }
23728
+ return {
23729
+ success: false,
23730
+ error: `Transaction reverted: ${revertName ?? "unknown reason"} (tx: ${receipt.hash})`
23531
23731
  };
23532
23732
  }
23533
23733
  if (this.config.debug) {
@@ -24218,6 +24418,11 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
24218
24418
  * (`MAX_ATTESTATION_AGE = 1 hour`) and replay-protects via
24219
24419
  * `authorizedAt` binding.
24220
24420
  *
24421
+ * WHO PAYS: in **service mode** lit-ops-server performs BOTH steps and its
24422
+ * relayer funds the transaction — the borrower is never prompted, because the
24423
+ * Bitcoin withdrawal this cleans up after has already settled. In **direct
24424
+ * mode** (CLI / tests / hardhat) the SDK's own signer submits step 2.
24425
+ *
24221
24426
  * `invalidatorTxid` is either:
24222
24427
  * - the borrower's own authorized broadcast (classified "consumed-as-authorized"
24223
24428
  * by the LIT validator), once it has ≥6 confirmations on the BTC
@@ -24230,7 +24435,6 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
24230
24435
  async recoverStaleSpend(params) {
24231
24436
  this.ensureInitialized();
24232
24437
  try {
24233
- const signer = this.getSignerOrThrow();
24234
24438
  const contractAddresses = this.getContractAddressesOrThrow();
24235
24439
  const btcSpendAuthorizerAddress = contractAddresses.btcSpendAuthorizer;
24236
24440
  if (!btcSpendAuthorizerAddress) {
@@ -24276,6 +24480,28 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
24276
24480
  success: false,
24277
24481
  error: json.error ?? "stale-spend recovery request failed"
24278
24482
  };
24483
+ if (!attRes.success || !attRes.attestation) {
24484
+ return {
24485
+ success: false,
24486
+ failedStep: "attestation",
24487
+ error: attRes.error ?? attRes.reason ?? `LIT recovery attestation rejected${attRes.failedStep ? ` at step ${attRes.failedStep}` : ""}`
24488
+ };
24489
+ }
24490
+ if (!attRes.transactionHash && !attRes.alreadyCleared) {
24491
+ return {
24492
+ success: false,
24493
+ failedStep: "submit",
24494
+ error: "lit-ops-server returned a stale-spend attestation but did not submit it. The server is on a build without the recovery relayer, or its STALE_SPEND_RELAYER_PRIVATE_KEY is unset."
24495
+ };
24496
+ }
24497
+ return {
24498
+ success: true,
24499
+ ...attRes.transactionHash && {
24500
+ transactionHash: attRes.transactionHash
24501
+ },
24502
+ ...attRes.classification && { classification: attRes.classification },
24503
+ attestation: attRes.attestation
24504
+ };
24279
24505
  } else {
24280
24506
  const rpcUrl = params.rpcUrl ?? this.config.ethRpcUrl;
24281
24507
  if (!rpcUrl) {
@@ -24325,7 +24551,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
24325
24551
  [
24326
24552
  "function cancelStaleSpendWithProof(bytes32 positionId, bytes32 utxoKey, uint256 authorizedAt, string calldata invalidatorTxid, uint256 attestationTimestamp, bytes calldata litSignature) external"
24327
24553
  ],
24328
- signer
24554
+ this.getSignerOrThrow()
24329
24555
  );
24330
24556
  const tx = await btcSpendAuthorizer["cancelStaleSpendWithProof"](
24331
24557
  attRes.attestation.positionId,
@@ -24655,13 +24881,11 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
24655
24881
  if (authResult.success) {
24656
24882
  break;
24657
24883
  }
24658
- const isQuantumError = authResult.error?.includes(
24659
- "QuantumOutsideWindow"
24660
- );
24884
+ const isQuantumError = isRetryableQuantumFailure(authResult.error);
24661
24885
  if (isQuantumError && wAttempt < MAX_WITHDRAWAL_QUANTUM_RETRIES) {
24662
24886
  if (this.config.debug) {
24663
24887
  log.warn(
24664
- `\u26A0\uFE0F Phase 1 QuantumOutsideWindow (attempt ${wAttempt}/${MAX_WITHDRAWAL_QUANTUM_RETRIES}). LIT action took too long. Retrying with fresh quantum...`
24888
+ `\u26A0\uFE0F Phase 1 quantum-timing failure (attempt ${wAttempt}/${MAX_WITHDRAWAL_QUANTUM_RETRIES}): ${authResult.error}. Retrying with a fresh quantum...`
24665
24889
  );
24666
24890
  }
24667
24891
  await new Promise((resolve) => setTimeout(resolve, 2e3));
@@ -25135,10 +25359,22 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
25135
25359
  "getLoanGrant",
25136
25360
  data
25137
25361
  );
25362
+ const raw = (0, import_ethers17.getBytes)(data);
25363
+ const withdrawScopeSupported = raw.length >= 128;
25364
+ const minWithdrawRatioBps = withdrawScopeSupported ? Number(
25365
+ import_ethers17.AbiCoder.defaultAbiCoder().decode(["uint32"], raw.slice(96, 128))[0]
25366
+ ) : 0;
25138
25367
  return {
25139
25368
  borrower,
25140
25369
  scopeBits: Number(scopeBits),
25141
- minCollateralRatioBps: Number(minCollateralRatioBps)
25370
+ minCollateralRatioBps: Number(minCollateralRatioBps),
25371
+ minWithdrawRatioBps,
25372
+ /**
25373
+ * True when the deployed registry is v2.1.0+ and therefore honours `SCOPE_WITHDRAW`. The
25374
+ * frontend gates its Withdrawal control on this so the control is absent — not disabled,
25375
+ * not "coming soon" — on a chain whose registry cannot support it.
25376
+ */
25377
+ withdrawScopeSupported
25142
25378
  };
25143
25379
  }
25144
25380
  /**
@@ -25180,10 +25416,14 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
25180
25416
  const mintEnabled = hasGrant && (scopes & 4) !== 0;
25181
25417
  const floor = grant.minCollateralRatioBps;
25182
25418
  const agent = user ? await registry.agentOf(user) : null;
25419
+ const withdrawEnabled = hasGrant && grant.withdrawScopeSupported && (scopes & 8) !== 0;
25183
25420
  return {
25184
25421
  renewEnabled: hasGrant && (scopes & 1) !== 0,
25185
25422
  repayEnabled: hasGrant && (scopes & 2) !== 0,
25186
25423
  mintEnabled,
25424
+ withdrawEnabled,
25425
+ withdrawScopeSupported: grant.withdrawScopeSupported,
25426
+ minWithdrawRatioBps: withdrawEnabled ? grant.minWithdrawRatioBps : 0,
25187
25427
  minCollateralRatioBps: mintEnabled ? floor : 0,
25188
25428
  effectiveMintFloorBps: mintEnabled ? Math.max(floor, protocolMin) : 0,
25189
25429
  protocolMinFloorBps: protocolMin,
@@ -25369,8 +25609,83 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
25369
25609
  const receipt = await tx.wait();
25370
25610
  return { hash: tx.hash, blockNumber: receipt?.blockNumber ?? 0 };
25371
25611
  }
25372
- /** Pre-flight the registry's `FloorBelowProtocolMin` guard with a message a user can act on. */
25373
- async assertMintFloorAtOrAboveProtocolMin(floorBps) {
25612
+ /**
25613
+ * Grant the agent the WITHDRAW scope for a position (registry v2.1.0+). Borrower-signed.
25614
+ *
25615
+ * CONTRACT WALLETS ONLY, and this is enforced here rather than on-chain. The withdrawal
25616
+ * validator's only non-borrower arm is `checkSafeAgentWithdrawAuthorization`, which requires a
25617
+ * factory-provenanced `AgentModule` whose `safe()` is the borrower — something an EOA cannot
25618
+ * have. The registry deliberately does NOT check wallet type ("Callable by any borrower (this
25619
+ * function does not itself require a Safe)"), so an EOA can record a grant that no code path
25620
+ * will ever satisfy. Refusing here keeps the supported path from writing a dead record.
25621
+ *
25622
+ * That refusal is ergonomics, NOT enforcement — `enableAutoWithdraw` is `external` and reachable
25623
+ * from any wallet. The real reason delegated withdrawal is closed to EOAs is that they can sign
25624
+ * inside the ~44s window themselves, so delegating buys them nothing and only adds risk.
25625
+ *
25626
+ * @param minWithdrawRatioBps Post-withdrawal collateral-ratio floor for AGENT withdrawals.
25627
+ * Clamped by the same governance minimum as the mint floor. **Inert at zero debt** — it
25628
+ * is a ratio, and the post-withdrawal ratio is infinite for any amount once debt is 0.
25629
+ * The operative bound there is the quorum-gated destination allowlist. Do not surface
25630
+ * this to users as a cap on how much an agent may withdraw.
25631
+ * There is no in-place raise: changing it means disable-then-re-enable, deliberately —
25632
+ * on the one scope that moves custodied BTC, incident response can only disable outright.
25633
+ */
25634
+ async enableAutoWithdraw(positionId, minWithdrawRatioBps, options) {
25635
+ const pid = this.toBytes32(positionId);
25636
+ await this.assertBorrowerIsContractWallet(pid);
25637
+ await this.assertMintFloorAtOrAboveProtocolMin(minWithdrawRatioBps, "withdraw");
25638
+ const { registry, agentAddress } = await this.prepareAgentDelegation(pid, options);
25639
+ const tx = await registry.enableAutoWithdraw(pid, minWithdrawRatioBps);
25640
+ const receipt = await tx.wait();
25641
+ return { hash: tx.hash, blockNumber: receipt?.blockNumber ?? 0, agentAddress };
25642
+ }
25643
+ /**
25644
+ * Revoke the WITHDRAW scope for a position (also zeroes its withdraw floor). Borrower-signed.
25645
+ *
25646
+ * No wallet-type check: revoking is the safe direction, and an EOA that recorded a grant before
25647
+ * this guard existed must be able to clear it.
25648
+ */
25649
+ async disableAutoWithdraw(positionId) {
25650
+ const addr = this.agentDelegationRegistryOrThrow();
25651
+ const signer = this.getSignerOrThrow();
25652
+ const registry = AgentDelegationRegistry__factory.connect(addr, signer);
25653
+ const pid = this.toBytes32(positionId);
25654
+ const tx = await registry.disableAutoWithdraw(pid);
25655
+ const receipt = await tx.wait();
25656
+ await this.clearGate1IfNoScopesRemain(pid);
25657
+ return { hash: tx.hash, blockNumber: receipt?.blockNumber ?? 0 };
25658
+ }
25659
+ /**
25660
+ * Layer 2 of the withdrawal-delegation gating: refuse to write a grant that could never be
25661
+ * satisfied. See `enableAutoWithdraw` for why this lives here and not in the registry.
25662
+ */
25663
+ async assertBorrowerIsContractWallet(pid) {
25664
+ const details = await this.getPositionDetailsView(pid);
25665
+ const borrower = details?.borrower;
25666
+ if (!borrower) {
25667
+ throw new SDKError({
25668
+ message: `Could not resolve the borrower for position ${pid} \u2014 refusing to write a delegation grant against an unverified position`,
25669
+ category: "VALIDATION" /* VALIDATION */,
25670
+ severity: "HIGH" /* HIGH */
25671
+ });
25672
+ }
25673
+ const code = await this.getProviderOrThrow().getCode(borrower);
25674
+ if (!code || code === "0x") {
25675
+ throw new SDKError({
25676
+ message: `Withdrawal delegation is only available to contract wallets. Borrower ${borrower} is an EOA, and the withdrawal validator has no delegate arm for one \u2014 an EOA can sign inside the withdrawal quantum window itself, so the grant would be recorded on-chain and never honoured.`,
25677
+ category: "VALIDATION" /* VALIDATION */,
25678
+ severity: "MEDIUM" /* MEDIUM */
25679
+ });
25680
+ }
25681
+ }
25682
+ /**
25683
+ * Pre-flight the registry's `FloorBelowProtocolMin` guard with a message a user can act on.
25684
+ *
25685
+ * `agentMintFloorMinBps` is the governance floor-of-floors for BOTH the mint and withdraw
25686
+ * grants — one lever, one invariant — so `scope` only shapes the message.
25687
+ */
25688
+ async assertMintFloorAtOrAboveProtocolMin(floorBps, scope = "mint") {
25374
25689
  if (!Number.isInteger(floorBps) || floorBps <= 0) {
25375
25690
  throw new SDKError({
25376
25691
  message: `Invalid collateral floor: ${floorBps} bps \u2014 must be a positive integer`,
@@ -25381,7 +25696,7 @@ Message: ${causeMessage}${quantumContext}${mintDebtDiagnostics}`
25381
25696
  const protocolMin = await this.getAgentMintFloorMinBps();
25382
25697
  if (protocolMin === 0) {
25383
25698
  throw new SDKError({
25384
- message: "Agent minting is not configured on this network (agentMintFloorMinBps is 0) \u2014 the registry would reject every mint grant",
25699
+ message: `Agent ${scope === "mint" ? "minting" : "withdrawal"} is not configured on this network (agentMintFloorMinBps is 0) \u2014 the registry would reject every ${scope} grant`,
25385
25700
  category: "CONFIGURATION" /* CONFIGURATION */,
25386
25701
  severity: "HIGH" /* HIGH */
25387
25702
  });