chainlesschain 0.51.0 → 0.66.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.
Files changed (39) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/.build-hash +1 -1
  3. package/src/assets/web-panel/assets/{AppLayout-Rvi759IS.js → AppLayout-6SPt_8Y_.js} +1 -1
  4. package/src/assets/web-panel/assets/{Dashboard-DBhFxXYQ.js → Dashboard-Br7kCwKJ.js} +2 -2
  5. package/src/assets/web-panel/assets/Dashboard-CKeMmCoT.css +1 -0
  6. package/src/assets/web-panel/assets/{index-uL0cZ8N_.js → index-tN-8TosE.js} +2 -2
  7. package/src/assets/web-panel/index.html +2 -2
  8. package/src/commands/agent-network.js +785 -0
  9. package/src/commands/automation.js +654 -0
  10. package/src/commands/dao.js +565 -0
  11. package/src/commands/did-v2.js +620 -0
  12. package/src/commands/economy.js +578 -0
  13. package/src/commands/evolution.js +391 -0
  14. package/src/commands/hmemory.js +442 -0
  15. package/src/commands/perf.js +433 -0
  16. package/src/commands/pipeline.js +449 -0
  17. package/src/commands/plugin-ecosystem.js +517 -0
  18. package/src/commands/sandbox.js +401 -0
  19. package/src/commands/social.js +311 -0
  20. package/src/commands/sso.js +798 -0
  21. package/src/commands/workflow.js +320 -0
  22. package/src/commands/zkp.js +227 -1
  23. package/src/index.js +21 -0
  24. package/src/lib/agent-economy.js +479 -0
  25. package/src/lib/agent-network.js +1121 -0
  26. package/src/lib/automation-engine.js +948 -0
  27. package/src/lib/dao-governance.js +569 -0
  28. package/src/lib/did-v2-manager.js +1127 -0
  29. package/src/lib/evolution-system.js +453 -0
  30. package/src/lib/hierarchical-memory.js +481 -0
  31. package/src/lib/perf-tuning.js +734 -0
  32. package/src/lib/pipeline-orchestrator.js +928 -0
  33. package/src/lib/plugin-ecosystem.js +1109 -0
  34. package/src/lib/sandbox-v2.js +306 -0
  35. package/src/lib/social-graph-analytics.js +707 -0
  36. package/src/lib/sso-manager.js +841 -0
  37. package/src/lib/workflow-engine.js +454 -1
  38. package/src/lib/zkp-engine.js +249 -20
  39. package/src/assets/web-panel/assets/Dashboard-BS-tzGNj.css +0 -1
@@ -5,11 +5,47 @@
5
5
 
6
6
  import crypto from "crypto";
7
7
 
8
+ /* ── Phase 92 canonical enums ─────────────────────────────── */
9
+
10
+ export const PROPOSAL_STATUS = Object.freeze({
11
+ DRAFT: "draft",
12
+ ACTIVE: "active",
13
+ QUEUE: "queue",
14
+ EXECUTE: "execute",
15
+ PASSED: "passed",
16
+ REJECTED: "rejected",
17
+ CANCELLED: "cancelled",
18
+ });
19
+
20
+ export const VOTE_TYPE = Object.freeze({
21
+ FOR: "for",
22
+ AGAINST: "against",
23
+ ABSTAIN: "abstain",
24
+ });
25
+
26
+ export const DELEGATION_STATUS = Object.freeze({
27
+ ACTIVE: "active",
28
+ REVOKED: "revoked",
29
+ EXPIRED: "expired",
30
+ });
31
+
32
+ export const TREASURY_TX_TYPE = Object.freeze({
33
+ ALLOCATION: "allocation",
34
+ WITHDRAWAL: "withdrawal",
35
+ REFUND: "refund",
36
+ REWARD: "reward",
37
+ DEPOSIT: "deposit",
38
+ });
39
+
8
40
  /* ── In-memory stores ──────────────────────────────────────── */
