@gvnrdao/dh-lit-ops 0.0.61 → 0.0.107

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.
@@ -415,6 +415,29 @@ class LitOps {
415
415
  };
416
416
  return this.actionExecutor.executeAction(request, litClient);
417
417
  }
418
+ /**
419
+ * TEST-ONLY: Execute arbitrary local LIT Action code for debugging PKP authorization.
420
+ *
421
+ * This helper is intended for Jest / integration tests to run a minimal action
422
+ * (e.g. sign-only) with either wildcard or CID-scoped session signatures.
423
+ */
424
+ async runLocalLitActionCodeForTest(options) {
425
+ const { code, pkpPublicKey, params, pkpTokenId, cid } = options;
426
+ const litClient = await this.clientManager.getClient({
427
+ litNetwork: this.config.network,
428
+ debug: this.config.debug,
429
+ });
430
+ const request = {
431
+ code,
432
+ cid, // Optional CID: when provided, session sigs are scoped to this action
433
+ pkpPublicKey,
434
+ params,
435
+ signer: this.config.signer,
436
+ pkpTokenId,
437
+ };
438
+ const result = await this.actionExecutor.executeAction(request, litClient);
439
+ return result.response;
440
+ }
418
441
  /**
419
442
  * Execute a LIT Action from IPFS
420
443
  */
