@gvnrdao/dh-sdk 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +77 -0
  2. package/dist/browser.d.ts +14 -0
  3. package/dist/browser.js +36 -0
  4. package/dist/constants/chunks/contract-abis.d.ts +6 -0
  5. package/dist/constants/chunks/contract-abis.js +39 -0
  6. package/dist/constants/chunks/environment.browser.d.ts +40 -0
  7. package/dist/constants/chunks/environment.browser.js +111 -0
  8. package/dist/constants/chunks/environment.d.ts +40 -0
  9. package/dist/constants/chunks/environment.js +146 -0
  10. package/dist/constants/chunks/sdk-config.d.ts +27 -0
  11. package/dist/constants/chunks/sdk-config.js +34 -0
  12. package/dist/constants/index.d.ts +9 -0
  13. package/dist/constants/index.js +28 -0
  14. package/dist/index.d.ts +21 -0
  15. package/dist/index.js +50 -0
  16. package/dist/interfaces/chunks/IBTCProof.d.ts +32 -0
  17. package/dist/interfaces/chunks/IBTCProof.js +2 -0
  18. package/dist/interfaces/chunks/SDKTypes.d.ts +2 -0
  19. package/dist/interfaces/chunks/SDKTypes.js +19 -0
  20. package/dist/interfaces/chunks/btc.i.d.ts +36 -0
  21. package/dist/interfaces/chunks/btc.i.js +5 -0
  22. package/dist/interfaces/chunks/config.i.d.ts +45 -0
  23. package/dist/interfaces/chunks/config.i.js +5 -0
  24. package/dist/interfaces/chunks/contract-interactions.i.d.ts +66 -0
  25. package/dist/interfaces/chunks/contract-interactions.i.js +5 -0
  26. package/dist/interfaces/chunks/lit-actions.d.ts +27 -0
  27. package/dist/interfaces/chunks/lit-actions.i.d.ts +44 -0
  28. package/dist/interfaces/chunks/lit-actions.i.js +5 -0
  29. package/dist/interfaces/chunks/lit-actions.js +2 -0
  30. package/dist/interfaces/chunks/loan-operations.d.ts +56 -0
  31. package/dist/interfaces/chunks/loan-operations.i.d.ts +85 -0
  32. package/dist/interfaces/chunks/loan-operations.i.js +5 -0
  33. package/dist/interfaces/chunks/loan-operations.js +2 -0
  34. package/dist/interfaces/chunks/pkp-integration.i.d.ts +88 -0
  35. package/dist/interfaces/chunks/pkp-integration.i.js +5 -0
  36. package/dist/interfaces/chunks/requests.i.d.ts +28 -0
  37. package/dist/interfaces/chunks/requests.i.js +5 -0
  38. package/dist/interfaces/chunks/ucd-minting.i.d.ts +34 -0
  39. package/dist/interfaces/chunks/ucd-minting.i.js +5 -0
  40. package/dist/interfaces/chunks/utility.i.d.ts +64 -0
  41. package/dist/interfaces/chunks/utility.i.js +5 -0
  42. package/dist/interfaces/index.d.ts +15 -0
  43. package/dist/interfaces/index.js +40 -0
  44. package/dist/modules/contract-interaction-manager.module.d.ts +86 -0
  45. package/dist/modules/contract-interaction-manager.module.js +136 -0
  46. package/dist/modules/diamond-hands-sdk.module.d.ts +109 -0
  47. package/dist/modules/diamond-hands-sdk.module.js +773 -0
  48. package/dist/modules/loan-operations-manager.module.d.ts +92 -0
  49. package/dist/modules/loan-operations-manager.module.js +171 -0
  50. package/dist/modules/pkp-integration-manager.module.d.ts +86 -0
  51. package/dist/modules/pkp-integration-manager.module.js +62 -0
  52. package/dist/types/loanStatus.d.ts +10 -0
  53. package/dist/types/loanStatus.js +16 -0
  54. package/dist/utils/chunks/bitcoin-utils.d.ts +51 -0
  55. package/dist/utils/chunks/bitcoin-utils.js +135 -0
  56. package/dist/utils/chunks/environment-utils.d.ts +11 -0
  57. package/dist/utils/chunks/environment-utils.js +21 -0
  58. package/dist/utils/chunks/pkp-utils.d.ts +19 -0
  59. package/dist/utils/chunks/pkp-utils.js +44 -0
  60. package/dist/utils/chunks/validation-utils.d.ts +30 -0
  61. package/dist/utils/chunks/validation-utils.js +58 -0
  62. package/dist/utils/index.d.ts +10 -0
  63. package/dist/utils/index.js +39 -0
  64. package/package.json +74 -0