9
41
  const _proposals = new Map();
10
42
  const _votes = new Map();
11
43
  const _delegations = new Map();
12
44
  const _treasury = { balance: 0, allocations: [] };
45
+ // Phase 92: richer delegation records keyed by id
46
+ const _delegationsV2 = new Map();
47
+ // Phase 92: treasury transaction log with balance_after
48
+ const _treasuryTxs = [];
13
49
 
14
50
  let _config = {
15
51
  votingPeriod: 604800000, // 7 days in ms
@@ -17,6 +53,16 @@ let _config = {
17
53
  executionDelay: 86400000, // 1 day in ms
18
54
  };
19
55
 
56
+ let _configV2 = {
57
+ votingDurationMs: 604800000,
58
+ quorumPercentage: 10,
59
+ timelockMs: 172800000,
60
+ quadraticEnabled: true,
61
+ maxDelegationDepth: 3,
62
+ proposalThreshold: 100,
63
+ maxSingleAllocation: 100000,
64
+ };
65
+
20
66
  /* ── Schema ────────────────────────────────────────────────── */
21
67
 
22
68
  export function ensureDAOv2Tables(db) {
@@ -280,12 +326,526 @@ export function configure(config) {
280
326
  return { ..._config };
281
327
  }
282
328
 
329
+ /* ══════════════════════════════════════════════════════════════
330
+ * Phase 92: DAO Governance 2.0 canonical surface
331
+ * ──────────────────────────────────────────────────────────── */
332
+
333
+ /* ── Proposal lifecycle (Draft → Active → Queue → Execute) ─── */
334
+
335
+ export function createProposalV2(db, opts) {
336
+ const {
337
+ title,
338
+ description,
339
+ proposerDid,
340
+ type = "standard",
341
+ actions = [],
342
+ votingDurationMs,
343
+ } = opts || {};
344
+
345
+ if (!title) throw new Error("Title is required");
346
+ if (!proposerDid) throw new Error("proposerDid is required");
347
+
348
+ const id = `prop-${Date.now()}-${crypto.randomBytes(4).toString("hex")}`;
349
+ const now = new Date().toISOString();
350
+ const duration = votingDurationMs ?? _configV2.votingDurationMs;
351
+
352
+ const proposal = {
353
+ id,
354
+ title,
355
+ description: description || "",
356
+ proposer: proposerDid,
357
+ proposerDid,
358
+ type,
359
+ status: PROPOSAL_STATUS.DRAFT,
360
+ actions: Array.isArray(actions) ? actions : [],
361
+ votesFor: 0,
362
+ votesAgainst: 0,
363
+ votesAbstain: 0,
364
+ quadraticWeightFor: 0,
365
+ quadraticWeightAgainst: 0,
366
+ votingType: "quadratic",
367
+ votingDurationMs: duration,
368
+ quorumReached: false,
369
+ votingStart: null,
370
+ votingEnd: null,
371
+ queueEnd: null,
372
+ executedAt: null,
373
+ cancelledAt: null,
374
+ createdAt: now,
375
+ updatedAt: now,
376
+ endsAt: null,
377
+ };
378
+
379
+ _proposals.set(id, proposal);
380
+
381
+ db.prepare(
382
+ `INSERT INTO dao_v2_proposals (id, title, description, proposer, status, votes_for, votes_against, voting_type, created_at, ends_at, executed_at)
383
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
384
+ ).run(
385
+ id,
386
+ title,
387
+ proposal.description,
388
+ proposerDid,
389
+ PROPOSAL_STATUS.DRAFT,
390
+ 0,
391
+ 0,
392
+ "quadratic",
393
+ now,
394
+ null,
395
+ null,
396
+ );
397
+
398
+ return proposal;
399
+ }
400
+
401
+ export function activateProposal(db, proposalId) {
402
+ const proposal = _proposals.get(proposalId);
403
+ if (!proposal) throw new Error(`Proposal not found: ${proposalId}`);
404
+ if (proposal.status !== PROPOSAL_STATUS.DRAFT) {
405
+ throw new Error(
406
+ `Only DRAFT proposals can be activated (current: ${proposal.status})`,
407
+ );
408
+ }
409
+
410
+ const now = Date.now();
411
+ const nowIso = new Date(now).toISOString();
412
+ const endsAtIso = new Date(now + proposal.votingDurationMs).toISOString();
413
+
414
+ proposal.status = PROPOSAL_STATUS.ACTIVE;
415
+ proposal.votingStart = nowIso;
416
+ proposal.votingEnd = endsAtIso;
417
+ proposal.endsAt = endsAtIso;
418
+ proposal.updatedAt = nowIso;
419
+
420
+ db.prepare(
421
+ `UPDATE dao_v2_proposals SET status = ?, ends_at = ? WHERE id = ?`,
422
+ ).run(PROPOSAL_STATUS.ACTIVE, endsAtIso, proposalId);
423
+
424
+ return proposal;
425
+ }
426
+
427
+ export function castVote(db, opts) {
428
+ const { proposalId, voterDid, voteType, voteCount = 1, balance } = opts || {};
429
+
430
+ const proposal = _proposals.get(proposalId);
431
+ if (!proposal) throw new Error(`Proposal not found: ${proposalId}`);
432
+ if (proposal.status !== PROPOSAL_STATUS.ACTIVE) {
433
+ throw new Error(`Proposal is not ACTIVE (current: ${proposal.status})`);
434
+ }
435
+ if (!Object.values(VOTE_TYPE).includes(voteType)) {
436
+ throw new Error(
437
+ `Invalid voteType: ${voteType}. Must be one of: ${Object.values(VOTE_TYPE).join(", ")}`,
438
+ );
439
+ }
440
+ if (typeof voteCount !== "number" || voteCount <= 0) {
441
+ throw new Error("voteCount must be a positive number");
442
+ }
443
+
444
+ const quadraticCost = _configV2.quadraticEnabled
445
+ ? voteCount * voteCount
446
+ : voteCount;
447
+
448
+ if (balance !== undefined && balance < quadraticCost) {
449
+ throw new Error(
450
+ `Insufficient balance: need ${quadraticCost}, have ${balance}`,
451
+ );
452
+ }
453
+
454
+ // Anti-sybil: reject duplicate voter on same proposal
455
+ for (const v of _votes.values()) {
456
+ if (v.proposalId === proposalId && v.voterDid === voterDid) {
457
+ throw new Error(`Voter ${voterDid} already voted on ${proposalId}`);
458
+ }
459
+ }
460
+
461
+ const voteId = `vote-${Date.now()}-${crypto.randomBytes(4).toString("hex")}`;
462
+ const now = new Date().toISOString();
463
+
464
+ // Quadratic weight = voteCount (linear weight), but cost = n²
465
+ if (voteType === VOTE_TYPE.FOR) {
466
+ proposal.votesFor += voteCount;
467
+ proposal.quadraticWeightFor += voteCount;
468
+ } else if (voteType === VOTE_TYPE.AGAINST) {
469
+ proposal.votesAgainst += voteCount;
470
+ proposal.quadraticWeightAgainst += voteCount;
471
+ } else {
472
+ proposal.votesAbstain += voteCount;
473
+ }
474
+
475
+ const record = {
476
+ id: voteId,
477
+ proposalId,
478
+ voter: voterDid,
479
+ voterDid,
480
+ voteType,
481
+ voteCount,
482
+ quadraticCost,
483
+ weight: voteCount,
484
+ direction: voteType,
485
+ delegatedFrom: null,
486
+ createdAt: now,
487
+ };
488
+
489
+ _votes.set(voteId, record);
490
+
491
+ db.prepare(
492
+ `INSERT INTO dao_v2_votes (id, proposal_id, voter, weight, direction, delegated_from, created_at)
493
+ VALUES (?, ?, ?, ?, ?, ?, ?)`,
494
+ ).run(voteId, proposalId, voterDid, voteCount, voteType, null, now);
495
+
496
+ db.prepare(
497
+ `UPDATE dao_v2_proposals SET votes_for = ?, votes_against = ? WHERE id = ?`,
498
+ ).run(proposal.votesFor, proposal.votesAgainst, proposalId);
499
+
500
+ return record;
501
+ }
502
+
503
+ /* ── Delegation v2 (cycle-safe, depth-limited, revocable) ─── */
504
+
505
+ function _resolveDelegateChain(fromDid) {
506
+ const chain = [];
507
+ const visited = new Set();
508
+ let cursor = fromDid;
509
+ while (cursor) {
510
+ if (visited.has(cursor)) return { cycle: true, chain };
511
+ visited.add(cursor);
512
+ const next = [..._delegationsV2.values()].find(
513
+ (d) => d.fromDid === cursor && d.status === DELEGATION_STATUS.ACTIVE,
514
+ );
515
+ if (!next) break;
516
+ chain.push(next);
517
+ cursor = next.toDid;
518
+ }
519
+ return { cycle: false, chain };
520
+ }
521
+
522
+ export function delegateVotingPower(db, opts) {
523
+ const { fromDid, toDid, weight = 1, expiresAt = null } = opts || {};
524
+
525
+ if (!fromDid) throw new Error("fromDid is required");
526
+ if (!toDid) throw new Error("toDid is required");
527
+ if (fromDid === toDid) throw new Error("Cannot delegate to self");
528
+
529
+ // Cycle detection: would toDid eventually delegate back to fromDid?
530
+ const probe = _resolveDelegateChain(toDid);
531
+ if (probe.cycle) {
532
+ throw new Error("Delegation chain contains a cycle");
533
+ }
534
+ if (probe.chain.some((d) => d.toDid === fromDid)) {
535
+ throw new Error("Cyclic delegation detected");
536
+ }
537
+ if (probe.chain.length + 1 > _configV2.maxDelegationDepth) {
538
+ throw new Error(
539
+ `Delegation depth exceeds maximum (${_configV2.maxDelegationDepth})`,
540
+ );
541
+ }
542
+
543
+ const id = `deleg-${Date.now()}-${crypto.randomBytes(4).toString("hex")}`;
544
+ const now = new Date().toISOString();
545
+
546
+ const record = {
547
+ id,
548
+ fromDid,
549
+ toDid,
550
+ weight,
551
+ status: DELEGATION_STATUS.ACTIVE,
552
+ expiresAt,
553
+ revokedAt: null,
554
+ createdAt: now,
555
+ };
556
+
557
+ _delegationsV2.set(id, record);
558
+
559
+ // Also mirror to legacy Map for backward compatibility
560
+ _delegations.set(fromDid, { delegator: fromDid, delegate: toDid, weight });
561
+
562
+ db.prepare(
563
+ `INSERT OR REPLACE INTO dao_v2_delegations (delegator, delegate, weight, created_at)
564
+ VALUES (?, ?, ?, ?)`,
565
+ ).run(fromDid, toDid, weight, now);
566
+
567
+ return record;
568
+ }
569
+
570
+ export function revokeDelegation(db, fromDid) {
571
+ let revoked = null;
572
+ for (const d of _delegationsV2.values()) {
573
+ if (d.fromDid === fromDid && d.status === DELEGATION_STATUS.ACTIVE) {
574
+ d.status = DELEGATION_STATUS.REVOKED;
575
+ d.revokedAt = new Date().toISOString();
576
+ revoked = d;
577
+ }
578
+ }
579
+ if (!revoked) throw new Error(`No active delegation from ${fromDid}`);
580
+ _delegations.delete(fromDid);
581
+ return revoked;
582
+ }
583
+
584
+ export function getActiveDelegations() {
585
+ const now = new Date().toISOString();
586
+ const out = [];
587
+ for (const d of _delegationsV2.values()) {
588
+ if (d.status !== DELEGATION_STATUS.ACTIVE) continue;
589
+ if (d.expiresAt && d.expiresAt <= now) {
590
+ d.status = DELEGATION_STATUS.EXPIRED;
591
+ continue;
592
+ }
593
+ out.push(d);
594
+ }
595
+ return out;
596
+ }
597
+
598
+ /* ── Queue + Execute (timelock) ─────────────────────────────── */
599
+
600
+ function _isQuorumReached(proposal) {
601
+ const total =
602
+ proposal.votesFor + proposal.votesAgainst + proposal.votesAbstain;
603
+ if (total <= 0) return false;
604
+ const requiredFor = (total * _configV2.quorumPercentage) / 100;
605
+ return proposal.votesFor >= requiredFor;
606
+ }
607
+
608
+ export function queueProposal(db, proposalId) {
609
+ const proposal = _proposals.get(proposalId);
610
+ if (!proposal) throw new Error(`Proposal not found: ${proposalId}`);
611
+ if (proposal.status !== PROPOSAL_STATUS.ACTIVE) {
612
+ throw new Error(
613
+ `Only ACTIVE proposals can be queued (current: ${proposal.status})`,
614
+ );
615
+ }
616
+ if (proposal.votesFor <= proposal.votesAgainst) {
617
+ throw new Error("Proposal does not have majority support");
618
+ }
619
+ if (!_isQuorumReached(proposal)) {
620
+ proposal.quorumReached = false;
621
+ throw new Error("Quorum not reached");
622
+ }
623
+
624
+ const now = Date.now();
625
+ proposal.quorumReached = true;
626
+ proposal.status = PROPOSAL_STATUS.QUEUE;
627
+ proposal.queueEnd = new Date(now + _configV2.timelockMs).toISOString();
628
+ proposal.updatedAt = new Date(now).toISOString();
629
+
630
+ db.prepare(`UPDATE dao_v2_proposals SET status = ? WHERE id = ?`).run(
631
+ PROPOSAL_STATUS.QUEUE,
632
+ proposalId,
633
+ );
634
+
635
+ return proposal;
636
+ }
637
+
638
+ export function executeProposalV2(db, proposalId) {
639
+ const proposal = _proposals.get(proposalId);
640
+ if (!proposal) throw new Error(`Proposal not found: ${proposalId}`);
641
+ if (proposal.status !== PROPOSAL_STATUS.QUEUE) {
642
+ throw new Error(
643
+ `Only QUEUED proposals can be executed (current: ${proposal.status})`,
644
+ );
645
+ }
646
+ const now = Date.now();
647
+ const queueEndMs = new Date(proposal.queueEnd).getTime();
648
+ if (now < queueEndMs) {
649
+ throw new Error(`Timelock not elapsed (ends at ${proposal.queueEnd})`);
650
+ }
651
+
652
+ proposal.status = PROPOSAL_STATUS.EXECUTE;
653
+ proposal.executedAt = new Date(now).toISOString();
654
+ proposal.updatedAt = proposal.executedAt;
655
+
656
+ db.prepare(
657
+ `UPDATE dao_v2_proposals SET status = ?, executed_at = ? WHERE id = ?`,
658
+ ).run(PROPOSAL_STATUS.EXECUTE, proposal.executedAt, proposalId);
659
+
660
+ return proposal;
661
+ }
662
+
663
+ export function cancelProposal(db, proposalId) {
664
+ const proposal = _proposals.get(proposalId);
665
+ if (!proposal) throw new Error(`Proposal not found: ${proposalId}`);
666
+ if (
667
+ proposal.status === PROPOSAL_STATUS.EXECUTE ||
668
+ proposal.status === PROPOSAL_STATUS.CANCELLED
669
+ ) {
670
+ throw new Error(`Cannot cancel proposal in status: ${proposal.status}`);
671
+ }
672
+ const now = new Date().toISOString();
673
+ proposal.status = PROPOSAL_STATUS.CANCELLED;
674
+ proposal.cancelledAt = now;
675
+ proposal.updatedAt = now;
676
+ db.prepare(`UPDATE dao_v2_proposals SET status = ? WHERE id = ?`).run(
677
+ PROPOSAL_STATUS.CANCELLED,
678
+ proposalId,
679
+ );
680
+ return proposal;
681
+ }
682
+
683
+ /* ── Treasury v2 (proposal-linked, balance_after) ──────────── */
684
+
685
+ export function allocateFundsV2(db, opts) {
686
+ const { proposalId, recipient, amount, asset = "native", memo } = opts || {};
687
+ if (!proposalId) throw new Error("proposalId is required");
688
+ if (!recipient) throw new Error("recipient is required");
689
+ if (typeof amount !== "number" || amount <= 0) {
690
+ throw new Error("amount must be a positive number");
691
+ }
692
+ if (amount > _configV2.maxSingleAllocation) {
693
+ throw new Error(
694
+ `Amount exceeds maxSingleAllocation (${_configV2.maxSingleAllocation})`,
695
+ );
696
+ }
697
+
698
+ const proposal = _proposals.get(proposalId);
699
+ if (!proposal) throw new Error(`Proposal not found: ${proposalId}`);
700
+ if (proposal.status !== PROPOSAL_STATUS.EXECUTE) {
701
+ throw new Error(
702
+ `Allocation requires EXECUTED proposal (current: ${proposal.status})`,
703
+ );
704
+ }
705
+ if (_treasury.balance < amount) {
706
+ throw new Error("Insufficient treasury balance");
707
+ }
708
+
709
+ _treasury.balance -= amount;
710
+ const balanceAfter = _treasury.balance;
711
+
712
+ const id = `tx-${Date.now()}-${crypto.randomBytes(4).toString("hex")}`;
713
+ const now = new Date().toISOString();
714
+
715
+ const tx = {
716
+ id,
717
+ txType: TREASURY_TX_TYPE.ALLOCATION,
718
+ proposalId,
719
+ recipient,
720
+ amount,
721
+ asset,
722
+ memo: memo || "",
723
+ balanceAfter,
724
+ createdAt: now,
725
+ };
726
+
727
+ _treasuryTxs.push(tx);
728
+ _treasury.allocations.push({
729
+ id,
730
+ proposalId,
731
+ amount,
732
+ description: memo || "",
733
+ date: now,
734
+ });
735
+
736
+ db.prepare(
737
+ `INSERT INTO dao_v2_treasury (id, type, amount, description, proposal_id, created_at)
738
+ VALUES (?, ?, ?, ?, ?, ?)`,
739
+ ).run(id, TREASURY_TX_TYPE.ALLOCATION, amount, memo || "", proposalId, now);
740
+
741
+ return tx;
742
+ }
743
+
744
+ export function getTreasuryState() {
745
+ const totalAllocated = _treasuryTxs
746
+ .filter((t) => t.txType === TREASURY_TX_TYPE.ALLOCATION)
747
+ .reduce((s, t) => s + t.amount, 0);
748
+ return {
749
+ balance: _treasury.balance,
750
+ totalAllocated,
751
+ transactions: [..._treasuryTxs],
752
+ recentTxs: [..._treasuryTxs].slice(-10).reverse(),
753
+ allocations: [..._treasury.allocations],
754
+ };
755
+ }
756
+
757
+ /* ── Governance stats v2 ────────────────────────────────────── */
758
+
759
+ export function getGovernanceStatsV2(totalMembers = 0) {
760
+ const all = [..._proposals.values()];
761
+ const byStatus = {};
762
+ for (const s of Object.values(PROPOSAL_STATUS)) byStatus[s] = 0;
763
+ for (const p of all) byStatus[p.status] = (byStatus[p.status] || 0) + 1;
764
+
765
+ const uniqueVoters = new Set([..._votes.values()].map((v) => v.voter));
766
+ const participationRate =
767
+ totalMembers > 0 ? uniqueVoters.size / totalMembers : 0;
768
+
769
+ const activeDelegs = getActiveDelegations();
770
+ const delegationCoverage =
771
+ totalMembers > 0 ? activeDelegs.length / totalMembers : 0;
772
+
773
+ return {
774
+ totalProposals: all.length,
775
+ byStatus,
776
+ uniqueVoters: uniqueVoters.size,
777
+ participationRate,
778
+ activeDelegations: activeDelegs.length,
779
+ delegationCoverage,
780
+ treasuryBalance: _treasury.balance,
781
+ };
782
+ }
783
+
784
+ /* ── Configuration v2 ───────────────────────────────────────── */
785
+
786
+ export function configureV2(updates = {}) {
787
+ const allowed = [
788
+ "votingDurationMs",
789
+ "quorumPercentage",
790
+ "timelockMs",
791
+ "quadraticEnabled",
792
+ "maxDelegationDepth",
793
+ "proposalThreshold",
794
+ "maxSingleAllocation",
795
+ ];
796
+ for (const key of allowed) {
797
+ if (updates[key] !== undefined) _configV2[key] = updates[key];
798
+ }
799
+ return { ..._configV2 };
800
+ }
801
+
802
+ export function getConfigV2() {
803
+ return { ..._configV2 };
804
+ }
805
+
806
+ /* ── Deposit v2 (mirror to tx log) ──────────────────────────── */
807
+
808
+ export function depositToTreasuryV2(db, opts) {
809
+ const { amount, asset = "native", memo, depositorDid } = opts || {};
810
+ if (typeof amount !== "number" || amount <= 0) {
811
+ throw new Error("amount must be a positive number");
812
+ }
813
+
814
+ _treasury.balance += amount;
815
+ const id = `tx-${Date.now()}-${crypto.randomBytes(4).toString("hex")}`;
816
+ const now = new Date().toISOString();
817
+
818
+ const tx = {
819
+ id,
820
+ txType: TREASURY_TX_TYPE.DEPOSIT,
821
+ proposalId: null,
822
+ recipient: null,
823
+ amount,
824
+ asset,
825
+ memo: memo || "",
826
+ balanceAfter: _treasury.balance,
827
+ depositorDid: depositorDid || null,
828
+ createdAt: now,
829
+ };
830
+
831
+ _treasuryTxs.push(tx);
832
+
833
+ db.prepare(
834
+ `INSERT INTO dao_v2_treasury (id, type, amount, description, proposal_id, created_at)
835
+ VALUES (?, ?, ?, ?, ?, ?)`,
836
+ ).run(id, TREASURY_TX_TYPE.DEPOSIT, amount, memo || "", null, now);
837
+
838
+ return tx;
839
+ }
840
+
283
841
  /* ── Reset (for testing) ───────────────────────────────────── */
284
842
 
285
843
  export function _resetState() {
286
844
  _proposals.clear();
287
845
  _votes.clear();
288
846
  _delegations.clear();
847
+ _delegationsV2.clear();
848
+ _treasuryTxs.length = 0;
289
849
  _treasury.balance = 0;
290
850
  _treasury.allocations = [];
291
851
  _config = {
@@ -293,4 +853,13 @@ export function _resetState() {
293
853
  quorum: 0.1,
294
854
  executionDelay: 86400000,
295
855
  };
856
+ _configV2 = {
857
+ votingDurationMs: 604800000,
858
+ quorumPercentage: 10,
859
+ timelockMs: 172800000,
860
+ quadraticEnabled: true,
861
+ maxDelegationDepth: 3,
862
+ proposalThreshold: 100,
863
+ maxSingleAllocation: 100000,
864
+ };
296
865
  }