@@ -630,7 +653,7 @@ class LitOps {
630
653
  * @param signer - The wallet to use for session signatures
631
654
  * @returns PKP validation result with signature verification
632
655
  */
633
- async validatePKPSecurity(targetPkpTokenId, expectedCid, signerPkp, signer) {
656
+ async validatePKPSecurity(targetPkpTokenId, expectedCid, signerPkp, signer, options) {
634
657
  console.log(`šŸ” LIT-OPS TRACE: validatePKPSecurity ENTRY`);
635
658
  console.log(` - targetPkpTokenId: "${targetPkpTokenId}"`);
636
659
  console.log(` - targetPkpTokenId type: ${typeof targetPkpTokenId}`);
@@ -649,18 +672,25 @@ class LitOps {
649
672
  // Get the PKP validator CID
650
673
  const pkpValidatorCid = (this.config.network === "datil"
651
674
  ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
652
- : dh_lit_actions_1.DH_LIT_ACTIONS_DATIL_TEST).pkpValidator.cid;
675
+ : (() => {
676
+ throw new Error("Unsupported LIT network");
677
+ })()).pkpValidator.cid;
678
+ // Convert validator CID to hex format for signature message
679
+ // The contract expects the validator's CID in the signature, not the PKP's CID
680
+ // Use registry CID (same as cr-lit branch) - this is the source of truth
681
+ const validatorCidHex = (0, dh_lit_actions_1.cidToHex)(pkpValidatorCid);
653
682
  if (this.config.debug) {
654
- console.log(` PKP Validator CID: ${pkpValidatorCid}`);
683
+ console.log(` PKP Validator CID (IPFS): ${pkpValidatorCid}`);
684
+ console.log(` Validator CID (hex): ${validatorCidHex}`);
685
+ console.log(` PKP's expected CID (hex): ${expectedCid}`);
655
686
  }
656
687
  // Execute PKP Validator LIT Action
657
688
  // NOTE: Even though the validator PKP is burned/immutable (read-only),
658
689
  // we still pass its tokenId so the session signature is scoped to the
659
690
  // validator resource. The Lit nodes will simply treat the resource as
660
691
  // non-signing, but scoping keeps the session principle of least privilege.
661
- console.log(`\nšŸ” LIT-OPS TRACE: About to call executeActionFromCID with jsParams:`);
662
- console.log(` - targetPkpTokenId: "${targetPkpTokenId}"`);
663
- console.log(` - expectedCid: "${expectedCid}"`);
692
+ // Normalize signer PKP token ID to hex format for session scoping
693
+ // NOTE: signerPkp IS the validator PKP (passed from call site)
664
694
  let sessionScopedSignerTokenId = signerPkp.tokenId;
665
695
  try {
666
696
  const normalizedHex = BigInt(signerPkp.tokenId)
@@ -671,22 +701,28 @@ class LitOps {
671
701
  catch (tokenErr) {
672
702
  console.warn("āš ļø Failed to normalize signer PKP tokenId to hex, using raw value", tokenErr);
673
703
  }
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
704
  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}`);
705
+ console.log(` Signer PKP (validator) Public Key: ${signerPkp.publicKey.substring(0, 20)}...`);
706
+ console.log(` Signer PKP (validator) Token ID: ${signerPkp.tokenId}`);
707
+ console.log(` Signer PKP (validator) ETH Address: ${signerPkp.ethAddress}`);
681
708
  }
682
- const litResult = await this.executeActionFromCID(pkpValidatorCid, signerPkp.publicKey, {
709
+ console.log(`\nšŸ” LIT-OPS TRACE: About to call executeActionFromCID with jsParams:`);
710
+ console.log(` - targetPkpTokenId: "${targetPkpTokenId}"`);
711
+ console.log(` - expectedCid: "${expectedCid}"`);
712
+ console.log(` - certificationCid: "${validatorCidHex}"`);
713
+ console.log(` - signerPkp.publicKey: "${signerPkp.publicKey.substring(0, 20)}..."`);
714
+ // Use signerPkp directly (which IS the validator PKP from call site, matching cr-lit)
715
+ // The LIT Action will use globalThis.publicKey which is set from pkpPublicKey parameter
716
+ const litResult = await this.executeActionFromCID(pkpValidatorCid, signerPkp.publicKey, // āœ… Use signerPkp.publicKey (validator PKP, matching cr-lit)
717
+ {
683
718
  targetPkpTokenId,
684
719
  expectedCid, // PKP's LIT Action CID (for validation check)
685
- certificationCid: validatorCidHex, // Validator's CID (for signature message)
720
+ certificationCid: validatorCidHex, // Validator's CID (for signature message) - from registry
686
721
  message: `PKP validation: ${Date.now()}`,
687
722
  // Ensure the LIT Action knows which network (datil vs datil-test)
688
723
  network: this.config.network,
689
- }, signer, sessionScopedSignerTokenId, signerPkp.ethAddress // Required for capacity delegation on Datil
724
+ }, signer, sessionScopedSignerTokenId, // āœ… Use normalized signerPkp.tokenId for session scoping (matching cr-lit)
725
+ signerPkp.ethAddress // āœ… Use signerPkp.ethAddress for capacity delegation (matching cr-lit)
690
726
  );
691
727
  if (this.config.debug) {
692
728
  console.log(` LIT Action Success: ${litResult.success}`);
@@ -745,7 +781,7 @@ class LitOps {
745
781
  console.log(" šŸ” Verifying signature authenticity...");
746
782
  }
747
783
  try {
748
- // Compute PKP address from public key (publicKey already includes 0x prefix)
784
+ // Compute validator PKP address from public key (CRITICAL: Use signerPkp.publicKey, matching cr-lit)
749
785
  const cleanPublicKey = signerPkp.publicKey.startsWith("0x")
750
786
  ? signerPkp.publicKey
751
787
  : `0x${signerPkp.publicKey}`;
@@ -893,10 +929,24 @@ class LitOps {
893
929
  litActionCids: litActionCids,
894
930
  }),
895
931
  }, timeoutMs);
932
+ // Read response body first (whether success or error)
933
+ const responseText = await response.text();
934
+ let result;
935
+ try {
936
+ result = JSON.parse(responseText);
937
+ }
938
+ catch {
939
+ // If response is not JSON, treat as plain text error
940
+ if (!response.ok) {
941
+ throw new Error(`Service request failed: ${response.status} ${response.statusText} - ${responseText}`);
942
+ }
943
+ throw new Error(`Invalid JSON response from service: ${responseText}`);
944
+ }
896
945
  if (!response.ok) {
897
- throw new Error(`Service request failed: ${response.status} ${response.statusText}`);
946
+ // Extract error message from JSON response
947
+ const errorDetails = result.error || result.message || response.statusText;
948
+ throw new Error(`Service request failed: ${response.status} ${errorDetails}`);
898
949
  }
899
- const result = (await response.json());
900
950
  const duration = Date.now() - startTime;
901
951
  // The service may wrap the LitOps result as { success, data }
902
952
  // Unwrap if needed to maintain a stable SDK return shape
@@ -1088,8 +1138,16 @@ class LitOps {
1088
1138
  console.log(` - pkpTokenId startsWith '0x': ${pkpData.tokenId.startsWith("0x")}`);
1089
1139
  console.log(` - litActionCidHex (expectedCid): "${litActionCidHex}"`);
1090
1140
  console.log(` - validatorPkp: "${validatorPkp}"`);
1141
+ // Get validator version and contract info from config
1142
+ const validatorVersion = this.config.validatorVersion;
1143
+ const ethRpcUrl = this.config.ethRpcUrl;
1144
+ const pkpValidationRegistry = this.config.pkpValidationRegistry;
1091
1145
  validationResult = await this.validatePKPSecurity(pkpData.tokenId, litActionCidHex, // Pass hex format CID
1092
- validatorPkp, signer);
1146
+ validatorPkp, signer, {
1147
+ validatorVersion,
1148
+ ethRpcUrl,
1149
+ pkpValidationRegistry,
1150
+ });
1093
1151
  // If validation succeeds, break out of retry loop
1094
1152
  if (validationResult.success) {
1095
1153
  if (this.config.debug && attempt > 1) {
@@ -1245,7 +1303,9 @@ class LitOps {
1245
1303
  console.log("šŸŽÆ Creating new Diamond Hands Loan PKP");
1246
1304
  console.log(" Using authorization CID:", (networkOverride ?? this.config.network) === "datil"
1247
1305
  ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL.authorizationDummy.cid
1248
- : dh_lit_actions_1.DH_LIT_ACTIONS_DATIL_TEST.authorizationDummy.cid);
1306
+ : (() => {
1307
+ throw new Error("Unsupported LIT network");
1308
+ })());
1249
1309
  console.log(` Mode: ${this.config.mode}`);
1250
1310
  if (networkOverride) {
1251
1311
  console.log(" Network override:", networkOverride);
@@ -1258,12 +1318,16 @@ class LitOps {
1258
1318
  throw new Error("Signer is required for standalone mode");
1259
1319
  }
1260
1320
  const selectedNetwork = networkOverride ?? this.config.network;
1261
- const authDummyCid = (selectedNetwork === "datil"
1321
+ const registry = selectedNetwork === "datil"
1262
1322
  ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
1263
- : dh_lit_actions_1.DH_LIT_ACTIONS_DATIL_TEST).authorizationDummy.cid;
1323
+ : (() => {
1324
+ throw new Error("Unsupported LIT network");
1325
+ })();
1326
+ // Vault PKP must authorize btcTransactionSigner for Bitcoin transaction signing
1327
+ const btcTxSignerCid = registry.btcTransactionSigner.cid;
1264
1328
  console.log(`šŸ” LIT-OPS TRACE: getNewDiamondHandsLoanPkp - selectedNetwork = "${selectedNetwork}"`);
1265
- console.log(`šŸ” LIT-OPS TRACE: getNewDiamondHandsLoanPkp - authorizationDummy.cid = "${authDummyCid}"`);
1266
- return this.createAndValidatePkpToLitAction(authDummyCid, effectiveSigner);
1329
+ console.log(`šŸ” LIT-OPS TRACE: getNewDiamondHandsLoanPkp - btcTransactionSigner.cid = "${btcTxSignerCid}"`);
1330
+ return this.createAndValidatePkpToLitAction(btcTxSignerCid, effectiveSigner);
1267
1331
  }
1268
1332
  /**
1269
1333
  * Request mint authorization from UCD Mint Validator LIT Action
@@ -1303,6 +1367,125 @@ class LitOps {
1303
1367
  }
1304
1368
  return this.requestMintAuthorizationStandalone(request);
1305
1369
  }
1370
+ /**
1371
+ * DEV ONLY: Run BTC Always Signer LIT Action
1372
+ * Intermediate testing tool for Bitcoin transaction structure validation
1373
+ * Signs Bitcoin transactions WITHOUT authorization checks
1374
+ *
1375
+ * @param request - Bitcoin transaction parameters
1376
+ * @returns Signature result with transaction structure
1377
+ */
1378
+ async runBtcAlwaysSigner(request) {
1379
+ if (this.config.debug) {
1380
+ console.log("šŸ”§ [runBtcAlwaysSigner] DEV MODE ONLY");
1381
+ console.log(" Mode:", this.config.mode);
1382
+ }
1383
+ // Service mode: Call server endpoint
1384
+ if (this.config.mode === "service") {
1385
+ return this.runBtcAlwaysSignerViaService(request);
1386
+ }
1387
+ // Standalone mode: Execute locally
1388
+ return this.runBtcAlwaysSignerStandalone(request);
1389
+ }
1390
+ /**
1391
+ * Run BTC Always Signer via service (SERVICE mode)
1392
+ */
1393
+ async runBtcAlwaysSignerViaService(request) {
1394
+ if (!this.config.serviceEndpoint) {
1395
+ throw new Error("Service endpoint not configured for service mode");
1396
+ }
1397
+ const url = `${this.config.serviceEndpoint}/api/lit/dev/btc-always-signer`;
1398
+ if (this.config.debug) {
1399
+ console.log("🌐 Running BTC always-signer via service:", url);
1400
+ console.log(" Destination:", request.destination);
1401
+ console.log(" Amount:", request.amount);
1402
+ }
1403
+ try {
1404
+ const response = await fetch(url, {
1405
+ method: "POST",
1406
+ headers: {
1407
+ "Content-Type": "application/json",
1408
+ },
1409
+ body: JSON.stringify(request),
1410
+ });
1411
+ if (!response.ok) {
1412
+ const errorText = await response.text();
1413
+ throw new Error(`Service error (${response.status}): ${errorText}`);
1414
+ }
1415
+ const result = (await response.json());
1416
+ if (this.config.debug) {
1417
+ console.log("āœ… BTC always-signer completed via service");
1418
+ }
1419
+ return result.data || result;
1420
+ }
1421
+ catch (error) {
1422
+ if (this.config.debug) {
1423
+ console.error("āŒ Service BTC always-signer failed:", error);
1424
+ }
1425
+ return {
1426
+ success: false,
1427
+ error: error instanceof Error ? error.message : String(error),
1428
+ };
1429
+ }
1430
+ }
1431
+ /**
1432
+ * Run BTC Always Signer locally (STANDALONE mode)
1433
+ */
1434
+ async runBtcAlwaysSignerStandalone(request) {
1435
+ // Get always-signer LIT Action CID
1436
+ const alwaysSignerCid = "QmRcQTDiZJq5arXngdxCX1LPnTW1fvx78tBD9vZmMXg8Ly";
1437
+ if (this.config.debug) {
1438
+ console.log("šŸ“” Calling BTC Always Signer LIT Action:");
1439
+ console.log(" CID:", alwaysSignerCid);
1440
+ console.log(" Destination:", request.destination);
1441
+ console.log(" Amount:", request.amount);
1442
+ }
1443
+ try {
1444
+ // Execute LIT Action
1445
+ // NOTE: The always-signer LIT Action expects params in (globalThis as any).params
1446
+ // so we nest the parameters under a 'params' key
1447
+ const result = await this.executeActionFromCID(alwaysSignerCid, request.pkpPublicKey, {
1448
+ params: {
1449
+ amount: request.amount,
1450
+ fee: request.fee,
1451
+ destination: request.destination,
1452
+ utxo: request.utxo,
1453
+ network: request.network,
1454
+ pkpPublicKey: request.pkpPublicKey,
1455
+ },
1456
+ }, this.config.signer, // Use signer from LitOps config
1457
+ undefined, // No PKP token ID needed
1458
+ undefined // No PKP ETH address needed
1459
+ );
1460
+ if (this.config.debug) {
1461
+ console.log("āœ… BTC Always Signer executed");
1462
+ console.log(" Success:", result.success);
1463
+ }
1464
+ // Parse response
1465
+ const response = typeof result.response === "string"
1466
+ ? JSON.parse(result.response)
1467
+ : result.response;
1468
+ // Extract actual signature from LIT SDK response
1469
+ // LIT SDK puts signatures in result.signatures.btcTxSig
1470
+ if (result.signatures?.btcTxSig) {
1471
+ response.signature = {
1472
+ r: result.signatures.btcTxSig.r,
1473
+ s: result.signatures.btcTxSig.s,
1474
+ recid: result.signatures.btcTxSig.recid,
1475
+ };
1476
+ }
1477
+ return response;
1478
+ }
1479
+ catch (error) {
1480
+ if (this.config.debug) {
1481
+ console.error("āŒ BTC Always Signer failed:", error);
1482
+ }
1483
+ return {
1484
+ success: false,
1485
+ error: error instanceof Error ? error.message : String(error),
1486
+ };
1487
+ }
1488
+ }
1306
1489
  /**
1307
1490
  * Request mint authorization via lit-ops-server (SERVICE mode)
1308
1491
  */
@@ -1379,15 +1562,26 @@ class LitOps {
1379
1562
  // Get LIT Action info from registry
1380
1563
  const litActions = this.config.network === "datil"
1381
1564
  ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
1382
- : dh_lit_actions_1.DH_LIT_ACTIONS_DATIL_TEST;
1565
+ : (() => {
1566
+ throw new Error("Unsupported LIT network");
1567
+ })();
1383
1568
  const litActionInfo = litActions.ucdMintValidator;
1384
1569
  if (!litActionInfo || !litActionInfo.cid || !litActionInfo.pkp) {
1385
1570
  throw new Error("UCD mint validator LIT Action not found in registry");
1386
1571
  }
1572
+ // CRITICAL: Use validator PKP details from registry (not loan PKP)
1573
+ // The validator PKP is the one that signs mint authorization messages
1574
+ const validatorPkpTokenId = litActionInfo.pkp.tokenId;
1575
+ const validatorCid = litActionInfo.cid;
1576
+ const validatorPkpPublicKey = litActionInfo.pkp.publicKey;
1577
+ const validatorPkpEthAddress = litActionInfo.pkp.ethAddress;
1387
1578
  if (this.config.debug) {
1388
1579
  console.log("šŸ“” Calling UCD Mint Validator LIT Action:");
1389
1580
  console.log(" Network:", this.config.network);
1390
- console.log(" CID:", litActionInfo.cid);
1581
+ console.log(" Validator CID:", validatorCid);
1582
+ console.log(" Validator PKP Token ID:", validatorPkpTokenId);
1583
+ console.log(" Validator PKP Public Key:", validatorPkpPublicKey);
1584
+ console.log(" Validator PKP ETH Address:", validatorPkpEthAddress);
1391
1585
  console.log(" Position:", request.authMessage.positionId);
1392
1586
  console.log(" Amount:", request.authMessage.amount);
1393
1587
  console.log(" Timestamp:", request.authMessage.timestamp);
@@ -1410,7 +1604,7 @@ class LitOps {
1410
1604
  chain = "sepolia";
1411
1605
  bitcoinProviderUrl =
1412
1606
  process.env.BITCOIN_PROVIDER_URL ||
1413
- "https://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api";
1607
+ "https://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora";
1414
1608
  break;
1415
1609
  case 1337: // Hardhat local testnet (actual deployment chainId)
1416
1610
  case 31337: // Hardhat local testnet (standard default)
@@ -1442,11 +1636,16 @@ class LitOps {
1442
1636
  action: request.authMessage.action,
1443
1637
  signature: request.userSignature,
1444
1638
  mode: request.authMessage.mode,
1445
- ...(request.authMessage.contracts && {
1446
- contracts: request.authMessage.contracts,
1447
- }),
1448
1639
  },
1449
1640
  amount: request.authMessage.amount,
1641
+ // Contract addresses must be at top level to become globalThis.contractAddresses in LIT Action
1642
+ ...(request.authMessage.contractAddresses && {
1643
+ contractAddresses: request.authMessage.contractAddresses,
1644
+ }),
1645
+ // Price providers with API keys for faster price fetching
1646
+ ...(request.priceProviders && {
1647
+ priceProviders: request.priceProviders,
1648
+ }),
1450
1649
  };
1451
1650
  // Add custom RPC URL for local development (ngrok tunnels)
1452
1651
  if (request.customRpcUrl) {
@@ -1455,14 +1654,115 @@ class LitOps {
1455
1654
  console.log(" Custom RPC URL:", request.customRpcUrl);
1456
1655
  }
1457
1656
  }
1458
- // Execute LIT Action (single attempt - retry logic is in SDK layer)
1459
- const result = await this.actionExecutor.executeAction({
1460
- cid: litActionInfo.cid,
1461
- pkpPublicKey: litActionInfo.pkp.publicKey,
1462
- params: litActionParams,
1463
- signer: this.config.signer,
1464
- }, litClient);
1657
+ // Verify PKP authorization before executing LIT Action
1658
+ // Use validator PKP details (not loan PKP)
1659
+ if (validatorCid && validatorPkpTokenId) {
1660
+ console.log("[LitOps] ======= PKP Authorization Check =======");
1661
+ console.log("[LitOps] Validator PKP Token ID:", validatorPkpTokenId);
1662
+ console.log("[LitOps] Validator Lit Action CID:", validatorCid);
1663
+ try {
1664
+ const pkpAuthorizer = new pkp_authorizer_module_1.PKPAuthorizer({
1665
+ debug: this.config.debug,
1666
+ signer: this.config.signer,
1667
+ network: this.config.network, // Pass LIT network (e.g., 'datil') for PKP authorization check
1668
+ });
1669
+ const isPermitted = await pkpAuthorizer.isActionPermitted(validatorPkpTokenId, validatorCid);
1670
+ console.log("[LitOps] PKP Authorization Status:", isPermitted ? "āœ… AUTHORIZED" : "āŒ NOT AUTHORIZED");
1671
+ if (!isPermitted) {
1672
+ console.warn("[LitOps] āš ļø PKP is not authorized for this Lit Action CID");
1673
+ console.warn("[LitOps] This may cause signing to fail or timeout");
1674
+ console.warn("[LitOps] Session signatures may use wildcard authorization instead");
1675
+ }
1676
+ // Get comprehensive permission check for debugging
1677
+ const permissionCheck = await pkpAuthorizer.checkPermissions(validatorPkpTokenId, validatorCid);
1678
+ console.log("[LitOps] Permitted Actions Count:", permissionCheck.permittedActions.length);
1679
+ console.log("[LitOps] CID in Permitted List:", permissionCheck.isPermitted);
1680
+ if (permissionCheck.permittedActions.length > 0) {
1681
+ console.log("[LitOps] Sample Permitted Actions:", permissionCheck.permittedActions.slice(0, 3));
1682
+ }
1683
+ }
1684
+ catch (authError) {
1685
+ console.warn("[LitOps] āš ļø Failed to check PKP authorization:", authError instanceof Error ? authError.message : String(authError));
1686
+ console.warn("[LitOps] Continuing with execution - may use wildcard authorization");
1687
+ }
1688
+ console.log("[LitOps] ================================");
1689
+ }
1690
+ else {
1691
+ console.log("[LitOps] āš ļø Skipping PKP authorization check (CID or tokenId missing)");
1692
+ if (!validatorCid) {
1693
+ console.log("[LitOps] Validator CID:", validatorCid);
1694
+ }
1695
+ if (!validatorPkpTokenId) {
1696
+ console.log("[LitOps] Validator PKP Token ID:", validatorPkpTokenId);
1697
+ }
1698
+ }
1699
+ // CRITICAL: Use validator PKP token ID (not loan PKP)
1700
+ // Session signatures must authorize the validator PKP to sign, not the loan PKP
1701
+ // PKP token ID should already be in decimal format from the registry
1702
+ // Don't normalize it - session-signature-manager will handle the conversion to hex
1703
+ // The registry stores token IDs as decimal strings, and session-signature-manager
1704
+ // expects decimal input which it then converts to hex for LitPKPResource
1705
+ const normalizedPkpTokenId = validatorPkpTokenId;
1706
+ // Check if we should use local code execution
1707
+ // CRITICAL: If we have a registry CID, always use IPFS mode
1708
+ // Never use local code when we have a deployed registry CID
1709
+ const useLocalCode = this.config.litActionExecution === 'local' && !validatorCid;
1710
+ if (this.config.debug) {
1711
+ console.log("šŸ” [DEBUG] litActionExecution config:", this.config.litActionExecution);
1712
+ console.log("šŸ” [DEBUG] Validator CID available:", validatorCid);
1713
+ console.log("šŸ” [DEBUG] useLocalCode:", useLocalCode, "(ignored if registry CID exists)");
1714
+ }
1715
+ let result;
1716
+ if (useLocalCode) {
1717
+ // Only use local code if NO registry CID is available (shouldn't happen in production)
1718
+ // Read code from local dist file
1719
+ const fs = require('fs');
1720
+ const path = require('path');
1721
+ // Try multiple possible paths
1722
+ const possiblePaths = [
1723
+ path.join(__dirname, '../../lit-actions/dist/ucd-mint-validator.action.js'),
1724
+ path.join(process.cwd(), 'lit-actions/dist/ucd-mint-validator.action.js'),
1725
+ path.join(process.cwd(), '../lit-actions/dist/ucd-mint-validator.action.js'),
1726
+ ];
1727
+ let codePath;
1728
+ for (const p of possiblePaths) {
1729
+ if (fs.existsSync(p)) {
1730
+ codePath = p;
1731
+ break;
1732
+ }
1733
+ }
1734
+ if (!codePath) {
1735
+ throw new Error(`Local LIT action code not found. Tried: ${possiblePaths.join(', ')}. Please build lit-actions first.`);
1736
+ }
1737
+ const code = fs.readFileSync(codePath, 'utf8');
1738
+ if (this.config.debug) {
1739
+ console.log("šŸ“¦ Using LOCAL LIT Action code (not IPFS)");
1740
+ console.log(" Code path:", codePath);
1741
+ console.log(" Code size:", code.length, "bytes");
1742
+ }
1743
+ result = await this.executeActionFromCode(code, validatorPkpPublicKey, // Use validator PKP public key
1744
+ litActionParams, this.config.signer, normalizedPkpTokenId, // Validator PKP token ID
1745
+ validatorCid // Validator CID for authorization validation
1746
+ );
1747
+ }
1748
+ else {
1749
+ // Always use IPFS mode when we have a registry CID
1750
+ // This ensures the Lit SDK uses the correct CID from the registry for authorization
1751
+ // Execute LIT Action (single attempt - retry logic is in SDK layer)
1752
+ // CRITICAL: Pass validator PKP token ID for session signature PKP scoping (fixes signEcdsa timeout)
1753
+ // Use normalized decimal format for consistent conversion in session-signature-manager
1754
+ result = await this.actionExecutor.executeAction({
1755
+ cid: validatorCid, // Use validator CID from registry
1756
+ pkpPublicKey: validatorPkpPublicKey, // Use validator PKP public key
1757
+ params: litActionParams,
1758
+ signer: this.config.signer,
1759
+ pkpTokenId: normalizedPkpTokenId, // Validator PKP token ID (decimal string)
1760
+ pkpEthAddress: validatorPkpEthAddress, // Validator PKP ETH address for capacity credit delegation
1761
+ }, litClient);
1762
+ }
1465
1763
  if (this.config.debug) {
1764
+ console.log("šŸ“‹ [DEBUG] litActionParams passed to LIT Action:", JSON.stringify(litActionParams, null, 2));
1765
+ console.log("šŸ“‹ [DEBUG] contractAddresses in litActionParams:", litActionParams.contractAddresses);
1466
1766
  console.log("šŸ“„ LIT Action response:", {
1467
1767
  success: result.success,
1468
1768
  hasSignature: !!result.signatures,
@@ -1502,6 +1802,8 @@ class LitOps {
1502
1802
  newCollateral: responseData?.newCollateral,
1503
1803
  btcPrice: responseData?.btcPrice,
1504
1804
  authorizedSpendsHash: responseData?.authorizedSpendsHash,
1805
+ ucdDebtHash: responseData?.ucdDebtHash,
1806
+ contractBundleHash: responseData?.contractBundleHash,
1505
1807
  timestamp: responseData?.timestamp,
1506
1808
  reason: responseData?.reason,
1507
1809
  failedStep: responseData?.failedStep,
@@ -1514,6 +1816,1013 @@ class LitOps {
1514
1816
  }
1515
1817
  return returnValue;
1516
1818
  }
1819
+ /**
1820
+ * Request extend position authorization from Extend Position Validator LIT Action
1821
+ *
1822
+ * In SERVICE mode: Delegates to lit-ops-server
1823
+ * In STANDALONE mode: Executes LIT action locally
1824
+ *
1825
+ * @param request - Authorization request with authMessage and userSignature
1826
+ * @returns Extend authorization response from LIT Action
1827
+ */
1828
+ async requestExtendAuthorization(request) {
1829
+ // In SERVICE mode, delegate to lit-ops-server
1830
+ if (this.config.mode === "service") {
1831
+ return this.requestExtendAuthorizationViaService(request);
1832
+ }
1833
+ return this.requestExtendAuthorizationStandalone(request);
1834
+ }
1835
+ /**
1836
+ * Request extend authorization via lit-ops-server (SERVICE mode)
1837
+ */
1838
+ async requestExtendAuthorizationViaService(request) {
1839
+ if (!this.config.serviceEndpoint) {
1840
+ throw new Error("Service endpoint not configured for service mode");
1841
+ }
1842
+ const url = `${this.config.serviceEndpoint}/api/lit/extend/authorize`;
1843
+ if (this.config.debug) {
1844
+ console.log("🌐 Requesting extend authorization via service:", url);
1845
+ console.log(" Position ID:", request.authMessage.positionId);
1846
+ console.log(" New Term:", request.authMessage.newTerm);
1847
+ }
1848
+ try {
1849
+ const response = await fetch(url, {
1850
+ method: "POST",
1851
+ headers: {
1852
+ "Content-Type": "application/json",
1853
+ },
1854
+ body: JSON.stringify({
1855
+ authMessage: request.authMessage,
1856
+ userSignature: request.userSignature,
1857
+ customRpcUrl: request.customRpcUrl,
1858
+ customBitcoinRpcUrl: request.customBitcoinRpcUrl,
1859
+ }),
1860
+ });
1861
+ if (!response.ok) {
1862
+ const errorText = await response.text();
1863
+ throw new Error(`Service error (${response.status}): ${errorText}`);
1864
+ }
1865
+ const envelope = await response.json();
1866
+ if (envelope.success && envelope.data) {
1867
+ return envelope.data;
1868
+ }
1869
+ else if (!envelope.success && envelope.error) {
1870
+ return {
1871
+ approved: false,
1872
+ error: envelope.error,
1873
+ reason: "Service returned error",
1874
+ };
1875
+ }
1876
+ return envelope;
1877
+ }
1878
+ catch (error) {
1879
+ if (this.config.debug) {
1880
+ console.error("āŒ Service extend authorization failed:", error);
1881
+ }
1882
+ return {
1883
+ approved: false,
1884
+ error: error instanceof Error ? error.message : String(error),
1885
+ reason: "Service request failed",
1886
+ };
1887
+ }
1888
+ }
1889
+ /**
1890
+ * Request extend authorization locally (STANDALONE mode)
1891
+ */
1892
+ async requestExtendAuthorizationStandalone(request) {
1893
+ // Get LIT Action info from registry
1894
+ const litActions = this.config.network === "datil"
1895
+ ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
1896
+ : (() => {
1897
+ throw new Error("Unsupported LIT network");
1898
+ })();
1899
+ // Note: extendPositionValidator needs to be added to registry
1900
+ // For now, we'll throw an error if not found
1901
+ const litActionInfo = litActions.extendPositionValidator;
1902
+ if (!litActionInfo || !litActionInfo.cid || !litActionInfo.pkp) {
1903
+ throw new Error("Extend position validator LIT Action not found in registry");
1904
+ }
1905
+ if (this.config.debug) {
1906
+ console.log("šŸ“” Calling Extend Position Validator LIT Action:");
1907
+ console.log(" Network:", this.config.network);
1908
+ console.log(" CID:", litActionInfo.cid);
1909
+ console.log(" Position:", request.authMessage.positionId);
1910
+ console.log(" New Term:", request.authMessage.newTerm);
1911
+ }
1912
+ // Verify PKP authorization before executing LIT Action
1913
+ if (litActionInfo.cid && litActionInfo.pkp.tokenId) {
1914
+ console.log("[LitOps] ======= PKP Authorization Check =======");
1915
+ console.log("[LitOps] PKP Token ID:", litActionInfo.pkp.tokenId);
1916
+ console.log("[LitOps] Lit Action CID:", litActionInfo.cid);
1917
+ try {
1918
+ const pkpAuthorizer = new pkp_authorizer_module_1.PKPAuthorizer({
1919
+ debug: this.config.debug,
1920
+ signer: this.config.signer,
1921
+ network: this.config.network, // Pass LIT network (e.g., 'datil') for PKP authorization check
1922
+ });
1923
+ const isPermitted = await pkpAuthorizer.isActionPermitted(litActionInfo.pkp.tokenId, litActionInfo.cid);
1924
+ console.log("[LitOps] PKP Authorization Status:", isPermitted ? "āœ… AUTHORIZED" : "āŒ NOT AUTHORIZED");
1925
+ if (!isPermitted) {
1926
+ console.warn("[LitOps] āš ļø PKP is not authorized for this Lit Action CID");
1927
+ console.warn("[LitOps] This may cause signing to fail or timeout");
1928
+ console.warn("[LitOps] Session signatures may use wildcard authorization instead");
1929
+ }
1930
+ // Get comprehensive permission check for debugging
1931
+ const permissionCheck = await pkpAuthorizer.checkPermissions(litActionInfo.pkp.tokenId, litActionInfo.cid);
1932
+ console.log("[LitOps] Permitted Actions Count:", permissionCheck.permittedActions.length);
1933
+ console.log("[LitOps] CID in Permitted List:", permissionCheck.isPermitted);
1934
+ if (permissionCheck.permittedActions.length > 0) {
1935
+ console.log("[LitOps] Sample Permitted Actions:", permissionCheck.permittedActions.slice(0, 3));
1936
+ }
1937
+ }
1938
+ catch (authError) {
1939
+ console.warn("[LitOps] āš ļø Failed to check PKP authorization:", authError instanceof Error ? authError.message : String(authError));
1940
+ console.warn("[LitOps] Continuing with execution - may use wildcard authorization");
1941
+ }
1942
+ console.log("[LitOps] ================================");
1943
+ }
1944
+ else {
1945
+ console.log("[LitOps] āš ļø Skipping PKP authorization check (CID or tokenId missing)");
1946
+ if (!litActionInfo.cid) {
1947
+ console.log("[LitOps] CID:", litActionInfo.cid);
1948
+ }
1949
+ if (!litActionInfo.pkp.tokenId) {
1950
+ console.log("[LitOps] PKP Token ID:", litActionInfo.pkp.tokenId);
1951
+ }
1952
+ }
1953
+ // Get LIT client for execution
1954
+ const litClient = await this.clientManager.getClient({
1955
+ litNetwork: this.config.network,
1956
+ debug: this.config.debug,
1957
+ });
1958
+ // Determine chain and Bitcoin provider from chainId
1959
+ let chain;
1960
+ let bitcoinProviderUrl;
1961
+ switch (request.authMessage.chainId) {
1962
+ case 1: // Ethereum mainnet
1963
+ chain = "ethereum";
1964
+ bitcoinProviderUrl =
1965
+ process.env.BITCOIN_PROVIDER_URL || "https://mempool.space/api";
1966
+ break;
1967
+ case 11155111: // Sepolia testnet
1968
+ chain = "sepolia";
1969
+ bitcoinProviderUrl =
1970
+ process.env.BITCOIN_PROVIDER_URL ||
1971
+ "https://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora";
1972
+ break;
1973
+ case 1337: // Hardhat local testnet (actual deployment chainId)
1974
+ case 31337: // Hardhat local testnet (standard default)
1975
+ chain = "hardhat";
1976
+ bitcoinProviderUrl =
1977
+ process.env.BITCOIN_PROVIDER_URL || "http://127.0.0.1:18443";
1978
+ break;
1979
+ default:
1980
+ throw new Error(`Unsupported chainId: ${request.authMessage.chainId}`);
1981
+ }
1982
+ if (request.customBitcoinRpcUrl) {
1983
+ bitcoinProviderUrl = request.customBitcoinRpcUrl;
1984
+ if (this.config.debug) {
1985
+ console.log(" Custom Bitcoin RPC URL:", request.customBitcoinRpcUrl);
1986
+ }
1987
+ }
1988
+ const litActionParams = {
1989
+ chain,
1990
+ bitcoinProviderUrl,
1991
+ auth: {
1992
+ positionId: request.authMessage.positionId,
1993
+ timestamp: request.authMessage.timestamp,
1994
+ chainId: request.authMessage.chainId,
1995
+ newTerm: request.authMessage.newTerm,
1996
+ action: request.authMessage.action,
1997
+ signature: request.userSignature,
1998
+ mode: request.authMessage.mode,
1999
+ },
2000
+ selectedTerm: request.authMessage.newTerm,
2001
+ // Contract addresses must be at top level to become globalThis.contractAddresses in LIT Action
2002
+ ...(request.authMessage.contractAddresses && {
2003
+ contractAddresses: request.authMessage.contractAddresses,
2004
+ }),
2005
+ // Price providers with API keys for faster price fetching
2006
+ ...(request.priceProviders && {
2007
+ priceProviders: request.priceProviders,
2008
+ }),
2009
+ };
2010
+ // Debug: Log priceProviders to verify they're being passed
2011
+ if (this.config.debug) {
2012
+ console.log(" Price Providers:", request.priceProviders);
2013
+ console.log(" Price Providers in litActionParams:", litActionParams.priceProviders);
2014
+ }
2015
+ if (request.customRpcUrl) {
2016
+ litActionParams.customRpcUrl = request.customRpcUrl;
2017
+ }
2018
+ // PKP token ID should already be in decimal format from the registry
2019
+ // Don't normalize it - session-signature-manager will handle the conversion to hex
2020
+ // The registry stores token IDs as decimal strings, and session-signature-manager
2021
+ // expects decimal input which it then converts to hex for LitPKPResource
2022
+ const normalizedPkpTokenId = litActionInfo.pkp.tokenId;
2023
+ // Check if we should use local code execution
2024
+ // CRITICAL: If we have a registry CID, always use IPFS mode
2025
+ // Never use local code when we have a deployed registry CID
2026
+ const useLocalCode = this.config.litActionExecution === 'local' && !litActionInfo.cid;
2027
+ if (this.config.debug) {
2028
+ console.log("šŸ” [DEBUG] litActionExecution config:", this.config.litActionExecution);
2029
+ console.log("šŸ” [DEBUG] Registry CID available:", litActionInfo.cid);
2030
+ console.log("šŸ” [DEBUG] useLocalCode:", useLocalCode, "(ignored if registry CID exists)");
2031
+ }
2032
+ let result;
2033
+ if (useLocalCode) {
2034
+ // Only use local code if NO registry CID is available (shouldn't happen in production)
2035
+ // Read code from local dist file
2036
+ const fs = require('fs');
2037
+ const path = require('path');
2038
+ // Try multiple possible paths
2039
+ const possiblePaths = [
2040
+ path.join(__dirname, '../../lit-actions/dist/extend-position-validator.action.js'),
2041
+ path.join(process.cwd(), 'lit-actions/dist/extend-position-validator.action.js'),
2042
+ path.join(process.cwd(), '../lit-actions/dist/extend-position-validator.action.js'),
2043
+ ];
2044
+ let codePath;
2045
+ for (const p of possiblePaths) {
2046
+ if (fs.existsSync(p)) {
2047
+ codePath = p;
2048
+ break;
2049
+ }
2050
+ }
2051
+ if (!codePath) {
2052
+ throw new Error(`Local LIT action code not found. Tried: ${possiblePaths.join(', ')}. Please build lit-actions first.`);
2053
+ }
2054
+ const code = fs.readFileSync(codePath, 'utf8');
2055
+ if (this.config.debug) {
2056
+ console.log("šŸ“¦ Using LOCAL LIT Action code (not IPFS)");
2057
+ console.log(" Code path:", codePath);
2058
+ console.log(" Code size:", code.length, "bytes");
2059
+ }
2060
+ result = await this.executeActionFromCode(code, litActionInfo.pkp.publicKey, litActionParams, this.config.signer, normalizedPkpTokenId, // Normalized to decimal string for consistent handling
2061
+ litActionInfo.cid // Still provide CID for authorization validation
2062
+ );
2063
+ }
2064
+ else {
2065
+ // Always use IPFS mode when we have a registry CID
2066
+ // This ensures the Lit SDK uses the correct CID from the registry for authorization
2067
+ // Execute LIT Action (single attempt - retry logic is in SDK layer)
2068
+ // CRITICAL: Pass pkpTokenId for session signature PKP scoping (fixes signEcdsa timeout)
2069
+ // Use normalized decimal format for consistent conversion in session-signature-manager
2070
+ result = await this.actionExecutor.executeAction({
2071
+ cid: litActionInfo.cid, // Use registry CID
2072
+ pkpPublicKey: litActionInfo.pkp.publicKey,
2073
+ params: litActionParams,
2074
+ signer: this.config.signer,
2075
+ pkpTokenId: normalizedPkpTokenId, // Normalized to decimal string for consistent handling
2076
+ pkpEthAddress: litActionInfo.pkp.ethAddress, // Required for capacity credit delegation
2077
+ }, litClient);
2078
+ }
2079
+ let responseData = result.response;
2080
+ if (typeof result.response === "string") {
2081
+ try {
2082
+ responseData = JSON.parse(result.response);
2083
+ }
2084
+ catch (e) {
2085
+ // Ignore parse errors
2086
+ }
2087
+ }
2088
+ // Handle signature: prefer LIT SDK format (result.signatures), fall back to responseData.signature
2089
+ let signature;
2090
+ if (result.signatures) {
2091
+ signature = JSON.stringify(result.signatures);
2092
+ }
2093
+ else if (responseData?.signature) {
2094
+ signature = responseData.signature;
2095
+ }
2096
+ return {
2097
+ approved: responseData?.approved ?? false,
2098
+ signature,
2099
+ btcPrice: responseData?.btcPrice,
2100
+ availableBTCBalance: responseData?.availableBTCBalance,
2101
+ extensionFee: responseData?.extensionFee,
2102
+ newTotalDebt: responseData?.newTotalDebt,
2103
+ newCollateralRatioBps: responseData?.newCollateralRatioBps,
2104
+ quantumTimestamp: responseData?.quantumTimestamp,
2105
+ timestamp: responseData?.timestamp,
2106
+ reason: responseData?.reason,
2107
+ failedStep: responseData?.failedStep,
2108
+ error: result.error,
2109
+ };
2110
+ }
2111
+ /**
2112
+ * Request BTC withdrawal authorization (dual-mode)
2113
+ *
2114
+ * @param request - Authorization request with authMessage and userSignature
2115
+ * @returns Withdrawal authorization response from LIT Action
2116
+ */
2117
+ async requestWithdrawalAuthorization(request) {
2118
+ // In SERVICE mode, delegate to lit-ops-server
2119
+ if (this.config.mode === "service") {
2120
+ if (this.config.debug) {
2121
+ console.log("🌐 [requestWithdrawalAuthorization] Using SERVICE mode - delegating to server");
2122
+ }
2123
+ return this.requestWithdrawalAuthorizationViaService(request);
2124
+ }
2125
+ // STANDALONE mode: Execute LIT action locally
2126
+ if (this.config.debug) {
2127
+ console.log("šŸ”§ [requestWithdrawalAuthorization] Using STANDALONE mode - executing locally");
2128
+ }
2129
+ return this.requestWithdrawalAuthorizationStandalone(request);
2130
+ }
2131
+ /**
2132
+ * Request withdrawal authorization via lit-ops-server (SERVICE mode)
2133
+ */
2134
+ async requestWithdrawalAuthorizationViaService(request) {
2135
+ if (!this.config.serviceEndpoint) {
2136
+ throw new Error("Service endpoint not configured for service mode");
2137
+ }
2138
+ const url = `${this.config.serviceEndpoint}/api/lit/withdrawal/authorize`;
2139
+ if (this.config.debug) {
2140
+ console.log("🌐 Requesting withdrawal authorization via service:", url);
2141
+ console.log(" Position ID:", request.authMessage.positionId);
2142
+ console.log(" Amount:", request.authMessage.amount);
2143
+ console.log(" Address:", request.authMessage.withdrawalAddress);
2144
+ }
2145
+ try {
2146
+ const response = await fetch(url, {
2147
+ method: "POST",
2148
+ headers: {
2149
+ "Content-Type": "application/json",
2150
+ },
2151
+ body: JSON.stringify({
2152
+ authMessage: request.authMessage,
2153
+ userSignature: request.userSignature,
2154
+ customRpcUrl: request.customRpcUrl,
2155
+ customBitcoinRpcUrl: request.customBitcoinRpcUrl,
2156
+ }),
2157
+ });
2158
+ if (!response.ok) {
2159
+ const errorText = await response.text();
2160
+ throw new Error(`Service error (${response.status}): ${errorText}`);
2161
+ }
2162
+ const envelope = await response.json();
2163
+ if (this.config.debug) {
2164
+ console.log("šŸ“„ Service response:", envelope);
2165
+ }
2166
+ // Return the result from service
2167
+ return {
2168
+ approved: envelope.approved ?? false,
2169
+ signature: envelope.signature,
2170
+ totalDeduction: envelope.totalDeduction,
2171
+ remainingCollateral: envelope.remainingCollateral,
2172
+ newCollateralRatioBps: envelope.newCollateralRatioBps,
2173
+ destinationAddress: envelope.destinationAddress,
2174
+ btcPrice: envelope.btcPrice,
2175
+ timestamp: envelope.timestamp,
2176
+ reason: envelope.reason,
2177
+ failedStep: envelope.failedStep,
2178
+ error: envelope.error,
2179
+ };
2180
+ }
2181
+ catch (error) {
2182
+ console.error("āŒ Service request failed:", error);
2183
+ return {
2184
+ approved: false,
2185
+ error: error instanceof Error ? error.message : String(error),
2186
+ reason: "Service request failed",
2187
+ };
2188
+ }
2189
+ }
2190
+ /**
2191
+ * Request withdrawal authorization locally (STANDALONE mode)
2192
+ */
2193
+ async requestWithdrawalAuthorizationStandalone(request) {
2194
+ // Get LIT Action info from registry
2195
+ const litActions = this.config.network === "datil"
2196
+ ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
2197
+ : (() => {
2198
+ throw new Error("Unsupported LIT network");
2199
+ })();
2200
+ const litActionInfo = litActions.btcWithdrawal;
2201
+ if (!litActionInfo || !litActionInfo.cid || !litActionInfo.pkp) {
2202
+ throw new Error("BTC withdrawal LIT Action not found in registry");
2203
+ }
2204
+ if (this.config.debug) {
2205
+ console.log("šŸ“” Calling BTC Withdrawal LIT Action:");
2206
+ console.log(" Network:", this.config.network);
2207
+ console.log(" CID:", litActionInfo.cid);
2208
+ console.log(" Position:", request.authMessage.positionId);
2209
+ console.log(" Amount:", request.authMessage.amount);
2210
+ console.log(" Address:", request.authMessage.withdrawalAddress);
2211
+ console.log(" Timestamp:", request.authMessage.timestamp);
2212
+ }
2213
+ // Get LIT client for execution
2214
+ const litClient = await this.clientManager.getClient({
2215
+ litNetwork: this.config.network,
2216
+ debug: this.config.debug,
2217
+ });
2218
+ // Determine chain and Bitcoin provider from chainId
2219
+ let chain;
2220
+ let bitcoinProviderUrl;
2221
+ switch (request.authMessage.chainId) {
2222
+ case 1: // Ethereum mainnet
2223
+ chain = "ethereum";
2224
+ bitcoinProviderUrl =
2225
+ process.env.BITCOIN_PROVIDER_URL || "https://mempool.space/api";
2226
+ break;
2227
+ case 11155111: // Sepolia testnet
2228
+ chain = "sepolia";
2229
+ bitcoinProviderUrl =
2230
+ process.env.BITCOIN_PROVIDER_URL ||
2231
+ "https://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora";
2232
+ break;
2233
+ case 1337: // Hardhat local testnet (actual deployment chainId)
2234
+ case 31337: // Hardhat local testnet (standard default)
2235
+ chain = "hardhat";
2236
+ bitcoinProviderUrl =
2237
+ process.env.BITCOIN_PROVIDER_URL || "http://127.0.0.1:18443";
2238
+ break;
2239
+ default:
2240
+ throw new Error(`Unsupported chainId: ${request.authMessage.chainId}`);
2241
+ }
2242
+ // Override with custom Bitcoin RPC URL if provided
2243
+ if (request.customBitcoinRpcUrl) {
2244
+ bitcoinProviderUrl = request.customBitcoinRpcUrl;
2245
+ if (this.config.debug) {
2246
+ console.log(" Custom Bitcoin RPC URL:", request.customBitcoinRpcUrl);
2247
+ }
2248
+ }
2249
+ // Execute LIT Action
2250
+ const litActionParams = {
2251
+ chain,
2252
+ bitcoinProviderUrl,
2253
+ auth: {
2254
+ positionId: request.authMessage.positionId,
2255
+ timestamp: request.authMessage.timestamp,
2256
+ chainId: request.authMessage.chainId,
2257
+ destinationAddress: request.authMessage.withdrawalAddress,
2258
+ amount: request.authMessage.amount,
2259
+ action: request.authMessage.action,
2260
+ signature: request.userSignature,
2261
+ mode: request.authMessage.mode,
2262
+ },
2263
+ // Always pass contractAddresses (undefined in prod mode)
2264
+ contractAddresses: request.authMessage.contractAddresses,
2265
+ };
2266
+ // Add custom RPC URL for local development
2267
+ if (request.customRpcUrl) {
2268
+ litActionParams.customRpcUrl = request.customRpcUrl;
2269
+ if (this.config.debug) {
2270
+ console.log(" Custom RPC URL:", request.customRpcUrl);
2271
+ }
2272
+ }
2273
+ // PKP token ID should already be in decimal format from the registry
2274
+ // Don't normalize it - session-signature-manager will handle the conversion to hex
2275
+ // The registry stores token IDs as decimal strings, and session-signature-manager
2276
+ // expects decimal input which it then converts to hex for LitPKPResource
2277
+ const normalizedPkpTokenId = litActionInfo.pkp.tokenId;
2278
+ // Execute LIT Action
2279
+ // CRITICAL: Pass pkpTokenId for session signature PKP scoping (fixes signEcdsa timeout)
2280
+ // Use normalized decimal format for consistent conversion in session-signature-manager
2281
+ const result = await this.actionExecutor.executeAction({
2282
+ cid: litActionInfo.cid,
2283
+ pkpPublicKey: litActionInfo.pkp.publicKey,
2284
+ params: litActionParams,
2285
+ signer: this.config.signer,
2286
+ pkpTokenId: normalizedPkpTokenId, // Normalized to decimal string for consistent handling
2287
+ pkpEthAddress: litActionInfo.pkp.ethAddress, // Required for capacity credit delegation
2288
+ }, litClient);
2289
+ if (this.config.debug) {
2290
+ console.log("šŸ“„ LIT Action response:", {
2291
+ success: result.success,
2292
+ hasSignature: !!result.signatures,
2293
+ error: result.error,
2294
+ });
2295
+ console.log("šŸ“„ LIT Action result.response (raw):", result.response);
2296
+ }
2297
+ // Parse response if it's a string
2298
+ let responseData = result.response;
2299
+ if (typeof result.response === "string") {
2300
+ try {
2301
+ responseData = JSON.parse(result.response);
2302
+ if (this.config.debug) {
2303
+ console.log("šŸ“„ Parsed response data:", responseData);
2304
+ }
2305
+ }
2306
+ catch (e) {
2307
+ if (this.config.debug) {
2308
+ console.error("āŒ Failed to parse response string:", e);
2309
+ }
2310
+ }
2311
+ }
2312
+ // Return standardized response including UTXO data from Bitcoin network query
2313
+ const returnValue = {
2314
+ approved: responseData?.approved ?? false,
2315
+ signature: result.signatures
2316
+ ? JSON.stringify(result.signatures)
2317
+ : undefined,
2318
+ actionHash: responseData?.actionHash,
2319
+ authorizedSpendsHash: responseData?.authorizedSpendsHash,
2320
+ ucdDebtHash: responseData?.ucdDebtHash,
2321
+ totalDeduction: responseData?.totalDeduction,
2322
+ remainingCollateral: responseData?.remainingCollateral,
2323
+ newCollateralRatioBps: responseData?.newCollateralRatioBps,
2324
+ destinationAddress: responseData?.destinationAddress,
2325
+ btcPrice: responseData?.btcPrice,
2326
+ timestamp: responseData?.timestamp,
2327
+ utxoTxid: responseData?.utxoTxid, // Real Bitcoin UTXO txid from network query
2328
+ utxoVout: responseData?.utxoVout, // Real Bitcoin UTXO vout from network query
2329
+ utxoSatoshis: responseData?.utxoSatoshis, // UTXO value for validation
2330
+ reason: responseData?.reason,
2331
+ failedStep: responseData?.failedStep,
2332
+ error: result.error,
2333
+ };
2334
+ if (this.config.debug) {
2335
+ console.log("šŸŽ STANDALONE RETURN VALUE:", JSON.stringify(returnValue, null, 2));
2336
+ }
2337
+ return returnValue;
2338
+ }
2339
+ /**
2340
+ * Request payment authorization from Process Payment Validator LIT Action
2341
+ *
2342
+ * In SERVICE mode: Delegates to lit-ops-server
2343
+ * In STANDALONE mode: Executes LIT action locally
2344
+ *
2345
+ * @param request - Payment authorization request
2346
+ * @returns Payment authorization response from LIT Action
2347
+ */
2348
+ async requestPaymentAuthorization(request) {
2349
+ // In SERVICE mode, delegate to lit-ops-server
2350
+ if (this.config.mode === "service") {
2351
+ return this.requestPaymentAuthorizationViaService(request);
2352
+ }
2353
+ return this.requestPaymentAuthorizationStandalone(request);
2354
+ }
2355
+ /**
2356
+ * Request payment authorization via lit-ops-server (SERVICE mode)
2357
+ */
2358
+ async requestPaymentAuthorizationViaService(request) {
2359
+ if (!this.config.serviceEndpoint) {
2360
+ throw new Error("Service endpoint not configured for service mode");
2361
+ }
2362
+ const url = `${this.config.serviceEndpoint}/api/lit/payment/authorize`;
2363
+ if (this.config.debug) {
2364
+ console.log("🌐 Requesting payment authorization via service:", url);
2365
+ console.log(" Position ID:", request.positionId);
2366
+ console.log(" Payment Amount:", request.paymentAmount);
2367
+ console.log(" Price Providers:", request.priceProviders);
2368
+ }
2369
+ const requestBody = {
2370
+ positionId: request.positionId,
2371
+ paymentAmount: request.paymentAmount,
2372
+ chainId: request.chainId,
2373
+ mode: request.mode,
2374
+ contractAddresses: request.contractAddresses,
2375
+ customRpcUrl: request.customRpcUrl,
2376
+ customBitcoinRpcUrl: request.customBitcoinRpcUrl,
2377
+ priceProviders: request.priceProviders,
2378
+ };
2379
+ try {
2380
+ const response = await fetch(url, {
2381
+ method: "POST",
2382
+ headers: {
2383
+ "Content-Type": "application/json",
2384
+ },
2385
+ body: JSON.stringify(requestBody),
2386
+ });
2387
+ if (!response.ok) {
2388
+ const errorText = await response.text();
2389
+ throw new Error(`Service error (${response.status}): ${errorText}`);
2390
+ }
2391
+ const envelope = await response.json();
2392
+ if (envelope.success && envelope.data) {
2393
+ return envelope.data;
2394
+ }
2395
+ else if (!envelope.success && envelope.error) {
2396
+ return {
2397
+ approved: false,
2398
+ error: envelope.error,
2399
+ reason: "Service returned error",
2400
+ };
2401
+ }
2402
+ return envelope;
2403
+ }
2404
+ catch (error) {
2405
+ if (this.config.debug) {
2406
+ console.error("āŒ Service payment authorization failed:", error);
2407
+ }
2408
+ return {
2409
+ approved: false,
2410
+ error: error instanceof Error ? error.message : String(error),
2411
+ reason: "Service request failed",
2412
+ };
2413
+ }
2414
+ }
2415
+ /**
2416
+ * Sign Bitcoin transaction for BTC withdrawal execution (Phase 2)
2417
+ *
2418
+ * Dual-mode method: Delegates to service endpoint or executes locally
2419
+ */
2420
+ async signBTCTransaction(request) {
2421
+ // In SERVICE mode, delegate to lit-ops-server
2422
+ if (this.config.mode === "service") {
2423
+ if (this.config.debug) {
2424
+ console.log("🌐 [signBTCTransaction] Using SERVICE mode - delegating to server");
2425
+ }
2426
+ return this.signBTCTransactionViaService(request);
2427
+ }
2428
+ // STANDALONE mode: Execute LIT action locally
2429
+ if (this.config.debug) {
2430
+ console.log("šŸ”§ [signBTCTransaction] Using STANDALONE mode - executing locally");
2431
+ }
2432
+ return this.signBTCTransactionStandalone(request);
2433
+ }
2434
+ /**
2435
+ * Sign Bitcoin transaction via lit-ops-server (SERVICE mode)
2436
+ */
2437
+ async signBTCTransactionViaService(request) {
2438
+ if (!this.config.serviceEndpoint) {
2439
+ throw new Error("Service endpoint not configured for service mode");
2440
+ }
2441
+ const url = `${this.config.serviceEndpoint}/api/lit/btc/sign-transaction`;
2442
+ if (this.config.debug) {
2443
+ console.log("🌐 Signing BTC transaction via service:", url);
2444
+ console.log(" Position ID:", request.auth.positionId);
2445
+ console.log(" UTXO:", `${request.auth.utxo.txid}:${request.auth.utxo.vout}`);
2446
+ console.log(" Network Fee:", request.auth.networkFee);
2447
+ }
2448
+ try {
2449
+ const response = await fetch(url, {
2450
+ method: "POST",
2451
+ headers: {
2452
+ "Content-Type": "application/json",
2453
+ },
2454
+ body: JSON.stringify(request),
2455
+ });
2456
+ if (!response.ok) {
2457
+ const errorText = await response.text();
2458
+ throw new Error(`Service error (${response.status}): ${errorText}`);
2459
+ }
2460
+ const envelope = await response.json();
2461
+ if (this.config.debug) {
2462
+ console.log("šŸ“„ Service response:", envelope);
2463
+ }
2464
+ return {
2465
+ success: envelope.success ?? false,
2466
+ unsignedTxHex: envelope.unsignedTxHex,
2467
+ sigHash: envelope.sigHash,
2468
+ signature: envelope.signature,
2469
+ pkpPublicKey: envelope.pkpPublicKey,
2470
+ pkpBtcAddress: envelope.pkpBtcAddress,
2471
+ destination: envelope.destination,
2472
+ userReceivesAmount: envelope.userReceivesAmount,
2473
+ networkFee: envelope.networkFee,
2474
+ changeAmount: envelope.changeAmount,
2475
+ utxo: envelope.utxo,
2476
+ authorization: envelope.authorization,
2477
+ timestamp: envelope.timestamp,
2478
+ action: envelope.action,
2479
+ error: envelope.error,
2480
+ failedStep: envelope.failedStep,
2481
+ };
2482
+ }
2483
+ catch (error) {
2484
+ console.error("āŒ Service request failed:", error);
2485
+ return {
2486
+ success: false,
2487
+ error: error instanceof Error ? error.message : String(error),
2488
+ };
2489
+ }
2490
+ }
2491
+ /**
2492
+ * Request payment authorization locally (STANDALONE mode)
2493
+ */
2494
+ async requestPaymentAuthorizationStandalone(request) {
2495
+ // Get LIT Action info from registry
2496
+ const litActions = this.config.network === "datil"
2497
+ ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
2498
+ : (() => {
2499
+ throw new Error("Unsupported LIT network");
2500
+ })();
2501
+ const litActionInfo = litActions.processPaymentValidator;
2502
+ if (!litActionInfo || !litActionInfo.cid || !litActionInfo.pkp) {
2503
+ throw new Error("Process payment validator LIT Action not found in registry");
2504
+ }
2505
+ if (this.config.debug) {
2506
+ console.log("šŸ“” Calling Process Payment Validator LIT Action:");
2507
+ console.log(" Network:", this.config.network);
2508
+ console.log(" CID:", litActionInfo.cid);
2509
+ console.log(" Position:", request.positionId);
2510
+ console.log(" Payment Amount:", request.paymentAmount);
2511
+ }
2512
+ // Verify PKP authorization before executing LIT Action
2513
+ if (litActionInfo.cid && litActionInfo.pkp.tokenId) {
2514
+ console.log("[LitOps] ======= PKP Authorization Check =======");
2515
+ console.log("[LitOps] PKP Token ID:", litActionInfo.pkp.tokenId);
2516
+ console.log("[LitOps] Lit Action CID:", litActionInfo.cid);
2517
+ try {
2518
+ const pkpAuthorizer = new pkp_authorizer_module_1.PKPAuthorizer({
2519
+ debug: this.config.debug,
2520
+ signer: this.config.signer,
2521
+ network: this.config.network, // Pass LIT network (e.g., 'datil') for PKP authorization check
2522
+ });
2523
+ const isPermitted = await pkpAuthorizer.isActionPermitted(litActionInfo.pkp.tokenId, litActionInfo.cid);
2524
+ console.log("[LitOps] PKP Authorization Status:", isPermitted ? "āœ… AUTHORIZED" : "āŒ NOT AUTHORIZED");
2525
+ if (!isPermitted) {
2526
+ console.warn("[LitOps] āš ļø PKP is not authorized for this Lit Action CID");
2527
+ console.warn("[LitOps] This may cause signing to fail or timeout");
2528
+ console.warn("[LitOps] Session signatures may use wildcard authorization instead");
2529
+ }
2530
+ // Get comprehensive permission check for debugging
2531
+ const permissionCheck = await pkpAuthorizer.checkPermissions(litActionInfo.pkp.tokenId, litActionInfo.cid);
2532
+ console.log("[LitOps] Permitted Actions Count:", permissionCheck.permittedActions.length);
2533
+ console.log("[LitOps] CID in Permitted List:", permissionCheck.isPermitted);
2534
+ if (permissionCheck.permittedActions.length > 0) {
2535
+ console.log("[LitOps] Sample Permitted Actions:", permissionCheck.permittedActions.slice(0, 3));
2536
+ }
2537
+ }
2538
+ catch (authError) {
2539
+ console.warn("[LitOps] āš ļø Failed to check PKP authorization:", authError instanceof Error ? authError.message : String(authError));
2540
+ console.warn("[LitOps] Continuing with execution - may use wildcard authorization");
2541
+ }
2542
+ console.log("[LitOps] ================================");
2543
+ }
2544
+ else {
2545
+ console.log("[LitOps] āš ļø Skipping PKP authorization check (CID or tokenId missing)");
2546
+ if (!litActionInfo.cid) {
2547
+ console.log("[LitOps] CID:", litActionInfo.cid);
2548
+ }
2549
+ if (!litActionInfo.pkp.tokenId) {
2550
+ console.log("[LitOps] PKP Token ID:", litActionInfo.pkp.tokenId);
2551
+ }
2552
+ }
2553
+ // PKP token ID should already be in decimal format from the registry
2554
+ // Don't normalize it - session-signature-manager will handle the conversion to hex
2555
+ // The registry stores token IDs as decimal strings, and session-signature-manager
2556
+ // expects decimal input which it then converts to hex for LitPKPResource
2557
+ const normalizedPkpTokenId = litActionInfo.pkp.tokenId;
2558
+ // Get LIT client for execution
2559
+ const litClient = await this.clientManager.getClient({
2560
+ litNetwork: this.config.network,
2561
+ debug: this.config.debug,
2562
+ });
2563
+ // Determine chain and Bitcoin provider from chainId
2564
+ let chain;
2565
+ let bitcoinProviderUrl;
2566
+ switch (request.chainId) {
2567
+ case 1:
2568
+ chain = "ethereum";
2569
+ bitcoinProviderUrl =
2570
+ process.env.BITCOIN_PROVIDER_URL || "https://mempool.space/api";
2571
+ break;
2572
+ case 11155111:
2573
+ chain = "sepolia";
2574
+ bitcoinProviderUrl =
2575
+ process.env.BITCOIN_PROVIDER_URL ||
2576
+ "https://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora";
2577
+ break;
2578
+ case 1337:
2579
+ case 31337:
2580
+ chain = "hardhat";
2581
+ bitcoinProviderUrl =
2582
+ process.env.BITCOIN_PROVIDER_URL || "http://127.0.0.1:18443";
2583
+ break;
2584
+ default:
2585
+ throw new Error(`Unsupported chainId: ${request.chainId}`);
2586
+ }
2587
+ if (request.customBitcoinRpcUrl) {
2588
+ bitcoinProviderUrl = request.customBitcoinRpcUrl;
2589
+ }
2590
+ // Support both legacy `contracts` (standalone) and newer `contractAddresses`
2591
+ const contractAddresses = request.contractAddresses ?? request.contracts;
2592
+ const litActionParams = {
2593
+ chain,
2594
+ bitcoinProviderUrl,
2595
+ positionId: request.positionId,
2596
+ paymentAmount: request.paymentAmount,
2597
+ ...(contractAddresses && {
2598
+ contractAddresses,
2599
+ }),
2600
+ ...(request.priceProviders && {
2601
+ priceProviders: request.priceProviders,
2602
+ }),
2603
+ };
2604
+ if (request.customRpcUrl) {
2605
+ litActionParams.customRpcUrl = request.customRpcUrl;
2606
+ }
2607
+ // Check if we should use local code execution
2608
+ // CRITICAL: If we have a registry CID, always use IPFS mode
2609
+ // Never use local code when we have a deployed registry CID
2610
+ const useLocalCode = this.config.litActionExecution === 'local' && !litActionInfo.cid;
2611
+ if (this.config.debug) {
2612
+ console.log("šŸ” [DEBUG] litActionExecution config:", this.config.litActionExecution);
2613
+ console.log("šŸ” [DEBUG] Registry CID available:", litActionInfo.cid);
2614
+ console.log("šŸ” [DEBUG] useLocalCode:", useLocalCode, "(ignored if registry CID exists)");
2615
+ }
2616
+ let result;
2617
+ if (useLocalCode) {
2618
+ // Only use local code if NO registry CID is available (shouldn't happen in production)
2619
+ // Read code from local dist file
2620
+ const fs = require('fs');
2621
+ const path = require('path');
2622
+ // Try multiple possible paths
2623
+ const possiblePaths = [
2624
+ path.join(__dirname, '../../lit-actions/dist/process-payment-validator.action.js'),
2625
+ path.join(process.cwd(), 'lit-actions/dist/process-payment-validator.action.js'),
2626
+ path.join(process.cwd(), '../lit-actions/dist/process-payment-validator.action.js'),
2627
+ ];
2628
+ let codePath;
2629
+ for (const p of possiblePaths) {
2630
+ if (fs.existsSync(p)) {
2631
+ codePath = p;
2632
+ break;
2633
+ }
2634
+ }
2635
+ if (!codePath) {
2636
+ throw new Error(`Local LIT action code not found. Tried: ${possiblePaths.join(', ')}. Please build lit-actions first.`);
2637
+ }
2638
+ const code = fs.readFileSync(codePath, 'utf8');
2639
+ if (this.config.debug) {
2640
+ console.log("šŸ“¦ Using LOCAL LIT Action code (not IPFS)");
2641
+ console.log(" Code path:", codePath);
2642
+ console.log(" Code size:", code.length, "bytes");
2643
+ }
2644
+ result = await this.executeActionFromCode(code, litActionInfo.pkp.publicKey, litActionParams, this.config.signer, normalizedPkpTokenId, // Normalized to decimal string for consistent handling
2645
+ litActionInfo.cid // Still provide CID for authorization validation
2646
+ );
2647
+ }
2648
+ else {
2649
+ // Always use IPFS mode when we have a registry CID
2650
+ // This ensures the Lit SDK uses the correct CID from the registry for authorization
2651
+ // Execute LIT Action (single attempt - retry logic is in SDK layer)
2652
+ // CRITICAL: Pass pkpTokenId for session signature PKP scoping (fixes signEcdsa timeout)
2653
+ // Use normalized decimal format for consistent conversion in session-signature-manager
2654
+ result = await this.actionExecutor.executeAction({
2655
+ cid: litActionInfo.cid, // Use registry CID
2656
+ pkpPublicKey: litActionInfo.pkp.publicKey,
2657
+ params: litActionParams,
2658
+ signer: this.config.signer,
2659
+ pkpTokenId: normalizedPkpTokenId, // Normalized to decimal string for consistent handling
2660
+ pkpEthAddress: litActionInfo.pkp.ethAddress, // Required for capacity credit delegation
2661
+ }, litClient);
2662
+ }
2663
+ let responseData = result.response;
2664
+ if (typeof result.response === "string") {
2665
+ try {
2666
+ responseData = JSON.parse(result.response);
2667
+ }
2668
+ catch (e) {
2669
+ // Ignore parse errors
2670
+ }
2671
+ }
2672
+ // Handle signature: prefer LIT SDK format (result.signatures), fall back to responseData.signature
2673
+ let signature;
2674
+ if (result.signatures) {
2675
+ signature = JSON.stringify(result.signatures);
2676
+ }
2677
+ else if (responseData?.signature) {
2678
+ signature = responseData.signature;
2679
+ }
2680
+ return {
2681
+ approved: responseData?.approved ?? false,
2682
+ signature,
2683
+ btcPrice: responseData?.btcPrice,
2684
+ paymentAmount: responseData?.paymentAmount,
2685
+ timestamp: responseData?.timestamp,
2686
+ reason: responseData?.reason,
2687
+ failedStep: responseData?.failedStep,
2688
+ error: result.error,
2689
+ };
2690
+ }
2691
+ /**
2692
+ * Sign Bitcoin transaction locally (STANDALONE mode)
2693
+ */
2694
+ async signBTCTransactionStandalone(request) {
2695
+ // Get LIT Action info from registry
2696
+ const litActions = this.config.network === "datil"
2697
+ ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
2698
+ : (() => {
2699
+ throw new Error("Unsupported LIT network");
2700
+ })();
2701
+ const litActionInfo = litActions.btcTransactionSigner;
2702
+ if (!litActionInfo || !litActionInfo.cid) {
2703
+ throw new Error("BTC transaction signer LIT Action not found in registry");
2704
+ }
2705
+ if (this.config.debug) {
2706
+ console.log("šŸ“” Calling BTC Transaction Signer LIT Action:");
2707
+ console.log(" Network:", this.config.network);
2708
+ console.log(" CID:", litActionInfo.cid);
2709
+ console.log(" Position:", request.auth.positionId);
2710
+ console.log(" UTXO:", `${request.auth.utxo.txid}:${request.auth.utxo.vout}`);
2711
+ console.log(" Network Fee:", request.auth.networkFee);
2712
+ }
2713
+ // Get LIT client for execution
2714
+ const litClient = await this.clientManager.getClient({
2715
+ litNetwork: this.config.network,
2716
+ debug: this.config.debug,
2717
+ });
2718
+ // Build LIT Action parameters
2719
+ // Determine Bitcoin provider URL based on network
2720
+ let bitcoinProviderUrl;
2721
+ const chainId = request.auth.chainId;
2722
+ switch (chainId) {
2723
+ case 1: // Ethereum mainnet
2724
+ bitcoinProviderUrl = process.env.BITCOIN_PROVIDER_URL || "https://mempool.space/api";
2725
+ break;
2726
+ case 11155111: // Sepolia testnet
2727
+ bitcoinProviderUrl = process.env.BITCOIN_PROVIDER_URL || "https://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api";
2728
+ break;
2729
+ case 1337: // Hardhat local testnet (actual deployment chainId)
2730
+ case 31337: // Hardhat local testnet (standard default)
2731
+ bitcoinProviderUrl = process.env.BITCOIN_PROVIDER_URL || "http://127.0.0.1:18443";
2732
+ break;
2733
+ default:
2734
+ throw new Error(`Unsupported chainId: ${chainId}`);
2735
+ }
2736
+ // Override with custom Bitcoin RPC URL if provided (for local development with ngrok)
2737
+ if (request.customBitcoinRpcUrl) {
2738
+ bitcoinProviderUrl = request.customBitcoinRpcUrl;
2739
+ if (this.config.debug) {
2740
+ console.log(" Custom Bitcoin RPC URL (override):", request.customBitcoinRpcUrl);
2741
+ }
2742
+ }
2743
+ const litActionParams = {
2744
+ chain: request.chain,
2745
+ bitcoinNetwork: request.bitcoinNetwork,
2746
+ bitcoinProviderUrl, // For Bitcoin UTXO queries
2747
+ auth: request.auth,
2748
+ publicKey: request.publicKey,
2749
+ contracts: request.contracts,
2750
+ };
2751
+ // Add custom EVM RPC URL for local development (Hardhat)
2752
+ if (request.customRpcUrl) {
2753
+ litActionParams.customRpcUrl = request.customRpcUrl;
2754
+ if (this.config.debug) {
2755
+ console.log(" Custom EVM RPC URL:", request.customRpcUrl);
2756
+ }
2757
+ }
2758
+ if (this.config.debug) {
2759
+ console.log(" Bitcoin Provider URL:", bitcoinProviderUrl);
2760
+ }
2761
+ // Execute LIT Action
2762
+ const result = await this.actionExecutor.executeAction({
2763
+ cid: litActionInfo.cid,
2764
+ pkpPublicKey: request.publicKey,
2765
+ params: litActionParams,
2766
+ signer: this.config.signer,
2767
+ }, litClient);
2768
+ if (this.config.debug) {
2769
+ console.log("šŸ“„ LIT Action response:", {
2770
+ success: result.success,
2771
+ hasSignature: !!result.signatures,
2772
+ error: result.error,
2773
+ });
2774
+ }
2775
+ // Parse response if it's a string
2776
+ let responseData = result.response;
2777
+ if (typeof result.response === "string") {
2778
+ try {
2779
+ responseData = JSON.parse(result.response);
2780
+ if (this.config.debug) {
2781
+ console.log("šŸ“„ Parsed response data:", responseData);
2782
+ }
2783
+ }
2784
+ catch (e) {
2785
+ if (this.config.debug) {
2786
+ console.error("āŒ Failed to parse response string:", e);
2787
+ }
2788
+ }
2789
+ }
2790
+ // Extract combined signature from LIT SDK result
2791
+ // LIT SDK returns signatures in result.signatures.btcTxSig after combination
2792
+ let combinedSignature;
2793
+ if (result.signatures && typeof result.signatures === 'object') {
2794
+ const btcTxSig = result.signatures.btcTxSig;
2795
+ if (btcTxSig && btcTxSig.signature) {
2796
+ // LIT SDK combined signature
2797
+ combinedSignature = btcTxSig.signature;
2798
+ if (this.config.debug && combinedSignature) {
2799
+ console.log("āœ… Extracted combined signature from LIT SDK:", {
2800
+ signatureLength: combinedSignature.length,
2801
+ signatureStart: combinedSignature.substring(0, 20) + "..."
2802
+ });
2803
+ }
2804
+ }
2805
+ }
2806
+ // Return standardized response
2807
+ return {
2808
+ success: responseData?.success ?? false,
2809
+ unsignedTxHex: responseData?.unsignedTxHex,
2810
+ sigHash: responseData?.sigHash,
2811
+ signature: combinedSignature || responseData?.signature,
2812
+ pkpPublicKey: responseData?.pkpPublicKey,
2813
+ pkpBtcAddress: responseData?.pkpBtcAddress,
2814
+ destination: responseData?.destination,
2815
+ userReceivesAmount: responseData?.userReceivesAmount,
2816
+ networkFee: responseData?.networkFee,
2817
+ changeAmount: responseData?.changeAmount,
2818
+ utxo: responseData?.utxo,
2819
+ authorization: responseData?.authorization,
2820
+ timestamp: responseData?.timestamp,
2821
+ action: responseData?.action,
2822
+ error: result.error || responseData?.error,
2823
+ failedStep: responseData?.failedStep,
2824
+ };
2825
+ }
1517
2826
  /**
1518
2827
  * Get trustless BTC price from Price Oracle LIT Action
1519
2828
  *
@@ -1530,7 +2839,9 @@ class LitOps {
1530
2839
  // Get LIT Action info from registry
1531
2840
  const litActions = (this.config.network === "datil"
1532
2841
  ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
1533
- : dh_lit_actions_1.DH_LIT_ACTIONS_DATIL_TEST);
2842
+ : (() => {
2843
+ throw new Error("Unsupported LIT network");
2844
+ })());
1534
2845
  console.log("litActions in getActions", litActions);
1535
2846
  const litActionInfo = litActions.priceOracle;
1536
2847
  if (!litActionInfo || !litActionInfo.cid) {