@gvnrdao/dh-lit-ops 0.0.73 → 0.0.75

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.
@@ -478,8 +478,6 @@ class LitOps {
478
478
  pkpPublicKey,
479
479
  params,
480
480
  signer,
481
- // Include pkpTokenId for session signature scoping (required for burned PKPs)
482
- pkpTokenId: pkpTokenId || params?.pkpTokenId,
483
481
  };
484
482
  return this.actionExecutor.executeAction(request, litClient);
485
483
  }
@@ -651,44 +649,28 @@ class LitOps {
651
649
  // Get the PKP validator CID
652
650
  const pkpValidatorCid = (this.config.network === "datil"
653
651
  ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
654
- : dh_lit_actions_1.DH_LIT_ACTIONS_DATIL_TEST).pkpValidator.cid;
652
+ : (() => {
653
+ throw new Error("Unsupported LIT network");
654
+ })()).pkpValidator.cid;
655
655
  if (this.config.debug) {
656
656
  console.log(` PKP Validator CID: ${pkpValidatorCid}`);
657
657
  }
658
658
  // Execute PKP Validator LIT Action
659
- // NOTE: Even though the validator PKP is burned/immutable (read-only),
660
- // we still pass its tokenId so the session signature is scoped to the
661
- // validator resource. The Lit nodes will simply treat the resource as
662
- // non-signing, but scoping keeps the session principle of least privilege.
659
+ // NOTE: We do NOT pass the validator PKP token ID here because the validator
660
+ // performs READ-ONLY operations (no signing). Passing undefined for pkpTokenId
661
+ // will skip adding LitPKPResource to the session signature, which is correct
662
+ // for burned/immutable validator PKPs that can't sign via session signatures.
663
663
  console.log(`\nšŸ” LIT-OPS TRACE: About to call executeActionFromCID with jsParams:`);
664
664
  console.log(` - targetPkpTokenId: "${targetPkpTokenId}"`);
665
665
  console.log(` - expectedCid: "${expectedCid}"`);
666
- let sessionScopedSignerTokenId = signerPkp.tokenId;
667
- try {
668
- const normalizedHex = BigInt(signerPkp.tokenId)
669
- .toString(16)
670
- .padStart(64, "0");
671
- sessionScopedSignerTokenId = `0x${normalizedHex}`;
672
- }
673
- catch (tokenErr) {
674
- console.warn("āš ļø Failed to normalize signer PKP tokenId to hex, using raw value", tokenErr);
675
- }
676
- // Convert validator CID to hex format for signature message
677
- // The contract expects the validator's CID in the signature, not the PKP's CID
678
- const validatorCidHex = (0, dh_lit_actions_1.cidToHex)(pkpValidatorCid);
679
- if (this.config.debug) {
680
- console.log(` Validator CID (IPFS): ${pkpValidatorCid}`);
681
- console.log(` Validator CID (hex): ${validatorCidHex}`);
682
- console.log(` PKP's expected CID (hex): ${expectedCid}`);
683
- }
684
666
  const litResult = await this.executeActionFromCID(pkpValidatorCid, signerPkp.publicKey, {
685
667
  targetPkpTokenId,
686
- expectedCid, // PKP's LIT Action CID (for validation check)
687
- certificationCid: validatorCidHex, // Validator's CID (for signature message)
668
+ expectedCid, // Expected to be in hex format
688
669
  message: `PKP validation: ${Date.now()}`,
689
670
  // Ensure the LIT Action knows which network (datil vs datil-test)
690
671
  network: this.config.network,
691
- }, signer, sessionScopedSignerTokenId, signerPkp.ethAddress // Required for capacity delegation on Datil
672
+ }, signer
673
+ // Intentionally NOT passing signerPkp.tokenId - validator is read-only
692
674
  );