@@ -0,0 +1,773 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DiamondHandsSDK = void 0;
4
+ const dh_lit_ops_1 = require("@gvnrdao/dh-lit-ops");
5
+ const ethers_1 = require("ethers");
6
+ const utils_1 = require("../utils");
7
+ // Position Manager ABI for smart contract interaction
8
+ const POSITION_MANAGER_ABI = [
9
+ "function createPosition(bytes32 pkpId, uint256 amount, uint256 requestedCollateralRatio, uint256 selectedTerm, bytes calldata validatorSignature) external returns (bool)",
10
+ "function calculateCollateralRatio(bytes32 positionId) external view returns (uint256)",
11
+ "function getPositionDetails(bytes32 positionId) external view returns (tuple(bytes32 positionId, bytes32 pkpId, address borrower, uint256 btcAmount, uint256 ucdDebt, uint256 createdAt, uint256 lastUpdated, bool isActive, uint256 collateralRatio, uint256 requestedCollateralRatio, uint256 selectedTerm))",
12
+ "function getUserPositions(address user) external view returns (bytes32[] memory)",
13
+ "function getPositionByPKP(bytes32 pkpId) external view returns (bytes32)",
14
+ "function getPKPPositionData(bytes32 pkpId) external view returns (tuple(bytes32 positionId, address borrower, uint256 amount, uint256 requestedCollateralRatio, uint256 selectedTerm, uint256 createdAt, bool isActive))",
15
+ "function getPKPLoanParams(bytes32 pkpId) external view returns (uint256 amount, uint256 requestedCollateralRatio, uint256 selectedTerm)",
16
+ "event PositionCreated(bytes32 indexed positionId, bytes32 indexed pkpId, address indexed borrower, uint256 amount, uint256 requestedCollateralRatio, uint256 selectedTerm)",
17
+ ];
18
+ /**
19
+ * Core Diamond Hands SDK Module
20
+ *
21
+ * This is the main SDK class that provides a high-level interface for Diamond Hands Protocol operations.
22
+ * It supports two operation modes:
23
+ * - Standalone: User provides signer and network, SDK creates PKPs directly via LIT Protocol
24
+ * - Service: SDK calls lit-ops-server backend which handles LIT Protocol operations
25
+ *
26
+ * Default behavior (if mode not specified):
27
+ * - Mode: "service"
28
+ * - Service endpoint: "http://localhost:3001"
29
+ */
30
+ class DiamondHandsSDK {
31
+ constructor(config) {
32
+ this.litOps = null;
33
+ this.initialized = false;
34
+ this.config = config;
35
+ // Apply smart defaults
36
+ this.mode = config.mode || "service"; // Default to service mode
37
+ this.serviceEndpoint = config.serviceEndpoint || "http://localhost:3001"; // Default to localhost
38
+ // In service mode, use contractSigner's provider if available, otherwise create from ethRpcUrl
39
+ if (config.ethRpcUrl) {
40
+ this.provider = new ethers_1.ethers.providers.JsonRpcProvider(config.ethRpcUrl);
41
+ }
42
+ else if (config.contractSigner &&
43
+ "provider" in config.contractSigner &&
44
+ config.contractSigner.provider) {
45
+ // Use MetaMask's provider for blockchain interactions
46
+ this.provider = config.contractSigner
47
+ .provider;
48
+ }
49
+ else {
50
+ // Fallback: will be set later when contractSigner is available
51
+ this.provider = null;
52
+ }
53
+ // Validate configuration based on mode
54
+ this.validateConfig();
55
+ if (this.config.debug) {
56
+ console.log("๐Ÿ—๏ธ Diamond Hands SDK initialized with config:");
57
+ console.log(` Mode: ${this.mode}`);
58
+ if (this.mode === "standalone") {
59
+ console.log(` Network: ${config.litNetwork}`);
60
+ console.log(` LIT-Ops Signer: ${config.litOpsSigner?.address}`);
61
+ }
62
+ else {
63
+ console.log(` Service Endpoint: ${this.serviceEndpoint}`);
64
+ }
65
+ console.log(` Contract: ${config.contractAddresses.positionManager}`);
66
+ }
67
+ }
68
+ /**
69
+ * Validate configuration based on the selected mode
70
+ */
71
+ validateConfig() {
72
+ if (this.mode === "standalone") {
73
+ if (!this.config.litOpsSigner) {
74
+ throw new Error("litOpsSigner is required for standalone mode");
75
+ }
76
+ if (!this.config.litNetwork) {
77
+ throw new Error("litNetwork is required for standalone mode");
78
+ }
79
+ if (!this.config.ethRpcUrl) {
80
+ throw new Error("ethRpcUrl is required for standalone mode");
81
+ }
82
+ if (this.config.serviceEndpoint) {
83
+ throw new Error("serviceEndpoint must not be provided for standalone mode");
84
+ }
85
+ }
86
+ else if (this.mode === "service") {
87
+ if (this.config.litOpsSigner) {
88
+ throw new Error("litOpsSigner must not be provided for service mode");
89
+ }
90
+ if (this.config.litNetwork) {
91
+ throw new Error("litNetwork must not be provided for service mode (determined by serviceEndpoint)");
92
+ }
93
+ // In service mode, either ethRpcUrl or contractSigner with provider is required
94
+ if (!this.config.ethRpcUrl && !this.config.contractSigner) {
95
+ throw new Error("Service mode requires either ethRpcUrl or contractSigner for blockchain operations");
96
+ }
97
+ }
98
+ }
99
+ /**
100
+ * Initialize the SDK with required dependencies
101
+ */
102
+ async initialize() {
103
+ if (this.initialized)
104
+ return;
105
+ try {
106
+ if (this.config.debug) {
107
+ console.log("๐Ÿš€ Initializing Diamond Hands SDK...");
108
+ console.log(` Mode: ${this.mode}`);
109
+ }
110
+ // Initialize LitOps only for standalone mode
111
+ if (this.mode === "standalone") {
112
+ if (this.config.debug) {
113
+ console.log("๐Ÿ”ง Initializing LitOps for standalone mode");
114
+ }
115
+ // LitOps will be initialized with signer during operations
116
+ // to support different signers for different operations
117
+ }
118
+ else {
119
+ if (this.config.debug) {
120
+ console.log(`๐ŸŒ Service mode - using endpoint: ${this.serviceEndpoint}`);
121
+ }
122
+ // Service mode - no LitOps initialization needed
123
+ }
124
+ this.initialized = true;
125
+ if (this.config.debug) {
126
+ console.log("โœ… Diamond Hands SDK initialized successfully");
127
+ }
128
+ }
129
+ catch (error) {
130
+ throw new Error(`SDK initialization failed: ${error instanceof Error ? error.message : error}`);
131
+ }
132
+ }
133
+ /**
134
+ * Create a new Diamond Hands loan by:
135
+ * 1. Creating and validating a PKP (via standalone LitOps or service endpoint)
136
+ * 2. Extracting the validator signature from PKP creation result
137
+ * 3. Funding the contract wallet if needed
138
+ * 4. Calling the smart contract createPosition method with PKP validator signature
139
+ */
140
+ async createLoan(request) {
141
+ this.ensureInitialized();
142
+ try {
143
+ if (this.config.debug) {
144
+ console.log("๐Ÿฆ Starting Diamond Hands loan creation");
145
+ console.log(` Mode: ${this.mode}`);
146
+ console.log(` Collateral Amount: ${request.collateralAmount}`);
147
+ console.log(` Collateral Ratio: ${request.collateralRatio}%`);
148
+ console.log(` Selected Term: ${request.selectedTerm} months`);
149
+ }
150
+ // Step 1: Create PKP using the appropriate mode
151
+ let pkpResult;
152
+ if (this.mode === "standalone") {
153
+ pkpResult = await this.createLoanStandalone(request);
154
+ }
155
+ else {
156
+ pkpResult = await this.createLoanService(request);
157
+ }
158
+ return pkpResult;
159
+ }
160
+ catch (error) {
161
+ const errorMessage = error instanceof Error ? error.message : String(error);
162
+ if (this.config.debug) {
163
+ console.error("โŒ Loan creation failed:", errorMessage);
164
+ console.error("Full error:", error);
165
+ }
166
+ return {
167
+ success: false,
168
+ error: `Loan creation failed: ${errorMessage}`,
169
+ };
170
+ }
171
+ }
172
+ /**
173
+ * Create loan in standalone mode - uses LitOps directly
174
+ */
175
+ async createLoanStandalone(request) {
176
+ if (this.config.debug) {
177
+ console.log("๐Ÿ”ง Standalone mode: Creating loan with LitOps");
178
+ console.log(` Network: ${this.config.litNetwork}`);
179
+ }
180
+ // Create dual-wallet setup (matching the working contract integration test)
181
+ // A) LIT-Ops wallet: Use the configured litOpsSigner with Yellowstone provider
182
+ if (!this.config.litOpsSigner) {
183
+ throw new Error("litOpsSigner is required in DiamondHandsSDKConfig for standalone mode");
184
+ }
185
+ const litProviderV5 = new ethers_1.ethers.providers.JsonRpcProvider("https://yellowstone-rpc.litprotocol.com");
186
+ const litOpsWallet = this.config.litOpsSigner.connect(litProviderV5);
187
+ // Setup contract wallet
188
+ const contractWallet = await this.setupContractWallet();
189
+ if (this.config.debug) {
190
+ console.log("๐Ÿ”ง Dual-wallet setup:");
191
+ console.log(` LIT-Ops wallet: ${litOpsWallet.address} (Yellowstone RPC)`);
192
+ const contractAddress = await contractWallet.getAddress();
193
+ console.log(` Contract wallet: ${contractAddress} (Local RPC)`);
194
+ console.log(` Wallets match: ${litOpsWallet.address === contractAddress}`);
195
+ }
196
+ // Initialize LitOps with the LIT-specific wallet
197
+ if (!this.litOps) {
198
+ this.litOps = new dh_lit_ops_1.LitOps({
199
+ mode: "standalone",
200
+ network: this.config.litNetwork || "datil-test",
201
+ signer: litOpsWallet, // Use LIT-Ops wallet with Yellowstone provider
202
+ debug: this.config.debug,
203
+ });
204
+ }
205
+ // Step 1: Create and validate PKP using lit-ops production method
206
+ if (this.config.debug) {
207
+ console.log("๐Ÿ“ Step 1: Creating PKP via LIT-Ops getNewDiamondHandsLoanPkp");
208
+ }
209
+ let pkpResult = await this.createPKPWithRetry(litOpsWallet);
210
+ if (!pkpResult.success) {
211
+ throw new Error(`PKP creation failed: ${pkpResult.error}`);
212
+ }
213
+ return this.processLoanCreation(pkpResult, contractWallet, request);
214
+ }
215
+ /**
216
+ * Create loan in service mode - calls lit-ops-server
217
+ */
218
+ async createLoanService(request) {
219
+ if (this.config.debug) {
220
+ console.log("๐ŸŒ Service mode: Creating loan via lit-ops-server");
221
+ console.log(` Service endpoint: ${this.serviceEndpoint}`);
222
+ }
223
+ // Step 1: Call service endpoint to create PKP
224
+ if (this.config.debug) {
225
+ console.log("๐Ÿ“ Step 1: Creating PKP via service endpoint");
226
+ }
227
+ const response = await fetch(`${this.serviceEndpoint}/api/lit/pkp/create-diamond-hands-loan`, {
228
+ method: "POST",
229
+ headers: {
230
+ "Content-Type": "application/json",
231
+ },
232
+ body: JSON.stringify({}), // Empty body, service handles PKP creation
233
+ });
234
+ if (!response.ok) {
235
+ throw new Error(`Service call failed: ${response.status} ${response.statusText}`);
236
+ }
237
+ const pkpResult = await response.json();
238
+ if (!pkpResult.success) {
239
+ throw new Error(`Service PKP creation failed: ${pkpResult.error || "Unknown error"}`);
240
+ }
241
+ if (this.config.debug) {
242
+ console.log("โœ… Service PKP creation successful");
243
+ console.log(` Token ID: ${pkpResult.pkpData?.tokenId}`);
244
+ console.log(` ETH Address: ${pkpResult.pkpData?.ethAddress}`);
245
+ }
246
+ // Setup contract wallet for local operations
247
+ const contractWallet = await this.setupContractWallet();
248
+ // Convert service response to expected format
249
+ const formattedPkpResult = {
250
+ success: true,
251
+ pkpData: {
252
+ tokenId: pkpResult.pkpData?.tokenId,
253
+ ethAddress: pkpResult.pkpData?.ethAddress,
254
+ publicKey: pkpResult.pkpData?.publicKey,
255
+ validationSignature: pkpResult.validationResult?.signature,
256
+ // Include Bitcoin addresses if available
257
+ btcAddresses: pkpResult.pkpData?.btcAddresses,
258
+ },
259
+ auditTrail: {
260
+ steps: pkpResult.auditTrail?.steps || [],
261
+ },
262
+ // Include validation results
263
+ validationResult: pkpResult.validationResult,
264
+ };
265
+ return this.processLoanCreation(formattedPkpResult, contractWallet, request, true);
266
+ }
267
+ /**
268
+ * Setup contract wallet for Ethereum transactions
269
+ */
270
+ async setupContractWallet() {
271
+ if (!this.config.contractSigner) {
272
+ throw new Error("contractSigner is required in DiamondHandsSDKConfig for contract operations");
273
+ }
274
+ let contractWallet;
275
+ if ("privateKey" in this.config.contractSigner &&
276
+ this.config.contractSigner.privateKey) {
277
+ // If signer is a Wallet with private key, connect it to local provider
278
+ contractWallet = new ethers_1.ethers.Wallet(this.config.contractSigner.privateKey, this.provider);
279
+ }
280
+ else {
281
+ // If signer is already connected (e.g., MetaMask), use it directly
282
+ contractWallet = this.config.contractSigner;
283
+ }
284
+ return contractWallet;
285
+ }
286
+ /**
287
+ * Create PKP with retry logic for LIT Protocol network issues
288
+ */
289
+ async createPKPWithRetry(litOpsWallet) {
290
+ let pkpResult;
291
+ let retryAttempted = false;
292
+ // Attempt PKP creation with retry logic for LIT Protocol network issues
293
+ try {
294
+ pkpResult = await this.litOps.getNewDiamondHandsLoanPkp(litOpsWallet // Use LIT-Ops wallet for PKP operations
295
+ );
296
+ }
297
+ catch (error) {
298
+ // Check if this is the known LIT SDK event parsing issue
299
+ if (!retryAttempted &&
300
+ error.message?.includes("Cannot read properties of undefined (reading 'topics')")) {
301
+ if (this.config.debug) {
302
+ console.log("โš ๏ธ LIT SDK event parsing failed, retrying once...");
303
+ }
304
+ retryAttempted = true;
305
+ // Wait a bit for network to settle
306
+ await new Promise((resolve) => setTimeout(resolve, 2000));
307
+ // Retry once
308
+ try {
309
+ pkpResult = await this.litOps.getNewDiamondHandsLoanPkp(litOpsWallet // Use LIT-Ops wallet for retry too
310
+ );
311
+ }
312
+ catch (retryError) {
313
+ if (this.config.debug) {
314
+ console.log("โŒ Retry also failed, this is a persistent LIT Protocol issue");
315
+ }
316
+ throw new Error(`PKP creation failed after retry: ${retryError instanceof Error ? retryError.message : retryError}`);
317
+ }
318
+ }
319
+ else {
320
+ throw error;
321
+ }
322
+ }
323
+ return pkpResult;
324
+ }
325
+ /**
326
+ * Process loan creation after PKP is created - handle signature extraction and contract calls
327
+ */
328
+ async processLoanCreation(pkpResult, contractWallet, request, isServiceMode = false) {
329
+ // Extract PKP data from result (following the test logic)
330
+ let createdPkpTokenId;
331
+ let createdPkpAddress;
332
+ let createdPkpPublicKey;
333
+ if (pkpResult.pkpData) {
334
+ createdPkpTokenId = pkpResult.pkpData.tokenId || "";
335
+ createdPkpAddress = pkpResult.pkpData.ethAddress || "";
336
+ createdPkpPublicKey = pkpResult.pkpData.publicKey || "";
337
+ if (this.config.debug) {
338
+ console.log("โœ… Using PKP data from main result");
339
+ }
340
+ }
341
+ else if (pkpResult.auditTrail &&
342
+ pkpResult.auditTrail.steps &&
343
+ pkpResult.auditTrail.steps.length > 0) {
344
+ const creationStep = pkpResult.auditTrail.steps.find((step) => step.description.includes("Create production PKP") && step.data);
345
+ if (creationStep && creationStep.data) {
346
+ createdPkpTokenId = creationStep.data.tokenId;
347
+ createdPkpAddress = creationStep.data.ethAddress;
348
+ createdPkpPublicKey = creationStep.data.publicKey;
349
+ if (this.config.debug) {
350
+ console.log("โœ… Using PKP data from audit trail");
351
+ }
352
+ }
353
+ else {
354
+ throw new Error("No valid PKP data found in result or audit trail");
355
+ }
356
+ }
357
+ else {
358
+ throw new Error("PKP creation failed completely with no audit trail");
359
+ }
360
+ if (this.config.debug) {
361
+ console.log("โœ… PKP Data Extracted:");
362
+ console.log(` Token ID: ${createdPkpTokenId}`);
363
+ console.log(` ETH Address: ${createdPkpAddress}`);
364
+ console.log(` Public Key: ${createdPkpPublicKey}`);
365
+ }
366
+ // Step 2: Extract the validator signature from the PKP creation result
367
+ if (this.config.debug) {
368
+ console.log("๐Ÿ“ Step 2: Extracting validator signature from PKP creation");
369
+ }
370
+ let validatorSignature;
371
+ if (isServiceMode) {
372
+ // Service mode: Use signature from service response
373
+ if (pkpResult.pkpData?.validationSignature) {
374
+ validatorSignature = pkpResult.pkpData.validationSignature;
375
+ if (this.config.debug) {
376
+ console.log(`โœ… Service mode: Validator signature extracted: ${validatorSignature}`);
377
+ }
378
+ }
379
+ else if (pkpResult.validationResult?.signature) {
380
+ // Fallback to validation result
381
+ validatorSignature = pkpResult.validationResult.signature;
382
+ if (this.config.debug) {
383
+ console.log(`โœ… Service mode: Validator signature extracted (fallback): ${validatorSignature}`);
384
+ }
385
+ }
386
+ else {
387
+ throw new Error("Service response missing validation signature");
388
+ }
389
+ }
390
+ else {
391
+ // Standalone mode: Extract signature from PKP result
392
+ // The signature is in the signatures.pkpValidation.signature field
393
+ if (pkpResult.signatures &&
394
+ pkpResult.signatures.pkpValidation &&
395
+ pkpResult.signatures.pkpValidation.signature) {
396
+ validatorSignature = pkpResult.signatures.pkpValidation.signature;
397
+ if (this.config.debug) {
398
+ console.log(`โœ… Validator signature extracted: ${validatorSignature}`);
399
+ }
400
+ }
401
+ else if (pkpResult.validationResult &&
402
+ pkpResult.validationResult.signature) {
403
+ // Fallback to old structure
404
+ validatorSignature = pkpResult.validationResult.signature;
405
+ if (this.config.debug) {
406
+ console.log(`โœ… Validator signature extracted (fallback): ${validatorSignature}`);
407
+ }
408
+ }
409
+ else {
410
+ if (this.config.debug) {
411
+ console.log("PKP Result structure:", JSON.stringify(pkpResult, null, 2));
412
+ }
413
+ throw new Error("No validator signature found in PKP creation result");
414
+ }
415
+ }
416
+ const signature = validatorSignature;
417
+ if (this.config.debug) {
418
+ console.log(`โœ… PKP Signature ready: ${signature}`);
419
+ }
420
+ // Convert tokenId to bytes32 for contract calls
421
+ const pkpId = ethers_1.ethers.utils.hexZeroPad(ethers_1.ethers.utils.hexlify(createdPkpTokenId), 32);
422
+ // Step 3: Prepare and execute contract transaction
423
+ if (this.config.debug) {
424
+ console.log("๐Ÿ“ Step 3: Executing smart contract transaction");
425
+ }
426
+ // Check if wallet needs funding
427
+ const contractWalletAddress = await contractWallet.getAddress();
428
+ const walletBalance = await this.provider.getBalance(contractWalletAddress);
429
+ if (walletBalance.lt(ethers_1.ethers.utils.parseEther("0.1"))) {
430
+ if (this.config.debug) {
431
+ console.log(`๐Ÿ’ฐ Contract wallet balance low (${ethers_1.ethers.utils.formatEther(walletBalance)} ETH), funding may be required`);
432
+ }
433
+ }
434
+ // Prepare contract interaction using contract wallet
435
+ const contract = new ethers_1.ethers.Contract(this.config.contractAddresses.positionManager, POSITION_MANAGER_ABI, contractWallet // Use contract wallet for local network operations
436
+ );
437
+ // Convert collateralAmount from BTC to satoshis (8 decimal places)
438
+ // Note: collateralAmount should be in BTC units as per contract interface
439
+ const amount = ethers_1.ethers.utils.parseUnits(request.collateralAmount.toString(), 8); // Convert collateralAmount (BTC) to satoshis for contract call
440
+ // Convert collateralRatio to basis points (multiply by 100)
441
+ const requestedCollateralRatioBasisPoints = request.collateralRatio * 100;
442
+ if (this.config.debug) {
443
+ console.log("๐Ÿ“ Calling smart contract createPosition");
444
+ console.log(` PKP ID (bytes32): ${pkpId}`);
445
+ console.log(` Amount: ${amount}`);
446
+ console.log(` Requested Collateral Ratio: ${requestedCollateralRatioBasisPoints} (${request.collateralRatio}%)`);
447
+ console.log(` Selected Term: ${request.selectedTerm} months`);
448
+ console.log(` Signature: ${signature}`);
449
+ console.log(` Contract: ${this.config.contractAddresses.positionManager}`);
450
+ }
451
+ // Step 4: Call smart contract createPosition with validator signature and new parameters
452
+ const tx = await contract.createPosition(pkpId, amount, requestedCollateralRatioBasisPoints, request.selectedTerm, signature);
453
+ const receipt = await tx.wait();
454
+ if (this.config.debug) {
455
+ console.log("โœ… Position created successfully!");
456
+ console.log(` Transaction hash: ${tx.hash}`);
457
+ console.log(` Gas used: ${receipt.gasUsed}`);
458
+ console.log(` Collateral Ratio: ${request.collateralRatio}%`);
459
+ console.log(` Loan Term: ${request.selectedTerm} months`);
460
+ console.log(` Mode: ${isServiceMode ? "service" : "standalone"}`);
461
+ }
462
+ // Extract position ID from events
463
+ const positionCreatedEvent = receipt.logs?.find((log) => {
464
+ try {
465
+ const parsed = contract.interface.parseLog(log);
466
+ return parsed?.name === "PositionCreated";
467
+ }
468
+ catch {
469
+ return false;
470
+ }
471
+ });
472
+ let positionId;
473
+ if (positionCreatedEvent) {
474
+ const parsed = contract.interface.parseLog(positionCreatedEvent);
475
+ positionId = parsed?.args?.positionId || "";
476
+ }
477
+ else {
478
+ // Fallback: generate position ID
479
+ const borrowerAddress = await contractWallet.getAddress();
480
+ positionId = ethers_1.ethers.utils.keccak256(ethers_1.ethers.utils.solidityPack(["bytes32", "address", "uint256"], [pkpId, borrowerAddress, Date.now()]));
481
+ }
482
+ // Generate Bitcoin addresses from PKP public key (if available)
483
+ let btcAddresses;
484
+ if (createdPkpPublicKey) {
485
+ btcAddresses = (0, utils_1.getBitcoinAddressesFromPkp)(createdPkpPublicKey);
486
+ if (this.config.debug) {
487
+ console.log("๐Ÿ“ Step 4: Bitcoin addresses generated from PKP");
488
+ console.log(` Mainnet: ${btcAddresses.mainnet}`);
489
+ console.log(` Testnet: ${btcAddresses.testnet}`);
490
+ console.log(` Regtest: ${btcAddresses.regtest}`);
491
+ }
492
+ }
493
+ return {
494
+ success: true,
495
+ transactionHash: tx.hash,
496
+ positionId: positionId,
497
+ pkpData: {
498
+ tokenId: createdPkpTokenId,
499
+ publicKey: createdPkpPublicKey,
500
+ ethAddress: createdPkpAddress,
501
+ validationSignature: validatorSignature,
502
+ btcAddresses: btcAddresses || {
503
+ mainnet: "",
504
+ testnet: "",
505
+ regtest: "",
506
+ },
507
+ },
508
+ auditTrail: {
509
+ pkpCreation: pkpResult.auditTrail,
510
+ validation: { signatureGeneration: validatorSignature },
511
+ contractCall: {
512
+ transactionHash: tx.hash,
513
+ positionId: positionId,
514
+ },
515
+ },
516
+ };
517
+ }
518
+ /**
519
+ * Get single loan by vault address
520
+ */
521
+ async getLoan(vaultAddress) {
522
+ this.ensureInitialized();
523
+ try {
524
+ const contract = new ethers_1.ethers.Contract(this.config.contractAddresses.positionManager, POSITION_MANAGER_ABI, this.provider);
525
+ // For now, we'll need to implement position lookup by vault address
526
+ // This would require additional contract methods or event querying
527
+ // Placeholder implementation:
528
+ throw new Error("getLoan not implemented yet - requires contract method to lookup by vault address");
529
+ }
530
+ catch (error) {
531
+ if (this.config.debug) {
532
+ console.error("Failed to get loan:", error);
533
+ }
534
+ return null;
535
+ }
536
+ }
537
+ /**
538
+ * Get loans by various criteria
539
+ */
540
+ async getLoans(query) {
541
+ this.ensureInitialized();
542
+ try {
543
+ // This would require implementing query methods in the smart contract
544
+ // or querying events from the blockchain
545
+ throw new Error("getLoans not implemented yet - requires contract query methods");
546
+ }
547
+ catch (error) {
548
+ if (this.config.debug) {
549
+ console.error("Failed to get loans:", error);
550
+ }
551
+ return [];
552
+ }
553
+ }
554
+ /**
555
+ * Get all positions for a user
556
+ */
557
+ async getUserPositions(userAddress) {
558
+ this.ensureInitialized();
559
+ try {
560
+ if (this.config.debug) {
561
+ console.log(`๐Ÿ“‹ Getting positions for user: ${userAddress}`);
562
+ }
563
+ const contract = new ethers_1.ethers.Contract(this.config.contractAddresses.positionManager, POSITION_MANAGER_ABI, this.provider);
564
+ // Call contract method to get user positions
565
+ const positionIds = await contract.getUserPositions(userAddress);
566
+ if (this.config.debug) {
567
+ console.log(` Found ${positionIds.length} positions`);
568
+ }
569
+ // Get details for each position
570
+ const positions = [];
571
+ for (const positionId of positionIds) {
572
+ try {
573
+ const positionDetails = await contract.getPositionDetails(positionId);
574
+ positions.push({
575
+ positionId: positionDetails.positionId,
576
+ pkpId: positionDetails.pkpId,
577
+ vaultAddress: positionDetails.pkpId, // Using pkpId as vault address for now
578
+ borrower: positionDetails.borrower,
579
+ collateralAmount: parseInt(ethers_1.ethers.utils.formatUnits(positionDetails.btcAmount, 8)),
580
+ ucdDebt: parseInt(ethers_1.ethers.utils.formatEther(positionDetails.ucdDebt)),
581
+ collateralRatio: positionDetails.collateralRatio,
582
+ status: positionDetails.isActive ? "active" : "closed",
583
+ createdAt: positionDetails.createdAt.toNumber(),
584
+ lastUpdated: positionDetails.lastUpdated.toNumber(),
585
+ });
586
+ }
587
+ catch (error) {
588
+ if (this.config.debug) {
589
+ console.warn(`Failed to get details for position ${positionId}:`, error);
590
+ }
591
+ }
592
+ }
593
+ return positions;
594
+ }
595
+ catch (error) {
596
+ if (this.config.debug) {
597
+ console.error("Failed to get user positions:", error);
598
+ }
599
+ return [];
600
+ }
601
+ }
602
+ /**
603
+ * Get position details by PKP ID
604
+ */
605
+ async getPositionByPKP(pkpId) {
606
+ this.ensureInitialized();
607
+ try {
608
+ if (this.config.debug) {
609
+ console.log(`๐Ÿ” Getting position for PKP: ${pkpId}`);
610
+ }
611
+ const contract = new ethers_1.ethers.Contract(this.config.contractAddresses.positionManager, POSITION_MANAGER_ABI, this.provider);
612
+ // Convert PKP ID to bytes32 if needed
613
+ const pkpIdBytes32 = ethers_1.ethers.utils.hexZeroPad(pkpId, 32);
614
+ // Get position ID from PKP
615
+ const positionId = await contract.getPositionByPKP(pkpIdBytes32);
616
+ if (positionId === ethers_1.ethers.constants.HashZero) {
617
+ if (this.config.debug) {
618
+ console.log(" No position found for this PKP");
619
+ }
620
+ return null;
621
+ }
622
+ // Get position details
623
+ const positionDetails = await contract.getPositionDetails(positionId);
624
+ return {
625
+ positionId: positionDetails.positionId,
626
+ pkpId: positionDetails.pkpId,
627
+ vaultAddress: positionDetails.pkpId, // Using pkpId as vault address for now
628
+ borrower: positionDetails.borrower,
629
+ collateralAmount: parseInt(ethers_1.ethers.utils.formatUnits(positionDetails.btcAmount, 8)),
630
+ ucdDebt: parseInt(ethers_1.ethers.utils.formatEther(positionDetails.ucdDebt)),
631
+ collateralRatio: positionDetails.collateralRatio,
632
+ status: positionDetails.isActive ? "active" : "closed",
633
+ createdAt: positionDetails.createdAt.toNumber(),
634
+ lastUpdated: positionDetails.lastUpdated.toNumber(),
635
+ };
636
+ }
637
+ catch (error) {
638
+ if (this.config.debug) {
639
+ console.error("Failed to get position by PKP:", error);
640
+ }
641
+ return null;
642
+ }
643
+ }
644
+ /**
645
+ * Get PKP loan parameters (amount, collateral ratio, term) directly by PKP ID
646
+ * This uses the optimized storage for quick access to core loan parameters
647
+ */
648
+ async getPKPLoanParams(pkpId) {
649
+ this.ensureInitialized();
650
+ try {
651
+ if (this.config.debug) {
652
+ console.log(`๐Ÿ“Š Getting PKP loan parameters for PKP: ${pkpId}`);
653
+ }
654
+ const contract = new ethers_1.ethers.Contract(this.config.contractAddresses.positionManager, POSITION_MANAGER_ABI, this.provider);
655
+ // Convert PKP ID to bytes32 if needed
656
+ const pkpIdBytes32 = ethers_1.ethers.utils.hexZeroPad(pkpId, 32);
657
+ // Get loan parameters directly from PKP storage
658
+ const [amount, requestedCollateralRatio, selectedTerm] = await contract.getPKPLoanParams(pkpIdBytes32);
659
+ if (amount.eq(0)) {
660
+ if (this.config.debug) {
661
+ console.log(" No loan parameters found for this PKP");
662
+ }
663
+ return null;
664
+ }
665
+ const result = {
666
+ amount: parseInt(ethers_1.ethers.utils.formatUnits(amount, 8)),
667
+ requestedCollateralRatio: requestedCollateralRatio.toNumber() / 100, // Convert from basis points
668
+ selectedTerm: selectedTerm.toNumber(),
669
+ };
670
+ if (this.config.debug) {
671
+ console.log(` Amount: ${result.amount} BTC`);
672
+ console.log(` Collateral Ratio: ${result.requestedCollateralRatio}%`);
673
+ console.log(` Term: ${result.selectedTerm} months`);
674
+ }
675
+ return result;
676
+ }
677
+ catch (error) {
678
+ if (this.config.debug) {
679
+ console.error("Failed to get PKP loan parameters:", error);
680
+ }
681
+ return null;
682
+ }
683
+ }
684
+ /**
685
+ * Get complete PKP position data by PKP ID
686
+ * This provides all stored PKP position information in one call
687
+ */
688
+ async getPKPPositionData(pkpId) {
689
+ this.ensureInitialized();
690
+ try {
691
+ if (this.config.debug) {
692
+ console.log(`๐Ÿ“‹ Getting complete PKP position data for PKP: ${pkpId}`);
693
+ }
694
+ const contract = new ethers_1.ethers.Contract(this.config.contractAddresses.positionManager, POSITION_MANAGER_ABI, this.provider);
695
+ // Convert PKP ID to bytes32 if needed
696
+ const pkpIdBytes32 = ethers_1.ethers.utils.hexZeroPad(pkpId, 32);
697
+ // Get complete PKP position data
698
+ const pkpData = await contract.getPKPPositionData(pkpIdBytes32);
699
+ if (!pkpData.isActive && pkpData.amount.eq(0)) {
700
+ if (this.config.debug) {
701
+ console.log(" No position data found for this PKP");
702
+ }
703
+ return null;
704
+ }
705
+ return {
706
+ positionId: pkpData.positionId,
707
+ borrower: pkpData.borrower,
708
+ amount: parseInt(ethers_1.ethers.utils.formatUnits(pkpData.amount, 8)),
709
+ requestedCollateralRatio: pkpData.requestedCollateralRatio.toNumber() / 100, // Convert from basis points
710
+ selectedTerm: pkpData.selectedTerm.toNumber(),
711
+ createdAt: pkpData.createdAt.toNumber(),
712
+ isActive: pkpData.isActive,
713
+ };
714
+ }
715
+ catch (error) {
716
+ if (this.config.debug) {
717
+ console.error("Failed to get PKP position data:", error);
718
+ }
719
+ return null;
720
+ }
721
+ }
722
+ /**
723
+ * Request UCD minting for a loan
724
+ */
725
+ async requestMintUCD(request) {
726
+ this.ensureInitialized();
727
+ try {
728
+ if (this.config.debug) {
729
+ console.log("๐Ÿช™ Requesting UCD minting...");
730
+ }
731
+ // This would call the mintUCD method on the position manager contract
732
+ throw new Error("requestMintUCD not implemented yet - requires mintUCD contract method");
733
+ }
734
+ catch (error) {
735
+ throw new Error(`UCD minting failed: ${error instanceof Error ? error.message : error}`);
736
+ }
737
+ }
738
+ /**
739
+ * Request loan liquidation
740
+ */
741
+ async requestLiquidation(request) {
742
+ this.ensureInitialized();
743
+ try {
744
+ if (this.config.debug) {
745
+ console.log("โšก Requesting loan liquidation...");
746
+ }
747
+ // This would call the liquidatePosition method on the position manager contract
748
+ throw new Error("requestLiquidation not implemented yet - requires liquidatePosition contract method");
749
+ }
750
+ catch (error) {
751
+ throw new Error(`Liquidation failed: ${error instanceof Error ? error.message : error}`);
752
+ }
753
+ }
754
+ /**
755
+ * Cleanup SDK resources
756
+ */
757
+ async destroy() {
758
+ if (this.litOps) {
759
+ await this.litOps.disconnect();
760
+ this.litOps = null;
761
+ }
762
+ this.initialized = false;
763
+ if (this.config.debug) {
764
+ console.log("๐Ÿงน SDK cleanup complete");
765
+ }
766
+ }
767
+ ensureInitialized() {
768
+ if (!this.initialized) {
769
+ throw new Error("SDK not initialized. Call initialize() first.");
770
+ }
771
+ }
772
+ }
773
+ exports.DiamondHandsSDK = DiamondHandsSDK;