@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.
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/interfaces/chunks/config.d.ts +6 -0
- package/dist/interfaces/chunks/config.d.ts.map +1 -1
- package/dist/modules/action-executor.module.d.ts.map +1 -1
- package/dist/modules/action-executor.module.js +103 -20
- package/dist/modules/action-executor.module.js.map +1 -1
- package/dist/modules/auth-manager.module.d.ts.map +1 -1
- package/dist/modules/auth-manager.module.js +148 -36
- package/dist/modules/auth-manager.module.js.map +1 -1
- package/dist/modules/lit-ops.module.d.ts +270 -2
- package/dist/modules/lit-ops.module.d.ts.map +1 -1
- package/dist/modules/lit-ops.module.js +1352 -90
- package/dist/modules/lit-ops.module.js.map +1 -1
- package/dist/modules/pkp-macros.module.d.ts +4 -0
- package/dist/modules/pkp-macros.module.d.ts.map +1 -1
- package/dist/modules/pkp-macros.module.js +28 -8
- package/dist/modules/pkp-macros.module.js.map +1 -1
- package/dist/modules/pkp-minter.module.d.ts +1 -0
- package/dist/modules/pkp-minter.module.d.ts.map +1 -1
- package/dist/modules/pkp-minter.module.js +77 -45
- package/dist/modules/pkp-minter.module.js.map +1 -1
- package/dist/modules/session-signature-manager.module.d.ts +0 -1
- package/dist/modules/session-signature-manager.module.d.ts.map +1 -1
- package/dist/modules/session-signature-manager.module.js +71 -36
- package/dist/modules/session-signature-manager.module.js.map +1 -1
- package/package.json +2 -2
|
@@ -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
|
-
:
|
|
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
|
-
|
|
664
|
-
|
|
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(`
|
|
681
|
-
console.log(`
|
|
682
|
-
console.log(` PKP
|
|
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
|
-
|
|
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.
|
|
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 (
|
|
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
|
-
|
|
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
|
-
:
|
|
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
|
|
1321
|
+
const registry = selectedNetwork === "datil"
|
|
1264
1322
|
? dh_lit_actions_1.DH_LIT_ACTIONS_DATIL
|
|
1265
|
-
:
|
|
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 -
|
|
1268
|
-
return this.createAndValidatePkpToLitAction(
|
|
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
|
-
:
|
|
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:",
|
|
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
|
-
//
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
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:
|
|
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
|
-
:
|
|
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) {
|