693
675
  if (this.config.debug) {
694
676
  console.log(` LIT Action Success: ${litResult.success}`);
@@ -1247,7 +1229,9 @@ class LitOps {
1247
1229
  console.log("šŸŽÆ Creating new Diamond Hands Loan PKP");
1248
1230
  console.log(" Using authorization CID:", (networkOverride ?? this.config.network) === "datil"
1249
1231
  ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL.authorizationDummy.cid
1250
- : dh_lit_actions_1.DH_LIT_ACTIONS_DATIL_TEST.authorizationDummy.cid);
1232
+ : (() => {
1233
+ throw new Error("Unsupported LIT network");
1234
+ })());
1251
1235
  console.log(` Mode: ${this.config.mode}`);
1252
1236
  if (networkOverride) {
1253
1237
  console.log(" Network override:", networkOverride);
@@ -1260,12 +1244,16 @@ class LitOps {
1260
1244
  throw new Error("Signer is required for standalone mode");
1261
1245
  }
1262
1246
  const selectedNetwork = networkOverride ?? this.config.network;
1263
- const authDummyCid = (selectedNetwork === "datil"
1247
+ const registry = selectedNetwork === "datil"
1264
1248
  ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
1265
- : dh_lit_actions_1.DH_LIT_ACTIONS_DATIL_TEST).authorizationDummy.cid;
1249
+ : (() => {
1250
+ throw new Error("Unsupported LIT network");
1251
+ })();
1252
+ // Vault PKP must authorize btcTransactionSigner for Bitcoin transaction signing
1253
+ const btcTxSignerCid = registry.btcTransactionSigner.cid;
1266
1254
  console.log(`šŸ” LIT-OPS TRACE: getNewDiamondHandsLoanPkp - selectedNetwork = "${selectedNetwork}"`);
1267
- console.log(`šŸ” LIT-OPS TRACE: getNewDiamondHandsLoanPkp - authorizationDummy.cid = "${authDummyCid}"`);
1268
- return this.createAndValidatePkpToLitAction(authDummyCid, effectiveSigner);
1255
+ console.log(`šŸ” LIT-OPS TRACE: getNewDiamondHandsLoanPkp - btcTransactionSigner.cid = "${btcTxSignerCid}"`);
1256
+ return this.createAndValidatePkpToLitAction(btcTxSignerCid, effectiveSigner);
1269
1257
  }
1270
1258
  /**
1271
1259
  * Request mint authorization from UCD Mint Validator LIT Action
@@ -1305,6 +1293,125 @@ class LitOps {
1305
1293
  }
1306
1294
  return this.requestMintAuthorizationStandalone(request);
1307
1295
  }
1296
+ /**
1297
+ * DEV ONLY: Run BTC Always Signer LIT Action
1298
+ * Intermediate testing tool for Bitcoin transaction structure validation
1299
+ * Signs Bitcoin transactions WITHOUT authorization checks
1300
+ *
1301
+ * @param request - Bitcoin transaction parameters
1302
+ * @returns Signature result with transaction structure
1303
+ */
1304
+ async runBtcAlwaysSigner(request) {
1305
+ if (this.config.debug) {
1306
+ console.log("šŸ”§ [runBtcAlwaysSigner] DEV MODE ONLY");
1307
+ console.log(" Mode:", this.config.mode);
1308
+ }
1309
+ // Service mode: Call server endpoint
1310
+ if (this.config.mode === "service") {
1311
+ return this.runBtcAlwaysSignerViaService(request);
1312
+ }
1313
+ // Standalone mode: Execute locally
1314
+ return this.runBtcAlwaysSignerStandalone(request);
1315
+ }
1316
+ /**
1317
+ * Run BTC Always Signer via service (SERVICE mode)
1318
+ */
1319
+ async runBtcAlwaysSignerViaService(request) {
1320
+ if (!this.config.serviceEndpoint) {
1321
+ throw new Error("Service endpoint not configured for service mode");
1322
+ }
1323
+ const url = `${this.config.serviceEndpoint}/api/lit/dev/btc-always-signer`;
1324
+ if (this.config.debug) {
1325
+ console.log("🌐 Running BTC always-signer via service:", url);
1326
+ console.log(" Destination:", request.destination);
1327
+ console.log(" Amount:", request.amount);
1328
+ }
1329
+ try {
1330
+ const response = await fetch(url, {
1331
+ method: "POST",
1332
+ headers: {
1333
+ "Content-Type": "application/json",
1334
+ },
1335
+ body: JSON.stringify(request),
1336
+ });
1337
+ if (!response.ok) {
1338
+ const errorText = await response.text();
1339
+ throw new Error(`Service error (${response.status}): ${errorText}`);
1340
+ }
1341
+ const result = (await response.json());
1342
+ if (this.config.debug) {
1343
+ console.log("āœ… BTC always-signer completed via service");
1344
+ }
1345
+ return result.data || result;
1346
+ }
1347
+ catch (error) {
1348
+ if (this.config.debug) {
1349
+ console.error("āŒ Service BTC always-signer failed:", error);
1350
+ }
1351
+ return {
1352
+ success: false,
1353
+ error: error instanceof Error ? error.message : String(error),
1354
+ };
1355
+ }
1356
+ }
1357
+ /**
1358
+ * Run BTC Always Signer locally (STANDALONE mode)
1359
+ */
1360
+ async runBtcAlwaysSignerStandalone(request) {
1361
+ // Get always-signer LIT Action CID
1362
+ const alwaysSignerCid = "QmRcQTDiZJq5arXngdxCX1LPnTW1fvx78tBD9vZmMXg8Ly";
1363
+ if (this.config.debug) {
1364
+ console.log("šŸ“” Calling BTC Always Signer LIT Action:");
1365
+ console.log(" CID:", alwaysSignerCid);
1366
+ console.log(" Destination:", request.destination);
1367
+ console.log(" Amount:", request.amount);
1368
+ }
1369
+ try {
1370
+ // Execute LIT Action
1371
+ // NOTE: The always-signer LIT Action expects params in (globalThis as any).params
1372
+ // so we nest the parameters under a 'params' key
1373
+ const result = await this.executeActionFromCID(alwaysSignerCid, request.pkpPublicKey, {
1374
+ params: {
1375
+ amount: request.amount,
1376
+ fee: request.fee,
1377
+ destination: request.destination,
1378
+ utxo: request.utxo,
1379
+ network: request.network,
1380
+ pkpPublicKey: request.pkpPublicKey,
1381
+ },
1382
+ }, this.config.signer, // Use signer from LitOps config
1383
+ undefined, // No PKP token ID needed
1384
+ undefined // No PKP ETH address needed
1385
+ );
1386
+ if (this.config.debug) {
1387
+ console.log("āœ… BTC Always Signer executed");
1388
+ console.log(" Success:", result.success);
1389
+ }
1390
+ // Parse response
1391
+ const response = typeof result.response === "string"
1392
+ ? JSON.parse(result.response)
1393
+ : result.response;
1394
+ // Extract actual signature from LIT SDK response
1395
+ // LIT SDK puts signatures in result.signatures.btcTxSig
1396
+ if (result.signatures?.btcTxSig) {
1397
+ response.signature = {
1398
+ r: result.signatures.btcTxSig.r,
1399
+ s: result.signatures.btcTxSig.s,
1400
+ recid: result.signatures.btcTxSig.recid,
1401
+ };
1402
+ }
1403
+ return response;
1404
+ }
1405
+ catch (error) {
1406
+ if (this.config.debug) {
1407
+ console.error("āŒ BTC Always Signer failed:", error);
1408
+ }
1409
+ return {
1410
+ success: false,
1411
+ error: error instanceof Error ? error.message : String(error),
1412
+ };
1413
+ }
1414
+ }
1308
1415
  /**
1309
1416
  * Request mint authorization via lit-ops-server (SERVICE mode)
1310
1417
  */
@@ -1381,7 +1488,9 @@ class LitOps {
1381
1488
  // Get LIT Action info from registry
1382
1489
  const litActions = this.config.network === "datil"
1383
1490
  ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
1384
- : dh_lit_actions_1.DH_LIT_ACTIONS_DATIL_TEST;
1491
+ : (() => {
1492
+ throw new Error("Unsupported LIT network");
1493
+ })();
1385
1494
  const litActionInfo = litActions.ucdMintValidator;
1386
1495
  if (!litActionInfo || !litActionInfo.cid || !litActionInfo.pkp) {
1387
1496
  throw new Error("UCD mint validator LIT Action not found in registry");
@@ -1412,7 +1521,7 @@ class LitOps {
1412
1521
  chain = "sepolia";
1413
1522
  bitcoinProviderUrl =
1414
1523
  process.env.BITCOIN_PROVIDER_URL ||
1415
- "https://diamond-hands-btc-faucet-6b39a1072059.herokuapp.com/api";
1524
+ "https://diamond-hands-btc-faucet-jw-b86e9451a147.herokuapp.com/api";
1416
1525
  break;
1417
1526
  case 1337: // Hardhat local testnet (actual deployment chainId)
1418
1527
  case 31337: // Hardhat local testnet (standard default)
@@ -1444,11 +1553,12 @@ class LitOps {
1444
1553
  action: request.authMessage.action,
1445
1554
  signature: request.userSignature,
1446
1555
  mode: request.authMessage.mode,
1447
- ...(request.authMessage.contracts && {
1448
- contracts: request.authMessage.contracts,
1449
- }),
1450
1556
  },
1451
1557
  amount: request.authMessage.amount,
1558
+ // Contracts must be at top level to become globalThis.contracts in LIT Action
1559
+ ...(request.authMessage.contracts && {
1560
+ contracts: request.authMessage.contracts,
1561
+ }),
1452
1562
  };
1453
1563
  // Add custom RPC URL for local development (ngrok tunnels)
1454
1564
  if (request.customRpcUrl) {
@@ -1463,6 +1573,7 @@ class LitOps {
1463
1573
  pkpPublicKey: litActionInfo.pkp.publicKey,
1464
1574
  params: litActionParams,
1465
1575
  signer: this.config.signer,
1576
+ priceProviders: request.priceProviders, // Pass price provider API keys (standalone mode)
1466
1577
  }, litClient);
1467
1578
  if (this.config.debug) {
1468
1579
  console.log("šŸ“„ LIT Action response:", {
@@ -1492,59 +1603,12 @@ class LitOps {
1492
1603
  console.log("šŸ” responseData (FULL OBJECT):", JSON.stringify(responseData, null, 2));
1493
1604
  console.log("šŸ” responseData keys:", Object.keys(responseData || {}));
1494
1605
  }
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
1606
  // Return standardized response
1545
1607
  const returnValue = {
1546
1608
  approved: responseData?.approved ?? false,
1547
- signature: actualSignature, // Return extracted hex string directly (matching pkp-authorization.module.ts pattern)
1609
+ signature: result.signatures
1610
+ ? JSON.stringify(result.signatures)
1611
+ : undefined,
1548
1612
  mintAmount: responseData?.mintAmount,
1549
1613
  mintFee: responseData?.mintFee,
1550
1614
  newDebt: responseData?.newDebt,
@@ -1563,6 +1627,477 @@ class LitOps {
1563
1627
  }
1564
1628
  return returnValue;
1565
1629
  }
1630
+ /**
1631
+ * Request BTC withdrawal authorization (dual-mode)
1632
+ *
1633
+ * @param request - Authorization request with authMessage and userSignature
1634
+ * @returns Withdrawal authorization response from LIT Action
1635
+ */
1636
+ async requestWithdrawalAuthorization(request) {
1637
+ // In SERVICE mode, delegate to lit-ops-server
1638
+ if (this.config.mode === "service") {
1639
+ if (this.config.debug) {
1640
+ console.log("🌐 [requestWithdrawalAuthorization] Using SERVICE mode - delegating to server");
1641
+ }
1642
+ return this.requestWithdrawalAuthorizationViaService(request);
1643
+ }
1644
+ // STANDALONE mode: Execute LIT action locally
1645
+ if (this.config.debug) {
1646
+ console.log("šŸ”§ [requestWithdrawalAuthorization] Using STANDALONE mode - executing locally");
1647
+ }
1648
+ return this.requestWithdrawalAuthorizationStandalone(request);
1649
+ }
1650
+ /**
1651
+ * Request withdrawal authorization via lit-ops-server (SERVICE mode)
1652
+ */
1653
+ async requestWithdrawalAuthorizationViaService(request) {
1654
+ if (!this.config.serviceEndpoint) {
1655
+ throw new Error("Service endpoint not configured for service mode");
1656
+ }
1657
+ const url = `${this.config.serviceEndpoint}/api/lit/withdraw-btc/authorize`;
1658
+ if (this.config.debug) {
1659
+ console.log("🌐 Requesting withdrawal authorization via service:", url);
1660
+ console.log(" Position ID:", request.authMessage.positionId);
1661
+ console.log(" Amount:", request.authMessage.amount);
1662
+ console.log(" Address:", request.authMessage.withdrawalAddress);
1663
+ }
1664
+ try {
1665
+ // Transform authMessage: SDK uses withdrawalAddress, server expects destinationAddress
1666
+ const transformedAuthMessage = {
1667
+ ...request.authMessage,
1668
+ destinationAddress: request.authMessage.withdrawalAddress,
1669
+ };
1670
+ // Remove withdrawalAddress from the transformed message
1671
+ delete transformedAuthMessage.withdrawalAddress;
1672
+ const response = await fetch(url, {
1673
+ method: "POST",
1674
+ headers: {
1675
+ "Content-Type": "application/json",
1676
+ },
1677
+ body: JSON.stringify({
1678
+ authMessage: transformedAuthMessage,
1679
+ userSignature: request.userSignature,
1680
+ customRpcUrl: request.customRpcUrl,
1681
+ customBitcoinRpcUrl: request.customBitcoinRpcUrl,
1682
+ }),
1683
+ });
1684
+ if (!response.ok) {
1685
+ const errorText = await response.text();
1686
+ throw new Error(`Service error (${response.status}): ${errorText}`);
1687
+ }
1688
+ const envelope = await response.json();
1689
+ if (this.config.debug) {
1690
+ console.log("šŸ“„ Service response:", envelope);
1691
+ }
1692
+ // Extract data from envelope (server wraps in { success, data } structure)
1693
+ const result = envelope.data || envelope;
1694
+ // Return the result from service
1695
+ return {
1696
+ approved: result.approved ?? false,
1697
+ signature: result.signature,
1698
+ actionHash: result.actionHash,
1699
+ authorizedSpendsHash: result.authorizedSpendsHash,
1700
+ ucdDebtHash: result.ucdDebtHash,
1701
+ totalDeduction: result.totalDeduction,
1702
+ remainingCollateral: result.remainingCollateral,
1703
+ newCollateralRatioBps: result.newCollateralRatioBps,
1704
+ destinationAddress: result.destinationAddress,
1705
+ btcPrice: result.btcPrice,
1706
+ timestamp: result.timestamp,
1707
+ utxoTxid: result.utxoTxid,
1708
+ utxoVout: result.utxoVout,
1709
+ utxoSatoshis: result.utxoSatoshis,
1710
+ reason: result.reason,
1711
+ failedStep: result.failedStep,
1712
+ error: result.error,
1713
+ };
1714
+ }
1715
+ catch (error) {
1716
+ console.error("āŒ Service request failed:", error);
1717
+ return {
1718
+ approved: false,
1719
+ error: error instanceof Error ? error.message : String(error),
1720
+ reason: "Service request failed",
1721
+ };
1722
+ }
1723
+ }
1724
+ /**
1725
+ * Request withdrawal authorization locally (STANDALONE mode)
1726
+ */
1727
+ async requestWithdrawalAuthorizationStandalone(request) {
1728
+ // Get LIT Action info from registry
1729
+ const litActions = this.config.network === "datil"
1730
+ ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
1731
+ : (() => {
1732
+ throw new Error("Unsupported LIT network");
1733
+ })();
1734
+ const litActionInfo = litActions.btcWithdrawal;
1735
+ if (!litActionInfo || !litActionInfo.cid || !litActionInfo.pkp) {
1736
+ throw new Error("BTC withdrawal LIT Action not found in registry");
1737
+ }
1738
+ if (this.config.debug) {
1739
+ console.log("šŸ“” Calling BTC Withdrawal LIT Action:");
1740
+ console.log(" Network:", this.config.network);
1741
+ console.log(" CID:", litActionInfo.cid);
1742
+ console.log(" Position:", request.authMessage.positionId);
1743
+ console.log(" Amount:", request.authMessage.amount);
1744
+ console.log(" Address:", request.authMessage.withdrawalAddress);
1745
+ console.log(" Timestamp:", request.authMessage.timestamp);
1746
+ }
1747
+ // Get LIT client for execution
1748
+ const litClient = await this.clientManager.getClient({
1749
+ litNetwork: this.config.network,
1750
+ debug: this.config.debug,
1751
+ });
1752
+ // Determine chain and Bitcoin provider from chainId
1753
+ let chain;
1754
+ let bitcoinProviderUrl;
1755
+ switch (request.authMessage.chainId) {
1756
+ case 1: // Ethereum mainnet
1757
+ chain = "ethereum";
1758
+ bitcoinProviderUrl =
1759
+ process.env.BITCOIN_PROVIDER_URL || "https://mempool.space/api";
1760
+ break;
1761
+ case 11155111: // Sepolia testnet
1762
+ chain = "sepolia";
1763
+ bitcoinProviderUrl =
1764
+ process.env.BITCOIN_PROVIDER_URL ||
1765
+ "https://diamond-hands-btc-faucet-jw-b86e9451a147.herokuapp.com/api";
1766
+ break;
1767
+ case 1337: // Hardhat local testnet (actual deployment chainId)
1768
+ case 31337: // Hardhat local testnet (standard default)
1769
+ chain = "hardhat";
1770
+ bitcoinProviderUrl =
1771
+ process.env.BITCOIN_PROVIDER_URL || "http://127.0.0.1:18443";
1772
+ break;
1773
+ default:
1774
+ throw new Error(`Unsupported chainId: ${request.authMessage.chainId}`);
1775
+ }
1776
+ // Override with custom Bitcoin RPC URL if provided
1777
+ if (request.customBitcoinRpcUrl) {
1778
+ bitcoinProviderUrl = request.customBitcoinRpcUrl;
1779
+ if (this.config.debug) {
1780
+ console.log(" Custom Bitcoin RPC URL:", request.customBitcoinRpcUrl);
1781
+ }
1782
+ }
1783
+ // Execute LIT Action
1784
+ const litActionParams = {
1785
+ chain,
1786
+ bitcoinProviderUrl,
1787
+ auth: {
1788
+ positionId: request.authMessage.positionId,
1789
+ timestamp: request.authMessage.timestamp,
1790
+ chainId: request.authMessage.chainId,
1791
+ destinationAddress: request.authMessage.withdrawalAddress,
1792
+ amount: request.authMessage.amount,
1793
+ action: request.authMessage.action,
1794
+ signature: request.userSignature,
1795
+ mode: request.authMessage.mode,
1796
+ },
1797
+ // Always pass contracts (undefined in prod mode)
1798
+ contracts: request.authMessage.contracts,
1799
+ };
1800
+ // Add custom RPC URL for local development
1801
+ if (request.customRpcUrl) {
1802
+ litActionParams.customRpcUrl = request.customRpcUrl;
1803
+ if (this.config.debug) {
1804
+ console.log(" Custom RPC URL:", request.customRpcUrl);
1805
+ }
1806
+ }
1807
+ // Execute LIT Action
1808
+ const result = await this.actionExecutor.executeAction({
1809
+ cid: litActionInfo.cid,
1810
+ pkpPublicKey: litActionInfo.pkp.publicKey,
1811
+ params: litActionParams,
1812
+ signer: this.config.signer,
1813
+ }, litClient);
1814
+ if (this.config.debug) {
1815
+ console.log("šŸ“„ LIT Action response:", {
1816
+ success: result.success,
1817
+ hasSignature: !!result.signatures,
1818
+ error: result.error,
1819
+ });
1820
+ console.log("šŸ“„ LIT Action result.response (raw):", result.response);
1821
+ }
1822
+ // Parse response if it's a string
1823
+ let responseData = result.response;
1824
+ if (typeof result.response === "string") {
1825
+ try {
1826
+ responseData = JSON.parse(result.response);
1827
+ if (this.config.debug) {
1828
+ console.log("šŸ“„ Parsed response data:", responseData);
1829
+ }
1830
+ }
1831
+ catch (e) {
1832
+ if (this.config.debug) {
1833
+ console.error("āŒ Failed to parse response string:", e);
1834
+ }
1835
+ }
1836
+ }
1837
+ // Return standardized response including UTXO data from Bitcoin network query
1838
+ const returnValue = {
1839
+ approved: responseData?.approved ?? false,
1840
+ signature: result.signatures
1841
+ ? JSON.stringify(result.signatures)
1842
+ : undefined,
1843
+ actionHash: responseData?.actionHash,
1844
+ authorizedSpendsHash: responseData?.authorizedSpendsHash,
1845
+ ucdDebtHash: responseData?.ucdDebtHash,
1846
+ totalDeduction: responseData?.totalDeduction,
1847
+ remainingCollateral: responseData?.remainingCollateral,
1848
+ newCollateralRatioBps: responseData?.newCollateralRatioBps,
1849
+ destinationAddress: responseData?.destinationAddress,
1850
+ btcPrice: responseData?.btcPrice,
1851
+ timestamp: responseData?.timestamp,
1852
+ utxoTxid: responseData?.utxoTxid, // Real Bitcoin UTXO txid from network query
1853
+ utxoVout: responseData?.utxoVout, // Real Bitcoin UTXO vout from network query
1854
+ utxoSatoshis: responseData?.utxoSatoshis, // UTXO value for validation
1855
+ reason: responseData?.reason,
1856
+ failedStep: responseData?.failedStep,
1857
+ error: result.error,
1858
+ };
1859
+ if (this.config.debug) {
1860
+ console.log("šŸŽ STANDALONE RETURN VALUE:", JSON.stringify(returnValue, null, 2));
1861
+ }
1862
+ return returnValue;
1863
+ }
1864
+ /**
1865
+ * Sign Bitcoin transaction for BTC withdrawal execution (Phase 2)
1866
+ *
1867
+ * Dual-mode method: Delegates to service endpoint or executes locally
1868
+ */
1869
+ async signBTCTransaction(request) {
1870
+ // In SERVICE mode, delegate to lit-ops-server
1871
+ if (this.config.mode === "service") {
1872
+ if (this.config.debug) {
1873
+ console.log("🌐 [signBTCTransaction] Using SERVICE mode - delegating to server");
1874
+ }
1875
+ return this.signBTCTransactionViaService(request);
1876
+ }
1877
+ // STANDALONE mode: Execute LIT action locally
1878
+ if (this.config.debug) {
1879
+ console.log("šŸ”§ [signBTCTransaction] Using STANDALONE mode - executing locally");
1880
+ }
1881
+ return this.signBTCTransactionStandalone(request);
1882
+ }
1883
+ /**
1884
+ * Sign Bitcoin transaction via lit-ops-server (SERVICE mode)
1885
+ */
1886
+ async signBTCTransactionViaService(request) {
1887
+ if (!this.config.serviceEndpoint) {
1888
+ throw new Error("Service endpoint not configured for service mode");
1889
+ }
1890
+ const url = `${this.config.serviceEndpoint}/api/lit/btc/sign-transaction`;
1891
+ if (this.config.debug) {
1892
+ console.log("🌐 Signing BTC transaction via service:", url);
1893
+ console.log(" Position ID:", request.auth.positionId);
1894
+ console.log(" UTXO:", `${request.auth.utxo.txid}:${request.auth.utxo.vout}`);
1895
+ console.log(" Network Fee:", request.auth.networkFee);
1896
+ }
1897
+ try {
1898
+ const response = await fetch(url, {
1899
+ method: "POST",
1900
+ headers: {
1901
+ "Content-Type": "application/json",
1902
+ },
1903
+ body: JSON.stringify(request),
1904
+ });
1905
+ if (!response.ok) {
1906
+ const errorText = await response.text();
1907
+ throw new Error(`Service error (${response.status}): ${errorText}`);
1908
+ }
1909
+ const envelope = await response.json();
1910
+ if (this.config.debug) {
1911
+ console.log("šŸ“„ Service response:", envelope);
1912
+ }
1913
+ // Extract data from envelope (server wraps in { success, data } structure)
1914
+ const result = envelope.data || envelope;
1915
+ return {
1916
+ success: result.success ?? false,
1917
+ unsignedTxHex: result.unsignedTxHex,
1918
+ sigHash: result.sigHash,
1919
+ signature: result.signature,
1920
+ pkpPublicKey: result.pkpPublicKey,
1921
+ pkpBtcAddress: result.pkpBtcAddress,
1922
+ destination: result.destination,
1923
+ userReceivesAmount: result.userReceivesAmount,
1924
+ networkFee: result.networkFee,
1925
+ changeAmount: result.changeAmount,
1926
+ utxo: result.utxo,
1927
+ authorization: result.authorization,
1928
+ timestamp: result.timestamp,
1929
+ action: result.action,
1930
+ error: result.error,
1931
+ failedStep: result.failedStep,
1932
+ };
1933
+ }
1934
+ catch (error) {
1935
+ console.error("āŒ Service request failed:", error);
1936
+ return {
1937
+ success: false,
1938
+ error: error instanceof Error ? error.message : String(error),
1939
+ };
1940
+ }
1941
+ }
1942
+ /**
1943
+ * Sign Bitcoin transaction locally (STANDALONE mode)
1944
+ */
1945
+ async signBTCTransactionStandalone(request) {
1946
+ // Get LIT Action info from registry
1947
+ const litActions = this.config.network === "datil"
1948
+ ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
1949
+ : (() => {
1950
+ throw new Error("Unsupported LIT network");
1951
+ })();
1952
+ const litActionInfo = litActions.btcTransactionSigner;
1953
+ if (!litActionInfo || !litActionInfo.cid) {
1954
+ throw new Error("BTC transaction signer LIT Action not found in registry");
1955
+ }
1956
+ if (this.config.debug) {
1957
+ console.log("šŸ“” Calling BTC Transaction Signer LIT Action:");
1958
+ console.log(" Network:", this.config.network);
1959
+ console.log(" CID:", litActionInfo.cid);
1960
+ console.log(" Position:", request.auth.positionId);
1961
+ console.log(" UTXO:", `${request.auth.utxo.txid}:${request.auth.utxo.vout}`);
1962
+ console.log(" Network Fee:", request.auth.networkFee);
1963
+ }
1964
+ // Get LIT client for execution
1965
+ const litClient = await this.clientManager.getClient({
1966
+ litNetwork: this.config.network,
1967
+ debug: this.config.debug,
1968
+ });
1969
+ // Build LIT Action parameters
1970
+ // Determine Bitcoin provider URL based on network
1971
+ let bitcoinProviderUrl;
1972
+ const chainId = request.auth.chainId;
1973
+ switch (chainId) {
1974
+ case 1: // Ethereum mainnet
1975
+ bitcoinProviderUrl = process.env.BITCOIN_PROVIDER_URL || "https://mempool.space/api";
1976
+ break;
1977
+ case 11155111: // Sepolia testnet
1978
+ bitcoinProviderUrl = process.env.BITCOIN_PROVIDER_URL || "https://diamond-hands-btc-faucet-jw-b86e9451a147.herokuapp.com/api";
1979
+ break;
1980
+ case 1337: // Hardhat local testnet (actual deployment chainId)
1981
+ case 31337: // Hardhat local testnet (standard default)
1982
+ bitcoinProviderUrl = process.env.BITCOIN_PROVIDER_URL || "http://127.0.0.1:18443";
1983
+ break;
1984
+ default:
1985
+ throw new Error(`Unsupported chainId: ${chainId}`);
1986
+ }
1987
+ // Override with custom Bitcoin RPC URL if provided (for local development with ngrok)
1988
+ if (request.customBitcoinRpcUrl) {
1989
+ bitcoinProviderUrl = request.customBitcoinRpcUrl;
1990
+ if (this.config.debug) {
1991
+ console.log(" Custom Bitcoin RPC URL (override):", request.customBitcoinRpcUrl);
1992
+ }
1993
+ }
1994
+ const litActionParams = {
1995
+ chain: request.chain,
1996
+ bitcoinNetwork: request.bitcoinNetwork,
1997
+ bitcoinProviderUrl, // For Bitcoin UTXO queries
1998
+ auth: request.auth,
1999
+ publicKey: request.publicKey,
2000
+ contracts: request.contracts,
2001
+ };
2002
+ // Add custom EVM RPC URL for local development (Hardhat)
2003
+ if (request.customRpcUrl) {
2004
+ litActionParams.customRpcUrl = request.customRpcUrl;
2005
+ if (this.config.debug) {
2006
+ console.log(" Custom EVM RPC URL:", request.customRpcUrl);
2007
+ }
2008
+ }
2009
+ if (this.config.debug) {
2010
+ console.log(" Bitcoin Provider URL:", bitcoinProviderUrl);
2011
+ }
2012
+ // Execute LIT Action
2013
+ const result = await this.actionExecutor.executeAction({
2014
+ cid: litActionInfo.cid,
2015
+ pkpPublicKey: request.publicKey,
2016
+ params: litActionParams,
2017
+ signer: this.config.signer,
2018
+ }, litClient);
2019
+ if (this.config.debug) {
2020
+ console.log("šŸ“„ LIT Action response:", {
2021
+ success: result.success,
2022
+ hasSignature: !!result.signatures,
2023
+ error: result.error,
2024
+ });
2025
+ // Log per-node debug information if available
2026
+ if (result.debug) {
2027
+ console.log("\nšŸ” PER-NODE DEBUG INFORMATION:");
2028
+ console.log("=====================================");
2029
+ const debugInfo = result.debug;
2030
+ if (debugInfo.allNodeResponses) {
2031
+ console.log("\nšŸ“Š All Node Responses:");
2032
+ console.log(JSON.stringify(debugInfo.allNodeResponses, null, 2));
2033
+ }
2034
+ if (debugInfo.allNodeLogs) {
2035
+ console.log("\nšŸ“ All Node Logs:");
2036
+ debugInfo.allNodeLogs.forEach((nodeLog, index) => {
2037
+ console.log(`\n--- Node ${index + 1} ---`);
2038
+ console.log(JSON.stringify(nodeLog, null, 2));
2039
+ });
2040
+ }
2041
+ if (debugInfo.rawNodeHTTPResponses) {
2042
+ console.log("\n🌐 Raw Node HTTP Responses:");
2043
+ console.log(JSON.stringify(debugInfo.rawNodeHTTPResponses, null, 2));
2044
+ }
2045
+ }
2046
+ else {
2047
+ console.log("āš ļø No per-node debug information available in result");
2048
+ }
2049
+ }
2050
+ // Parse response if it's a string
2051
+ let responseData = result.response;
2052
+ if (typeof result.response === "string") {
2053
+ try {
2054
+ responseData = JSON.parse(result.response);
2055
+ if (this.config.debug) {
2056
+ console.log("šŸ“„ Parsed response data:", responseData);
2057
+ }
2058
+ }
2059
+ catch (e) {
2060
+ if (this.config.debug) {
2061
+ console.error("āŒ Failed to parse response string:", e);
2062
+ }
2063
+ }
2064
+ }
2065
+ // Extract combined signature from LIT SDK result
2066
+ // LIT SDK returns signatures in result.signatures.btcTxSig after combination
2067
+ let combinedSignature;
2068
+ if (result.signatures && typeof result.signatures === 'object') {
2069
+ const btcTxSig = result.signatures.btcTxSig;
2070
+ if (btcTxSig && btcTxSig.signature) {
2071
+ // LIT SDK combined signature
2072
+ combinedSignature = btcTxSig.signature;
2073
+ if (this.config.debug && combinedSignature) {
2074
+ console.log("āœ… Extracted combined signature from LIT SDK:", {
2075
+ signatureLength: combinedSignature.length,
2076
+ signatureStart: combinedSignature.substring(0, 20) + "..."
2077
+ });
2078
+ }
2079
+ }
2080
+ }
2081
+ // Return standardized response
2082
+ return {
2083
+ success: responseData?.success ?? false,
2084
+ unsignedTxHex: responseData?.unsignedTxHex,
2085
+ sigHash: responseData?.sigHash,
2086
+ signature: combinedSignature || responseData?.signature,
2087
+ pkpPublicKey: responseData?.pkpPublicKey,
2088
+ pkpBtcAddress: responseData?.pkpBtcAddress,
2089
+ destination: responseData?.destination,
2090
+ userReceivesAmount: responseData?.userReceivesAmount,
2091
+ networkFee: responseData?.networkFee,
2092
+ changeAmount: responseData?.changeAmount,
2093
+ utxo: responseData?.utxo,
2094
+ authorization: responseData?.authorization,
2095
+ timestamp: responseData?.timestamp,
2096
+ action: responseData?.action,
2097
+ error: result.error || responseData?.error,
2098
+ failedStep: responseData?.failedStep,
2099
+ };
2100
+ }
1566
2101
  /**
1567
2102
  * Get trustless BTC price from Price Oracle LIT Action
1568
2103
  *
@@ -1579,7 +2114,9 @@ class LitOps {
1579
2114
  // Get LIT Action info from registry
1580
2115
  const litActions = (this.config.network === "datil"
1581
2116
  ? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
1582
- : dh_lit_actions_1.DH_LIT_ACTIONS_DATIL_TEST);
2117
+ : (() => {
2118
+ throw new Error("Unsupported LIT network");
2119
+ })());
1583
2120
  console.log("litActions in getActions", litActions);
1584
2121
  const litActionInfo = litActions.priceOracle;
1585
2122
  if (!litActionInfo || !litActionInfo.cid) {