@gvnrdao/dh-lit-ops 0.0.73 → 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
  */
@@ -478,8 +501,6 @@ class LitOps {
478
501
  pkpPublicKey,
479
502
  params,
480
503
  signer,
481
- // Include pkpTokenId for session signature scoping (required for burned PKPs)
482
- pkpTokenId: pkpTokenId || params?.pkpTokenId,
483
504
  };
484
505
  return this.actionExecutor.executeAction(request, litClient);
485
506
  }
@@ -632,7 +653,7 @@ class LitOps {
632
653
  * @param signer - The wallet to use for session signatures
633
654
  * @returns PKP validation result with signature verification
634
655
  */
635
- async validatePKPSecurity(targetPkpTokenId, expectedCid, signerPkp, signer) {
656
+ async validatePKPSecurity(targetPkpTokenId, expectedCid, signerPkp, signer, options) {
636
657
  console.log(`šŸ” LIT-OPS TRACE: validatePKPSecurity ENTRY`);
637
658
  console.log(` - targetPkpTokenId: "${targetPkpTokenId}"`);
638
659
  console.log(` - targetPkpTokenId type: ${typeof targetPkpTokenId}`);
@@ -651,18 +672,25 @@ class LitOps {
651
672
  // Get the PKP validator CID
652
673
  const pkpValidatorCid = (this.config.network === "datil"
653
674
  ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
654
- : 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);
655
682
  if (this.config.debug) {
656
- 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}`);
657
686
  }
658
687
  // Execute PKP Validator LIT Action
659
688
  // NOTE: Even though the validator PKP is burned/immutable (read-only),
660
689
  // we still pass its tokenId so the session signature is scoped to the
661
690
  // validator resource. The Lit nodes will simply treat the resource as
662
691
  // non-signing, but scoping keeps the session principle of least privilege.
663
- console.log(`\nšŸ” LIT-OPS TRACE: About to call executeActionFromCID with jsParams:`);
664
- console.log(` - targetPkpTokenId: "${targetPkpTokenId}"`);
665
- 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)
666
694
  let sessionScopedSignerTokenId = signerPkp.tokenId;
667
695
  try {
668
696
  const normalizedHex = BigInt(signerPkp.tokenId)
@@ -673,22 +701,28 @@ class LitOps {
673
701
  catch (tokenErr) {
674
702
  console.warn("āš ļø Failed to normalize signer PKP tokenId to hex, using raw value", tokenErr);
675
703
  }
676
- // Convert validator CID to hex format for signature message
677
- // The contract expects the validator's CID in the signature, not the PKP's CID
678
- const validatorCidHex = (0, dh_lit_actions_1.cidToHex)(pkpValidatorCid);
679
704
  if (this.config.debug) {
680
- console.log(` Validator CID (IPFS): ${pkpValidatorCid}`);
681
- console.log(` Validator CID (hex): ${validatorCidHex}`);
682
- console.log(` PKP's expected CID (hex): ${expectedCid}`);
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}`);
683
708
  }
684
- 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
+ {
685
718
  targetPkpTokenId,
686
719
  expectedCid, // PKP's LIT Action CID (for validation check)
687
- certificationCid: validatorCidHex, // Validator's CID (for signature message)
720
+ certificationCid: validatorCidHex, // Validator's CID (for signature message) - from registry
688
721
  message: `PKP validation: ${Date.now()}`,
689
722
  // Ensure the LIT Action knows which network (datil vs datil-test)
690
723
  network: this.config.network,
691
- }, 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)
692
726
  );
693
727
  if (this.config.debug) {
694
728
  console.log(` LIT Action Success: ${litResult.success}`);
@@ -747,7 +781,7 @@ class LitOps {
747
781
  console.log(" šŸ” Verifying signature authenticity...");
748
782
  }
749
783
  try {
750
- // 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)
751
785
  const cleanPublicKey = signerPkp.publicKey.startsWith("0x")
752
786
  ? signerPkp.publicKey
753
787
  : `0x${signerPkp.publicKey}`;
@@ -895,10 +929,24 @@ class LitOps {
895
929
  litActionCids: litActionCids,
896
930
  }),
897
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
+ }
898
945
  if (!response.ok) {
899
- 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}`);
900
949
  }
901
- const result = (await response.json());
902
950
  const duration = Date.now() - startTime;
903
951
  // The service may wrap the LitOps result as { success, data }
904
952
  // Unwrap if needed to maintain a stable SDK return shape
@@ -1090,8 +1138,16 @@ class LitOps {
1090
1138
  console.log(` - pkpTokenId startsWith '0x': ${pkpData.tokenId.startsWith("0x")}`);
1091
1139
  console.log(` - litActionCidHex (expectedCid): "${litActionCidHex}"`);
1092
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;
1093
1145
  validationResult = await this.validatePKPSecurity(pkpData.tokenId, litActionCidHex, // Pass hex format CID
1094
- validatorPkp, signer);
1146
+ validatorPkp, signer, {
1147
+ validatorVersion,
1148
+ ethRpcUrl,
1149
+ pkpValidationRegistry,
1150
+ });
1095
1151
  // If validation succeeds, break out of retry loop
1096
1152
  if (validationResult.success) {
1097
1153
  if (this.config.debug && attempt > 1) {
@@ -1247,7 +1303,9 @@ class LitOps {
1247
1303
  console.log("šŸŽÆ Creating new Diamond Hands Loan PKP");
1248
1304
  console.log(" Using authorization CID:", (networkOverride ?? this.config.network) === "datil"
1249
1305
  ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL.authorizationDummy.cid
1250
- : dh_lit_actions_1.DH_LIT_ACTIONS_DATIL_TEST.authorizationDummy.cid);
1306
+ : (() => {
1307
+ throw new Error("Unsupported LIT network");
1308
+ })());
1251
1309
  console.log(` Mode: ${this.config.mode}`);
1252
1310
  if (networkOverride) {
1253
1311
  console.log(" Network override:", networkOverride);
@@ -1260,12 +1318,16 @@ class LitOps {
1260
1318
  throw new Error("Signer is required for standalone mode");
1261
1319
  }
1262
1320
  const selectedNetwork = networkOverride ?? this.config.network;
1263
- const authDummyCid = (selectedNetwork === "datil"
1321
+ const registry = selectedNetwork === "datil"
1264
1322
  ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
1265
- : 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;
1266
1328
  console.log(`šŸ” LIT-OPS TRACE: getNewDiamondHandsLoanPkp - selectedNetwork = "${selectedNetwork}"`);
1267
- console.log(`šŸ” LIT-OPS TRACE: getNewDiamondHandsLoanPkp - authorizationDummy.cid = "${authDummyCid}"`);
1268
- return this.createAndValidatePkpToLitAction(authDummyCid, effectiveSigner);
1329
+ console.log(`šŸ” LIT-OPS TRACE: getNewDiamondHandsLoanPkp - btcTransactionSigner.cid = "${btcTxSignerCid}"`);
1330
+ return this.createAndValidatePkpToLitAction(btcTxSignerCid, effectiveSigner);
1269
1331
  }
1270
1332
  /**
1271
1333
  * Request mint authorization from UCD Mint Validator LIT Action
@@ -1305,6 +1367,125 @@ class LitOps {
1305
1367
  }
1306
1368
  return this.requestMintAuthorizationStandalone(request);
1307
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
+ }
1308
1489
  /**
1309
1490
  * Request mint authorization via lit-ops-server (SERVICE mode)
1310
1491
  */
@@ -1381,15 +1562,26 @@ class LitOps {
1381
1562
  // Get LIT Action info from registry
1382
1563
  const litActions = this.config.network === "datil"
1383
1564
  ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
1384
- : dh_lit_actions_1.DH_LIT_ACTIONS_DATIL_TEST;
1565
+ : (() => {
1566
+ throw new Error("Unsupported LIT network");
1567
+ })();
1385
1568
  const litActionInfo = litActions.ucdMintValidator;
1386
1569
  if (!litActionInfo || !litActionInfo.cid || !litActionInfo.pkp) {
1387
1570
  throw new Error("UCD mint validator LIT Action not found in registry");
1388
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;
1389
1578
  if (this.config.debug) {
1390
1579
  console.log("šŸ“” Calling UCD Mint Validator LIT Action:");
1391
1580
  console.log(" Network:", this.config.network);
1392
- 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);
1393
1585
  console.log(" Position:", request.authMessage.positionId);
1394
1586
  console.log(" Amount:", request.authMessage.amount);
1395
1587
  console.log(" Timestamp:", request.authMessage.timestamp);
@@ -1412,7 +1604,7 @@ class LitOps {
1412
1604
  chain = "sepolia";
1413
1605
  bitcoinProviderUrl =
1414
1606
  process.env.BITCOIN_PROVIDER_URL ||
1415
- "https://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api";
1607
+ "https://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api/esplora";
1416
1608
  break;
1417
1609
  case 1337: // Hardhat local testnet (actual deployment chainId)
1418
1610
  case 31337: // Hardhat local testnet (standard default)
@@ -1444,11 +1636,16 @@ class LitOps {
1444
1636
  action: request.authMessage.action,
1445
1637
  signature: request.userSignature,
1446
1638
  mode: request.authMessage.mode,
1447
- ...(request.authMessage.contracts && {
1448
- contracts: request.authMessage.contracts,
1449
- }),
1450
1639
  },
1451
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
+ }),
1452
1649
  };
1453
1650
  // Add custom RPC URL for local development (ngrok tunnels)
1454
1651
  if (request.customRpcUrl) {
@@ -1457,14 +1654,115 @@ class LitOps {
1457
1654
  console.log(" Custom RPC URL:", request.customRpcUrl);
1458
1655
  }
1459
1656
  }
1460
- // Execute LIT Action (single attempt - retry logic is in SDK layer)
1461
- const result = await this.actionExecutor.executeAction({
1462
- cid: litActionInfo.cid,
1463
- pkpPublicKey: litActionInfo.pkp.publicKey,
1464
- params: litActionParams,
1465
- signer: this.config.signer,
1466
- }, 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
+ }
1467
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);
1468
1766
  console.log("šŸ“„ LIT Action response:", {
1469
1767
  success: result.success,
1470
1768
  hasSignature: !!result.signatures,
@@ -1492,65 +1790,20 @@ class LitOps {
1492
1790
  console.log("šŸ” responseData (FULL OBJECT):", JSON.stringify(responseData, null, 2));
1493
1791
  console.log("šŸ” responseData keys:", Object.keys(responseData || {}));
1494
1792
  }
1495
- // Extract actual signature from Lit Action execution result
1496
- // The signature is in result.signatures.ucdMintAuth.signature (not in response JSON)
1497
- let actualSignature;
1498
- try {
1499
- if (result.signatures && typeof result.signatures === 'object') {
1500
- const signatures = result.signatures;
1501
- if (signatures.ucdMintAuth && signatures.ucdMintAuth.signature) {
1502
- const sig = signatures.ucdMintAuth.signature;
1503
- if (typeof sig === 'string' && sig !== 'success') {
1504
- actualSignature = sig;
1505
- if (this.config.debug) {
1506
- console.log('āœ… Extracted signature from Lit Action execution result', {
1507
- signatureLength: actualSignature.length,
1508
- signaturePreview: actualSignature.substring(0, 20) + '...',
1509
- });
1510
- }
1511
- }
1512
- else if (this.config.debug) {
1513
- console.warn('āš ļø Signature in Lit Action result is not a valid string', {
1514
- signatureType: typeof sig,
1515
- signatureValue: sig,
1516
- });
1517
- }
1518
- }
1519
- else if (this.config.debug) {
1520
- console.warn('āš ļø No ucdMintAuth signature found in Lit Action execution result', {
1521
- signaturesKeys: Object.keys(signatures),
1522
- });
1523
- }
1524
- }
1525
- else if (this.config.debug) {
1526
- console.warn('āš ļø Lit Action execution result has no signatures object', {
1527
- hasSignatures: !!result.signatures,
1528
- signaturesType: typeof result.signatures,
1529
- });
1530
- }
1531
- }
1532
- catch (sigError) {
1533
- // Don't fail authorization if signature extraction fails - signature is optional
1534
- if (this.config.debug) {
1535
- console.warn('āš ļø Error extracting signature from Lit Action result (non-fatal)', {
1536
- error: sigError instanceof Error ? sigError.message : String(sigError),
1537
- });
1538
- }
1539
- }
1540
- // Ensure signature is a hex string (add 0x prefix if missing)
1541
- if (actualSignature && !actualSignature.startsWith("0x")) {
1542
- actualSignature = "0x" + actualSignature;
1543
- }
1544
1793
  // Return standardized response
1545
1794
  const returnValue = {
1546
1795
  approved: responseData?.approved ?? false,
1547
- signature: actualSignature, // Return extracted hex string directly (matching pkp-authorization.module.ts pattern)
1796
+ signature: result.signatures
1797
+ ? JSON.stringify(result.signatures)
1798
+ : undefined,
1548
1799
  mintAmount: responseData?.mintAmount,
1549
1800
  mintFee: responseData?.mintFee,
1550
1801
  newDebt: responseData?.newDebt,
1551
1802
  newCollateral: responseData?.newCollateral,
1552
1803
  btcPrice: responseData?.btcPrice,
1553
1804
  authorizedSpendsHash: responseData?.authorizedSpendsHash,
1805
+ ucdDebtHash: responseData?.ucdDebtHash,
1806
+ contractBundleHash: responseData?.contractBundleHash,
1554
1807
  timestamp: responseData?.timestamp,
1555
1808
  reason: responseData?.reason,
1556
1809
  failedStep: responseData?.failedStep,
@@ -1563,6 +1816,1013 @@ class LitOps {
1563
1816
  }
1564
1817
  return returnValue;
1565
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
+ }
1566
2826
  /**
1567
2827
  * Get trustless BTC price from Price Oracle LIT Action
1568
2828
  *
@@ -1579,7 +2839,9 @@ class LitOps {
1579
2839
  // Get LIT Action info from registry
1580
2840
  const litActions = (this.config.network === "datil"
1581
2841
  ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
1582
- : dh_lit_actions_1.DH_LIT_ACTIONS_DATIL_TEST);
2842
+ : (() => {
2843
+ throw new Error("Unsupported LIT network");
2844
+ })());
1583
2845
  console.log("litActions in getActions", litActions);
1584
2846
  const litActionInfo = litActions.priceOracle;
1585
2847
  if (!litActionInfo || !litActionInfo.cid) {