@gvnrdao/dh-lit-ops 0.0.54 → 0.0.61

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 (40) 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 +37 -77
  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 +261 -292
  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 +0 -1
  37. package/dist/modules/session-signature-manager.module.d.ts.map +1 -1
  38. package/dist/modules/session-signature-manager.module.js +26 -21
  39. package/dist/modules/session-signature-manager.module.js.map +1 -1
  40. 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
  }
@@ -605,12 +635,10 @@ class LitOps {
605
635
  console.log(` - targetPkpTokenId: "${targetPkpTokenId}"`);
606
636
  console.log(` - targetPkpTokenId type: ${typeof targetPkpTokenId}`);
607
637
  console.log(` - targetPkpTokenId length: ${targetPkpTokenId.length}`);
608
- console.log(` - targetPkpTokenId startsWith '0x': ${targetPkpTokenId.startsWith('0x')}`);
638
+ console.log(` - targetPkpTokenId startsWith '0x': ${targetPkpTokenId.startsWith("0x")}`);
609
639
  console.log(` - expectedCid (WILL BE PASSED TO LIT ACTION): "${expectedCid}"`);
610
640
  console.log(` - signerPkp.tokenId: "${signerPkp.tokenId}"`);
611
641
  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
642
  if (this.config.debug) {
615
643
  console.log("\n🔍 LitOps.validatePKPSecurity");
616
644
  console.log(` Target PKP: ${targetPkpTokenId}`);
@@ -619,49 +647,46 @@ class LitOps {
619
647
  }
620
648
  try {
621
649
  // 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);
650
+ const pkpValidatorCid = (this.config.network === "datil"
651
+ ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
652
+ : dh_lit_actions_1.DH_LIT_ACTIONS_DATIL_TEST).pkpValidator.cid;
637
653
  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
654
  console.log(` PKP Validator CID: ${pkpValidatorCid}`);
641
- console.log(` Validator Version: ${registry.pkpValidator.version}`);
642
- console.log(` Validator CID (hex, for signature): ${validatorCidHex}`);
643
655
  }
644
656
  // 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.
657
+ // NOTE: Even though the validator PKP is burned/immutable (read-only),
658
+ // we still pass its tokenId so the session signature is scoped to the
659
+ // validator resource. The Lit nodes will simply treat the resource as
660
+ // non-signing, but scoping keeps the session principle of least privilege.
649
661
  console.log(`\n🔍 LIT-OPS TRACE: About to call executeActionFromCID with jsParams:`);
650
662
  console.log(` - targetPkpTokenId: "${targetPkpTokenId}"`);
651
663
  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
664
+ let sessionScopedSignerTokenId = signerPkp.tokenId;
665
+ try {
666
+ const normalizedHex = BigInt(signerPkp.tokenId)
667
+ .toString(16)
668
+ .padStart(64, "0");
669
+ sessionScopedSignerTokenId = `0x${normalizedHex}`;
670
+ }
671
+ catch (tokenErr) {
672
+ console.warn("⚠️ Failed to normalize signer PKP tokenId to hex, using raw value", tokenErr);
673
+ }
674
+ // Convert validator CID to hex format for signature message
675
+ // The contract expects the validator's CID in the signature, not the PKP's CID
676
+ const validatorCidHex = (0, dh_lit_actions_1.cidToHex)(pkpValidatorCid);
677
+ if (this.config.debug) {
678
+ console.log(` Validator CID (IPFS): ${pkpValidatorCid}`);
679
+ console.log(` Validator CID (hex): ${validatorCidHex}`);
680
+ console.log(` PKP's expected CID (hex): ${expectedCid}`);
681
+ }
655
682
  const litResult = await this.executeActionFromCID(pkpValidatorCid, signerPkp.publicKey, {
656
683
  targetPkpTokenId,
657
- expectedCid, // Expected to be in hex format
658
- certificationCid: validatorCidHex, // Validator CID for signature (contract expects this)
684
+ expectedCid, // PKP's LIT Action CID (for validation check)
685
+ certificationCid: validatorCidHex, // Validator's CID (for signature message)
659
686
  message: `PKP validation: ${Date.now()}`,
660
687
  // Ensure the LIT Action knows which network (datil vs datil-test)
661
688
  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
689
+ }, signer, sessionScopedSignerTokenId, signerPkp.ethAddress // Required for capacity delegation on Datil
665
690
  );
666
691
  if (this.config.debug) {
667
692
  console.log(` LIT Action Success: ${litResult.success}`);
@@ -710,14 +735,8 @@ class LitOps {
710
735
  // For signing errors, we skip signature verification (architectural limitation)
711
736
  if (isSigningError) {
712
737
  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
738
  if (this.config.debug) {
719
739
  console.log(" ℹ️ Signature verification skipped - burned PKP limitation");
720
- console.log(` PKP Address: ${pkpAddress}`);
721
740
  }
722
741
  }
723
742
  else if (litResult.signatures &&
@@ -857,10 +876,9 @@ class LitOps {
857
876
  }
858
877
  const retries = this.config.pkpServiceRetryCount ?? 2;
859
878
  const retryDelay = this.config.pkpServiceRetryDelayMs ?? 2000;
860
- const timeoutMs = this.config.pkpServiceTimeoutMs ?? 480000; // 8 minutes to match server timeout
879
+ const timeoutMs = this.config.pkpServiceTimeoutMs ?? 120000;
861
880
  let attempt = 0;
862
881
  let lastError = null;
863
- let rateLimitDetected = false;
864
882
  while (attempt <= retries) {
865
883
  if (this.config.debug) {
866
884
  console.log(` 🌐 Service mode: calling ${this.config.serviceEndpoint}/api/lit/pkp/create-diamond-hands-loan (attempt ${attempt + 1}/${retries + 1})`);
@@ -876,37 +894,7 @@ class LitOps {
876
894
  }),
877
895
  }, timeoutMs);
878
896
  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);
897
+ throw new Error(`Service request failed: ${response.status} ${response.statusText}`);
910
898
  }
911
899
  const result = (await response.json());
912
900
  const duration = Date.now() - startTime;
@@ -937,21 +925,14 @@ class LitOps {
937
925
  message.includes("NodeJsTimeoutError") ||
938
926
  message.includes("Gateway") ||
939
927
  message.includes("502");
940
- const isRateLimit = rateLimitDetected ||
941
- message.toLowerCase().includes('rate_limit') ||
942
- message.toLowerCase().includes('rate limit');
943
928
  if (this.config.debug) {
944
- console.error(`❌ Service request failed on attempt ${attempt + 1}/${retries + 1}:\n Message: ${message}`);
929
+ console.error(`❌ Service request failed on attempt ${attempt + 1}/$${retries + 1}:\n Message: ${message}`);
945
930
  }
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;
931
+ if (attempt < retries && isTimeout) {
951
932
  if (this.config.debug) {
952
- console.log(` Retrying after ${backoffDelay}ms due to ${isRateLimit ? 'rate limit' : 'transient'} error...`);
933
+ console.log(` Retrying after ${retryDelay}ms due to transient error...`);
953
934
  }
954
- await new Promise((resolve) => setTimeout(resolve, backoffDelay));
935
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
955
936
  attempt++;
956
937
  continue;
957
938
  }
@@ -1045,138 +1026,13 @@ class LitOps {
1045
1026
  console.log(` ℹ️ Continuing to validation anyway - may fail if propagation incomplete`);
1046
1027
  }
1047
1028
  }
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())
1029
+ // Additional delay after burn verification for network consensus
1131
1030
  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
- }
1031
+ console.log("\n Additional delay for network consensus (10 seconds)...");
1174
1032
  }
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
1033
+ await new Promise((resolve) => setTimeout(resolve, 10000));
1034
+ if (this.config.debug) {
1035
+ console.log(` Network propagation and consensus complete`);
1180
1036
  }
