chainlesschain 0.51.0 → 0.81.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/package.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AppLayout-Rvi759IS.js → AppLayout-6SPt_8Y_.js} +1 -1
- package/src/assets/web-panel/assets/{Dashboard-DBhFxXYQ.js → Dashboard-Br7kCwKJ.js} +2 -2
- package/src/assets/web-panel/assets/Dashboard-CKeMmCoT.css +1 -0
- package/src/assets/web-panel/assets/{index-uL0cZ8N_.js → index-tN-8TosE.js} +2 -2
- package/src/assets/web-panel/index.html +2 -2
- package/src/commands/a2a.js +380 -0
- package/src/commands/agent-network.js +785 -0
- package/src/commands/automation.js +654 -0
- package/src/commands/bi.js +348 -0
- package/src/commands/crosschain.js +218 -0
- package/src/commands/dao.js +565 -0
- package/src/commands/did-v2.js +620 -0
- package/src/commands/dlp.js +341 -0
- package/src/commands/economy.js +578 -0
- package/src/commands/evolution.js +391 -0
- package/src/commands/evomap.js +394 -0
- package/src/commands/federation.js +283 -0
- package/src/commands/hmemory.js +442 -0
- package/src/commands/inference.js +318 -0
- package/src/commands/lowcode.js +356 -0
- package/src/commands/marketplace.js +256 -0
- package/src/commands/perf.js +433 -0
- package/src/commands/pipeline.js +449 -0
- package/src/commands/plugin-ecosystem.js +517 -0
- package/src/commands/privacy.js +321 -0
- package/src/commands/reputation.js +261 -0
- package/src/commands/sandbox.js +401 -0
- package/src/commands/siem.js +246 -0
- package/src/commands/sla.js +259 -0
- package/src/commands/social.js +311 -0
- package/src/commands/sso.js +798 -0
- package/src/commands/stress.js +230 -0
- package/src/commands/terraform.js +245 -0
- package/src/commands/workflow.js +320 -0
- package/src/commands/zkp.js +562 -1
- package/src/index.js +21 -0
- package/src/lib/a2a-protocol.js +451 -0
- package/src/lib/agent-economy.js +479 -0
- package/src/lib/agent-network.js +1121 -0
- package/src/lib/app-builder.js +239 -0
- package/src/lib/automation-engine.js +948 -0
- package/src/lib/bi-engine.js +338 -0
- package/src/lib/cross-chain.js +345 -0
- package/src/lib/dao-governance.js +569 -0
- package/src/lib/did-v2-manager.js +1127 -0
- package/src/lib/dlp-engine.js +389 -0
- package/src/lib/evolution-system.js +453 -0
- package/src/lib/evomap-federation.js +177 -0
- package/src/lib/evomap-governance.js +276 -0
- package/src/lib/federation-hardening.js +259 -0
- package/src/lib/hierarchical-memory.js +481 -0
- package/src/lib/inference-network.js +330 -0
- package/src/lib/perf-tuning.js +734 -0
- package/src/lib/pipeline-orchestrator.js +928 -0
- package/src/lib/plugin-ecosystem.js +1109 -0
- package/src/lib/privacy-computing.js +427 -0
- package/src/lib/reputation-optimizer.js +299 -0
- package/src/lib/sandbox-v2.js +306 -0
- package/src/lib/siem-exporter.js +333 -0
- package/src/lib/skill-marketplace.js +325 -0
- package/src/lib/sla-manager.js +275 -0
- package/src/lib/social-graph-analytics.js +707 -0
- package/src/lib/sso-manager.js +841 -0
- package/src/lib/stress-tester.js +330 -0
- package/src/lib/terraform-manager.js +363 -0
- package/src/lib/workflow-engine.js +454 -1
- package/src/lib/zkp-engine.js +523 -20
- package/src/assets/web-panel/assets/Dashboard-BS-tzGNj.css +0 -1
package/src/lib/bi-engine.js
CHANGED
|
@@ -297,3 +297,341 @@ export function _resetState() {
|
|
|
297
297
|
_reports.clear();
|
|
298
298
|
_scheduledReports.clear();
|
|
299
299
|
}
|
|
300
|
+
|
|
301
|
+
// ─── Phase 95 V2 surface (strictly additive) ───────────────────────
|
|
302
|
+
|
|
303
|
+
export const CHART_TYPE = Object.freeze({
|
|
304
|
+
TABLE: "table",
|
|
305
|
+
BAR: "bar",
|
|
306
|
+
LINE: "line",
|
|
307
|
+
PIE: "pie",
|
|
308
|
+
AREA: "area",
|
|
309
|
+
SCATTER: "scatter",
|
|
310
|
+
HEATMAP: "heatmap",
|
|
311
|
+
FUNNEL: "funnel",
|
|
312
|
+
GAUGE: "gauge",
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
export const ANOMALY_METHOD = Object.freeze({
|
|
316
|
+
Z_SCORE: "z_score",
|
|
317
|
+
IQR: "iqr",
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
export const REPORT_FORMAT = Object.freeze({
|
|
321
|
+
PDF: "pdf",
|
|
322
|
+
EXCEL: "excel",
|
|
323
|
+
CSV: "csv",
|
|
324
|
+
JSON: "json",
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
export const REPORT_STATUS = Object.freeze({
|
|
328
|
+
DRAFT: "draft",
|
|
329
|
+
GENERATED: "generated",
|
|
330
|
+
SCHEDULED: "scheduled",
|
|
331
|
+
ARCHIVED: "archived",
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
export const DASHBOARD_LAYOUT = Object.freeze({
|
|
335
|
+
GRID: "grid",
|
|
336
|
+
FLOW: "flow",
|
|
337
|
+
TABS: "tabs",
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
const _reportStatusV2 = new Map();
|
|
341
|
+
const _statusHistoryV2 = new Map();
|
|
342
|
+
|
|
343
|
+
const _allowedReportTransitions = Object.freeze({
|
|
344
|
+
draft: new Set(["generated", "archived"]),
|
|
345
|
+
generated: new Set(["scheduled", "archived", "draft"]),
|
|
346
|
+
scheduled: new Set(["generated", "archived"]),
|
|
347
|
+
archived: new Set(["draft"]),
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
function _quantile(sorted, q) {
|
|
351
|
+
if (sorted.length === 0) return NaN;
|
|
352
|
+
const pos = (sorted.length - 1) * q;
|
|
353
|
+
const lo = Math.floor(pos);
|
|
354
|
+
const hi = Math.ceil(pos);
|
|
355
|
+
if (lo === hi) return sorted[lo];
|
|
356
|
+
return sorted[lo] + (pos - lo) * (sorted[hi] - sorted[lo]);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export function nlQueryV2({ query, schema } = {}) {
|
|
360
|
+
if (!query || typeof query !== "string") {
|
|
361
|
+
throw new Error("query must be a non-empty string");
|
|
362
|
+
}
|
|
363
|
+
const normalized = query.replace(/['"]/g, "").trim().toLowerCase();
|
|
364
|
+
const tokens = normalized.split(/\s+/);
|
|
365
|
+
|
|
366
|
+
// Detect table name from schema (first match) or fall back to "data"
|
|
367
|
+
let table = "data";
|
|
368
|
+
const knownTables =
|
|
369
|
+
schema && Array.isArray(schema.tables)
|
|
370
|
+
? schema.tables.map((t) => String(t).toLowerCase())
|
|
371
|
+
: [];
|
|
372
|
+
for (const t of knownTables) {
|
|
373
|
+
if (tokens.includes(t)) {
|
|
374
|
+
table = t;
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Detect aggregate intent
|
|
380
|
+
let aggregate = null;
|
|
381
|
+
let intent = "list";
|
|
382
|
+
let visualization = CHART_TYPE.TABLE;
|
|
383
|
+
|
|
384
|
+
if (/\b(count|how many)\b/.test(normalized)) {
|
|
385
|
+
aggregate = "COUNT(*)";
|
|
386
|
+
intent = "count";
|
|
387
|
+
visualization = CHART_TYPE.BAR;
|
|
388
|
+
} else if (/\b(sum|total)\b/.test(normalized)) {
|
|
389
|
+
aggregate = "SUM(value)";
|
|
390
|
+
intent = "sum";
|
|
391
|
+
visualization = CHART_TYPE.GAUGE;
|
|
392
|
+
} else if (/\b(avg|average|mean)\b/.test(normalized)) {
|
|
393
|
+
aggregate = "AVG(value)";
|
|
394
|
+
intent = "avg";
|
|
395
|
+
visualization = CHART_TYPE.GAUGE;
|
|
396
|
+
} else if (/\b(trend|over time|timeline|history)\b/.test(normalized)) {
|
|
397
|
+
intent = "trend";
|
|
398
|
+
visualization = CHART_TYPE.LINE;
|
|
399
|
+
} else if (/\b(distribution|breakdown)\b/.test(normalized)) {
|
|
400
|
+
intent = "distribution";
|
|
401
|
+
visualization = CHART_TYPE.PIE;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Detect top N
|
|
405
|
+
let limit = null;
|
|
406
|
+
const topMatch = normalized.match(/\btop\s+(\d+)\b/);
|
|
407
|
+
if (topMatch) {
|
|
408
|
+
limit = parseInt(topMatch[1], 10);
|
|
409
|
+
if (intent === "list") {
|
|
410
|
+
intent = "top";
|
|
411
|
+
visualization = CHART_TYPE.BAR;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Build SQL
|
|
416
|
+
let sql;
|
|
417
|
+
if (aggregate) {
|
|
418
|
+
sql = `SELECT ${aggregate} FROM ${table}`;
|
|
419
|
+
} else {
|
|
420
|
+
sql = `SELECT * FROM ${table}`;
|
|
421
|
+
}
|
|
422
|
+
if (limit !== null) {
|
|
423
|
+
sql += ` ORDER BY value DESC LIMIT ${limit}`;
|
|
424
|
+
} else if (intent === "trend") {
|
|
425
|
+
sql += ` ORDER BY created_at ASC`;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return {
|
|
429
|
+
id: crypto.randomUUID(),
|
|
430
|
+
query,
|
|
431
|
+
intent,
|
|
432
|
+
sql,
|
|
433
|
+
table,
|
|
434
|
+
aggregate,
|
|
435
|
+
limit,
|
|
436
|
+
visualization,
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
export function detectAnomalyV2({
|
|
441
|
+
data,
|
|
442
|
+
method = ANOMALY_METHOD.Z_SCORE,
|
|
443
|
+
threshold,
|
|
444
|
+
} = {}) {
|
|
445
|
+
if (!Array.isArray(data) || data.length === 0) {
|
|
446
|
+
throw new Error("data must be a non-empty array of numbers");
|
|
447
|
+
}
|
|
448
|
+
const valid = Object.values(ANOMALY_METHOD);
|
|
449
|
+
if (!valid.includes(method)) {
|
|
450
|
+
throw new Error(
|
|
451
|
+
`Invalid method '${method}'. Expected one of ${valid.join(", ")}`,
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (method === ANOMALY_METHOD.Z_SCORE) {
|
|
456
|
+
const t = threshold == null ? 2 : threshold;
|
|
457
|
+
const legacy = detectAnomaly(data, { threshold: t });
|
|
458
|
+
return { method, threshold: t, ...legacy };
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// IQR method
|
|
462
|
+
const sorted = [...data].sort((a, b) => a - b);
|
|
463
|
+
const q1 = _quantile(sorted, 0.25);
|
|
464
|
+
const q3 = _quantile(sorted, 0.75);
|
|
465
|
+
const iqr = q3 - q1;
|
|
466
|
+
const multiplier = threshold == null ? 1.5 : threshold;
|
|
467
|
+
const lowerBound = q1 - multiplier * iqr;
|
|
468
|
+
const upperBound = q3 + multiplier * iqr;
|
|
469
|
+
|
|
470
|
+
const anomalies = [];
|
|
471
|
+
for (let i = 0; i < data.length; i++) {
|
|
472
|
+
if (data[i] < lowerBound || data[i] > upperBound) {
|
|
473
|
+
anomalies.push({ index: i, value: data[i] });
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return {
|
|
478
|
+
method,
|
|
479
|
+
threshold: multiplier,
|
|
480
|
+
q1,
|
|
481
|
+
q3,
|
|
482
|
+
iqr,
|
|
483
|
+
lowerBound,
|
|
484
|
+
upperBound,
|
|
485
|
+
anomalies,
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
export function predictTrendV2({ data, periods = 3, method = "linear" } = {}) {
|
|
490
|
+
if (!Array.isArray(data) || data.length < 2) {
|
|
491
|
+
throw new Error("data must be an array with at least 2 points");
|
|
492
|
+
}
|
|
493
|
+
if (method !== "linear") {
|
|
494
|
+
throw new Error(`Invalid method '${method}'. Only 'linear' is supported`);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const legacy = predictTrend(data, periods);
|
|
498
|
+
|
|
499
|
+
// Compute R² (coefficient of determination)
|
|
500
|
+
const n = data.length;
|
|
501
|
+
const meanY = data.reduce((s, v) => s + v, 0) / n;
|
|
502
|
+
let ssRes = 0;
|
|
503
|
+
let ssTot = 0;
|
|
504
|
+
for (let i = 0; i < n; i++) {
|
|
505
|
+
const predicted = legacy.slope * i + (meanY - legacy.slope * ((n - 1) / 2));
|
|
506
|
+
ssRes += (data[i] - predicted) ** 2;
|
|
507
|
+
ssTot += (data[i] - meanY) ** 2;
|
|
508
|
+
}
|
|
509
|
+
let r2 = ssTot === 0 ? 1 : 1 - ssRes / ssTot;
|
|
510
|
+
if (r2 < 0) r2 = 0;
|
|
511
|
+
if (r2 > 1) r2 = 1;
|
|
512
|
+
|
|
513
|
+
return {
|
|
514
|
+
method,
|
|
515
|
+
...legacy,
|
|
516
|
+
r2: Math.round(r2 * 1000) / 1000,
|
|
517
|
+
confidence: r2 >= 0.7 ? "high" : r2 >= 0.4 ? "medium" : "low",
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
export function recommendChart({ intent, dataShape } = {}) {
|
|
522
|
+
if (intent) {
|
|
523
|
+
const i = String(intent).toLowerCase();
|
|
524
|
+
if (i.includes("trend") || i.includes("time")) return CHART_TYPE.LINE;
|
|
525
|
+
if (i.includes("distribution") || i.includes("share"))
|
|
526
|
+
return CHART_TYPE.PIE;
|
|
527
|
+
if (i.includes("compare") || i.includes("top")) return CHART_TYPE.BAR;
|
|
528
|
+
if (i.includes("correlation")) return CHART_TYPE.SCATTER;
|
|
529
|
+
if (i.includes("funnel")) return CHART_TYPE.FUNNEL;
|
|
530
|
+
if (i.includes("heat")) return CHART_TYPE.HEATMAP;
|
|
531
|
+
if (i.includes("gauge") || i.includes("kpi")) return CHART_TYPE.GAUGE;
|
|
532
|
+
}
|
|
533
|
+
if (dataShape) {
|
|
534
|
+
if (dataShape.dimensions === 1 && dataShape.categorical)
|
|
535
|
+
return CHART_TYPE.PIE;
|
|
536
|
+
if (dataShape.timeseries) return CHART_TYPE.LINE;
|
|
537
|
+
if (dataShape.dimensions === 2) return CHART_TYPE.SCATTER;
|
|
538
|
+
}
|
|
539
|
+
return CHART_TYPE.TABLE;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
export function createDashboardV2(db, { name, widgets = [], layout } = {}) {
|
|
543
|
+
if (!name) throw new Error("name is required");
|
|
544
|
+
let layoutSpec;
|
|
545
|
+
if (!layout) {
|
|
546
|
+
layoutSpec = { type: DASHBOARD_LAYOUT.GRID, columns: 2 };
|
|
547
|
+
} else if (typeof layout === "string") {
|
|
548
|
+
const valid = Object.values(DASHBOARD_LAYOUT);
|
|
549
|
+
if (!valid.includes(layout)) {
|
|
550
|
+
throw new Error(
|
|
551
|
+
`Invalid layout '${layout}'. Expected one of ${valid.join(", ")}`,
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
layoutSpec = { type: layout, columns: 2 };
|
|
555
|
+
} else {
|
|
556
|
+
const type = layout.type || DASHBOARD_LAYOUT.GRID;
|
|
557
|
+
const valid = Object.values(DASHBOARD_LAYOUT);
|
|
558
|
+
if (!valid.includes(type)) {
|
|
559
|
+
throw new Error(
|
|
560
|
+
`Invalid layout type '${type}'. Expected one of ${valid.join(", ")}`,
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
layoutSpec = { ...layout, type };
|
|
564
|
+
}
|
|
565
|
+
return createDashboard(db, name, widgets, layoutSpec);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
export function updateReportStatus(db, { reportId, status } = {}) {
|
|
569
|
+
const valid = Object.values(REPORT_STATUS);
|
|
570
|
+
if (!valid.includes(status)) {
|
|
571
|
+
throw new Error(
|
|
572
|
+
`Invalid status '${status}'. Expected one of ${valid.join(", ")}`,
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
const current = _reportStatusV2.get(reportId) || REPORT_STATUS.DRAFT;
|
|
576
|
+
const allowed = _allowedReportTransitions[current];
|
|
577
|
+
if (!allowed.has(status) && current !== status) {
|
|
578
|
+
throw new Error(`Invalid status transition: ${current} → ${status}`);
|
|
579
|
+
}
|
|
580
|
+
_reportStatusV2.set(reportId, status);
|
|
581
|
+
const hist = _statusHistoryV2.get(reportId) || [];
|
|
582
|
+
hist.push({
|
|
583
|
+
from: current,
|
|
584
|
+
to: status,
|
|
585
|
+
at: new Date().toISOString(),
|
|
586
|
+
});
|
|
587
|
+
_statusHistoryV2.set(reportId, hist);
|
|
588
|
+
return { reportId, status, previous: current };
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
export function getReportStatus(reportId) {
|
|
592
|
+
return _reportStatusV2.get(reportId) || REPORT_STATUS.DRAFT;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
export function getReportStatusHistory(reportId) {
|
|
596
|
+
return (_statusHistoryV2.get(reportId) || []).slice();
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
export function getBIStatsV2(db) {
|
|
600
|
+
const dashboards = db.prepare(`SELECT id FROM bi_dashboards`).all();
|
|
601
|
+
const reports = db.prepare(`SELECT id, format FROM bi_reports`).all();
|
|
602
|
+
const scheduled = db.prepare(`SELECT id FROM bi_scheduled`).all();
|
|
603
|
+
|
|
604
|
+
const byStatus = {
|
|
605
|
+
draft: 0,
|
|
606
|
+
generated: 0,
|
|
607
|
+
scheduled: 0,
|
|
608
|
+
archived: 0,
|
|
609
|
+
};
|
|
610
|
+
for (const r of reports) {
|
|
611
|
+
const s = _reportStatusV2.get(r.id) || REPORT_STATUS.DRAFT;
|
|
612
|
+
byStatus[s] = (byStatus[s] || 0) + 1;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const byFormat = {};
|
|
616
|
+
for (const r of reports) {
|
|
617
|
+
const f = r.format || "pdf";
|
|
618
|
+
byFormat[f] = (byFormat[f] || 0) + 1;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
return {
|
|
622
|
+
dashboards: dashboards.length,
|
|
623
|
+
reports: {
|
|
624
|
+
total: reports.length,
|
|
625
|
+
byStatus,
|
|
626
|
+
byFormat,
|
|
627
|
+
},
|
|
628
|
+
scheduled: scheduled.length,
|
|
629
|
+
templates: _templates.length,
|
|
630
|
+
chartTypes: Object.values(CHART_TYPE).length,
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
export function _resetV2State() {
|
|
635
|
+
_reportStatusV2.clear();
|
|
636
|
+
_statusHistoryV2.clear();
|
|
637
|
+
}
|
package/src/lib/cross-chain.js
CHANGED
|
@@ -666,4 +666,349 @@ export function _resetState() {
|
|
|
666
666
|
_swaps.clear();
|
|
667
667
|
_messages.clear();
|
|
668
668
|
_chainConfigs.clear();
|
|
669
|
+
_maxActiveBridgesPerAddress = DEFAULT_MAX_ACTIVE_BRIDGES_PER_ADDRESS;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/* ══════════════════════════════════════════════════════════
|
|
673
|
+
* Phase 89 — Cross-Chain V2 surface (strictly additive)
|
|
674
|
+
*
|
|
675
|
+
* Adds canonical enums, per-address bridge concurrency cap,
|
|
676
|
+
* chain config CRUD, patch-merged state-machine setters,
|
|
677
|
+
* auto-expire swaps, and all-enum-key stats V2.
|
|
678
|
+
* ══════════════════════════════════════════════════════════ */
|
|
679
|
+
|
|
680
|
+
/* ── V2 Frozen Enums ────────────────────────────────────── */
|
|
681
|
+
|
|
682
|
+
export const BRIDGE_STATUS_V2 = Object.freeze({
|
|
683
|
+
PENDING: "pending",
|
|
684
|
+
LOCKED: "locked",
|
|
685
|
+
MINTED: "minted",
|
|
686
|
+
COMPLETED: "completed",
|
|
687
|
+
REFUNDED: "refunded",
|
|
688
|
+
FAILED: "failed",
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
export const SWAP_STATUS_V2 = Object.freeze({
|
|
692
|
+
INITIATED: "initiated",
|
|
693
|
+
HASH_LOCKED: "hash_locked",
|
|
694
|
+
CLAIMED: "claimed",
|
|
695
|
+
REFUNDED: "refunded",
|
|
696
|
+
EXPIRED: "expired",
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
export const MESSAGE_STATUS_V2 = Object.freeze({
|
|
700
|
+
PENDING: "pending",
|
|
701
|
+
SENT: "sent",
|
|
702
|
+
DELIVERED: "delivered",
|
|
703
|
+
FAILED: "failed",
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
export const CHAIN_ID_V2 = Object.freeze({
|
|
707
|
+
ETHEREUM: "ethereum",
|
|
708
|
+
POLYGON: "polygon",
|
|
709
|
+
BSC: "bsc",
|
|
710
|
+
ARBITRUM: "arbitrum",
|
|
711
|
+
SOLANA: "solana",
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
const DEFAULT_MAX_ACTIVE_BRIDGES_PER_ADDRESS = 3;
|
|
715
|
+
export const CROSSCHAIN_DEFAULT_MAX_ACTIVE_BRIDGES_PER_ADDRESS =
|
|
716
|
+
DEFAULT_MAX_ACTIVE_BRIDGES_PER_ADDRESS;
|
|
717
|
+
|
|
718
|
+
let _maxActiveBridgesPerAddress = DEFAULT_MAX_ACTIVE_BRIDGES_PER_ADDRESS;
|
|
719
|
+
|
|
720
|
+
/* ── State Machine Definitions ──────────────────────────── */
|
|
721
|
+
|
|
722
|
+
const BRIDGE_TRANSITIONS_V2 = new Map([
|
|
723
|
+
["pending", new Set(["locked", "failed"])],
|
|
724
|
+
["locked", new Set(["minted", "refunded", "failed"])],
|
|
725
|
+
["minted", new Set(["completed", "failed"])],
|
|
726
|
+
]);
|
|
727
|
+
const BRIDGE_TERMINALS_V2 = new Set(["completed", "refunded", "failed"]);
|
|
728
|
+
|
|
729
|
+
const SWAP_TRANSITIONS_V2 = new Map([
|
|
730
|
+
["initiated", new Set(["hash_locked", "claimed", "refunded", "expired"])],
|
|
731
|
+
["hash_locked", new Set(["claimed", "refunded", "expired"])],
|
|
732
|
+
]);
|
|
733
|
+
const SWAP_TERMINALS_V2 = new Set(["claimed", "refunded", "expired"]);
|
|
734
|
+
|
|
735
|
+
const MESSAGE_TRANSITIONS_V2 = new Map([
|
|
736
|
+
["pending", new Set(["sent", "failed"])],
|
|
737
|
+
["sent", new Set(["delivered", "failed"])],
|
|
738
|
+
["failed", new Set(["pending"])],
|
|
739
|
+
]);
|
|
740
|
+
const MESSAGE_TERMINALS_V2 = new Set(["delivered"]);
|
|
741
|
+
|
|
742
|
+
/* ── Concurrency Cap ───────────────────────────────────── */
|
|
743
|
+
|
|
744
|
+
export function setMaxActiveBridgesPerAddress(n) {
|
|
745
|
+
if (typeof n !== "number" || Number.isNaN(n) || n < 1) {
|
|
746
|
+
throw new Error("Max active bridges must be a positive integer");
|
|
747
|
+
}
|
|
748
|
+
_maxActiveBridgesPerAddress = Math.floor(n);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
export function getMaxActiveBridgesPerAddress() {
|
|
752
|
+
return _maxActiveBridgesPerAddress;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
export function getActiveBridgeCount(address) {
|
|
756
|
+
let count = 0;
|
|
757
|
+
for (const b of _bridges.values()) {
|
|
758
|
+
if (BRIDGE_TERMINALS_V2.has(b.status)) continue;
|
|
759
|
+
if (address == null || b.sender_address === address) count += 1;
|
|
760
|
+
}
|
|
761
|
+
return count;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/* ── Chain Config V2 ───────────────────────────────────── */
|
|
765
|
+
|
|
766
|
+
export function configureChainV2({
|
|
767
|
+
chainId,
|
|
768
|
+
rpcUrl,
|
|
769
|
+
contractAddress,
|
|
770
|
+
enabled = true,
|
|
771
|
+
}) {
|
|
772
|
+
if (!_validateChain(chainId)) {
|
|
773
|
+
throw new Error(`Unsupported chain: ${chainId}`);
|
|
774
|
+
}
|
|
775
|
+
const cfg = {
|
|
776
|
+
chainId,
|
|
777
|
+
rpcUrl: rpcUrl || null,
|
|
778
|
+
contractAddress: contractAddress || null,
|
|
779
|
+
enabled: enabled !== false,
|
|
780
|
+
updatedAt: _now(),
|
|
781
|
+
};
|
|
782
|
+
_chainConfigs.set(chainId, cfg);
|
|
783
|
+
return { ...cfg };
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
export function getChainConfigV2(chainId) {
|
|
787
|
+
const cfg = _chainConfigs.get(chainId);
|
|
788
|
+
return cfg ? { ...cfg } : null;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
export function listChainsV2() {
|
|
792
|
+
return Object.values(SUPPORTED_CHAINS).map((chain) => {
|
|
793
|
+
const cfg = _chainConfigs.get(chain.id);
|
|
794
|
+
return {
|
|
795
|
+
...chain,
|
|
796
|
+
enabled: cfg ? cfg.enabled : false,
|
|
797
|
+
rpcUrl: cfg ? cfg.rpcUrl : null,
|
|
798
|
+
contractAddress: cfg ? cfg.contractAddress : null,
|
|
799
|
+
};
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/* ── Bridge V2 (throws + cap enforcement) ──────────────── */
|
|
804
|
+
|
|
805
|
+
export function bridgeAssetV2(
|
|
806
|
+
db,
|
|
807
|
+
{ fromChain, toChain, asset, amount, senderAddress, recipientAddress },
|
|
808
|
+
) {
|
|
809
|
+
if (!_validateChain(fromChain)) {
|
|
810
|
+
throw new Error(`Unsupported source chain: ${fromChain}`);
|
|
811
|
+
}
|
|
812
|
+
if (!_validateChain(toChain)) {
|
|
813
|
+
throw new Error(`Unsupported destination chain: ${toChain}`);
|
|
814
|
+
}
|
|
815
|
+
if (fromChain === toChain) {
|
|
816
|
+
throw new Error("Source and destination chains must differ");
|
|
817
|
+
}
|
|
818
|
+
if (!amount || amount <= 0) {
|
|
819
|
+
throw new Error("Amount must be positive");
|
|
820
|
+
}
|
|
821
|
+
if (amount > DEFAULT_CONFIG.maxBridgeAmount) {
|
|
822
|
+
throw new Error(
|
|
823
|
+
`Amount ${amount} exceeds max ${DEFAULT_CONFIG.maxBridgeAmount}`,
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
if (senderAddress) {
|
|
827
|
+
const active = getActiveBridgeCount(senderAddress);
|
|
828
|
+
if (active >= _maxActiveBridgesPerAddress) {
|
|
829
|
+
throw new Error(
|
|
830
|
+
`Max active bridges per address reached (${active}/${_maxActiveBridgesPerAddress})`,
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
const result = bridgeAsset(db, {
|
|
836
|
+
fromChain,
|
|
837
|
+
toChain,
|
|
838
|
+
asset,
|
|
839
|
+
amount,
|
|
840
|
+
senderAddress,
|
|
841
|
+
recipientAddress,
|
|
842
|
+
});
|
|
843
|
+
return result;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/* ── Generic patch-merged state setters ────────────────── */
|
|
847
|
+
|
|
848
|
+
export function setBridgeStatusV2(db, bridgeId, newStatus, patch = {}) {
|
|
849
|
+
const b = _bridges.get(bridgeId);
|
|
850
|
+
if (!b) throw new Error(`Bridge not found: ${bridgeId}`);
|
|
851
|
+
|
|
852
|
+
const allowed = BRIDGE_TRANSITIONS_V2.get(b.status);
|
|
853
|
+
if (!allowed || !allowed.has(newStatus)) {
|
|
854
|
+
throw new Error(`Invalid bridge transition: ${b.status} → ${newStatus}`);
|
|
855
|
+
}
|
|
856
|
+
if (!Object.values(BRIDGE_STATUS_V2).includes(newStatus)) {
|
|
857
|
+
throw new Error(`Unknown bridge status: ${newStatus}`);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
b.status = newStatus;
|
|
861
|
+
if (patch.lockTxHash !== undefined) b.lock_tx_hash = patch.lockTxHash;
|
|
862
|
+
if (patch.mintTxHash !== undefined) b.mint_tx_hash = patch.mintTxHash;
|
|
863
|
+
if (patch.errorMessage !== undefined) b.error_message = patch.errorMessage;
|
|
864
|
+
if (BRIDGE_TERMINALS_V2.has(newStatus) && !b.completed_at) {
|
|
865
|
+
b.completed_at = _now();
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
db.prepare(
|
|
869
|
+
`UPDATE cc_bridges SET status = ?, lock_tx_hash = ?, mint_tx_hash = ?,
|
|
870
|
+
completed_at = ?, error_message = ? WHERE id = ?`,
|
|
871
|
+
).run(
|
|
872
|
+
b.status,
|
|
873
|
+
b.lock_tx_hash,
|
|
874
|
+
b.mint_tx_hash,
|
|
875
|
+
b.completed_at,
|
|
876
|
+
b.error_message,
|
|
877
|
+
bridgeId,
|
|
878
|
+
);
|
|
879
|
+
|
|
880
|
+
return { ...b };
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
export function setSwapStatusV2(db, swapId, newStatus, patch = {}) {
|
|
884
|
+
const s = _swaps.get(swapId);
|
|
885
|
+
if (!s) throw new Error(`Swap not found: ${swapId}`);
|
|
886
|
+
|
|
887
|
+
const allowed = SWAP_TRANSITIONS_V2.get(s.status);
|
|
888
|
+
if (!allowed || !allowed.has(newStatus)) {
|
|
889
|
+
throw new Error(`Invalid swap transition: ${s.status} → ${newStatus}`);
|
|
890
|
+
}
|
|
891
|
+
if (!Object.values(SWAP_STATUS_V2).includes(newStatus)) {
|
|
892
|
+
throw new Error(`Unknown swap status: ${newStatus}`);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
s.status = newStatus;
|
|
896
|
+
if (patch.claimTxHash !== undefined) s.claim_tx_hash = patch.claimTxHash;
|
|
897
|
+
if (patch.refundTxHash !== undefined) s.refund_tx_hash = patch.refundTxHash;
|
|
898
|
+
|
|
899
|
+
db.prepare(
|
|
900
|
+
`UPDATE cc_swaps SET status = ?, claim_tx_hash = ?, refund_tx_hash = ?
|
|
901
|
+
WHERE id = ?`,
|
|
902
|
+
).run(s.status, s.claim_tx_hash, s.refund_tx_hash, swapId);
|
|
903
|
+
|
|
904
|
+
const out = { ...s };
|
|
905
|
+
delete out.secret;
|
|
906
|
+
return out;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
export function setMessageStatusV2(db, messageId, newStatus, patch = {}) {
|
|
910
|
+
const m = _messages.get(messageId);
|
|
911
|
+
if (!m) throw new Error(`Message not found: ${messageId}`);
|
|
912
|
+
|
|
913
|
+
const allowed = MESSAGE_TRANSITIONS_V2.get(m.status);
|
|
914
|
+
if (!allowed || !allowed.has(newStatus)) {
|
|
915
|
+
throw new Error(`Invalid message transition: ${m.status} → ${newStatus}`);
|
|
916
|
+
}
|
|
917
|
+
if (!Object.values(MESSAGE_STATUS_V2).includes(newStatus)) {
|
|
918
|
+
throw new Error(`Unknown message status: ${newStatus}`);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
m.status = newStatus;
|
|
922
|
+
if (patch.sourceTxHash !== undefined) m.source_tx_hash = patch.sourceTxHash;
|
|
923
|
+
if (patch.destinationTxHash !== undefined)
|
|
924
|
+
m.destination_tx_hash = patch.destinationTxHash;
|
|
925
|
+
if (newStatus === "delivered" && !m.delivered_at) m.delivered_at = _now();
|
|
926
|
+
if (newStatus === "pending") m.retries += 1;
|
|
927
|
+
|
|
928
|
+
db.prepare(
|
|
929
|
+
`UPDATE cc_messages SET status = ?, source_tx_hash = ?, destination_tx_hash = ?,
|
|
930
|
+
delivered_at = ?, retries = ? WHERE id = ?`,
|
|
931
|
+
).run(
|
|
932
|
+
m.status,
|
|
933
|
+
m.source_tx_hash,
|
|
934
|
+
m.destination_tx_hash,
|
|
935
|
+
m.delivered_at,
|
|
936
|
+
m.retries,
|
|
937
|
+
messageId,
|
|
938
|
+
);
|
|
939
|
+
|
|
940
|
+
return { ...m };
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
/* ── Auto-expire swaps ─────────────────────────────────── */
|
|
944
|
+
|
|
945
|
+
export function autoExpireSwapsV2(db) {
|
|
946
|
+
const now = _now();
|
|
947
|
+
const expired = [];
|
|
948
|
+
for (const s of _swaps.values()) {
|
|
949
|
+
if (s.status !== "initiated" && s.status !== "hash_locked") continue;
|
|
950
|
+
if (s.expires_at > now) continue;
|
|
951
|
+
|
|
952
|
+
s.status = "expired";
|
|
953
|
+
db.prepare("UPDATE cc_swaps SET status = ? WHERE id = ?").run(
|
|
954
|
+
"expired",
|
|
955
|
+
s.id,
|
|
956
|
+
);
|
|
957
|
+
const out = { ...s };
|
|
958
|
+
delete out.secret;
|
|
959
|
+
expired.push(out);
|
|
960
|
+
}
|
|
961
|
+
return expired;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
/* ── Stats V2 ──────────────────────────────────────────── */
|
|
965
|
+
|
|
966
|
+
export function getCrossChainStatsV2() {
|
|
967
|
+
const bridgesByStatus = {};
|
|
968
|
+
for (const v of Object.values(BRIDGE_STATUS_V2)) bridgesByStatus[v] = 0;
|
|
969
|
+
const swapsByStatus = {};
|
|
970
|
+
for (const v of Object.values(SWAP_STATUS_V2)) swapsByStatus[v] = 0;
|
|
971
|
+
const messagesByStatus = {};
|
|
972
|
+
for (const v of Object.values(MESSAGE_STATUS_V2)) messagesByStatus[v] = 0;
|
|
973
|
+
const chainUsage = {};
|
|
974
|
+
for (const v of Object.values(CHAIN_ID_V2)) chainUsage[v] = 0;
|
|
975
|
+
|
|
976
|
+
let totalBridgeVolume = 0;
|
|
977
|
+
let totalFees = 0;
|
|
978
|
+
let activeBridges = 0;
|
|
979
|
+
for (const b of _bridges.values()) {
|
|
980
|
+
bridgesByStatus[b.status] = (bridgesByStatus[b.status] || 0) + 1;
|
|
981
|
+
chainUsage[b.from_chain] = (chainUsage[b.from_chain] || 0) + 1;
|
|
982
|
+
chainUsage[b.to_chain] = (chainUsage[b.to_chain] || 0) + 1;
|
|
983
|
+
totalBridgeVolume += b.amount;
|
|
984
|
+
totalFees += b.fee_amount;
|
|
985
|
+
if (!BRIDGE_TERMINALS_V2.has(b.status)) activeBridges += 1;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
for (const s of _swaps.values()) {
|
|
989
|
+
swapsByStatus[s.status] = (swapsByStatus[s.status] || 0) + 1;
|
|
990
|
+
chainUsage[s.from_chain] = (chainUsage[s.from_chain] || 0) + 1;
|
|
991
|
+
chainUsage[s.to_chain] = (chainUsage[s.to_chain] || 0) + 1;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
for (const m of _messages.values()) {
|
|
995
|
+
messagesByStatus[m.status] = (messagesByStatus[m.status] || 0) + 1;
|
|
996
|
+
chainUsage[m.from_chain] = (chainUsage[m.from_chain] || 0) + 1;
|
|
997
|
+
chainUsage[m.to_chain] = (chainUsage[m.to_chain] || 0) + 1;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
return {
|
|
1001
|
+
totalBridges: _bridges.size,
|
|
1002
|
+
totalSwaps: _swaps.size,
|
|
1003
|
+
totalMessages: _messages.size,
|
|
1004
|
+
activeBridges,
|
|
1005
|
+
maxActiveBridgesPerAddress: _maxActiveBridgesPerAddress,
|
|
1006
|
+
bridgesByStatus,
|
|
1007
|
+
swapsByStatus,
|
|
1008
|
+
messagesByStatus,
|
|
1009
|
+
chainUsage,
|
|
1010
|
+
totalBridgeVolume,
|
|
1011
|
+
totalFees: Math.round(totalFees * 1000) / 1000,
|
|
1012
|
+
configuredChains: _chainConfigs.size,
|
|
1013
|
+
};
|
|
669
1014
|
}
|