@gvnrdao/dh-lit-ops 0.0.55 → 0.0.73

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.
Files changed (39) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -3
  4. package/dist/index.js.map +1 -1
  5. package/dist/interfaces/chunks/authentication.d.ts +0 -1
  6. package/dist/interfaces/chunks/authentication.d.ts.map +1 -1
  7. package/dist/interfaces/chunks/config.d.ts +22 -1
  8. package/dist/interfaces/chunks/config.d.ts.map +1 -1
  9. package/dist/interfaces/chunks/config.js +53 -0
  10. package/dist/interfaces/chunks/config.js.map +1 -1
  11. package/dist/interfaces/chunks/lit-action-execution.d.ts +0 -2
  12. package/dist/interfaces/chunks/lit-action-execution.d.ts.map +1 -1
  13. package/dist/interfaces/chunks/pkp-operations.d.ts +0 -1
  14. package/dist/interfaces/chunks/pkp-operations.d.ts.map +1 -1
  15. package/dist/modules/action-executor.module.d.ts.map +1 -1
  16. package/dist/modules/action-executor.module.js +26 -106
  17. package/dist/modules/action-executor.module.js.map +1 -1
  18. package/dist/modules/auth-manager.module.d.ts.map +1 -1
  19. package/dist/modules/auth-manager.module.js +41 -76
  20. package/dist/modules/auth-manager.module.js.map +1 -1
  21. package/dist/modules/capacity-master.module.d.ts +2 -2
  22. package/dist/modules/capacity-master.module.d.ts.map +1 -1
  23. package/dist/modules/capacity-master.module.js +29 -42
  24. package/dist/modules/capacity-master.module.js.map +1 -1
  25. package/dist/modules/lit-ops.module.d.ts +137 -7
  26. package/dist/modules/lit-ops.module.d.ts.map +1 -1
  27. package/dist/modules/lit-ops.module.js +313 -295
  28. package/dist/modules/lit-ops.module.js.map +1 -1
  29. package/dist/modules/pkp-macros.module.d.ts +1 -3
  30. package/dist/modules/pkp-macros.module.d.ts.map +1 -1
  31. package/dist/modules/pkp-macros.module.js +15 -41
  32. package/dist/modules/pkp-macros.module.js.map +1 -1
  33. package/dist/modules/pkp-signer.module.d.ts.map +1 -1
  34. package/dist/modules/pkp-signer.module.js +2 -1
  35. package/dist/modules/pkp-signer.module.js.map +1 -1
  36. package/dist/modules/session-signature-manager.module.d.ts.map +1 -1
  37. package/dist/modules/session-signature-manager.module.js +26 -20
  38. package/dist/modules/session-signature-manager.module.js.map +1 -1
  39. package/package.json +2 -2