1181
1037
  // Step 2: Validate PKP security using pkp-validator LIT Action
1182
1038
  const step2Start = Date.now();
@@ -1202,64 +1058,24 @@ class LitOps {
1202
1058
  // }
1203
1059
  // Use production deployments
1204
1060
  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
1061
  if (this.config.debug) {
1218
1062
  console.log(" Using production validator PKP", validatorPkp);
1219
1063
  }
1220
1064
  if (!validatorPkp) {
1221
1065
  throw new Error("PKP validator PKP not found in dh-lit-actions package");
1222
1066
  }
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);
1067
+ // Convert IPFS CID to hex format for validation (using first CID)
1068
+ const firstCid = cids[0];
1069
+ console.log(`🔍 LIT-OPS TRACE: firstCid (cids[0]) = "${firstCid}"`);
1070
+ console.log(`🔍 LIT-OPS TRACE: All cids array:`, cids);
1071
+ const litActionCidHex = (0, dh_lit_actions_1.cidToHex)(firstCid);
1237
1072
  console.log(`🔍 LIT-OPS TRACE: litActionCidHex after cidToHex() = "${litActionCidHex}"`);
1238
1073
  if (this.config.debug) {
1239
- console.log(` Converting CID: ${authorizationCid} → ${litActionCidHex}`);
1074
+ console.log(` Converting CID: ${firstCid} → ${litActionCidHex}`);
1240
1075
  }
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...");
1257
- }
1258
- await new Promise((resolve) => setTimeout(resolve, 5000));
1259
1076
  // Attempt validation with retry logic for network propagation issues
