@sip-protocol/sdk 0.4.0 → 0.5.0

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.js CHANGED
@@ -32,10 +32,14 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  ATTESTATION_VERSION: () => ATTESTATION_VERSION,
34
34
  AptosStealthService: () => AptosStealthService,
35
+ AuditorKeyDerivation: () => AuditorKeyDerivation,
36
+ AuditorType: () => AuditorType,
35
37
  BaseWalletAdapter: () => BaseWalletAdapter,
36
38
  CHAIN_NUMERIC_IDS: () => CHAIN_NUMERIC_IDS,
37
39
  COSMOS_CHAIN_PREFIXES: () => CHAIN_PREFIXES,
38
40
  ComplianceManager: () => ComplianceManager,
41
+ ComplianceReporter: () => ComplianceReporter,
42
+ ConditionalDisclosure: () => ConditionalDisclosure,
39
43
  CosmosStealthService: () => CosmosStealthService,
40
44
  CryptoError: () => CryptoError,
41
45
  DEFAULT_THRESHOLD: () => DEFAULT_THRESHOLD,
@@ -87,6 +91,7 @@ __export(index_exports, {
87
91
  SmartRouter: () => SmartRouter,
88
92
  SolanaWalletAdapter: () => SolanaWalletAdapter,
89
93
  SwapStatus: () => SwapStatus,
94
+ ThresholdViewingKey: () => ThresholdViewingKey,
90
95
  Treasury: () => Treasury,
91
96
  TrezorWalletAdapter: () => TrezorWalletAdapter,
92
97
  ValidationError: () => ValidationError,
@@ -171,6 +176,7 @@ __export(index_exports, {
171
176
  generateEd25519StealthAddress: () => generateEd25519StealthAddress,
172
177
  generateEd25519StealthMetaAddress: () => generateEd25519StealthMetaAddress,
173
178
  generateIntentId: () => generateIntentId,
179
+ generatePdfReport: () => generatePdfReport,
174
180
  generateRandomBytes: () => generateRandomBytes,
175
181
  generateStealthAddress: () => generateStealthAddress,
176
182
  generateStealthMetaAddress: () => generateStealthMetaAddress,
@@ -303,6 +309,14 @@ var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
303
309
  ErrorCode2["SIGNATURE_FAILED"] = "SIP_3005";
304
310
  ErrorCode2["INVALID_CURVE_POINT"] = "SIP_3006";
305
311
  ErrorCode2["INVALID_SCALAR"] = "SIP_3007";
312
+ ErrorCode2["INVALID_KEY_SIZE"] = "SIP_3008";
313
+ ErrorCode2["INVALID_ENCRYPTED_DATA"] = "SIP_3009";
314
+ ErrorCode2["INVALID_COMMITMENT"] = "SIP_3010";
315
+ ErrorCode2["INVALID_TIME_LOCK"] = "SIP_3011";
316
+ ErrorCode2["INVALID_SHARE"] = "SIP_3012";
317
+ ErrorCode2["INVALID_THRESHOLD"] = "SIP_3013";
318
+ ErrorCode2["CRYPTO_OPERATION_FAILED"] = "SIP_3014";
319
+ ErrorCode2["INVALID_FORMAT"] = "SIP_3015";
306
320
  ErrorCode2["PROOF_FAILED"] = "SIP_4000";
307
321
  ErrorCode2["PROOF_GENERATION_FAILED"] = "SIP_4001";
308
322
  ErrorCode2["PROOF_VERIFICATION_FAILED"] = "SIP_4002";
@@ -8952,6 +8966,79 @@ var ComplianceManager = class _ComplianceManager {
8952
8966
  getAllReports() {
8953
8967
  return Array.from(this.reports.values());
8954
8968
  }
8969
+ // ─── Dashboard Data API ──────────────────────────────────────────────────────
8970
+ /**
8971
+ * Get list of auditors for dashboard UI
8972
+ *
8973
+ * Returns a simplified view of auditors with essential info.
8974
+ * Alias for getAllAuditors() but returns AuditorRegistration directly.
8975
+ *
8976
+ * @returns Array of auditor registrations
8977
+ */
8978
+ getAuditorList() {
8979
+ return this.getAllAuditors();
8980
+ }
8981
+ /**
8982
+ * Get pending disclosure requests for dashboard
8983
+ *
8984
+ * Returns disclosure requests waiting for approval/denial.
8985
+ * This is for disclosure REQUESTS, not disclosed transactions.
8986
+ *
8987
+ * @returns Array of pending disclosure requests
8988
+ */
8989
+ getPendingDisclosures() {
8990
+ return this.getPendingRequests();
8991
+ }
8992
+ /**
8993
+ * Get disclosure history for a specific auditor
8994
+ *
8995
+ * Returns all disclosed transactions that were shared with this auditor,
8996
+ * sorted by disclosure date (most recent first).
8997
+ *
8998
+ * @param auditorId - Auditor ID to get history for
8999
+ * @returns Array of disclosed transactions for this auditor
9000
+ */
9001
+ getDisclosureHistory(auditorId) {
9002
+ return this.getDisclosedTransactions(auditorId).sort((a, b) => b.disclosedAt - a.disclosedAt);
9003
+ }
9004
+ /**
9005
+ * Get compliance metrics for dashboard
9006
+ *
9007
+ * Calculates key compliance metrics including:
9008
+ * - Total auditors (active + inactive)
9009
+ * - Total disclosures made
9010
+ * - Pending disclosure requests
9011
+ * - Approval rate (approved / total resolved requests)
9012
+ * - Average processing time for disclosure requests
9013
+ *
9014
+ * @returns Compliance metrics object
9015
+ */
9016
+ getComplianceMetrics() {
9017
+ const allAuditors = this.getAllAuditors();
9018
+ const allDisclosures = this.getDisclosedTransactions();
9019
+ const pendingRequests = this.getPendingRequests();
9020
+ const allRequests = Array.from(this.disclosureRequests.values());
9021
+ const resolvedRequests = allRequests.filter((r) => r.status !== "pending");
9022
+ const approvedRequests = resolvedRequests.filter((r) => r.status === "approved");
9023
+ const approvalRate = resolvedRequests.length > 0 ? approvedRequests.length / resolvedRequests.length : 0;
9024
+ let averageProcessingTime;
9025
+ if (resolvedRequests.length > 0) {
9026
+ const totalProcessingTime = resolvedRequests.reduce((sum, req) => {
9027
+ if (req.resolvedAt) {
9028
+ return sum + (req.resolvedAt - req.requestedAt);
9029
+ }
9030
+ return sum;
9031
+ }, 0);
9032
+ averageProcessingTime = totalProcessingTime / resolvedRequests.length;
9033
+ }
9034
+ return {
9035
+ totalAuditors: allAuditors.length,
9036
+ totalDisclosures: allDisclosures.length,
9037
+ pendingDisclosures: pendingRequests.length,
9038
+ approvalRate,
9039
+ averageProcessingTime
9040
+ };
9041
+ }
8955
9042
  // ─── Audit Log ───────────────────────────────────────────────────────────────
8956
9043
  /**
8957
9044
  * Get audit log entries
@@ -9150,147 +9237,1767 @@ var ComplianceManager = class _ComplianceManager {
9150
9237
  highRisk,
9151
9238
  flaggedTransactions: flagged
9152
9239
  }
9153
- };
9154
- if (includeTransactions) {
9155
- data.transactions = transactions;
9240
+ };
9241
+ if (includeTransactions) {
9242
+ data.transactions = transactions;
9243
+ }
9244
+ return data;
9245
+ }
9246
+ generateCSV(transactions) {
9247
+ const headers = [
9248
+ "Transaction ID",
9249
+ "Disclosure ID",
9250
+ "Type",
9251
+ "Direction",
9252
+ "Token",
9253
+ "Amount",
9254
+ "Sender",
9255
+ "Recipient",
9256
+ "Chain",
9257
+ "Privacy Level",
9258
+ "Timestamp",
9259
+ "TX Hash",
9260
+ "Block",
9261
+ "Risk Score",
9262
+ "Purpose",
9263
+ "Memo"
9264
+ ];
9265
+ const rows = transactions.map((tx) => [
9266
+ tx.transactionId,
9267
+ tx.disclosureId,
9268
+ tx.type,
9269
+ tx.direction,
9270
+ tx.token.symbol,
9271
+ tx.amount.toString(),
9272
+ tx.sender,
9273
+ tx.recipient,
9274
+ tx.chain,
9275
+ tx.privacyLevel,
9276
+ new Date(tx.timestamp * 1e3).toISOString(),
9277
+ tx.txHash,
9278
+ tx.blockNumber.toString(),
9279
+ tx.riskScore?.toString() ?? "",
9280
+ tx.purpose ?? "",
9281
+ tx.memo ?? ""
9282
+ ]);
9283
+ const escapeForCSV = (val) => {
9284
+ let escaped = val;
9285
+ if (/^[=+\-@|\t]/.test(escaped)) {
9286
+ escaped = `'${escaped}`;
9287
+ }
9288
+ return `"${escaped.replace(/"/g, '""')}"`;
9289
+ };
9290
+ const csvRows = [headers, ...rows].map((row) => row.map(escapeForCSV).join(","));
9291
+ return csvRows.join("\n");
9292
+ }
9293
+ };
9294
+ function generateId(prefix) {
9295
+ return `${prefix}_${(0, import_utils20.bytesToHex)((0, import_utils20.randomBytes)(12))}`;
9296
+ }
9297
+ function validateRegisterAuditorParams(params) {
9298
+ if (!params.organization?.trim()) {
9299
+ throw new ValidationError(
9300
+ "organization is required",
9301
+ "organization",
9302
+ void 0,
9303
+ "SIP_2008" /* MISSING_REQUIRED */
9304
+ );
9305
+ }
9306
+ if (!params.contactName?.trim()) {
9307
+ throw new ValidationError(
9308
+ "contact name is required",
9309
+ "contactName",
9310
+ void 0,
9311
+ "SIP_2008" /* MISSING_REQUIRED */
9312
+ );
9313
+ }
9314
+ if (!params.contactEmail?.trim()) {
9315
+ throw new ValidationError(
9316
+ "contact email is required",
9317
+ "contactEmail",
9318
+ void 0,
9319
+ "SIP_2008" /* MISSING_REQUIRED */
9320
+ );
9321
+ }
9322
+ if (!params.publicKey?.trim()) {
9323
+ throw new ValidationError(
9324
+ "public key is required",
9325
+ "publicKey",
9326
+ void 0,
9327
+ "SIP_2008" /* MISSING_REQUIRED */
9328
+ );
9329
+ }
9330
+ if (!params.scope) {
9331
+ throw new ValidationError(
9332
+ "audit scope is required",
9333
+ "scope",
9334
+ void 0,
9335
+ "SIP_2008" /* MISSING_REQUIRED */
9336
+ );
9337
+ }
9338
+ }
9339
+ function validateReportParams(params) {
9340
+ if (!params.title?.trim()) {
9341
+ throw new ValidationError(
9342
+ "report title is required",
9343
+ "title",
9344
+ void 0,
9345
+ "SIP_2008" /* MISSING_REQUIRED */
9346
+ );
9347
+ }
9348
+ if (!params.type) {
9349
+ throw new ValidationError(
9350
+ "report type is required",
9351
+ "type",
9352
+ void 0,
9353
+ "SIP_2008" /* MISSING_REQUIRED */
9354
+ );
9355
+ }
9356
+ if (!params.format) {
9357
+ throw new ValidationError(
9358
+ "report format is required",
9359
+ "format",
9360
+ void 0,
9361
+ "SIP_2008" /* MISSING_REQUIRED */
9362
+ );
9363
+ }
9364
+ if (params.startDate === void 0 || params.startDate === null || params.endDate === void 0 || params.endDate === null) {
9365
+ throw new ValidationError(
9366
+ "date range is required",
9367
+ "dateRange",
9368
+ void 0,
9369
+ "SIP_2008" /* MISSING_REQUIRED */
9370
+ );
9371
+ }
9372
+ if (params.startDate >= params.endDate) {
9373
+ throw new ValidationError(
9374
+ "start date must be before end date",
9375
+ "dateRange",
9376
+ void 0,
9377
+ "SIP_2001" /* INVALID_INPUT */
9378
+ );
9379
+ }
9380
+ }
9381
+
9382
+ // src/compliance/reports.ts
9383
+ var import_sha25615 = require("@noble/hashes/sha256");
9384
+ var import_utils21 = require("@noble/hashes/utils");
9385
+
9386
+ // src/compliance/pdf.ts
9387
+ function generatePdfReport(report, options = {}) {
9388
+ const {
9389
+ title = "SIP Protocol Audit Report",
9390
+ organization = "",
9391
+ includeTransactions = true,
9392
+ maxTransactions = 100
9393
+ } = options;
9394
+ const content = buildPdfContent(report, {
9395
+ title,
9396
+ organization,
9397
+ includeTransactions,
9398
+ maxTransactions
9399
+ });
9400
+ return generatePdfBinary(content, title);
9401
+ }
9402
+ function buildPdfContent(report, options) {
9403
+ const lines = [];
9404
+ lines.push(options.title);
9405
+ if (options.organization && options.organization.trim() !== "") {
9406
+ lines.push(`Organization: ${options.organization}`);
9407
+ }
9408
+ lines.push(`Report ID: ${report.reportId}`);
9409
+ lines.push(
9410
+ `Generated: ${report.generatedAt.toISOString().replace("T", " ").slice(0, 19)} UTC`
9411
+ );
9412
+ lines.push("");
9413
+ lines.push("Report Period");
9414
+ lines.push(` Start: ${formatDate(report.period.start)}`);
9415
+ lines.push(` End: ${formatDate(report.period.end)}`);
9416
+ lines.push("");
9417
+ lines.push("Summary Statistics");
9418
+ lines.push(` Total Transactions: ${report.summary.transactionCount}`);
9419
+ lines.push(
9420
+ ` Total Volume: ${formatBigInt(report.summary.totalVolume)}`
9421
+ );
9422
+ lines.push(
9423
+ ` Unique Counterparties: ${report.summary.uniqueCounterparties}`
9424
+ );
9425
+ lines.push("");
9426
+ if (options.includeTransactions && report.transactions.length > 0) {
9427
+ lines.push("Transaction Details");
9428
+ lines.push("");
9429
+ const txToShow = Math.min(
9430
+ report.transactions.length,
9431
+ options.maxTransactions
9432
+ );
9433
+ for (let i = 0; i < txToShow; i++) {
9434
+ const tx = report.transactions[i];
9435
+ lines.push(`Transaction ${i + 1}/${report.transactions.length}`);
9436
+ lines.push(` ID: ${tx.id}`);
9437
+ lines.push(` Sender: ${truncateAddress(tx.sender)}`);
9438
+ lines.push(` Recipient: ${truncateAddress(tx.recipient)}`);
9439
+ lines.push(` Amount: ${formatAmount(tx.amount)}`);
9440
+ lines.push(
9441
+ ` Timestamp: ${formatTimestamp(tx.timestamp)}`
9442
+ );
9443
+ if (tx.txHash) {
9444
+ lines.push(` Tx Hash: ${truncateHash(tx.txHash)}`);
9445
+ }
9446
+ if (tx.metadata && Object.keys(tx.metadata).length > 0) {
9447
+ lines.push(` Metadata: ${JSON.stringify(tx.metadata)}`);
9448
+ }
9449
+ lines.push("");
9450
+ }
9451
+ if (report.transactions.length > options.maxTransactions) {
9452
+ lines.push(
9453
+ `... and ${report.transactions.length - options.maxTransactions} more transactions`
9454
+ );
9455
+ lines.push("");
9456
+ }
9457
+ }
9458
+ lines.push("---");
9459
+ lines.push(
9460
+ "This report was generated by SIP Protocol compliance tools."
9461
+ );
9462
+ lines.push("For verification, please contact your compliance officer.");
9463
+ return lines.join("\n");
9464
+ }
9465
+ function generatePdfBinary(content, title) {
9466
+ const objects = [];
9467
+ objects.push(
9468
+ "1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj"
9469
+ );
9470
+ objects.push(
9471
+ "2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj"
9472
+ );
9473
+ objects.push(
9474
+ "3 0 obj\n<< /Type /Page /Parent 2 0 R /Resources 4 0 R /MediaBox [0 0 612 792] /Contents 5 0 R >>\nendobj"
9475
+ );
9476
+ objects.push(
9477
+ "4 0 obj\n<< /Font << /F1 << /Type /Font /Subtype /Type1 /BaseFont /Courier >> >> >>\nendobj"
9478
+ );
9479
+ const contentStream = buildContentStream(content);
9480
+ objects.push(
9481
+ `5 0 obj
9482
+ << /Length ${contentStream.length} >>
9483
+ stream
9484
+ ${contentStream}
9485
+ endstream
9486
+ endobj`
9487
+ );
9488
+ const now = /* @__PURE__ */ new Date();
9489
+ const pdfDate = formatPdfDate(now);
9490
+ objects.push(
9491
+ `6 0 obj
9492
+ << /Title (${title}) /Author (SIP Protocol) /Creator (SIP SDK) /CreationDate (${pdfDate}) >>
9493
+ endobj`
9494
+ );
9495
+ const pdfParts = [];
9496
+ pdfParts.push("%PDF-1.4\n%\xE2\xE3\xCF\xD3\n");
9497
+ const xrefOffsets = [0];
9498
+ let currentOffset = pdfParts.join("").length;
9499
+ for (const obj of objects) {
9500
+ xrefOffsets.push(currentOffset);
9501
+ pdfParts.push(obj + "\n");
9502
+ currentOffset = pdfParts.join("").length;
9503
+ }
9504
+ const xrefStart = currentOffset;
9505
+ pdfParts.push("xref\n");
9506
+ pdfParts.push(`0 ${xrefOffsets.length}
9507
+ `);
9508
+ for (let i = 0; i < xrefOffsets.length; i++) {
9509
+ if (i === 0) {
9510
+ pdfParts.push("0000000000 65535 f \n");
9511
+ } else {
9512
+ const offset = String(xrefOffsets[i]).padStart(10, "0");
9513
+ pdfParts.push(`${offset} 00000 n
9514
+ `);
9515
+ }
9516
+ }
9517
+ pdfParts.push("trailer\n");
9518
+ pdfParts.push(
9519
+ `<< /Size ${xrefOffsets.length} /Root 1 0 R /Info 6 0 R >>
9520
+ `
9521
+ );
9522
+ pdfParts.push("startxref\n");
9523
+ pdfParts.push(`${xrefStart}
9524
+ `);
9525
+ pdfParts.push("%%EOF");
9526
+ const pdfString = pdfParts.join("");
9527
+ const encoder = new TextEncoder();
9528
+ return encoder.encode(pdfString);
9529
+ }
9530
+ function buildContentStream(text) {
9531
+ const lines = text.split("\n");
9532
+ const commands = [];
9533
+ commands.push("BT");
9534
+ commands.push("/F1 10 Tf");
9535
+ commands.push("12 TL");
9536
+ let y = 742;
9537
+ for (const line of lines) {
9538
+ commands.push(`50 ${y} Td`);
9539
+ const escaped = escapePdfString(line);
9540
+ commands.push(`(${escaped}) Tj`);
9541
+ y -= 12;
9542
+ if (y < 50) {
9543
+ break;
9544
+ }
9545
+ }
9546
+ commands.push("ET");
9547
+ return commands.join("\n");
9548
+ }
9549
+ function escapePdfString(str) {
9550
+ return str.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)").replace(/\r/g, "\\r").replace(/\n/g, "\\n");
9551
+ }
9552
+ function formatPdfDate(date) {
9553
+ const year = date.getUTCFullYear();
9554
+ const month = String(date.getUTCMonth() + 1).padStart(2, "0");
9555
+ const day = String(date.getUTCDate()).padStart(2, "0");
9556
+ const hour = String(date.getUTCHours()).padStart(2, "0");
9557
+ const minute = String(date.getUTCMinutes()).padStart(2, "0");
9558
+ const second = String(date.getUTCSeconds()).padStart(2, "0");
9559
+ return `D:${year}${month}${day}${hour}${minute}${second}Z`;
9560
+ }
9561
+ function formatDate(date) {
9562
+ return date.toISOString().split("T")[0];
9563
+ }
9564
+ function formatBigInt(value) {
9565
+ return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
9566
+ }
9567
+ function formatAmount(amount) {
9568
+ try {
9569
+ const num = BigInt(amount);
9570
+ return formatBigInt(num);
9571
+ } catch {
9572
+ return amount;
9573
+ }
9574
+ }
9575
+ function formatTimestamp(timestamp) {
9576
+ const date = new Date(timestamp * 1e3);
9577
+ return date.toISOString().replace("T", " ").slice(0, 19) + " UTC";
9578
+ }
9579
+ function truncateAddress(address) {
9580
+ if (address.length <= 12) {
9581
+ return address;
9582
+ }
9583
+ return `${address.slice(0, 6)}...${address.slice(-4)}`;
9584
+ }
9585
+ function truncateHash(hash2) {
9586
+ if (hash2.length <= 16) {
9587
+ return hash2;
9588
+ }
9589
+ return `${hash2.slice(0, 8)}...${hash2.slice(-8)}`;
9590
+ }
9591
+
9592
+ // src/compliance/reports.ts
9593
+ var ComplianceReporter = class {
9594
+ /**
9595
+ * Generate an audit report from encrypted transactions
9596
+ *
9597
+ * Decrypts transactions using the provided viewing key, filters by date range,
9598
+ * and generates a comprehensive report with summary statistics.
9599
+ *
9600
+ * @param params - Report generation parameters
9601
+ * @returns Audit report with decrypted transactions and statistics
9602
+ * @throws {ValidationError} If parameters are invalid
9603
+ * @throws {CryptoError} If decryption fails
9604
+ */
9605
+ async generateAuditReport(params) {
9606
+ this.validateParams(params);
9607
+ const viewingKey = this.normalizeViewingKey(params.viewingKey);
9608
+ const decryptedTransactions = this.decryptTransactions(
9609
+ params.transactions,
9610
+ viewingKey,
9611
+ params.startDate,
9612
+ params.endDate
9613
+ );
9614
+ const summary = this.calculateSummary(decryptedTransactions);
9615
+ const period = this.determinePeriod(
9616
+ decryptedTransactions,
9617
+ params.startDate,
9618
+ params.endDate
9619
+ );
9620
+ const reportId = `audit_${generateRandomBytes(16).slice(2)}`;
9621
+ return {
9622
+ reportId,
9623
+ generatedAt: /* @__PURE__ */ new Date(),
9624
+ period,
9625
+ transactions: decryptedTransactions,
9626
+ summary
9627
+ };
9628
+ }
9629
+ /**
9630
+ * Validate report generation parameters
9631
+ */
9632
+ validateParams(params) {
9633
+ if (!params.viewingKey) {
9634
+ throw new ValidationError(
9635
+ "viewingKey is required",
9636
+ "viewingKey",
9637
+ void 0,
9638
+ "SIP_2008" /* MISSING_REQUIRED */
9639
+ );
9640
+ }
9641
+ if (!params.transactions) {
9642
+ throw new ValidationError(
9643
+ "transactions array is required",
9644
+ "transactions",
9645
+ void 0,
9646
+ "SIP_2008" /* MISSING_REQUIRED */
9647
+ );
9648
+ }
9649
+ if (!Array.isArray(params.transactions)) {
9650
+ throw new ValidationError(
9651
+ "transactions must be an array",
9652
+ "transactions",
9653
+ { received: typeof params.transactions },
9654
+ "SIP_2001" /* INVALID_INPUT */
9655
+ );
9656
+ }
9657
+ if (params.format !== "json" && params.format !== "pdf") {
9658
+ throw new ValidationError(
9659
+ "only JSON and PDF formats are supported",
9660
+ "format",
9661
+ { received: params.format },
9662
+ "SIP_2001" /* INVALID_INPUT */
9663
+ );
9664
+ }
9665
+ if (params.startDate && params.endDate) {
9666
+ if (params.startDate > params.endDate) {
9667
+ throw new ValidationError(
9668
+ "startDate must be before endDate",
9669
+ "startDate",
9670
+ {
9671
+ startDate: params.startDate.toISOString(),
9672
+ endDate: params.endDate.toISOString()
9673
+ },
9674
+ "SIP_2001" /* INVALID_INPUT */
9675
+ );
9676
+ }
9677
+ }
9678
+ }
9679
+ /**
9680
+ * Normalize viewing key to ViewingKey object
9681
+ */
9682
+ normalizeViewingKey(viewingKey) {
9683
+ if (typeof viewingKey === "string") {
9684
+ const keyHex = viewingKey.startsWith("0x") ? viewingKey.slice(2) : viewingKey;
9685
+ const keyBytes = (0, import_utils21.hexToBytes)(keyHex);
9686
+ const hashBytes = (0, import_sha25615.sha256)(keyBytes);
9687
+ return {
9688
+ key: `0x${keyHex}`,
9689
+ path: "m/0",
9690
+ hash: `0x${(0, import_utils21.bytesToHex)(hashBytes)}`
9691
+ };
9692
+ }
9693
+ return viewingKey;
9694
+ }
9695
+ /**
9696
+ * Decrypt transactions and filter by date range
9697
+ */
9698
+ decryptTransactions(encrypted, viewingKey, startDate, endDate) {
9699
+ const decrypted = [];
9700
+ const errors = [];
9701
+ for (let i = 0; i < encrypted.length; i++) {
9702
+ try {
9703
+ const txData = decryptWithViewing(encrypted[i], viewingKey);
9704
+ if (startDate || endDate) {
9705
+ const txDate = new Date(txData.timestamp * 1e3);
9706
+ if (startDate && txDate < startDate) {
9707
+ continue;
9708
+ }
9709
+ if (endDate && txDate > endDate) {
9710
+ continue;
9711
+ }
9712
+ }
9713
+ decrypted.push({
9714
+ id: `tx_${i}`,
9715
+ sender: txData.sender,
9716
+ recipient: txData.recipient,
9717
+ amount: txData.amount,
9718
+ timestamp: txData.timestamp
9719
+ });
9720
+ } catch (error) {
9721
+ errors.push({ index: i, error });
9722
+ }
9723
+ }
9724
+ if (decrypted.length === 0 && encrypted.length > 0) {
9725
+ throw new CryptoError(
9726
+ `Failed to decrypt any transactions. First error: ${errors[0]?.error.message}`,
9727
+ "SIP_3002" /* DECRYPTION_FAILED */,
9728
+ {
9729
+ context: {
9730
+ totalTransactions: encrypted.length,
9731
+ failedCount: errors.length
9732
+ }
9733
+ }
9734
+ );
9735
+ }
9736
+ return decrypted;
9737
+ }
9738
+ /**
9739
+ * Calculate summary statistics
9740
+ */
9741
+ calculateSummary(transactions) {
9742
+ let totalVolume = 0n;
9743
+ const counterparties = /* @__PURE__ */ new Set();
9744
+ for (const tx of transactions) {
9745
+ try {
9746
+ const amount = BigInt(tx.amount);
9747
+ totalVolume += amount;
9748
+ } catch (error) {
9749
+ console.warn(`Skipping invalid amount in transaction ${tx.id}: ${tx.amount}`);
9750
+ }
9751
+ counterparties.add(tx.sender);
9752
+ counterparties.add(tx.recipient);
9753
+ }
9754
+ return {
9755
+ totalVolume,
9756
+ transactionCount: transactions.length,
9757
+ uniqueCounterparties: counterparties.size
9758
+ };
9759
+ }
9760
+ /**
9761
+ * Determine report period from transactions and params
9762
+ */
9763
+ determinePeriod(transactions, startDate, endDate) {
9764
+ if (startDate && endDate) {
9765
+ return { start: startDate, end: endDate };
9766
+ }
9767
+ if (transactions.length === 0) {
9768
+ const now = /* @__PURE__ */ new Date();
9769
+ return { start: now, end: now };
9770
+ }
9771
+ let minTimestamp = transactions[0].timestamp;
9772
+ let maxTimestamp = transactions[0].timestamp;
9773
+ for (const tx of transactions) {
9774
+ if (tx.timestamp < minTimestamp) {
9775
+ minTimestamp = tx.timestamp;
9776
+ }
9777
+ if (tx.timestamp > maxTimestamp) {
9778
+ maxTimestamp = tx.timestamp;
9779
+ }
9780
+ }
9781
+ return {
9782
+ start: startDate || new Date(minTimestamp * 1e3),
9783
+ end: endDate || new Date(maxTimestamp * 1e3)
9784
+ };
9785
+ }
9786
+ /**
9787
+ * Export audit report to PDF format
9788
+ *
9789
+ * Generates a professionally formatted PDF document from an audit report.
9790
+ * Works in both Node.js and browser environments.
9791
+ *
9792
+ * @param report - The audit report to export
9793
+ * @param options - PDF export options
9794
+ * @returns PDF document as Uint8Array
9795
+ *
9796
+ * @example
9797
+ * ```typescript
9798
+ * const reporter = new ComplianceReporter()
9799
+ * const report = await reporter.generateAuditReport({...})
9800
+ *
9801
+ * const pdfBytes = reporter.exportToPdf(report, {
9802
+ * title: 'Q1 2025 Audit Report',
9803
+ * organization: 'ACME Corp',
9804
+ * })
9805
+ *
9806
+ * // Save to file (Node.js)
9807
+ * fs.writeFileSync('report.pdf', pdfBytes)
9808
+ * ```
9809
+ */
9810
+ exportToPdf(report, options) {
9811
+ return generatePdfReport(report, options);
9812
+ }
9813
+ /**
9814
+ * Export transactions to regulatory compliance formats
9815
+ *
9816
+ * Decrypts and exports transactions in formats required by regulators:
9817
+ * - FATF: Financial Action Task Force Travel Rule format
9818
+ * - FINCEN: FinCEN Suspicious Activity Report (SAR) format
9819
+ * - CSV: Generic comma-separated values format
9820
+ *
9821
+ * @param params - Export parameters
9822
+ * @returns Regulatory export in the specified format
9823
+ * @throws {ValidationError} If parameters are invalid
9824
+ * @throws {CryptoError} If decryption fails
9825
+ *
9826
+ * @example
9827
+ * ```typescript
9828
+ * const reporter = new ComplianceReporter()
9829
+ *
9830
+ * // FATF Travel Rule export
9831
+ * const fatfExport = await reporter.exportForRegulator({
9832
+ * viewingKey: myViewingKey,
9833
+ * transactions: encryptedTxs,
9834
+ * jurisdiction: 'EU',
9835
+ * format: 'FATF',
9836
+ * currency: 'EUR',
9837
+ * })
9838
+ *
9839
+ * // FINCEN SAR export (US only)
9840
+ * const fincenExport = await reporter.exportForRegulator({
9841
+ * viewingKey: myViewingKey,
9842
+ * transactions: suspiciousTxs,
9843
+ * jurisdiction: 'US',
9844
+ * format: 'FINCEN',
9845
+ * })
9846
+ *
9847
+ * // CSV export
9848
+ * const csvExport = await reporter.exportForRegulator({
9849
+ * viewingKey: myViewingKey,
9850
+ * transactions: encryptedTxs,
9851
+ * jurisdiction: 'SG',
9852
+ * format: 'CSV',
9853
+ * })
9854
+ * ```
9855
+ */
9856
+ async exportForRegulator(params) {
9857
+ this.validateRegulatoryParams(params);
9858
+ const viewingKey = this.normalizeViewingKey(params.viewingKey);
9859
+ const decryptedTransactions = this.decryptTransactions(
9860
+ params.transactions,
9861
+ viewingKey,
9862
+ params.startDate,
9863
+ params.endDate
9864
+ );
9865
+ const reportId = `reg_${generateRandomBytes(16).slice(2)}`;
9866
+ switch (params.format) {
9867
+ case "FATF":
9868
+ return this.exportToFATF(
9869
+ reportId,
9870
+ decryptedTransactions,
9871
+ params.jurisdiction,
9872
+ params.currency || "USD"
9873
+ );
9874
+ case "FINCEN":
9875
+ return this.exportToFINCEN(
9876
+ reportId,
9877
+ decryptedTransactions,
9878
+ params.startDate,
9879
+ params.endDate,
9880
+ params.currency || "USD"
9881
+ );
9882
+ case "CSV":
9883
+ return this.exportToCSV(
9884
+ reportId,
9885
+ decryptedTransactions,
9886
+ params.jurisdiction,
9887
+ params.currency || "USD"
9888
+ );
9889
+ default:
9890
+ throw new ValidationError(
9891
+ `unsupported format: ${params.format}`,
9892
+ "format",
9893
+ { received: params.format },
9894
+ "SIP_2001" /* INVALID_INPUT */
9895
+ );
9896
+ }
9897
+ }
9898
+ /**
9899
+ * Validate regulatory export parameters
9900
+ */
9901
+ validateRegulatoryParams(params) {
9902
+ if (!params.viewingKey) {
9903
+ throw new ValidationError(
9904
+ "viewingKey is required",
9905
+ "viewingKey",
9906
+ void 0,
9907
+ "SIP_2008" /* MISSING_REQUIRED */
9908
+ );
9909
+ }
9910
+ if (!params.transactions) {
9911
+ throw new ValidationError(
9912
+ "transactions array is required",
9913
+ "transactions",
9914
+ void 0,
9915
+ "SIP_2008" /* MISSING_REQUIRED */
9916
+ );
9917
+ }
9918
+ if (!Array.isArray(params.transactions)) {
9919
+ throw new ValidationError(
9920
+ "transactions must be an array",
9921
+ "transactions",
9922
+ { received: typeof params.transactions },
9923
+ "SIP_2001" /* INVALID_INPUT */
9924
+ );
9925
+ }
9926
+ if (!params.jurisdiction) {
9927
+ throw new ValidationError(
9928
+ "jurisdiction is required",
9929
+ "jurisdiction",
9930
+ void 0,
9931
+ "SIP_2008" /* MISSING_REQUIRED */
9932
+ );
9933
+ }
9934
+ const validJurisdictions = ["US", "EU", "UK", "SG"];
9935
+ if (!validJurisdictions.includes(params.jurisdiction)) {
9936
+ throw new ValidationError(
9937
+ `invalid jurisdiction. Must be one of: ${validJurisdictions.join(", ")}`,
9938
+ "jurisdiction",
9939
+ { received: params.jurisdiction },
9940
+ "SIP_2001" /* INVALID_INPUT */
9941
+ );
9942
+ }
9943
+ if (!params.format) {
9944
+ throw new ValidationError(
9945
+ "format is required",
9946
+ "format",
9947
+ void 0,
9948
+ "SIP_2008" /* MISSING_REQUIRED */
9949
+ );
9950
+ }
9951
+ const validFormats = ["FATF", "FINCEN", "CSV"];
9952
+ if (!validFormats.includes(params.format)) {
9953
+ throw new ValidationError(
9954
+ `invalid format. Must be one of: ${validFormats.join(", ")}`,
9955
+ "format",
9956
+ { received: params.format },
9957
+ "SIP_2001" /* INVALID_INPUT */
9958
+ );
9959
+ }
9960
+ if (params.format === "FINCEN" && params.jurisdiction !== "US") {
9961
+ throw new ValidationError(
9962
+ "FINCEN format is only available for US jurisdiction",
9963
+ "format",
9964
+ { jurisdiction: params.jurisdiction, format: params.format },
9965
+ "SIP_2001" /* INVALID_INPUT */
9966
+ );
9967
+ }
9968
+ if (params.startDate && params.endDate) {
9969
+ if (params.startDate > params.endDate) {
9970
+ throw new ValidationError(
9971
+ "startDate must be before endDate",
9972
+ "startDate",
9973
+ {
9974
+ startDate: params.startDate.toISOString(),
9975
+ endDate: params.endDate.toISOString()
9976
+ },
9977
+ "SIP_2001" /* INVALID_INPUT */
9978
+ );
9979
+ }
9980
+ }
9981
+ }
9982
+ /**
9983
+ * Export to FATF Travel Rule format
9984
+ */
9985
+ exportToFATF(reportId, transactions, jurisdiction, currency) {
9986
+ const fatfTransactions = transactions.map((tx) => ({
9987
+ originatorAccount: tx.sender,
9988
+ beneficiaryAccount: tx.recipient,
9989
+ amount: tx.amount,
9990
+ currency,
9991
+ transactionRef: tx.id,
9992
+ timestamp: new Date(tx.timestamp * 1e3).toISOString()
9993
+ }));
9994
+ return {
9995
+ reportId,
9996
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
9997
+ jurisdiction,
9998
+ transactions: fatfTransactions
9999
+ };
10000
+ }
10001
+ /**
10002
+ * Export to FINCEN SAR format
10003
+ */
10004
+ exportToFINCEN(reportId, transactions, startDate, endDate, currency = "USD") {
10005
+ let totalAmount = 0n;
10006
+ for (const tx of transactions) {
10007
+ try {
10008
+ totalAmount += BigInt(tx.amount);
10009
+ } catch (error) {
10010
+ }
10011
+ }
10012
+ const period = this.determinePeriod(transactions, startDate, endDate);
10013
+ const fincenTransactions = transactions.map((tx) => ({
10014
+ transactionDate: new Date(tx.timestamp * 1e3).toISOString(),
10015
+ amount: tx.amount,
10016
+ currency,
10017
+ narrativeSummary: `Transfer from ${tx.sender} to ${tx.recipient}`,
10018
+ transactionRef: tx.id,
10019
+ parties: {
10020
+ sender: tx.sender,
10021
+ recipient: tx.recipient
10022
+ }
10023
+ }));
10024
+ return {
10025
+ reportId,
10026
+ filingType: "SAR",
10027
+ reportDate: (/* @__PURE__ */ new Date()).toISOString(),
10028
+ jurisdiction: "US",
10029
+ summary: {
10030
+ transactionCount: transactions.length,
10031
+ totalAmount: totalAmount.toString(),
10032
+ period: {
10033
+ start: period.start.toISOString(),
10034
+ end: period.end.toISOString()
10035
+ }
10036
+ },
10037
+ transactions: fincenTransactions
10038
+ };
10039
+ }
10040
+ /**
10041
+ * Export to CSV format
10042
+ */
10043
+ exportToCSV(reportId, transactions, jurisdiction, currency) {
10044
+ const headers = [
10045
+ "Transaction ID",
10046
+ "Timestamp",
10047
+ "Sender",
10048
+ "Recipient",
10049
+ "Amount",
10050
+ "Currency"
10051
+ ];
10052
+ const rows = transactions.map((tx) => [
10053
+ tx.id,
10054
+ new Date(tx.timestamp * 1e3).toISOString(),
10055
+ tx.sender,
10056
+ tx.recipient,
10057
+ tx.amount,
10058
+ currency
10059
+ ]);
10060
+ return {
10061
+ reportId,
10062
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
10063
+ jurisdiction,
10064
+ headers,
10065
+ rows
10066
+ };
10067
+ }
10068
+ };
10069
+
10070
+ // src/compliance/conditional.ts
10071
+ var import_sha25616 = require("@noble/hashes/sha256");
10072
+ var import_utils22 = require("@noble/hashes/utils");
10073
+ var import_chacha3 = require("@noble/ciphers/chacha.js");
10074
+ var ConditionalDisclosure = class {
10075
+ /**
10076
+ * Create a time-locked disclosure
10077
+ *
10078
+ * Encrypts the viewing key with a deterministic key derived from
10079
+ * the commitment and reveal time. The key can only be reconstructed
10080
+ * after the specified time/block height.
10081
+ *
10082
+ * @param params - Time-lock parameters
10083
+ * @returns Time-lock result with encrypted key
10084
+ * @throws {ValidationError} If parameters are invalid
10085
+ * @throws {CryptoError} If encryption fails
10086
+ */
10087
+ createTimeLocked(params) {
10088
+ if (!params.viewingKey || !params.viewingKey.startsWith("0x")) {
10089
+ throw new ValidationError(
10090
+ "Invalid viewing key format",
10091
+ "viewingKey",
10092
+ { viewingKey: params.viewingKey },
10093
+ "SIP_2006" /* INVALID_KEY */
10094
+ );
10095
+ }
10096
+ if (!params.commitment || !params.commitment.startsWith("0x")) {
10097
+ throw new ValidationError(
10098
+ "Invalid commitment format",
10099
+ "commitment",
10100
+ { commitment: params.commitment },
10101
+ "SIP_3010" /* INVALID_COMMITMENT */
10102
+ );
10103
+ }
10104
+ let revealAfterSeconds;
10105
+ let type;
10106
+ if (params.revealAfter instanceof Date) {
10107
+ revealAfterSeconds = Math.floor(params.revealAfter.getTime() / 1e3);
10108
+ type = "timestamp";
10109
+ } else if (typeof params.revealAfter === "number") {
10110
+ if (params.revealAfter > 1e10) {
10111
+ revealAfterSeconds = Math.floor(params.revealAfter / 1e3);
10112
+ type = "timestamp";
10113
+ } else {
10114
+ revealAfterSeconds = params.revealAfter;
10115
+ type = "blockheight";
10116
+ }
10117
+ if (revealAfterSeconds <= 0) {
10118
+ throw new ValidationError(
10119
+ "Reveal time/block height must be positive",
10120
+ "revealAfter",
10121
+ { revealAfter: revealAfterSeconds },
10122
+ "SIP_3011" /* INVALID_TIME_LOCK */
10123
+ );
10124
+ }
10125
+ } else {
10126
+ throw new ValidationError(
10127
+ "Invalid revealAfter type (must be Date or number)",
10128
+ "revealAfter",
10129
+ { revealAfter: params.revealAfter },
10130
+ "SIP_3011" /* INVALID_TIME_LOCK */
10131
+ );
10132
+ }
10133
+ try {
10134
+ const encryptionKey = this._deriveEncryptionKey(
10135
+ params.commitment,
10136
+ revealAfterSeconds
10137
+ );
10138
+ const nonce = (0, import_utils22.randomBytes)(24);
10139
+ const viewingKeyBytes = (0, import_utils22.hexToBytes)(params.viewingKey.slice(2));
10140
+ const cipher = (0, import_chacha3.xchacha20poly1305)(encryptionKey, nonce);
10141
+ const encryptedKey = cipher.encrypt(viewingKeyBytes);
10142
+ const commitmentData = new Uint8Array([
10143
+ ...viewingKeyBytes,
10144
+ ...this._numberToBytes(revealAfterSeconds)
10145
+ ]);
10146
+ const commitmentHash = (0, import_sha25616.sha256)(commitmentData);
10147
+ return {
10148
+ encryptedKey: "0x" + (0, import_utils22.bytesToHex)(encryptedKey),
10149
+ nonce: "0x" + (0, import_utils22.bytesToHex)(nonce),
10150
+ revealAfter: revealAfterSeconds,
10151
+ verificationCommitment: "0x" + (0, import_utils22.bytesToHex)(commitmentHash),
10152
+ encryptionCommitment: params.commitment,
10153
+ type
10154
+ };
10155
+ } catch (error) {
10156
+ if (error instanceof ValidationError) {
10157
+ throw error;
10158
+ }
10159
+ throw new CryptoError(
10160
+ "Failed to create time-locked disclosure",
10161
+ "SIP_3001" /* ENCRYPTION_FAILED */,
10162
+ {
10163
+ cause: error instanceof Error ? error : void 0,
10164
+ operation: "createTimeLocked"
10165
+ }
10166
+ );
10167
+ }
10168
+ }
10169
+ /**
10170
+ * Check if a time-lock is unlocked and retrieve the viewing key
10171
+ *
10172
+ * @param timeLock - Time-lock result to check
10173
+ * @param currentTimeOrBlock - Current time (Date/number) or block height (number)
10174
+ * @returns Unlock result with viewing key if unlocked
10175
+ * @throws {ValidationError} If time-lock format is invalid
10176
+ * @throws {CryptoError} If decryption fails
10177
+ */
10178
+ checkUnlocked(timeLock, currentTimeOrBlock) {
10179
+ if (!timeLock.encryptedKey || !timeLock.encryptedKey.startsWith("0x")) {
10180
+ throw new ValidationError(
10181
+ "Invalid encrypted key format",
10182
+ "encryptedKey",
10183
+ { encryptedKey: timeLock.encryptedKey },
10184
+ "SIP_3009" /* INVALID_ENCRYPTED_DATA */
10185
+ );
10186
+ }
10187
+ if (!timeLock.nonce || !timeLock.nonce.startsWith("0x")) {
10188
+ throw new ValidationError(
10189
+ "Invalid nonce format",
10190
+ "nonce",
10191
+ { nonce: timeLock.nonce },
10192
+ "SIP_3009" /* INVALID_ENCRYPTED_DATA */
10193
+ );
10194
+ }
10195
+ if (!timeLock.verificationCommitment || !timeLock.verificationCommitment.startsWith("0x")) {
10196
+ throw new ValidationError(
10197
+ "Invalid verification commitment format",
10198
+ "verificationCommitment",
10199
+ { commitment: timeLock.verificationCommitment },
10200
+ "SIP_3010" /* INVALID_COMMITMENT */
10201
+ );
10202
+ }
10203
+ if (!timeLock.encryptionCommitment || !timeLock.encryptionCommitment.startsWith("0x")) {
10204
+ throw new ValidationError(
10205
+ "Invalid encryption commitment format",
10206
+ "encryptionCommitment",
10207
+ { commitment: timeLock.encryptionCommitment },
10208
+ "SIP_3010" /* INVALID_COMMITMENT */
10209
+ );
10210
+ }
10211
+ let currentValue;
10212
+ if (currentTimeOrBlock instanceof Date) {
10213
+ currentValue = Math.floor(currentTimeOrBlock.getTime() / 1e3);
10214
+ } else if (typeof currentTimeOrBlock === "number") {
10215
+ currentValue = currentTimeOrBlock;
10216
+ } else {
10217
+ currentValue = Math.floor(Date.now() / 1e3);
10218
+ }
10219
+ const unlocked = currentValue >= timeLock.revealAfter;
10220
+ if (!unlocked) {
10221
+ return { unlocked: false };
10222
+ }
10223
+ try {
10224
+ const encryptionKey = this._deriveEncryptionKey(
10225
+ timeLock.encryptionCommitment,
10226
+ timeLock.revealAfter
10227
+ );
10228
+ const nonce = (0, import_utils22.hexToBytes)(timeLock.nonce.slice(2));
10229
+ const encryptedData = (0, import_utils22.hexToBytes)(timeLock.encryptedKey.slice(2));
10230
+ const cipher = (0, import_chacha3.xchacha20poly1305)(encryptionKey, nonce);
10231
+ const decryptedBytes = cipher.decrypt(encryptedData);
10232
+ const viewingKey = "0x" + (0, import_utils22.bytesToHex)(decryptedBytes);
10233
+ return {
10234
+ unlocked: true,
10235
+ viewingKey
10236
+ };
10237
+ } catch (error) {
10238
+ if (error instanceof ValidationError || error instanceof CryptoError) {
10239
+ throw error;
10240
+ }
10241
+ throw new CryptoError(
10242
+ "Failed to decrypt time-locked viewing key",
10243
+ "SIP_3002" /* DECRYPTION_FAILED */,
10244
+ {
10245
+ cause: error instanceof Error ? error : void 0,
10246
+ operation: "checkUnlocked"
10247
+ }
10248
+ );
10249
+ }
10250
+ }
10251
+ /**
10252
+ * Verify a time-lock commitment
10253
+ *
10254
+ * Verifies that the verification commitment in the time-lock matches the hash
10255
+ * of the provided viewing key and reveal time.
10256
+ *
10257
+ * @param timeLock - Time-lock to verify
10258
+ * @param viewingKey - Viewing key to verify against
10259
+ * @returns True if commitment is valid
10260
+ */
10261
+ verifyCommitment(timeLock, viewingKey) {
10262
+ try {
10263
+ const viewingKeyBytes = (0, import_utils22.hexToBytes)(viewingKey.slice(2));
10264
+ const commitmentData = new Uint8Array([
10265
+ ...viewingKeyBytes,
10266
+ ...this._numberToBytes(timeLock.revealAfter)
10267
+ ]);
10268
+ const expectedCommitment = (0, import_sha25616.sha256)(commitmentData);
10269
+ const actualCommitment = (0, import_utils22.hexToBytes)(timeLock.verificationCommitment.slice(2));
10270
+ if (expectedCommitment.length !== actualCommitment.length) {
10271
+ return false;
10272
+ }
10273
+ let diff = 0;
10274
+ for (let i = 0; i < expectedCommitment.length; i++) {
10275
+ diff |= expectedCommitment[i] ^ actualCommitment[i];
10276
+ }
10277
+ return diff === 0;
10278
+ } catch {
10279
+ return false;
10280
+ }
10281
+ }
10282
+ /**
10283
+ * Derive deterministic encryption key from commitment and reveal time
10284
+ *
10285
+ * @private
10286
+ */
10287
+ _deriveEncryptionKey(commitment, revealAfter) {
10288
+ const commitmentBytes = (0, import_utils22.hexToBytes)(commitment.slice(2));
10289
+ const timeBytes = this._numberToBytes(revealAfter);
10290
+ const combined = new Uint8Array([...commitmentBytes, ...timeBytes]);
10291
+ const key = (0, import_sha25616.sha256)(combined);
10292
+ if (key.length !== 32) {
10293
+ throw new CryptoError(
10294
+ "Derived key must be 32 bytes",
10295
+ "SIP_3008" /* INVALID_KEY_SIZE */,
10296
+ {
10297
+ context: { actualSize: key.length, expectedSize: 32 },
10298
+ operation: "_deriveEncryptionKey"
10299
+ }
10300
+ );
10301
+ }
10302
+ return key;
10303
+ }
10304
+ /**
10305
+ * Convert number to 8-byte big-endian representation
10306
+ *
10307
+ * @private
10308
+ */
10309
+ _numberToBytes(num) {
10310
+ const bytes = new Uint8Array(8);
10311
+ const view = new DataView(bytes.buffer);
10312
+ view.setBigUint64(0, BigInt(Math.floor(num)), false);
10313
+ return bytes;
10314
+ }
10315
+ };
10316
+
10317
+ // src/compliance/conditional-threshold.ts
10318
+ var import_secp256k17 = require("@noble/curves/secp256k1");
10319
+ var import_sha25617 = require("@noble/hashes/sha256");
10320
+ var import_utils23 = require("@noble/hashes/utils");
10321
+ var CURVE_ORDER2 = import_secp256k17.secp256k1.CURVE.n;
10322
+
10323
+ // src/compliance/threshold.ts
10324
+ var import_sha25618 = require("@noble/hashes/sha256");
10325
+ var import_utils24 = require("@noble/hashes/utils");
10326
+ var FIELD_PRIME = 2n ** 256n - 189n;
10327
+ var ThresholdViewingKey = class {
10328
+ /**
10329
+ * Create threshold shares from a viewing key
10330
+ *
10331
+ * @param params - Configuration parameters
10332
+ * @returns Threshold shares with commitment
10333
+ * @throws ValidationError if parameters are invalid
10334
+ *
10335
+ * @example
10336
+ * ```typescript
10337
+ * const threshold = ThresholdViewingKey.create({
10338
+ * threshold: 3,
10339
+ * totalShares: 5,
10340
+ * viewingKey: '0xabc123...',
10341
+ * })
10342
+ * ```
10343
+ */
10344
+ static create(params) {
10345
+ this.validateParams(params.threshold, params.totalShares);
10346
+ this.validateViewingKey(params.viewingKey);
10347
+ const secret = this.viewingKeyToSecret(params.viewingKey);
10348
+ const keyLength = params.viewingKey.slice(2).length;
10349
+ const coefficients = this.generateCoefficients(params.threshold, secret);
10350
+ const commitment = this.createCommitment(secret, coefficients);
10351
+ const shares = [];
10352
+ for (let i = 1; i <= params.totalShares; i++) {
10353
+ const x = BigInt(i);
10354
+ const y = this.evaluatePolynomial(coefficients, x);
10355
+ shares.push(this.encodeShare(x, y, keyLength, commitment));
10356
+ }
10357
+ return {
10358
+ shares,
10359
+ commitment,
10360
+ threshold: params.threshold,
10361
+ totalShares: params.totalShares
10362
+ };
10363
+ }
10364
+ /**
10365
+ * Reconstruct viewing key from threshold shares
10366
+ *
10367
+ * @param shares - Array of encoded shares (must be >= threshold)
10368
+ * @returns Reconstructed viewing key
10369
+ * @throws ValidationError if insufficient or invalid shares
10370
+ *
10371
+ * @example
10372
+ * ```typescript
10373
+ * const viewingKey = ThresholdViewingKey.reconstruct([
10374
+ * 'share1',
10375
+ * 'share2',
10376
+ * 'share3',
10377
+ * ])
10378
+ * ```
10379
+ */
10380
+ static reconstruct(shares) {
10381
+ if (!shares || shares.length === 0) {
10382
+ throw new ValidationError(
10383
+ "at least one share is required",
10384
+ "shares",
10385
+ { received: shares },
10386
+ "SIP_2008" /* MISSING_REQUIRED */
10387
+ );
10388
+ }
10389
+ const decodedShares = shares.map((s) => this.decodeShare(s));
10390
+ const commitment = decodedShares[0].commitment;
10391
+ const keyLength = decodedShares[0].keyLength;
10392
+ for (const share of decodedShares) {
10393
+ if (share.commitment !== commitment) {
10394
+ throw new ValidationError(
10395
+ "shares must all have the same commitment",
10396
+ "shares",
10397
+ { commitment },
10398
+ "SIP_3012" /* INVALID_SHARE */
10399
+ );
10400
+ }
10401
+ if (share.keyLength !== keyLength) {
10402
+ throw new ValidationError(
10403
+ "shares must all have the same key length",
10404
+ "shares",
10405
+ { keyLength },
10406
+ "SIP_3012" /* INVALID_SHARE */
10407
+ );
10408
+ }
10409
+ }
10410
+ const xCoords = new Set(decodedShares.map((s) => s.x.toString()));
10411
+ if (xCoords.size !== decodedShares.length) {
10412
+ throw new ValidationError(
10413
+ "shares must have unique x-coordinates",
10414
+ "shares",
10415
+ void 0,
10416
+ "SIP_3012" /* INVALID_SHARE */
10417
+ );
10418
+ }
10419
+ const secret = this.lagrangeInterpolate(decodedShares);
10420
+ return this.secretToViewingKey(secret, keyLength);
10421
+ }
10422
+ /**
10423
+ * Verify a share without revealing the viewing key
10424
+ *
10425
+ * @param share - Encoded share to verify
10426
+ * @param expectedCommitment - Expected commitment hash
10427
+ * @returns True if share is valid
10428
+ *
10429
+ * @example
10430
+ * ```typescript
10431
+ * const isValid = ThresholdViewingKey.verifyShare(
10432
+ * 'share1',
10433
+ * 'commitment_hash'
10434
+ * )
10435
+ * ```
10436
+ */
10437
+ static verifyShare(share, expectedCommitment) {
10438
+ try {
10439
+ const decoded = this.decodeShare(share);
10440
+ return decoded.commitment === expectedCommitment;
10441
+ } catch {
10442
+ return false;
10443
+ }
10444
+ }
10445
+ // ─── Private Helper Methods ─────────────────────────────────────────────────
10446
+ /**
10447
+ * Validate threshold and total shares parameters
10448
+ */
10449
+ static validateParams(threshold, totalShares) {
10450
+ if (!Number.isInteger(threshold) || threshold < 2) {
10451
+ throw new ValidationError(
10452
+ "threshold must be an integer >= 2",
10453
+ "threshold",
10454
+ { received: threshold },
10455
+ "SIP_3013" /* INVALID_THRESHOLD */
10456
+ );
10457
+ }
10458
+ if (!Number.isInteger(totalShares) || totalShares < threshold) {
10459
+ throw new ValidationError(
10460
+ "totalShares must be an integer >= threshold",
10461
+ "totalShares",
10462
+ { received: totalShares, threshold },
10463
+ "SIP_3013" /* INVALID_THRESHOLD */
10464
+ );
10465
+ }
10466
+ if (totalShares > 255) {
10467
+ throw new ValidationError(
10468
+ "totalShares must be <= 255",
10469
+ "totalShares",
10470
+ { received: totalShares },
10471
+ "SIP_3013" /* INVALID_THRESHOLD */
10472
+ );
10473
+ }
10474
+ }
10475
+ /**
10476
+ * Validate viewing key format
10477
+ */
10478
+ static validateViewingKey(viewingKey) {
10479
+ if (!viewingKey || typeof viewingKey !== "string") {
10480
+ throw new ValidationError(
10481
+ "viewingKey is required",
10482
+ "viewingKey",
10483
+ { received: viewingKey },
10484
+ "SIP_2008" /* MISSING_REQUIRED */
10485
+ );
10486
+ }
10487
+ if (!viewingKey.startsWith("0x")) {
10488
+ throw new ValidationError(
10489
+ "viewingKey must be hex-encoded (start with 0x)",
10490
+ "viewingKey",
10491
+ { received: viewingKey },
10492
+ "SIP_3015" /* INVALID_FORMAT */
10493
+ );
10494
+ }
10495
+ if (viewingKey.length < 66) {
10496
+ throw new ValidationError(
10497
+ "viewingKey must be at least 32 bytes",
10498
+ "viewingKey",
10499
+ { received: viewingKey.length },
10500
+ "SIP_3015" /* INVALID_FORMAT */
10501
+ );
10502
+ }
10503
+ }
10504
+ /**
10505
+ * Convert viewing key to secret (bigint)
10506
+ */
10507
+ static viewingKeyToSecret(viewingKey) {
10508
+ const bytes = (0, import_utils24.hexToBytes)(viewingKey.slice(2));
10509
+ let secret = 0n;
10510
+ for (let i = 0; i < bytes.length; i++) {
10511
+ secret = secret << 8n | BigInt(bytes[i]);
10512
+ }
10513
+ return this.mod(secret, FIELD_PRIME);
10514
+ }
10515
+ /**
10516
+ * Convert secret (bigint) back to viewing key
10517
+ * @param secret - The secret as bigint
10518
+ * @param hexLength - Length of the hex string (without 0x prefix)
10519
+ */
10520
+ static secretToViewingKey(secret, hexLength) {
10521
+ let hex = secret.toString(16);
10522
+ hex = hex.padStart(hexLength, "0");
10523
+ return `0x${hex}`;
10524
+ }
10525
+ /**
10526
+ * Generate random polynomial coefficients
10527
+ * Polynomial: f(x) = a₀ + a₁x + a₂x² + ... + aₙ₋₁xⁿ⁻¹
10528
+ * where a₀ = secret
10529
+ */
10530
+ static generateCoefficients(threshold, secret) {
10531
+ const coefficients = [secret];
10532
+ for (let i = 1; i < threshold; i++) {
10533
+ const randomCoeff = this.randomFieldElement();
10534
+ coefficients.push(randomCoeff);
10535
+ }
10536
+ return coefficients;
10537
+ }
10538
+ /**
10539
+ * Generate a random field element
10540
+ */
10541
+ static randomFieldElement() {
10542
+ const bytes = (0, import_utils24.randomBytes)(32);
10543
+ let value = 0n;
10544
+ for (let i = 0; i < bytes.length; i++) {
10545
+ value = value << 8n | BigInt(bytes[i]);
10546
+ }
10547
+ return this.mod(value, FIELD_PRIME);
10548
+ }
10549
+ /**
10550
+ * Evaluate polynomial at point x
10551
+ * f(x) = a₀ + a₁x + a₂x² + ... + aₙ₋₁xⁿ⁻¹
10552
+ */
10553
+ static evaluatePolynomial(coefficients, x) {
10554
+ let result = 0n;
10555
+ let xPower = 1n;
10556
+ for (const coeff of coefficients) {
10557
+ result = this.mod(result + this.mod(coeff * xPower, FIELD_PRIME), FIELD_PRIME);
10558
+ xPower = this.mod(xPower * x, FIELD_PRIME);
10559
+ }
10560
+ return result;
10561
+ }
10562
+ /**
10563
+ * Create commitment hash from secret and coefficients
10564
+ */
10565
+ static createCommitment(secret, coefficients) {
10566
+ const data = [secret, ...coefficients].map((c) => c.toString(16).padStart(64, "0")).join("");
10567
+ const hash2 = (0, import_sha25618.sha256)((0, import_utils24.hexToBytes)(data));
10568
+ return (0, import_utils24.bytesToHex)(hash2);
10569
+ }
10570
+ /**
10571
+ * Encode share as string: "x:y:len:commitment"
10572
+ */
10573
+ static encodeShare(x, y, keyLength, commitment) {
10574
+ const xHex = x.toString(16).padStart(2, "0");
10575
+ const yHex = y.toString(16).padStart(64, "0");
10576
+ const lenHex = keyLength.toString(16).padStart(4, "0");
10577
+ return `${xHex}:${yHex}:${lenHex}:${commitment}`;
10578
+ }
10579
+ /**
10580
+ * Decode share from string
10581
+ */
10582
+ static decodeShare(share) {
10583
+ if (!share || typeof share !== "string") {
10584
+ throw new ValidationError(
10585
+ "share must be a non-empty string",
10586
+ "share",
10587
+ { received: share },
10588
+ "SIP_3012" /* INVALID_SHARE */
10589
+ );
10590
+ }
10591
+ const parts = share.split(":");
10592
+ if (parts.length !== 4) {
10593
+ throw new ValidationError(
10594
+ 'share must have format "x:y:len:commitment"',
10595
+ "share",
10596
+ { received: share },
10597
+ "SIP_3012" /* INVALID_SHARE */
10598
+ );
10599
+ }
10600
+ const [xHex, yHex, lenHex, commitment] = parts;
10601
+ try {
10602
+ const x = BigInt(`0x${xHex}`);
10603
+ const y = BigInt(`0x${yHex}`);
10604
+ const keyLength = parseInt(lenHex, 16);
10605
+ if (x <= 0n) {
10606
+ throw new ValidationError(
10607
+ "share x-coordinate must be positive",
10608
+ "share",
10609
+ { x },
10610
+ "SIP_3012" /* INVALID_SHARE */
10611
+ );
10612
+ }
10613
+ if (keyLength < 64) {
10614
+ throw new ValidationError(
10615
+ "key length must be at least 64 (32 bytes)",
10616
+ "share",
10617
+ { keyLength },
10618
+ "SIP_3012" /* INVALID_SHARE */
10619
+ );
10620
+ }
10621
+ return { x, y, keyLength, commitment };
10622
+ } catch (error) {
10623
+ throw new ValidationError(
10624
+ "failed to decode share",
10625
+ "share",
10626
+ { error: error.message },
10627
+ "SIP_3012" /* INVALID_SHARE */
10628
+ );
9156
10629
  }
9157
- return data;
9158
10630
  }
9159
- generateCSV(transactions) {
9160
- const headers = [
9161
- "Transaction ID",
9162
- "Disclosure ID",
9163
- "Type",
9164
- "Direction",
9165
- "Token",
9166
- "Amount",
9167
- "Sender",
9168
- "Recipient",
9169
- "Chain",
9170
- "Privacy Level",
9171
- "Timestamp",
9172
- "TX Hash",
9173
- "Block",
9174
- "Risk Score",
9175
- "Purpose",
9176
- "Memo"
9177
- ];
9178
- const rows = transactions.map((tx) => [
9179
- tx.transactionId,
9180
- tx.disclosureId,
9181
- tx.type,
9182
- tx.direction,
9183
- tx.token.symbol,
9184
- tx.amount.toString(),
9185
- tx.sender,
9186
- tx.recipient,
9187
- tx.chain,
9188
- tx.privacyLevel,
9189
- new Date(tx.timestamp * 1e3).toISOString(),
9190
- tx.txHash,
9191
- tx.blockNumber.toString(),
9192
- tx.riskScore?.toString() ?? "",
9193
- tx.purpose ?? "",
9194
- tx.memo ?? ""
9195
- ]);
9196
- const escapeForCSV = (val) => {
9197
- let escaped = val;
9198
- if (/^[=+\-@|\t]/.test(escaped)) {
9199
- escaped = `'${escaped}`;
10631
+ /**
10632
+ * Lagrange interpolation to reconstruct secret
10633
+ * Evaluates polynomial at x=0 to get f(0) = secret
10634
+ */
10635
+ static lagrangeInterpolate(shares) {
10636
+ let secret = 0n;
10637
+ for (let i = 0; i < shares.length; i++) {
10638
+ const xi = shares[i].x;
10639
+ const yi = shares[i].y;
10640
+ let numerator = 1n;
10641
+ let denominator = 1n;
10642
+ for (let j = 0; j < shares.length; j++) {
10643
+ if (i === j) continue;
10644
+ const xj = shares[j].x;
10645
+ numerator = this.mod(numerator * this.mod(-xj, FIELD_PRIME), FIELD_PRIME);
10646
+ denominator = this.mod(denominator * this.mod(xi - xj, FIELD_PRIME), FIELD_PRIME);
9200
10647
  }
9201
- return `"${escaped.replace(/"/g, '""')}"`;
9202
- };
9203
- const csvRows = [headers, ...rows].map((row) => row.map(escapeForCSV).join(","));
9204
- return csvRows.join("\n");
10648
+ const coeff = this.mod(numerator * this.modInverse(denominator, FIELD_PRIME), FIELD_PRIME);
10649
+ secret = this.mod(secret + this.mod(yi * coeff, FIELD_PRIME), FIELD_PRIME);
10650
+ }
10651
+ return secret;
9205
10652
  }
9206
- };
9207
- function generateId(prefix) {
9208
- return `${prefix}_${(0, import_utils20.bytesToHex)((0, import_utils20.randomBytes)(12))}`;
9209
- }
9210
- function validateRegisterAuditorParams(params) {
9211
- if (!params.organization?.trim()) {
9212
- throw new ValidationError(
9213
- "organization is required",
9214
- "organization",
9215
- void 0,
9216
- "SIP_2008" /* MISSING_REQUIRED */
9217
- );
10653
+ /**
10654
+ * Modular arithmetic: a mod m
10655
+ */
10656
+ static mod(a, m) {
10657
+ return (a % m + m) % m;
9218
10658
  }
9219
- if (!params.contactName?.trim()) {
9220
- throw new ValidationError(
9221
- "contact name is required",
9222
- "contactName",
9223
- void 0,
9224
- "SIP_2008" /* MISSING_REQUIRED */
9225
- );
10659
+ /**
10660
+ * Modular multiplicative inverse using Extended Euclidean Algorithm
10661
+ * Returns x such that (a * x) mod m = 1
10662
+ */
10663
+ static modInverse(a, m) {
10664
+ const a0 = this.mod(a, m);
10665
+ if (a0 === 0n) {
10666
+ throw new CryptoError(
10667
+ "modular inverse does not exist (a = 0)",
10668
+ "SIP_3014" /* CRYPTO_OPERATION_FAILED */,
10669
+ { operation: "modInverse" }
10670
+ );
10671
+ }
10672
+ let [old_r, r] = [a0, m];
10673
+ let [old_s, s] = [1n, 0n];
10674
+ while (r !== 0n) {
10675
+ const quotient = old_r / r;
10676
+ [old_r, r] = [r, old_r - quotient * r];
10677
+ [old_s, s] = [s, old_s - quotient * s];
10678
+ }
10679
+ if (old_r !== 1n) {
10680
+ throw new CryptoError(
10681
+ "modular inverse does not exist (gcd != 1)",
10682
+ "SIP_3014" /* CRYPTO_OPERATION_FAILED */,
10683
+ { operation: "modInverse" }
10684
+ );
10685
+ }
10686
+ return this.mod(old_s, m);
9226
10687
  }
9227
- if (!params.contactEmail?.trim()) {
9228
- throw new ValidationError(
9229
- "contact email is required",
9230
- "contactEmail",
9231
- void 0,
9232
- "SIP_2008" /* MISSING_REQUIRED */
9233
- );
10688
+ };
10689
+
10690
+ // src/compliance/derivation.ts
10691
+ var import_sha25619 = require("@noble/hashes/sha256");
10692
+ var import_sha5123 = require("@noble/hashes/sha512");
10693
+ var import_hmac2 = require("@noble/hashes/hmac");
10694
+ var import_utils25 = require("@noble/hashes/utils");
10695
+ var AuditorType = /* @__PURE__ */ ((AuditorType2) => {
10696
+ AuditorType2[AuditorType2["PRIMARY"] = 0] = "PRIMARY";
10697
+ AuditorType2[AuditorType2["REGULATORY"] = 1] = "REGULATORY";
10698
+ AuditorType2[AuditorType2["INTERNAL"] = 2] = "INTERNAL";
10699
+ AuditorType2[AuditorType2["TAX"] = 3] = "TAX";
10700
+ return AuditorType2;
10701
+ })(AuditorType || {});
10702
+ var AuditorKeyDerivation = class {
10703
+ /**
10704
+ * SIP Protocol coin type (BIP-44 registered)
10705
+ *
10706
+ * Note: This is a placeholder. In production, register with SLIP-44:
10707
+ * https://github.com/satoshilabs/slips/blob/master/slip-0044.md
10708
+ */
10709
+ static COIN_TYPE = 1234;
10710
+ /**
10711
+ * BIP-44 purpose field
10712
+ */
10713
+ static PURPOSE = 44;
10714
+ /**
10715
+ * Hardened derivation flag (2^31)
10716
+ */
10717
+ static HARDENED = 2147483648;
10718
+ /**
10719
+ * Generate BIP-44 derivation path
10720
+ *
10721
+ * @param auditorType - Type of auditor key
10722
+ * @param account - Account index (default: 0)
10723
+ * @returns BIP-44 style path string
10724
+ *
10725
+ * @example
10726
+ * ```typescript
10727
+ * AuditorKeyDerivation.derivePath(AuditorType.REGULATORY)
10728
+ * // Returns: "m/44'/1234'/0'/1"
10729
+ *
10730
+ * AuditorKeyDerivation.derivePath(AuditorType.TAX, 5)
10731
+ * // Returns: "m/44'/1234'/5'/3"
10732
+ * ```
10733
+ */
10734
+ static derivePath(auditorType, account = 0) {
10735
+ this.validateAuditorType(auditorType);
10736
+ this.validateAccount(account);
10737
+ return `m/${this.PURPOSE}'/${this.COIN_TYPE}'/${account}'/${auditorType}`;
9234
10738
  }
9235
- if (!params.publicKey?.trim()) {
9236
- throw new ValidationError(
9237
- "public key is required",
9238
- "publicKey",
9239
- void 0,
9240
- "SIP_2008" /* MISSING_REQUIRED */
9241
- );
10739
+ /**
10740
+ * Derive a viewing key for an auditor
10741
+ *
10742
+ * Uses BIP-32 style hierarchical deterministic key derivation:
10743
+ * 1. Derive master key from seed
10744
+ * 2. Harden purpose (44')
10745
+ * 3. Harden coin type (1234')
10746
+ * 4. Harden account index
10747
+ * 5. Derive auditor type (non-hardened)
10748
+ *
10749
+ * @param params - Derivation parameters
10750
+ * @returns Derived viewing key with metadata
10751
+ *
10752
+ * @throws {ValidationError} If parameters are invalid
10753
+ *
10754
+ * @example
10755
+ * ```typescript
10756
+ * const regulatoryKey = AuditorKeyDerivation.deriveViewingKey({
10757
+ * masterSeed: randomBytes(32),
10758
+ * auditorType: AuditorType.REGULATORY,
10759
+ * })
10760
+ *
10761
+ * console.log(regulatoryKey.path) // "m/44'/1234'/0'/1"
10762
+ * console.log(regulatoryKey.viewingKey.key) // "0x..."
10763
+ * ```
10764
+ */
10765
+ static deriveViewingKey(params) {
10766
+ const { masterSeed, auditorType, account = 0 } = params;
10767
+ this.validateMasterSeed(masterSeed);
10768
+ this.validateAuditorType(auditorType);
10769
+ this.validateAccount(account);
10770
+ const path = this.derivePath(auditorType, account);
10771
+ const indices = [
10772
+ this.PURPOSE | this.HARDENED,
10773
+ // 44' (hardened)
10774
+ this.COIN_TYPE | this.HARDENED,
10775
+ // 1234' (hardened)
10776
+ account | this.HARDENED,
10777
+ // account' (hardened)
10778
+ auditorType
10779
+ // auditorType (non-hardened)
10780
+ ];
10781
+ const masterData = (0, import_hmac2.hmac)(import_sha5123.sha512, (0, import_utils25.utf8ToBytes)("SIP-MASTER-SEED"), masterSeed);
10782
+ let currentKey = new Uint8Array(masterData.slice(0, 32));
10783
+ let chainCode = new Uint8Array(masterData.slice(32, 64));
10784
+ try {
10785
+ for (let i = 0; i < indices.length; i++) {
10786
+ const index = indices[i];
10787
+ const derived = this.deriveChildKey(currentKey, chainCode, index);
10788
+ if (i > 0) {
10789
+ secureWipe(currentKey);
10790
+ }
10791
+ currentKey = new Uint8Array(derived.key);
10792
+ chainCode = new Uint8Array(derived.chainCode);
10793
+ }
10794
+ const keyHex = `0x${(0, import_utils25.bytesToHex)(currentKey)}`;
10795
+ const hashBytes = (0, import_sha25619.sha256)(currentKey);
10796
+ const hash2 = `0x${(0, import_utils25.bytesToHex)(hashBytes)}`;
10797
+ const viewingKey = {
10798
+ key: keyHex,
10799
+ path,
10800
+ hash: hash2
10801
+ };
10802
+ return {
10803
+ path,
10804
+ viewingKey,
10805
+ auditorType,
10806
+ account
10807
+ };
10808
+ } finally {
10809
+ secureWipe(currentKey);
10810
+ secureWipe(chainCode);
10811
+ }
9242
10812
  }
9243
- if (!params.scope) {
9244
- throw new ValidationError(
9245
- "audit scope is required",
9246
- "scope",
9247
- void 0,
9248
- "SIP_2008" /* MISSING_REQUIRED */
9249
- );
10813
+ /**
10814
+ * Derive multiple viewing keys at once
10815
+ *
10816
+ * Efficiently derives keys for multiple auditor types from the same
10817
+ * master seed. This is more efficient than calling deriveViewingKey
10818
+ * multiple times as it reuses intermediate derivations.
10819
+ *
10820
+ * @param params - Derivation parameters
10821
+ * @returns Array of derived viewing keys
10822
+ *
10823
+ * @example
10824
+ * ```typescript
10825
+ * const keys = AuditorKeyDerivation.deriveMultiple({
10826
+ * masterSeed: randomBytes(32),
10827
+ * auditorTypes: [
10828
+ * AuditorType.PRIMARY,
10829
+ * AuditorType.REGULATORY,
10830
+ * AuditorType.INTERNAL,
10831
+ * ],
10832
+ * })
10833
+ *
10834
+ * // keys[0] -> PRIMARY key (m/44'/1234'/0'/0)
10835
+ * // keys[1] -> REGULATORY key (m/44'/1234'/0'/1)
10836
+ * // keys[2] -> INTERNAL key (m/44'/1234'/0'/2)
10837
+ * ```
10838
+ */
10839
+ static deriveMultiple(params) {
10840
+ const { masterSeed, auditorTypes, account = 0 } = params;
10841
+ this.validateMasterSeed(masterSeed);
10842
+ this.validateAccount(account);
10843
+ if (!auditorTypes || auditorTypes.length === 0) {
10844
+ throw new ValidationError(
10845
+ "at least one auditor type is required",
10846
+ "auditorTypes",
10847
+ { received: auditorTypes },
10848
+ "SIP_2008" /* MISSING_REQUIRED */
10849
+ );
10850
+ }
10851
+ for (const type of auditorTypes) {
10852
+ this.validateAuditorType(type);
10853
+ }
10854
+ const uniqueTypes = Array.from(new Set(auditorTypes));
10855
+ const commonIndices = [
10856
+ this.PURPOSE | this.HARDENED,
10857
+ // 44' (hardened)
10858
+ this.COIN_TYPE | this.HARDENED,
10859
+ // 1234' (hardened)
10860
+ account | this.HARDENED
10861
+ // account' (hardened)
10862
+ ];
10863
+ const masterData = (0, import_hmac2.hmac)(import_sha5123.sha512, (0, import_utils25.utf8ToBytes)("SIP-MASTER-SEED"), masterSeed);
10864
+ let commonKey = new Uint8Array(masterData.slice(0, 32));
10865
+ let commonChainCode = new Uint8Array(masterData.slice(32, 64));
10866
+ try {
10867
+ for (let i = 0; i < commonIndices.length; i++) {
10868
+ const index = commonIndices[i];
10869
+ const derived = this.deriveChildKey(commonKey, commonChainCode, index);
10870
+ if (i > 0) {
10871
+ secureWipe(commonKey);
10872
+ }
10873
+ commonKey = new Uint8Array(derived.key);
10874
+ commonChainCode = new Uint8Array(derived.chainCode);
10875
+ }
10876
+ const results = [];
10877
+ for (const auditorType of uniqueTypes) {
10878
+ const derived = this.deriveChildKey(commonKey, commonChainCode, auditorType);
10879
+ try {
10880
+ const keyHex = `0x${(0, import_utils25.bytesToHex)(derived.key)}`;
10881
+ const hashBytes = (0, import_sha25619.sha256)(derived.key);
10882
+ const hash2 = `0x${(0, import_utils25.bytesToHex)(hashBytes)}`;
10883
+ const path = this.derivePath(auditorType, account);
10884
+ const viewingKey = {
10885
+ key: keyHex,
10886
+ path,
10887
+ hash: hash2
10888
+ };
10889
+ results.push({
10890
+ path,
10891
+ viewingKey,
10892
+ auditorType,
10893
+ account
10894
+ });
10895
+ } finally {
10896
+ secureWipe(derived.key);
10897
+ secureWipe(derived.chainCode);
10898
+ }
10899
+ }
10900
+ return results;
10901
+ } finally {
10902
+ secureWipe(commonKey);
10903
+ secureWipe(commonChainCode);
10904
+ }
9250
10905
  }
9251
- }
9252
- function validateReportParams(params) {
9253
- if (!params.title?.trim()) {
9254
- throw new ValidationError(
9255
- "report title is required",
9256
- "title",
9257
- void 0,
9258
- "SIP_2008" /* MISSING_REQUIRED */
9259
- );
10906
+ /**
10907
+ * Get human-readable name for auditor type
10908
+ *
10909
+ * @param auditorType - Auditor type enum value
10910
+ * @returns Friendly name string
10911
+ */
10912
+ static getAuditorTypeName(auditorType) {
10913
+ switch (auditorType) {
10914
+ case 0 /* PRIMARY */:
10915
+ return "Primary";
10916
+ case 1 /* REGULATORY */:
10917
+ return "Regulatory";
10918
+ case 2 /* INTERNAL */:
10919
+ return "Internal";
10920
+ case 3 /* TAX */:
10921
+ return "Tax Authority";
10922
+ default:
10923
+ return `Unknown (${auditorType})`;
10924
+ }
9260
10925
  }
9261
- if (!params.type) {
9262
- throw new ValidationError(
9263
- "report type is required",
9264
- "type",
9265
- void 0,
9266
- "SIP_2008" /* MISSING_REQUIRED */
9267
- );
10926
+ // ─── Private Helpers ─────────────────────────────────────────────────────────
10927
+ /**
10928
+ * Derive a child key using BIP-32 HMAC-SHA512 derivation
10929
+ *
10930
+ * @param parentKey - Parent key bytes (32 bytes)
10931
+ * @param chainCode - Parent chain code (32 bytes)
10932
+ * @param index - Child index (use | HARDENED for hardened derivation)
10933
+ * @returns Derived key and chain code
10934
+ */
10935
+ static deriveChildKey(parentKey, chainCode, index) {
10936
+ const isHardened = (index & this.HARDENED) !== 0;
10937
+ const data = new Uint8Array(37);
10938
+ if (isHardened) {
10939
+ data[0] = 0;
10940
+ data.set(parentKey, 1);
10941
+ } else {
10942
+ data[0] = 1;
10943
+ data.set(parentKey, 1);
10944
+ }
10945
+ const indexView = new DataView(data.buffer, 33, 4);
10946
+ indexView.setUint32(0, index, false);
10947
+ const hmacResult = (0, import_hmac2.hmac)(import_sha5123.sha512, chainCode, data);
10948
+ const childKey = new Uint8Array(hmacResult.slice(0, 32));
10949
+ const childChainCode = new Uint8Array(hmacResult.slice(32, 64));
10950
+ return {
10951
+ key: childKey,
10952
+ chainCode: childChainCode
10953
+ };
9268
10954
  }
9269
- if (!params.format) {
9270
- throw new ValidationError(
9271
- "report format is required",
9272
- "format",
9273
- void 0,
9274
- "SIP_2008" /* MISSING_REQUIRED */
9275
- );
10955
+ /**
10956
+ * Validate master seed
10957
+ */
10958
+ static validateMasterSeed(seed) {
10959
+ if (!seed || seed.length < 32) {
10960
+ throw new ValidationError(
10961
+ "master seed must be at least 32 bytes",
10962
+ "masterSeed",
10963
+ { received: seed?.length ?? 0 },
10964
+ "SIP_2001" /* INVALID_INPUT */
10965
+ );
10966
+ }
9276
10967
  }
9277
- if (params.startDate === void 0 || params.startDate === null || params.endDate === void 0 || params.endDate === null) {
9278
- throw new ValidationError(
9279
- "date range is required",
9280
- "dateRange",
9281
- void 0,
9282
- "SIP_2008" /* MISSING_REQUIRED */
9283
- );
10968
+ /**
10969
+ * Validate auditor type
10970
+ */
10971
+ static validateAuditorType(type) {
10972
+ const validTypes = [
10973
+ 0 /* PRIMARY */,
10974
+ 1 /* REGULATORY */,
10975
+ 2 /* INTERNAL */,
10976
+ 3 /* TAX */
10977
+ ];
10978
+ if (!validTypes.includes(type)) {
10979
+ throw new ValidationError(
10980
+ `invalid auditor type: ${type}`,
10981
+ "auditorType",
10982
+ { received: type, valid: validTypes },
10983
+ "SIP_2001" /* INVALID_INPUT */
10984
+ );
10985
+ }
9284
10986
  }
9285
- if (params.startDate >= params.endDate) {
9286
- throw new ValidationError(
9287
- "start date must be before end date",
9288
- "dateRange",
9289
- void 0,
9290
- "SIP_2001" /* INVALID_INPUT */
9291
- );
10987
+ /**
10988
+ * Validate account index
10989
+ */
10990
+ static validateAccount(account) {
10991
+ if (!Number.isInteger(account) || account < 0 || account >= this.HARDENED) {
10992
+ throw new ValidationError(
10993
+ `account must be a non-negative integer less than ${this.HARDENED}`,
10994
+ "account",
10995
+ { received: account },
10996
+ "SIP_2001" /* INVALID_INPUT */
10997
+ );
10998
+ }
9292
10999
  }
9293
- }
11000
+ };
9294
11001
 
9295
11002
  // src/wallet/errors.ts
9296
11003
  var import_types18 = require("@sip-protocol/types");
@@ -12439,7 +14146,7 @@ function createTrezorAdapter(config) {
12439
14146
 
12440
14147
  // src/wallet/hardware/mock.ts
12441
14148
  var import_types51 = require("@sip-protocol/types");
12442
- var import_utils21 = require("@noble/hashes/utils");
14149
+ var import_utils26 = require("@noble/hashes/utils");
12443
14150
  var MockLedgerAdapter = class extends BaseWalletAdapter {
12444
14151
  chain;
12445
14152
  name = "mock-ledger";
@@ -12684,15 +14391,15 @@ var MockLedgerAdapter = class extends BaseWalletAdapter {
12684
14391
  }
12685
14392
  }
12686
14393
  generateMockAddress(index) {
12687
- const bytes = (0, import_utils21.randomBytes)(20);
14394
+ const bytes = (0, import_utils26.randomBytes)(20);
12688
14395
  bytes[0] = index;
12689
- return `0x${(0, import_utils21.bytesToHex)(bytes)}`;
14396
+ return `0x${(0, import_utils26.bytesToHex)(bytes)}`;
12690
14397
  }
12691
14398
  generateMockPublicKey(index) {
12692
- const bytes = (0, import_utils21.randomBytes)(33);
14399
+ const bytes = (0, import_utils26.randomBytes)(33);
12693
14400
  bytes[0] = 2;
12694
14401
  bytes[1] = index;
12695
- return `0x${(0, import_utils21.bytesToHex)(bytes)}`;
14402
+ return `0x${(0, import_utils26.bytesToHex)(bytes)}`;
12696
14403
  }
12697
14404
  generateMockSignature(data) {
12698
14405
  const sig = new Uint8Array(65);
@@ -12701,7 +14408,7 @@ var MockLedgerAdapter = class extends BaseWalletAdapter {
12701
14408
  sig[32 + i] = (data[i % data.length] ?? 0) ^ i * 11;
12702
14409
  }
12703
14410
  sig[64] = 27;
12704
- return `0x${(0, import_utils21.bytesToHex)(sig)}`;
14411
+ return `0x${(0, import_utils26.bytesToHex)(sig)}`;
12705
14412
  }
12706
14413
  delay(ms) {
12707
14414
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -12890,15 +14597,15 @@ var MockTrezorAdapter = class extends BaseWalletAdapter {
12890
14597
  }
12891
14598
  }
12892
14599
  generateMockAddress(index) {
12893
- const bytes = (0, import_utils21.randomBytes)(20);
14600
+ const bytes = (0, import_utils26.randomBytes)(20);
12894
14601
  bytes[0] = index + 100;
12895
- return `0x${(0, import_utils21.bytesToHex)(bytes)}`;
14602
+ return `0x${(0, import_utils26.bytesToHex)(bytes)}`;
12896
14603
  }
12897
14604
  generateMockPublicKey(index) {
12898
- const bytes = (0, import_utils21.randomBytes)(33);
14605
+ const bytes = (0, import_utils26.randomBytes)(33);
12899
14606
  bytes[0] = 3;
12900
14607
  bytes[1] = index + 100;
12901
- return `0x${(0, import_utils21.bytesToHex)(bytes)}`;
14608
+ return `0x${(0, import_utils26.bytesToHex)(bytes)}`;
12902
14609
  }
12903
14610
  generateMockSignature(data) {
12904
14611
  const sig = new Uint8Array(65);
@@ -12907,7 +14614,7 @@ var MockTrezorAdapter = class extends BaseWalletAdapter {
12907
14614
  sig[32 + i] = (data[i % data.length] ?? 0) ^ i * 17;
12908
14615
  }
12909
14616
  sig[64] = 28;
12910
- return `0x${(0, import_utils21.bytesToHex)(sig)}`;
14617
+ return `0x${(0, import_utils26.bytesToHex)(sig)}`;
12911
14618
  }
12912
14619
  delay(ms) {
12913
14620
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -12926,10 +14633,14 @@ var import_types54 = require("@sip-protocol/types");
12926
14633
  0 && (module.exports = {
12927
14634
  ATTESTATION_VERSION,
12928
14635
  AptosStealthService,
14636
+ AuditorKeyDerivation,
14637
+ AuditorType,
12929
14638
  BaseWalletAdapter,
12930
14639
  CHAIN_NUMERIC_IDS,
12931
14640
  COSMOS_CHAIN_PREFIXES,
12932
14641
  ComplianceManager,
14642
+ ComplianceReporter,
14643
+ ConditionalDisclosure,
12933
14644
  CosmosStealthService,
12934
14645
  CryptoError,
12935
14646
  DEFAULT_THRESHOLD,
@@ -12981,6 +14692,7 @@ var import_types54 = require("@sip-protocol/types");
12981
14692
  SmartRouter,
12982
14693
  SolanaWalletAdapter,
12983
14694
  SwapStatus,
14695
+ ThresholdViewingKey,
12984
14696
  Treasury,
12985
14697
  TrezorWalletAdapter,
12986
14698
  ValidationError,
@@ -13065,6 +14777,7 @@ var import_types54 = require("@sip-protocol/types");
13065
14777
  generateEd25519StealthAddress,
13066
14778
  generateEd25519StealthMetaAddress,
13067
14779
  generateIntentId,
14780
+ generatePdfReport,
13068
14781
  generateRandomBytes,
13069
14782
  generateStealthAddress,
13070
14783
  generateStealthMetaAddress,