@@ -56,6 +56,38 @@ const session_signature_manager_module_1 = require("./session-signature-manager.
56
56
  const capacity_master_module_1 = require("./capacity-master.module");
57
57
  class LitOps {
58
58
  constructor(config) {
59
+ // CRITICAL VALIDATION: Reject datil-test immediately with terrifying error
60
+ if (config.network === "datil-test") {
61
+ throw new Error("\n\n" +
62
+ "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" +
63
+ "❌ ❌ ❌ FATAL ERROR: datil-test IS NOT SUPPORTED ❌ ❌ ❌\n" +
64
+ "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" +
65
+ "\n" +
66
+ "The 'datil-test' network has been PERMANENTLY REMOVED from this codebase.\n" +
67
+ "\n" +
68
+ "WHY THIS ERROR EXISTS:\n" +
69
+ " • datil-test was unstable and caused production failures\n" +
70
+ " • datil-test is deprecated by LIT Protocol\n" +
71
+ " • Using datil-test was masking real production issues\n" +
72
+ "\n" +
73
+ "WHAT YOU MUST DO:\n" +
74
+ " 1. Change ALL 'datil-test' references to 'datil'\n" +
75
+ " 2. Use capacity credits for testing on datil network\n" +
76
+ " 3. Test with the ACTUAL production network (datil)\n" +
77
+ "\n" +
78
+ "WHERE TO LOOK:\n" +
79
+ " • Configuration files (.env, config files)\n" +
80
+ " • Test files (*.test.ts)\n" +
81
+ " • SDK initialization code\n" +
82
+ " • LitOps/LIT Protocol integration points\n" +
83
+ "\n" +
84
+ "EXAMPLE FIX:\n" +
85
+ " ❌ BAD: network: 'datil-test'\n" +
86
+ " ✅ GOOD: network: 'datil'\n" +
87
+ "\n" +
88
+ "This is NOT negotiable. datil-test support will NEVER return.\n" +
89
+ "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
90
+ }
59
91
  this.config = {
60
92
  domain: "diamond-hands.local",
61
93
  sessionExpirationMinutes: 30, // Extended from 15 to 30 for longer retry cycles
@@ -290,8 +322,7 @@ class LitOps {
290
322
  * Execute a LIT Action from CID
291
323
  */
292
324
  async executeActionFromCID(cid, pkpPublicKey, params, signer, pkpTokenId, // Optional PKP token ID for session signature scoping
293
- pkpEthAddress, // Optional PKP Ethereum address for capacity credit delegation
294
- additionalPkpTokenIds // Additional PKP token IDs for multi-PKP operations
325
+ pkpEthAddress // Optional PKP Ethereum address for capacity credit delegation
295
326
  ) {
296
327
  const litClient = await this.clientManager.getClient({
297
328
  litNetwork: this.config.network,
@@ -304,7 +335,6 @@ class LitOps {
304
335
  signer,
305
336
  pkpTokenId, // Pass through pkpTokenId for session signature scoping
306
337
  pkpEthAddress, // Pass through pkpEthAddress for capacity delegation
307
- additionalPkpTokenIds, // Pass through additional PKP token IDs for multi-PKP operations
308
338
  };
309
339
  return this.actionExecutor.executeAction(request, litClient);
310
340
  }
@@ -448,6 +478,8 @@ class LitOps {
448
478
  pkpPublicKey,
449
479
  params,
450
480
  signer,
481
+ // Include pkpTokenId for session signature scoping (required for burned PKPs)
482
+ pkpTokenId: pkpTokenId || params?.pkpTokenId,
451
483
  };
452
484
  return this.actionExecutor.executeAction(request, litClient);
453
485
  }
@@ -605,12 +637,10 @@ class LitOps {
605
637
  console.log(` - targetPkpTokenId: "${targetPkpTokenId}"`);
606
638
  console.log(` - targetPkpTokenId type: ${typeof targetPkpTokenId}`);
607
639
  console.log(` - targetPkpTokenId length: ${targetPkpTokenId.length}`);
608
- console.log(` - targetPkpTokenId startsWith '0x': ${targetPkpTokenId.startsWith('0x')}`);
640
+ console.log(` - targetPkpTokenId startsWith '0x': ${targetPkpTokenId.startsWith("0x")}`);
609
641
  console.log(` - expectedCid (WILL BE PASSED TO LIT ACTION): "${expectedCid}"`);
610
642
  console.log(` - signerPkp.tokenId: "${signerPkp.tokenId}"`);
611
643
  console.log(` - signerPkp.ethAddress: "${signerPkp.ethAddress}"`);
612
- console.log(` - signerPkp.publicKey: "${signerPkp.publicKey?.substring(0, 50)}..."`);
613
- console.log(` - signerPkp.publicKey full: "${signerPkp.publicKey}"`);
614
644
  if (this.config.debug) {
615
645
  console.log("\n🔍 LitOps.validatePKPSecurity");
616
646
  console.log(` Target PKP: ${targetPkpTokenId}`);
@@ -619,49 +649,46 @@ class LitOps {
619
649
  }
620
650
  try {
621
651
  // Get the PKP validator CID
622
- // CRITICAL: Ensure datil network uses DH_LIT_ACTIONS_DATIL (not DATIL_TEST)
623
- const isDatil = this.config.network === "datil";
624
- const registry = isDatil ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL : dh_lit_actions_1.DH_LIT_ACTIONS_DATIL_TEST;
625
- // Validation: Ensure we're using the correct registry for datil network
626
- if (isDatil && registry !== dh_lit_actions_1.DH_LIT_ACTIONS_DATIL) {
627
- throw new Error(`Network mismatch: Expected DH_LIT_ACTIONS_DATIL for datil network, but got different registry`);
628
- }
629
- const pkpValidatorCid = registry.pkpValidator.cid;
630
- // Validation: Ensure datil network uses the correct validator CID (version 2.0.0)
631
- const EXPECTED_DATIL_VALIDATOR_CID = "QmfQjsJTxpd26tBXh5qobxncBeq2iWCPUNDTfUgVGqLis1";
632
- if (isDatil && pkpValidatorCid !== EXPECTED_DATIL_VALIDATOR_CID) {
633
- throw new Error(`Invalid validator CID for datil network: Expected ${EXPECTED_DATIL_VALIDATOR_CID} (version 2.0.0), but got ${pkpValidatorCid}`);
634
- }
635
- // Get validator CID in hex format for signature (contract expects this CID in signature)
636
- const validatorCidHex = (0, dh_lit_actions_1.cidToHex)(pkpValidatorCid);
652
+ const pkpValidatorCid = (this.config.network === "datil"
653
+ ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
654
+ : dh_lit_actions_1.DH_LIT_ACTIONS_DATIL_TEST).pkpValidator.cid;
637
655
  if (this.config.debug) {
638
- console.log(` Network: ${this.config.network}`);
639
- console.log(` Registry: ${isDatil ? "DH_LIT_ACTIONS_DATIL" : "DH_LIT_ACTIONS_DATIL_TEST"}`);
640
656
  console.log(` PKP Validator CID: ${pkpValidatorCid}`);
641
- console.log(` Validator Version: ${registry.pkpValidator.version}`);
642
- console.log(` Validator CID (hex, for signature): ${validatorCidHex}`);
643
657
  }
644
658
  // Execute PKP Validator LIT Action
645
- // NOTE: We do NOT pass the validator PKP token ID here because the validator
646
- // performs READ-ONLY operations (no signing). Passing undefined for pkpTokenId
647
- // will skip adding LitPKPResource to the session signature, which is correct
648
- // for burned/immutable validator PKPs that can't sign via session signatures.
659
+ // NOTE: Even though the validator PKP is burned/immutable (read-only),
660
+ // we still pass its tokenId so the session signature is scoped to the
661
+ // validator resource. The Lit nodes will simply treat the resource as
662
+ // non-signing, but scoping keeps the session principle of least privilege.
649
663
  console.log(`\n🔍 LIT-OPS TRACE: About to call executeActionFromCID with jsParams:`);
650
664
  console.log(` - targetPkpTokenId: "${targetPkpTokenId}"`);
651
665
  console.log(` - expectedCid: "${expectedCid}"`);
652
- // Match main branch pattern: do NOT pass pkpEthAddress
653
- // Capacity credits are owned by the signer (serverWallet), so auth-manager
654
- // will delegate to signer.getAddress() which is correct
666
+ let sessionScopedSignerTokenId = signerPkp.tokenId;
667
+ try {
668
+ const normalizedHex = BigInt(signerPkp.tokenId)
669
+ .toString(16)
670
+ .padStart(64, "0");
671
+ sessionScopedSignerTokenId = `0x${normalizedHex}`;
672
+ }
673
+ catch (tokenErr) {
674
+ console.warn("⚠️ Failed to normalize signer PKP tokenId to hex, using raw value", tokenErr);
675
+ }
676
+ // Convert validator CID to hex format for signature message
677
+ // The contract expects the validator's CID in the signature, not the PKP's CID
678
+ const validatorCidHex = (0, dh_lit_actions_1.cidToHex)(pkpValidatorCid);
679
+ if (this.config.debug) {
680
+ console.log(` Validator CID (IPFS): ${pkpValidatorCid}`);
681
+ console.log(` Validator CID (hex): ${validatorCidHex}`);
682
+ console.log(` PKP's expected CID (hex): ${expectedCid}`);
683
+ }
655
684
  const litResult = await this.executeActionFromCID(pkpValidatorCid, signerPkp.publicKey, {
656
685
  targetPkpTokenId,
657
- expectedCid, // Expected to be in hex format
658
- certificationCid: validatorCidHex, // Validator CID for signature (contract expects this)
686
+ expectedCid, // PKP's LIT Action CID (for validation check)
687
+ certificationCid: validatorCidHex, // Validator's CID (for signature message)
659
688
  message: `PKP validation: ${Date.now()}`,
660
689
  // Ensure the LIT Action knows which network (datil vs datil-test)
661
690
  network: this.config.network,
662
- }, signer
663
- // Intentionally NOT passing signerPkp.tokenId - validator is read-only
664
- // Intentionally NOT passing pkpEthAddress - match main branch pattern
691
+ }, signer, sessionScopedSignerTokenId, signerPkp.ethAddress // Required for capacity delegation on Datil
665
692
  );
666
693
  if (this.config.debug) {
667
694
  console.log(` LIT Action Success: ${litResult.success}`);
@@ -710,14 +737,8 @@ class LitOps {
710
737
  // For signing errors, we skip signature verification (architectural limitation)
711
738
  if (isSigningError) {
712
739
  signatureValid = true; // Mark as valid since validation succeeded
713
- // Compute PKP address from public key for consistency
714
- const cleanPublicKey = signerPkp.publicKey.startsWith("0x")
715
- ? signerPkp.publicKey
716
- : `0x${signerPkp.publicKey}`;
717
- pkpAddress = ethers_1.ethers.utils.computeAddress(cleanPublicKey);
718
740
  if (this.config.debug) {
719
741
  console.log(" ℹ️ Signature verification skipped - burned PKP limitation");
720
- console.log(` PKP Address: ${pkpAddress}`);
721
742
  }
722
743
  }
723
744
  else if (litResult.signatures &&
@@ -857,10 +878,9 @@ class LitOps {
857
878
  }
858
879
  const retries = this.config.pkpServiceRetryCount ?? 2;
859
880
  const retryDelay = this.config.pkpServiceRetryDelayMs ?? 2000;
860
- const timeoutMs = this.config.pkpServiceTimeoutMs ?? 480000; // 8 minutes to match server timeout
881
+ const timeoutMs = this.config.pkpServiceTimeoutMs ?? 120000;
861
882
  let attempt = 0;
862
883
  let lastError = null;
863
- let rateLimitDetected = false;
864
884
  while (attempt <= retries) {
865
885
  if (this.config.debug) {
866
886
  console.log(` 🌐 Service mode: calling ${this.config.serviceEndpoint}/api/lit/pkp/create-diamond-hands-loan (attempt ${attempt + 1}/${retries + 1})`);
@@ -876,37 +896,7 @@ class LitOps {
876
896
  }),
877
897
  }, timeoutMs);
878
898
  if (!response.ok) {
879
- const errorText = await response.text();
880
- let errorMessage = `Service request failed: ${response.status} ${response.statusText}`;
881
- // Try to parse error text as JSON to extract error message
882
- let parsedError = null;
883
- try {
884
- parsedError = JSON.parse(errorText);
885
- if (parsedError.error) {
886
- errorMessage = `${errorMessage} - ${parsedError.error}`;
887
- }
888
- else if (parsedError.message) {
889
- errorMessage = `${errorMessage} - ${parsedError.message}`;
890
- }
891
- else if (errorText && errorText.length > 0) {
892
- errorMessage = `${errorMessage} - ${errorText}`;
893
- }
894
- }
895
- catch {
896
- // Not JSON, use raw text if available
897
- if (errorText && errorText.length > 0) {
898
- errorMessage = `${errorMessage} - ${errorText}`;
899
- }
900
- }
901
- // Check for rate limit errors
902
- const isRateLimit = response.status === 429 ||
903
- errorText.toLowerCase().includes('rate_limit') ||
904
- errorText.toLowerCase().includes('rate limit');
905
- if (isRateLimit) {
906
- rateLimitDetected = true;
907
- errorMessage = `Rate limit exceeded: ${errorText || errorMessage}`;
908
- }
909
- throw new Error(errorMessage);
899
+ throw new Error(`Service request failed: ${response.status} ${response.statusText}`);
910
900
  }
911
901
  const result = (await response.json());
912
902
  const duration = Date.now() - startTime;
@@ -937,21 +927,14 @@ class LitOps {
937
927
  message.includes("NodeJsTimeoutError") ||
938
928
  message.includes("Gateway") ||
939
929
  message.includes("502");
940
- const isRateLimit = rateLimitDetected ||
941
- message.toLowerCase().includes('rate_limit') ||
942
- message.toLowerCase().includes('rate limit');
943
930
  if (this.config.debug) {
944
- console.error(`❌ Service request failed on attempt ${attempt + 1}/${retries + 1}:\n Message: ${message}`);
931
+ console.error(`❌ Service request failed on attempt ${attempt + 1}/$${retries + 1}:\n Message: ${message}`);
945
932
  }
946
- // For rate limit errors, use exponential backoff with longer delays
947
- if (attempt < retries && (isTimeout || isRateLimit)) {
948
- const backoffDelay = isRateLimit
949
- ? Math.min(retryDelay * Math.pow(3, attempt), 60000) // Exponential backoff: 2s, 6s, 18s, max 60s
950
- : retryDelay;
933
+ if (attempt < retries && isTimeout) {
951
934
  if (this.config.debug) {
952
- console.log(` Retrying after ${backoffDelay}ms due to ${isRateLimit ? 'rate limit' : 'transient'} error...`);
935
+ console.log(` Retrying after ${retryDelay}ms due to transient error...`);
953
936
  }
954
- await new Promise((resolve) => setTimeout(resolve, backoffDelay));
937
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
955
938
  attempt++;
956
939
  continue;
957
940
  }
@@ -1045,138 +1028,13 @@ class LitOps {
1045
1028
  console.log(` ℹ️ Continuing to validation anyway - may fail if propagation incomplete`);
1046
1029
  }
1047
1030
  }
1048
- // Robust pre-validation: wait for mint receipt + confirmations on Yellowstone
1049
- // This avoids racing propagation by keying off confirmed blocks
1050
- try {
1051
- const ethers5 = await Promise.resolve().then(() => __importStar(require("ethers")));
1052
- const rpcUrl = this.config.network === "datil"
1053
- ? "https://yellowstone-rpc.litprotocol.com"
1054
- : "https://yellowstone-rpc.litprotocol.com"; // Same for both currently
1055
- const mintTxHash = pkpData.mintTransactionHash;
1056
- const confirmationsTarget = this.config.yellowstoneConfirmations ?? 2; // Reduced from 3 to 2 to reduce polling
1057
- const receiptPollMs = this.config.yellowstoneReceiptPollMs ?? 10000; // Increased from 5s to 10s to reduce rate limit pressure
1058
- const receiptTimeoutMs = this.config.yellowstoneReceiptTimeoutMs ?? 180000; // 2 minutes
1059
- if (this.config.debug) {
1060
- console.log("\n ⏳ Waiting for PKP mint transaction receipt and confirmations...");
1061
- console.log(` - mintTxHash: ${mintTxHash}`);
1062
- console.log(` - confirmationsTarget: ${confirmationsTarget}`);
1063
- console.log(` - poll interval: ${receiptPollMs}ms, timeout: ${receiptTimeoutMs}ms`);
1064
- }
1065
- const startedAt = Date.now();
1066
- let receipt = null;
1067
- // Poll for receipt
1068
- while (Date.now() - startedAt < receiptTimeoutMs) {
1069
- const receiptPayload = {
1070
- method: "eth_getTransactionReceipt",
1071
- params: [mintTxHash],
1072
- id: 1,
1073
- jsonrpc: "2.0",
1074
- };
1075
- const resp = await fetch(rpcUrl, {
1076
- method: "POST",
1077
- headers: { "Content-Type": "application/json" },
1078
- body: JSON.stringify(receiptPayload),
1079
- });
1080
- const json = (await resp.json());
1081
- if (json && json.result) {
1082
- receipt = json.result;
1083
- break;
1084
- }
1085
- await new Promise((r) => setTimeout(r, receiptPollMs));
1086
- }
1087
- if (!receipt) {
1088
- if (this.config.debug) {
1089
- console.log(" ⚠️ Mint receipt not found within timeout; continuing optimistically");
1090
- }
1091
- }
1092
- else {
1093
- // Wait for confirmations
1094
- const mintedAtBlock = ethers5.BigNumber.from(receipt.blockNumber).toNumber();
1095
- if (this.config.debug) {
1096
- console.log(` ✅ Mint receipt found at block ${mintedAtBlock}`);
1097
- }
1098
- let currentBlock = mintedAtBlock;
1099
- while (currentBlock < mintedAtBlock + confirmationsTarget) {
1100
- const blockNumPayload = { method: "eth_blockNumber", params: [], id: 1, jsonrpc: "2.0" };
1101
- const bResp = await fetch(rpcUrl, {
1102
- method: "POST",
1103
- headers: { "Content-Type": "application/json" },
1104
- body: JSON.stringify(blockNumPayload),
1105
- });
1106
- const bJson = (await bResp.json());
1107
- if (bJson && bJson.result) {
1108
- currentBlock = ethers5.BigNumber.from(bJson.result).toNumber();
1109
- if (this.config.debug) {
1110
- console.log(` ⛓️ Yellowstone block: ${currentBlock} (target >= ${mintedAtBlock + confirmationsTarget})`);
1111
- }
1112
- if (currentBlock >= mintedAtBlock + confirmationsTarget) {
1113
- break;
1114
- }
1115
- }
1116
- // Use same polling interval as receipt polling (10s) to reduce rate limit pressure
1117
- await new Promise((r) => setTimeout(r, receiptPollMs));
1118
- }
1119
- if (this.config.debug) {
1120
- console.log(" ✅ Required confirmations reached; proceeding to existence check");
1121
- }
1122
- }
1123
- }
1124
- catch (confirmWaitError) {
1125
- if (this.config.debug) {
1126
- console.log(" ⚠️ Error during receipt/confirmation wait:", confirmWaitError);
1127
- console.log(" Continuing with existence check regardless");
1128
- }
1129
- }
1130
- // Step 1.75: Verify PKP existence using ownerOf() (more reliable than exists())
1031
+ // Additional delay after burn verification for network consensus
1131
1032
  if (this.config.debug) {
1132
- console.log("\n Step 1.75: Pre-validation PKP ownerOf() check on Chronicle Yellowstone...");
1133
- }
1134
- try {
1135
- const ethers5 = await Promise.resolve().then(() => __importStar(require("ethers")));
1136
- const pkpTokenIdBN = ethers5.BigNumber.from(pkpData.tokenId);
1137
- const ownerOfCalldata = ethers5.utils.concat([
1138
- ethers5.utils.id("ownerOf(uint256)").substring(0, 10),
1139
- ethers5.utils.defaultAbiCoder.encode(["uint256"], [pkpTokenIdBN]),
1140
- ]);
1141
- const rpcUrl = this.config.network === "datil"
1142
- ? "https://yellowstone-rpc.litprotocol.com"
1143
- : "https://yellowstone-rpc.litprotocol.com";
1144
- const pkpNftAddress = process.env.LIT_PKP_NFT_ADDRESS && process.env.LIT_PKP_NFT_ADDRESS.trim() !== ""
1145
- ? process.env.LIT_PKP_NFT_ADDRESS
1146
- : (this.config.network === "datil"
1147
- ? dh_lit_actions_1.SEPOLIA_DEPLOYMENT.litProtocol?.pkpNftContract
1148
- : dh_lit_actions_1.LOCALHOST_DEPLOYMENT.litProtocol?.pkpNftContract);
1149
- const ownerRpcPayload = {
1150
- method: "eth_call",
1151
- params: [
1152
- {
1153
- to: pkpNftAddress,
1154
- data: ethers5.utils.hexlify(ownerOfCalldata),
1155
- },
1156
- "latest",
1157
- ],
1158
- id: 1,
1159
- jsonrpc: "2.0",
1160
- };
1161
- const ownerResponse = await fetch(rpcUrl, {
1162
- method: "POST",
1163
- headers: { "Content-Type": "application/json" },
1164
- body: JSON.stringify(ownerRpcPayload),
1165
- });
1166
- const ownerResult = (await ownerResponse.json());
1167
- if (ownerResult.error || !ownerResult.result || ownerResult.result === '0x') {
1168
- throw new Error(`ownerOf() failed or returned empty (likely propagation): ${ownerResult.error?.message || ownerResult.error || 'empty result'}`);
1169
- }
1170
- const owner = ethers5.utils.defaultAbiCoder.decode(["address"], ownerResult.result)[0];
1171
- if (this.config.debug) {
1172
- console.log(` ✅ ownerOf() returned: ${owner}`);
1173
- }
1033
+ console.log("\n Additional delay for network consensus (10 seconds)...");
1174
1034
  }
1175
- catch (preValidationError) {
1176
- if (this.config.debug) {
1177
- console.error(` Pre-validation ownerOf() check failed:`, preValidationError);
1178
- }
1179
- // Do not hard-fail here; proceed to validator with retry logic
1035
+ await new Promise((resolve) => setTimeout(resolve, 10000));
1036
+ if (this.config.debug) {
1037
+ console.log(` Network propagation and consensus complete`);
1180
1038
  }
1181
1039
  // Step 2: Validate PKP security using pkp-validator LIT Action
1182
1040
  const step2Start = Date.now();
@@ -1202,64 +1060,24 @@ class LitOps {
1202
1060
  // }
1203
1061
  // Use production deployments
1204
1062
  validatorPkp = dh_lit_actions_1.DATIL_DEPLOYMENTS.pkpValidator.pkp;
1205
- // Log validator PKP data for debugging
1206
- console.log(`🔍 LIT-OPS TRACE: Validator PKP Source Check:`);
1207
- console.log(` - DATIL_DEPLOYMENTS.pkpValidator.pkp exists:`, !!validatorPkp);
1208
- console.log(` - DATIL_DEPLOYMENTS.pkpValidator.cid:`, dh_lit_actions_1.DATIL_DEPLOYMENTS.pkpValidator.cid);
1209
- if (validatorPkp) {
1210
- console.log(` - validatorPkp.ethAddress:`, validatorPkp.ethAddress);
1211
- console.log(` - validatorPkp.publicKey:`, validatorPkp.publicKey?.substring(0, 50) + '...');
1212
- console.log(` - validatorPkp.tokenId:`, validatorPkp.tokenId);
1213
- }
1214
- else {
1215
- console.log(` - DATIL_DEPLOYMENTS.pkpValidator keys:`, Object.keys(dh_lit_actions_1.DATIL_DEPLOYMENTS.pkpValidator));
1216
- }
1217
1063
  if (this.config.debug) {
1218
1064
  console.log(" Using production validator PKP", validatorPkp);
1219
1065
  }
1220
1066
  if (!validatorPkp) {
1221
1067
  throw new Error("PKP validator PKP not found in dh-lit-actions package");
1222
1068
  }
1223
- // TWO DIFFERENT CIDs ARE USED IN PKP VALIDATION:
1224
- // 1. Authorization CID - validates loan PKP has correct operational CID (auth dummy)
1225
- // 2. Validator CID - used by validator PKP to sign certification for on-chain contract
1226
- // OPERATIONAL VALIDATION: Check loan PKP has authorization dummy CID
1227
- // IMPORTANT: Loan PKPs are authorized with operational CIDs (auth dummy + UCD mint)
1228
- // They do NOT have the validator CID - that's only for the centralized validator PKP
1229
- // We validate that the loan PKP has the expected authorization dummy CID
1230
- const authorizationCid = cids[0]; // First CID in array (authorization dummy)
1231
- if (!authorizationCid) {
1232
- throw new Error("Authorization CID not found in cids array");
1233
- }
1234
- console.log(`🔍 LIT-OPS TRACE: authorizationCid (first in array) = "${authorizationCid}"`);
1235
- console.log(`🔍 LIT-OPS TRACE: Authorized actions cids array:`, cids);
1236
- const litActionCidHex = (0, dh_lit_actions_1.cidToHex)(authorizationCid);
1069
+ // Convert IPFS CID to hex format for validation (using first CID)
1070
+ const firstCid = cids[0];
1071
+ console.log(`🔍 LIT-OPS TRACE: firstCid (cids[0]) = "${firstCid}"`);
1072
+ console.log(`🔍 LIT-OPS TRACE: All cids array:`, cids);
1073
+ const litActionCidHex = (0, dh_lit_actions_1.cidToHex)(firstCid);
1237
1074
  console.log(`🔍 LIT-OPS TRACE: litActionCidHex after cidToHex() = "${litActionCidHex}"`);
1238
1075
  if (this.config.debug) {
1239
- console.log(` Converting CID: ${authorizationCid} → ${litActionCidHex}`);
1240
- }
1241
- // CERTIFICATION SIGNATURE: Get validator CID for on-chain contract signature
1242
- // The on-chain contract expects the validator PKP to sign with the VALIDATOR CID
1243
- // NOT the authorization CID! This is the CID registered in cidForVersion mapping.
1244
- const validatorCid = (this.config.network === "datil"
1245
- ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
1246
- : dh_lit_actions_1.DH_LIT_ACTIONS_DATIL_TEST).pkpValidator.cid;
1247
- const validatorCidHex = (0, dh_lit_actions_1.cidToHex)(validatorCid);
1248
- console.log(`🔍 LIT-OPS TRACE: validatorCid (for signature) = "${validatorCid}"`);
1249
- console.log(`🔍 LIT-OPS TRACE: validatorCidHex (for signature) = "${validatorCidHex}"`);
1250
- if (this.config.debug) {
1251
- console.log(` Validator CID for signature: ${validatorCid} → ${validatorCidHex}`);
1252
- }
1253
- // Wait for PKP to propagate on the network before validation
1254
- // This helps avoid "execution reverted" errors when checking PKP existence
1255
- if (this.config.debug) {
1256
- console.log("\n ⏳ Waiting 5 seconds for PKP network propagation before validation...");
1076
+ console.log(` Converting CID: ${firstCid} → ${litActionCidHex}`);
1257
1077
  }
1258
- await new Promise((resolve) => setTimeout(resolve, 5000));
1259
1078
  // Attempt validation with retry logic for network propagation issues
1260
1079
  let validationResult;
1261
- const maxValidationRetries = 5; // Increased from 3 to 5 for better reliability
1262
- const retryDelays = [2000, 4000, 8000, 16000]; // Exponential backoff: 2s, 4s, 8s, 16s
1080
+ const maxValidationRetries = 2;
1263
1081
  let lastValidationError = null;
1264
1082
  for (let attempt = 1; attempt <= maxValidationRetries; attempt++) {
1265
1083
  try {
@@ -1283,11 +1101,11 @@ class LitOps {
1283
1101
  }
1284
1102
  // Validation returned but marked as unsuccessful
1285
1103
  lastValidationError = new Error(`PKP validation failed: ${validationResult.error || "Unknown validation error"}`);
1286
- // If not the last attempt, wait before retrying with exponential backoff
1104
+ // If not the last attempt, wait before retrying
1287
1105
  if (attempt < maxValidationRetries) {
1288
- const retryDelay = retryDelays[attempt - 1]; // Exponential backoff
1106
+ const retryDelay = 10000; // 10 seconds between validation retries
1289
1107
  if (this.config.debug) {
1290
- console.log(` ⏳ Exponential backoff: Waiting ${retryDelay}ms before retry ${attempt + 1}/${maxValidationRetries}...`);
1108
+ console.log(` ⏳ Waiting ${retryDelay}ms before retry...`);
1291
1109
  }
1292
1110
  await new Promise((resolve) => setTimeout(resolve, retryDelay));
1293
1111
  }
@@ -1307,11 +1125,11 @@ class LitOps {
1307
1125
  console.log(` ℹ️ This appears to be a network propagation issue`);
1308
1126
  }
1309
1127
  }
1310
- // If this is a propagation error and not the last attempt, retry with exponential backoff
1128
+ // If this is a propagation error and not the last attempt, retry
1311
1129
  if (isPropagationError && attempt < maxValidationRetries) {
1312
- const retryDelay = retryDelays[attempt - 1]; // Exponential backoff
1130
+ const retryDelay = 15000; // 15 seconds for propagation issues
1313
1131
  if (this.config.debug) {
1314
- console.log(` ⏳ Exponential backoff: Waiting ${retryDelay}ms for network propagation before retry ${attempt + 1}/${maxValidationRetries}...`);
1132
+ console.log(` ⏳ Waiting ${retryDelay}ms for network propagation before retry...`);
1315
1133
  }
1316
1134
  await new Promise((resolve) => setTimeout(resolve, retryDelay));
1317
1135
  continue;
@@ -1481,7 +1299,7 @@ class LitOps {
1481
1299
  console.log(" Has clientManager:", !!this.clientManager);
1482
1300
  console.log(" clientManager type:", typeof this.clientManager);
1483
1301
  if (this.clientManager) {
1484
- console.log(" clientManager keys:", Object.keys(this.clientManager).join(', '));
1302
+ console.log(" clientManager keys:", Object.keys(this.clientManager).join(", "));
1485
1303
  console.log(" Has getClient:", typeof this.clientManager.getClient);
1486
1304
  }
1487
1305
  }
@@ -1498,7 +1316,7 @@ class LitOps {
1498
1316
  if (this.config.debug) {
1499
1317
  console.log("🌐 Requesting mint authorization via service:", url);
1500
1318
  console.log(" Position ID:", request.authMessage.positionId);
1501
- console.log(" Amount:", request.authMessage.requestedAmount);
1319
+ console.log(" Amount:", request.authMessage.amount);
1502
1320
  }
1503
1321
  try {
1504
1322
  const response = await fetch(url, {
@@ -1509,6 +1327,8 @@ class LitOps {
1509
1327
  body: JSON.stringify({
1510
1328
  authMessage: request.authMessage,
1511
1329
  userSignature: request.userSignature,
1330
+ customRpcUrl: request.customRpcUrl,
1331
+ customBitcoinRpcUrl: request.customBitcoinRpcUrl,
1512
1332
  }),
1513
1333
  });
1514
1334
  if (!response.ok) {
@@ -1571,46 +1391,77 @@ class LitOps {
1571
1391
  console.log(" Network:", this.config.network);
1572
1392
  console.log(" CID:", litActionInfo.cid);
1573
1393
  console.log(" Position:", request.authMessage.positionId);
1574
- console.log(" Amount:", request.authMessage.requestedAmount);
1575
- console.log(" Quantum:", request.authMessage.quantumTimestamp);
1394
+ console.log(" Amount:", request.authMessage.amount);
1395
+ console.log(" Timestamp:", request.authMessage.timestamp);
1576
1396
  }
1577
1397
  // Get LIT client for execution
1578
1398
  const litClient = await this.clientManager.getClient({
1579
1399
  litNetwork: this.config.network,
1580
1400
  debug: this.config.debug,
1581
1401
  });
1582
- // Determine chain from chainId in authMessage
1402
+ // Determine chain and Bitcoin provider from chainId in authMessage
1583
1403
  let chain;
1404
+ let bitcoinProviderUrl;
1584
1405
  switch (request.authMessage.chainId) {
1585
- case 1:
1406
+ case 1: // Ethereum mainnet
1586
1407
  chain = "ethereum";
1408
+ bitcoinProviderUrl =
1409
+ process.env.BITCOIN_PROVIDER_URL || "https://mempool.space/api";
1587
1410
  break;
1588
- case 11155111:
1411
+ case 11155111: // Sepolia testnet
1589
1412
  chain = "sepolia";
1413
+ bitcoinProviderUrl =
1414
+ process.env.BITCOIN_PROVIDER_URL ||
1415
+ "https://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api";
1590
1416
  break;
1591
- case 31337: // Hardhat local testnet
1592
- chain = "sepolia"; // Use sepolia config for local testing
1417
+ case 1337: // Hardhat local testnet (actual deployment chainId)
1418
+ case 31337: // Hardhat local testnet (standard default)
1419
+ chain = "hardhat"; // Use hardhat identifier for local testing
1420
+ bitcoinProviderUrl =
1421
+ process.env.BITCOIN_PROVIDER_URL || "http://127.0.0.1:18443"; // Local Bitcoin regtest
1593
1422
  break;
1594
1423
  default:
1595
1424
  throw new Error(`Unsupported chainId: ${request.authMessage.chainId}`);
1596
1425
  }
1597
- // Bitcoin provider URL (use env or default to mempool.space for now)
1598
- const bitcoinProviderUrl = process.env.BITCOIN_PROVIDER_URL || "https://mempool.space/api";
1426
+ // Override with custom Bitcoin RPC URL if provided (for local development)
1427
+ if (request.customBitcoinRpcUrl) {
1428
+ bitcoinProviderUrl = request.customBitcoinRpcUrl;
1429
+ if (this.config.debug) {
1430
+ console.log(" Custom Bitcoin RPC URL:", request.customBitcoinRpcUrl);
1431
+ }
1432
+ }
1599
1433
  // Execute LIT Action
1600
- // authMessage already contains quantumTimestamp calculated by SDK
1434
+ // authMessage already contains timestamp calculated by SDK
1601
1435
  // lit-ops just forwards - no business logic
1436
+ const litActionParams = {
1437
+ chain,
1438
+ bitcoinProviderUrl,
1439
+ auth: {
1440
+ positionId: request.authMessage.positionId,
1441
+ timestamp: request.authMessage.timestamp,
1442
+ chainId: request.authMessage.chainId,
1443
+ amount: request.authMessage.amount,
1444
+ action: request.authMessage.action,
1445
+ signature: request.userSignature,
1446
+ mode: request.authMessage.mode,
1447
+ ...(request.authMessage.contracts && {
1448
+ contracts: request.authMessage.contracts,
1449
+ }),
1450
+ },
1451
+ amount: request.authMessage.amount,
1452
+ };
1453
+ // Add custom RPC URL for local development (ngrok tunnels)
1454
+ if (request.customRpcUrl) {
1455
+ litActionParams.customRpcUrl = request.customRpcUrl;
1456
+ if (this.config.debug) {
1457
+ console.log(" Custom RPC URL:", request.customRpcUrl);
1458
+ }
1459
+ }
1460
+ // Execute LIT Action (single attempt - retry logic is in SDK layer)
1602
1461
  const result = await this.actionExecutor.executeAction({
1603
1462
  cid: litActionInfo.cid,
1604
1463
  pkpPublicKey: litActionInfo.pkp.publicKey,
1605
- params: {
1606
- chain,
1607
- bitcoinProviderUrl,
1608
- auth: {
1609
- message: request.authMessage,
1610
- signature: request.userSignature,
1611
- },
1612
- amount: request.authMessage.requestedAmount,
1613
- },
1464
+ params: litActionParams,
1614
1465
  signer: this.config.signer,
1615
1466
  }, litClient);
1616
1467
  if (this.config.debug) {
@@ -1624,7 +1475,7 @@ class LitOps {
1624
1475
  }
1625
1476
  // Parse response if it's a string (LIT Actions return JSON as string)
1626
1477
  let responseData = result.response;
1627
- if (typeof result.response === 'string') {
1478
+ if (typeof result.response === "string") {
1628
1479
  try {
1629
1480
  responseData = JSON.parse(result.response);
1630
1481
  if (this.config.debug) {
@@ -1641,12 +1492,59 @@ class LitOps {
1641
1492
  console.log("🔍 responseData (FULL OBJECT):", JSON.stringify(responseData, null, 2));
1642
1493
  console.log("🔍 responseData keys:", Object.keys(responseData || {}));
1643
1494
  }
1495
+ // Extract actual signature from Lit Action execution result
1496
+ // The signature is in result.signatures.ucdMintAuth.signature (not in response JSON)
1497
+ let actualSignature;
1498
+ try {
1499
+ if (result.signatures && typeof result.signatures === 'object') {
1500
+ const signatures = result.signatures;
1501
+ if (signatures.ucdMintAuth && signatures.ucdMintAuth.signature) {
1502
+ const sig = signatures.ucdMintAuth.signature;
1503
+ if (typeof sig === 'string' && sig !== 'success') {
1504
+ actualSignature = sig;
1505
+ if (this.config.debug) {
1506
+ console.log('✅ Extracted signature from Lit Action execution result', {
1507
+ signatureLength: actualSignature.length,
1508
+ signaturePreview: actualSignature.substring(0, 20) + '...',
1509
+ });
1510
+ }
1511
+ }
1512
+ else if (this.config.debug) {
1513
+ console.warn('⚠️ Signature in Lit Action result is not a valid string', {
1514
+ signatureType: typeof sig,
1515
+ signatureValue: sig,
1516
+ });
1517
+ }
1518
+ }
1519
+ else if (this.config.debug) {
1520
+ console.warn('⚠️ No ucdMintAuth signature found in Lit Action execution result', {
1521
+ signaturesKeys: Object.keys(signatures),
1522
+ });
1523
+ }
1524
+ }
1525
+ else if (this.config.debug) {
1526
+ console.warn('⚠️ Lit Action execution result has no signatures object', {
1527
+ hasSignatures: !!result.signatures,
1528
+ signaturesType: typeof result.signatures,
1529
+ });
1530
+ }
1531
+ }
1532
+ catch (sigError) {
1533
+ // Don't fail authorization if signature extraction fails - signature is optional
1534
+ if (this.config.debug) {
1535
+ console.warn('⚠️ Error extracting signature from Lit Action result (non-fatal)', {
1536
+ error: sigError instanceof Error ? sigError.message : String(sigError),
1537
+ });
1538
+ }
1539
+ }
1540
+ // Ensure signature is a hex string (add 0x prefix if missing)
1541
+ if (actualSignature && !actualSignature.startsWith("0x")) {
1542
+ actualSignature = "0x" + actualSignature;
1543
+ }
1644
1544
  // Return standardized response
1645
1545
  const returnValue = {
1646
1546
  approved: responseData?.approved ?? false,
1647
- signature: result.signatures
1648
- ? JSON.stringify(result.signatures)
1649
- : undefined,
1547
+ signature: actualSignature, // Return extracted hex string directly (matching pkp-authorization.module.ts pattern)
1650
1548
  mintAmount: responseData?.mintAmount,
1651
1549
  mintFee: responseData?.mintFee,
1652
1550
  newDebt: responseData?.newDebt,
@@ -1829,6 +1727,126 @@ class LitOps {
1829
1727
  results,
1830
1728
  };
1831
1729
  }
1730
+ /**
1731
+ * NOLA (No LIT Action) - Vault Snapshot
1732
+ *
1733
+ * Cost-optimized data access for position health metrics.
1734
+ * Executes vault-snapshot logic in Node.js without paying for LIT Action execution.
1735
+ *
1736
+ * Modes:
1737
+ * - Standalone: Calls executeVaultSnapshot directly with provided RPC URLs
1738
+ * - Service: Sends request to lit-ops-server which provides credentials
1739
+ *
1740
+ * @param params - Position and network configuration
1741
+ * @returns Complete VaultSnapshot with position health data
1742
+ /
1743
+ async getVaultSnapshot(params: {
1744
+ positionId: string;
1745
+ network: "sepolia" | "ethereum" | "localhost";
1746
+ // Standalone mode requires these:
1747
+ contractAddress?: string;
1748
+ termManagerAddress?: string;
1749
+ loanOpsManagerAddress?: string;
1750
+ chainId?: number;
1751
+ rpcUrl?: string;
1752
+ bitcoinProviderUrl?: string;
1753
+ minConfirmations?: number;
1754
+ }) {
1755
+ if (this.config.mode === "standalone") {
1756
+ // Standalone mode: Direct execution with provided credentials
1757
+ if (
1758
+ !params.contractAddress ||
1759
+ !params.rpcUrl ||
1760
+ !params.bitcoinProviderUrl
1761
+ ) {
1762
+ throw new Error(
1763
+ "Standalone mode requires: contractAddress, rpcUrl, bitcoinProviderUrl"
1764
+ );
1765
+ }
1766
+ if (!params.termManagerAddress || !params.loanOpsManagerAddress) {
1767
+ throw new Error(
1768
+ "Standalone mode requires: termManagerAddress, loanOpsManagerAddress"
1769
+ );
1770
+ }
1771
+
1772
+ // Import NOLA executor
1773
+ const { executeVaultSnapshot } = await import("@gvnrdao/dh-lit-actions");
1774
+
1775
+ // Execute directly
1776
+ return await executeVaultSnapshot({
1777
+ positionId: params.positionId,
1778
+ contractAddress: params.contractAddress,
1779
+ termManagerAddress: params.termManagerAddress,
1780
+ loanOpsManagerAddress: params.loanOpsManagerAddress,
1781
+ chain: params.network,
1782
+ chainId: params.chainId,
1783
+ rpcUrl: params.rpcUrl,
1784
+ bitcoinProviderUrl: params.bitcoinProviderUrl,
1785
+ minConfirmations: params.minConfirmations || 6,
1786
+ });
1787
+ } else {
1788
+ // Service mode: Call lit-ops-server endpoint
1789
+ if (!this.config.serviceEndpoint) {
1790
+ throw new Error("serviceEndpoint is required for service mode");
1791
+ }
1792
+
1793
+ const response = await fetch(
1794
+ `${this.config.serviceEndpoint}/api/lit/nola/vault-snapshot`,
1795
+ {
1796
+ method: "POST",
1797
+ headers: {
1798
+ "Content-Type": "application/json",
1799
+ },
1800
+ body: JSON.stringify({
1801
+ positionId: params.positionId,
1802
+ network: params.network,
1803
+ }),
1804
+ }
1805
+ );
1806
+
1807
+ if (!response.ok) {
1808
+ const errorText = await response.text();
1809
+ throw new Error(
1810
+ `NOLA service request failed: ${response.status} ${errorText}`
1811
+ );
1812
+ }
1813
+
1814
+ const apiResponse: any = await response.json();
1815
+
1816
+ // Convert string BigInts back to BigInt type
1817
+ const convertStringsToBigInt = (obj: any): any => {
1818
+ if (obj === null || obj === undefined) return obj;
1819
+
1820
+ if (typeof obj === "string") {
1821
+ // Check if string is a valid BigInt representation
1822
+ if (/^\d+$/.test(obj)) {
1823
+ try {
1824
+ return BigInt(obj);
1825
+ } catch {
1826
+ return obj;
1827
+ }
1828
+ }
1829
+ return obj;
1830
+ }
1831
+
1832
+ if (Array.isArray(obj)) {
1833
+ return obj.map(convertStringsToBigInt);
1834
+ }
1835
+
1836
+ if (typeof obj === "object") {
1837
+ const converted: any = {};
1838
+ for (const key in obj) {
1839
+ converted[key] = convertStringsToBigInt(obj[key]);
1840
+ }
1841
+ return converted;
1842
+ }
1843
+
1844
+ return obj;
1845
+ };
1846
+
1847
+ return convertStringsToBigInt(apiResponse.data); // Unwrap and convert
1848
+ }
1849
+ } */
1832
1850
  async disconnect() {
1833
1851
  // Ensure capacity module timers are cleaned up first
1834
1852
  try {