1260
1077
  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
1078
+ const maxValidationRetries = 2;
1263
1079
  let lastValidationError = null;
1264
1080
  for (let attempt = 1; attempt <= maxValidationRetries; attempt++) {
1265
1081
  try {
@@ -1283,11 +1099,11 @@ class LitOps {
1283
1099
  }
1284
1100
  // Validation returned but marked as unsuccessful
1285
1101
  lastValidationError = new Error(`PKP validation failed: ${validationResult.error || "Unknown validation error"}`);
1286
- // If not the last attempt, wait before retrying with exponential backoff
1102
+ // If not the last attempt, wait before retrying
1287
1103
  if (attempt < maxValidationRetries) {
1288
- const retryDelay = retryDelays[attempt - 1]; // Exponential backoff
1104
+ const retryDelay = 10000; // 10 seconds between validation retries
1289
1105
  if (this.config.debug) {
1290
- console.log(` ⏳ Exponential backoff: Waiting ${retryDelay}ms before retry ${attempt + 1}/${maxValidationRetries}...`);
1106
+ console.log(` ⏳ Waiting ${retryDelay}ms before retry...`);
1291
1107
  }
1292
1108
  await new Promise((resolve) => setTimeout(resolve, retryDelay));
1293
1109
  }
@@ -1307,11 +1123,11 @@ class LitOps {
1307
1123
  console.log(` ℹ️ This appears to be a network propagation issue`);
1308
1124
  }
1309
1125
  }
1310
- // If this is a propagation error and not the last attempt, retry with exponential backoff
1126
+ // If this is a propagation error and not the last attempt, retry
1311
1127
  if (isPropagationError && attempt < maxValidationRetries) {
1312
- const retryDelay = retryDelays[attempt - 1]; // Exponential backoff
1128
+ const retryDelay = 15000; // 15 seconds for propagation issues
1313
1129
  if (this.config.debug) {
1314
- console.log(` ⏳ Exponential backoff: Waiting ${retryDelay}ms for network propagation before retry ${attempt + 1}/${maxValidationRetries}...`);
1130
+ console.log(` ⏳ Waiting ${retryDelay}ms for network propagation before retry...`);
1315
1131
  }
1316
1132
  await new Promise((resolve) => setTimeout(resolve, retryDelay));
1317
1133
  continue;
@@ -1481,7 +1297,7 @@ class LitOps {
1481
1297
  console.log(" Has clientManager:", !!this.clientManager);
1482
1298
  console.log(" clientManager type:", typeof this.clientManager);
1483
1299
  if (this.clientManager) {
1484
- console.log(" clientManager keys:", Object.keys(this.clientManager).join(', '));
1300
+ console.log(" clientManager keys:", Object.keys(this.clientManager).join(", "));
1485
1301
  console.log(" Has getClient:", typeof this.clientManager.getClient);
1486
1302
  }
1487
1303
  }
@@ -1498,7 +1314,7 @@ class LitOps {
1498
1314
  if (this.config.debug) {
1499
1315
  console.log("🌐 Requesting mint authorization via service:", url);
1500
1316
  console.log(" Position ID:", request.authMessage.positionId);
1501
- console.log(" Amount:", request.authMessage.requestedAmount);
1317
+ console.log(" Amount:", request.authMessage.amount);
1502
1318
  }
1503
1319
  try {
1504
1320
  const response = await fetch(url, {
@@ -1509,6 +1325,8 @@ class LitOps {
1509
1325
  body: JSON.stringify({
1510
1326
  authMessage: request.authMessage,
1511
1327
  userSignature: request.userSignature,
1328
+ customRpcUrl: request.customRpcUrl,
1329
+ customBitcoinRpcUrl: request.customBitcoinRpcUrl,
1512
1330
  }),
1513
1331
  });
1514
1332
  if (!response.ok) {
@@ -1571,46 +1389,77 @@ class LitOps {
1571
1389
  console.log(" Network:", this.config.network);
1572
1390
  console.log(" CID:", litActionInfo.cid);
1573
1391
  console.log(" Position:", request.authMessage.positionId);
1574
- console.log(" Amount:", request.authMessage.requestedAmount);
1575
- console.log(" Quantum:", request.authMessage.quantumTimestamp);
1392
+ console.log(" Amount:", request.authMessage.amount);
1393
+ console.log(" Timestamp:", request.authMessage.timestamp);
1576
1394
  }
1577
1395
  // Get LIT client for execution
1578
1396
  const litClient = await this.clientManager.getClient({
1579
1397
  litNetwork: this.config.network,
1580
1398
  debug: this.config.debug,
1581
1399
  });
1582
- // Determine chain from chainId in authMessage
1400
+ // Determine chain and Bitcoin provider from chainId in authMessage
1583
1401
  let chain;
1402
+ let bitcoinProviderUrl;
1584
1403
  switch (request.authMessage.chainId) {
1585
- case 1:
1404
+ case 1: // Ethereum mainnet
1586
1405
  chain = "ethereum";
1406
+ bitcoinProviderUrl =
1407
+ process.env.BITCOIN_PROVIDER_URL || "https://mempool.space/api";
1587
1408
  break;
1588
- case 11155111:
1409
+ case 11155111: // Sepolia testnet
1589
1410
  chain = "sepolia";
1411
+ bitcoinProviderUrl =
1412
+ process.env.BITCOIN_PROVIDER_URL ||
1413
+ "https://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api";
1590
1414
  break;
1591
- case 31337: // Hardhat local testnet
1592
- chain = "sepolia"; // Use sepolia config for local testing
1415
+ case 1337: // Hardhat local testnet (actual deployment chainId)
1416
+ case 31337: // Hardhat local testnet (standard default)
1417
+ chain = "hardhat"; // Use hardhat identifier for local testing
1418
+ bitcoinProviderUrl =
1419
+ process.env.BITCOIN_PROVIDER_URL || "http://127.0.0.1:18443"; // Local Bitcoin regtest
1593
1420
  break;
1594
1421
  default:
1595
1422
  throw new Error(`Unsupported chainId: ${request.authMessage.chainId}`);
1596
1423
  }
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";
1424
+ // Override with custom Bitcoin RPC URL if provided (for local development)
1425
+ if (request.customBitcoinRpcUrl) {
1426
+ bitcoinProviderUrl = request.customBitcoinRpcUrl;
1427
+ if (this.config.debug) {
1428
+ console.log(" Custom Bitcoin RPC URL:", request.customBitcoinRpcUrl);
1429
+ }
1430
+ }
1599
1431
  // Execute LIT Action
1600
- // authMessage already contains quantumTimestamp calculated by SDK
1432
+ // authMessage already contains timestamp calculated by SDK
1601
1433
  // lit-ops just forwards - no business logic
1434
+ const litActionParams = {
1435
+ chain,
1436
+ bitcoinProviderUrl,
1437
+ auth: {
1438
+ positionId: request.authMessage.positionId,
1439
+ timestamp: request.authMessage.timestamp,
1440
+ chainId: request.authMessage.chainId,
1441
+ amount: request.authMessage.amount,
1442
+ action: request.authMessage.action,
1443
+ signature: request.userSignature,
1444
+ mode: request.authMessage.mode,
1445
+ ...(request.authMessage.contracts && {
1446
+ contracts: request.authMessage.contracts,
1447
+ }),
1448
+ },
1449
+ amount: request.authMessage.amount,
1450
+ };
1451
+ // Add custom RPC URL for local development (ngrok tunnels)
1452
+ if (request.customRpcUrl) {
1453
+ litActionParams.customRpcUrl = request.customRpcUrl;
1454
+ if (this.config.debug) {
1455
+ console.log(" Custom RPC URL:", request.customRpcUrl);
1456
+ }
1457
+ }
1458
+ // Execute LIT Action (single attempt - retry logic is in SDK layer)
1602
1459
  const result = await this.actionExecutor.executeAction({
1603
1460
  cid: litActionInfo.cid,
1604
1461
  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
- },
1462
+ params: litActionParams,
1614
1463
  signer: this.config.signer,
1615
1464
  }, litClient);
1616
1465
  if (this.config.debug) {
@@ -1624,7 +1473,7 @@ class LitOps {
1624
1473
  }
1625
1474
  // Parse response if it's a string (LIT Actions return JSON as string)
1626
1475
  let responseData = result.response;
1627
- if (typeof result.response === 'string') {
1476
+ if (typeof result.response === "string") {
1628
1477
  try {
1629
1478
  responseData = JSON.parse(result.response);
1630
1479
  if (this.config.debug) {
@@ -1829,6 +1678,126 @@ class LitOps {
1829
1678
  results,
1830
1679
  };
1831
1680
  }
1681
+ /**
1682
+ * NOLA (No LIT Action) - Vault Snapshot
1683
+ *
1684
+ * Cost-optimized data access for position health metrics.
1685
+ * Executes vault-snapshot logic in Node.js without paying for LIT Action execution.
1686
+ *
1687
+ * Modes:
1688
+ * - Standalone: Calls executeVaultSnapshot directly with provided RPC URLs
1689
+ * - Service: Sends request to lit-ops-server which provides credentials
1690
+ *
1691
+ * @param params - Position and network configuration
1692
+ * @returns Complete VaultSnapshot with position health data
1693
+ /
1694
+ async getVaultSnapshot(params: {
1695
+ positionId: string;
1696
+ network: "sepolia" | "ethereum" | "localhost";
1697
+ // Standalone mode requires these:
1698
+ contractAddress?: string;
1699
+ termManagerAddress?: string;
1700
+ loanOpsManagerAddress?: string;
1701
+ chainId?: number;
1702
+ rpcUrl?: string;
1703
+ bitcoinProviderUrl?: string;
1704
+ minConfirmations?: number;
1705
+ }) {
1706
+ if (this.config.mode === "standalone") {
1707
+ // Standalone mode: Direct execution with provided credentials
1708
+ if (
1709
+ !params.contractAddress ||
1710
+ !params.rpcUrl ||
1711
+ !params.bitcoinProviderUrl
1712
+ ) {
1713
+ throw new Error(
1714
+ "Standalone mode requires: contractAddress, rpcUrl, bitcoinProviderUrl"
1715
+ );
1716
+ }
1717
+ if (!params.termManagerAddress || !params.loanOpsManagerAddress) {
1718
+ throw new Error(
1719
+ "Standalone mode requires: termManagerAddress, loanOpsManagerAddress"
1720
+ );
1721
+ }
1722
+
1723
+ // Import NOLA executor
1724
+ const { executeVaultSnapshot } = await import("@gvnrdao/dh-lit-actions");
1725
+
1726
+ // Execute directly
1727
+ return await executeVaultSnapshot({
1728
+ positionId: params.positionId,
1729
+ contractAddress: params.contractAddress,
1730
+ termManagerAddress: params.termManagerAddress,
1731
+ loanOpsManagerAddress: params.loanOpsManagerAddress,
1732
+ chain: params.network,
1733
+ chainId: params.chainId,
1734
+ rpcUrl: params.rpcUrl,
1735
+ bitcoinProviderUrl: params.bitcoinProviderUrl,
1736
+ minConfirmations: params.minConfirmations || 6,
1737
+ });
1738
+ } else {
1739
+ // Service mode: Call lit-ops-server endpoint
1740
+ if (!this.config.serviceEndpoint) {
1741
+ throw new Error("serviceEndpoint is required for service mode");
1742
+ }
1743
+
1744
+ const response = await fetch(
1745
+ `${this.config.serviceEndpoint}/api/lit/nola/vault-snapshot`,
1746
+ {
1747
+ method: "POST",
1748
+ headers: {
1749
+ "Content-Type": "application/json",
1750
+ },
1751
+ body: JSON.stringify({
1752
+ positionId: params.positionId,
1753
+ network: params.network,
1754
+ }),
1755
+ }
1756
+ );
1757
+
1758
+ if (!response.ok) {
1759
+ const errorText = await response.text();
1760
+ throw new Error(
1761
+ `NOLA service request failed: ${response.status} ${errorText}`
1762
+ );
1763
+ }
1764
+
1765
+ const apiResponse: any = await response.json();
1766
+
1767
+ // Convert string BigInts back to BigInt type
1768
+ const convertStringsToBigInt = (obj: any): any => {
1769
+ if (obj === null || obj === undefined) return obj;
1770
+
1771
+ if (typeof obj === "string") {
1772
+ // Check if string is a valid BigInt representation
1773
+ if (/^\d+$/.test(obj)) {
1774
+ try {
1775
+ return BigInt(obj);
1776
+ } catch {
1777
+ return obj;
1778
+ }
1779
+ }
1780
+ return obj;
1781
+ }
1782
+
1783
+ if (Array.isArray(obj)) {
1784
+ return obj.map(convertStringsToBigInt);
1785
+ }
1786
+
1787
+ if (typeof obj === "object") {
1788
+ const converted: any = {};
1789
+ for (const key in obj) {
1790
+ converted[key] = convertStringsToBigInt(obj[key]);
1791
+ }
1792
+ return converted;
1793
+ }
1794
+
1795
+ return obj;
1796
+ };
1797
+
1798
+ return convertStringsToBigInt(apiResponse.data); // Unwrap and convert
1799
+ }
1800
+ } */
1832
1801
  async disconnect() {
1833
1802
  // Ensure capacity module timers are cleaned up first
1834
1803
  try {