File size: 34,355 Bytes
60050be
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {FHE, euint64, ebool, externalEuint64} from "@fhevm/solidity/lib/FHE.sol";
import {SepoliaConfig} from "@fhevm/solidity/config/ZamaConfig.sol";

interface IReputationBadge {
    function mintOrUpgrade(uint256 agentId, address operator, uint8 tier) external;
    function tierOf(uint256 agentId) external view returns (uint8);
}

interface IInsurancePool {
    function receivePenalty(uint256 agentId) external payable;
    function delegateCredit(uint256 agentId, uint256 amount) external;
    function repayCredit(uint256 agentId) external payable;
}

/**
 * @title CipherTrust
 * @notice Confidential underwriting protocol for autonomous agents & robots.
 *
 * Autonomous AI trading bots, delivery robots, drone fleets, and DePIN devices
 * increasingly hold funds and execute tasks without human supervision. There is
 * no confidential way today to score their reliability and price the
 * collateral/insurance they must post -- any naive on-chain reputation system
 * leaks competitively sensitive operational data (uptime, error rates, routes,
 * strategy performance) to rivals, because blockchains are public by default.
 *
 * CipherTrust computes a rolling trust score and a required collateral bond
 * entirely under Fully Homomorphic Encryption. Operators, insurers, and task
 * marketplaces can rely on the *outcome* (bond tier, sufficiency check)
 * without ever seeing the raw encrypted telemetry that produced it.
 *
 * v0.2 additions (see docs/COMPETITIVE_ANALYSIS.md for why these were added):
 *  - Multi-oracle quorum: telemetry only affects the score once N independent
 *    oracles agree within a round, reducing single-oracle trust assumptions.
 *  - Async confidential slashing: an oracle can request a confidential SLA
 *    breach check; the breach flag is decrypted via Zama's public-decrypt +
 *    signature-verification flow before any penalty is applied on-chain.
 *  - Optional composability hooks into a soulbound ReputationBadge (public,
 *    selectively-revealed trust tier) and an InsurancePool (LP yield funded
 *    by slashing penalties), so other protocols can build on CipherTrust's
 *    output without ever touching an agent's raw telemetry.
 *
 * NOTE: this is an MVP scaffold. Verify every FHE.* call (especially the
 * makePubliclyDecryptable / checkSignatures async-decrypt flow) against the
 * exact current version of fhevm-solidity pinned in package.json before
 * deploying to a live network -- the FHE Solidity API surface evolves
 * between releases, and this flow has not yet been compiled/tested.
 */
contract CipherTrust is SepoliaConfig {
    address public admin;
    uint256 public nextAgentId;

    struct Agent {
        address operator;
        bool registered;
        bool active;
        uint256 identityId; // optional link into AgentIdentityRegistry, 0 if unset
        euint64 trustScore; // encrypted, 0-1000 scale
        euint64 requiredBond; // encrypted, wei
        uint256 postedBond; // public collateral currently deposited (wei)
        ebool bondSufficient; // encrypted boolean: postedBond >= requiredBond
        uint256 breachCount; // public count of confirmed SLA breaches
        uint256 trustScoreVar; // estimation uncertainty variance, initialized to 100 (public)
        euint64 liquidationThreshold; // encrypted minimum trust score before liquidation
        uint256 delegatedBond; // public delegated bond amount borrowed from the pool (wei)
        uint256 interestAccumulated; // public interest accumulated (wei)
        uint256 lastInterestUpdateTime; // timestamp of the last yield accrual
    }

    struct Task {
        uint256 agentId;
        address client;
        uint256 coverageLimit; // maximum ETH coverage (wei)
        bool active;
    }

    struct Lease {
        address lessee;
        uint256 agentId;
        uint256 hardwareId;
        uint256 requiredBond; // underwriting bond (wei)
        uint256 startTimestamp;
        bool active;
    }

    uint256 public nextTaskId = 1;
    mapping(uint256 => Task) public tasks;

    uint256 public nextLeaseId = 1;
    mapping(uint256 => Lease) public leases;
    mapping(address => uint256) public userActiveLeaseId;
    mapping(uint256 => uint256) public claimRequestTask; // decryption requestId => taskId
    mapping(uint256 => uint256) public agentActiveTaskId; // agentId => active taskId (0 if none)

    struct PendingRound {
        uint32 count;
        bool initialized;
        euint64 sumCompletion;
        euint64 sumUptime;
        euint64 sumLatency;
        euint64 sumError;
    }

    mapping(uint256 => Agent) private _agents;
    mapping(address => bool) public authorizedOracles;
    mapping(address => bool) public authorizedUnderwriters;

    // FHE-Stream: Confidential Staking Yields & Payroll Streams
    struct SalaryStream {
        euint64 flowRate;
        uint256 lastClaimBlock;
        bool active;
    }
    mapping(address => SalaryStream) private _salaryStreams;
    mapping(uint256 => address) public streamRequests;

    mapping(uint256 => PendingRound) private _pendingRounds; // agentId => in-flight quorum round
    mapping(uint256 => uint256) public currentRoundId; // agentId => round id
    mapping(uint256 => mapping(address => uint256)) private _oracleLastRound; // agentId => oracle => last round id + 1 submitted
    uint32 public quorumThreshold = 1; // number of independent oracles required per round

    uint256 public nextTierRequestId = 1;
    mapping(uint256 => uint256) public tierRequestAgent;
    mapping(uint256 => bytes32) public tierRequestHandle;

    uint256 public nextSlashRequestId = 1;
    mapping(uint256 => uint256) public slashRequestAgent;
    mapping(uint256 => bytes32) public slashRequestHandle;

    uint256 public nextLiquidationRequestId = 1;
    mapping(uint256 => uint256) public liquidationRequestAgent;

    IReputationBadge public reputationBadge;
    IInsurancePool public insurancePool;

    // FHE-ML Neural Perceptron Weights (Underwriter Configurable)
    uint32 public weightCompletion = 40;
    uint32 public weightUptime = 30;
    uint32 public weightLatency = 15;
    uint32 public weightError = 80;
    uint32 public neuronBias = 200;
    uint32 public maxNeuralRiskThreshold = 1200; // ReLU risk limit

    uint64 private constant W_COMPLETION = 40;
    uint64 private constant W_UPTIME = 30;
    uint64 private constant W_LATENCY = 15;
    uint64 private constant W_ERROR = 15;

    uint64 private constant HIGH_TRUST_THRESHOLD = 750;
    uint64 private constant MED_TRUST_THRESHOLD = 400;

    uint64 private constant HIGH_TRUST_BOND = uint64(0.1 ether);
    uint64 private constant MED_TRUST_BOND = uint64(1 ether);
    uint64 private constant LOW_TRUST_BOND = uint64(5 ether);

    uint256 private constant SLASH_BPS = 1000; // 10% of posted bond
    uint256 private constant ORACLE_VAR = 50;
    uint256 private constant PREMIUM_PER_VAR_WEI = 0.04 ether; // 0.04 ETH per unit of variance

    event AgentRegistered(uint256 indexed agentId, address indexed operator, uint256 identityId);
    event OracleAuthorized(address indexed oracle);
    event UnderwriterAuthorized(address indexed underwriter);
    event TelemetrySubmitted(uint256 indexed agentId, address indexed oracle, uint256 roundId);
    event ScoreUpdated(uint256 indexed agentId, uint256 roundId);
    event BondDeposited(uint256 indexed agentId, uint256 amount, uint256 totalPosted);
    event BondWithdrawn(uint256 indexed agentId, uint256 amount);
    event TierRevealRequested(uint256 indexed agentId, uint256 indexed requestId);
    event TierRevealed(uint256 indexed agentId, uint64 tierCode);
    event SlashCheckRequested(uint256 indexed agentId, uint256 indexed requestId);
    event SlashCheckFulfilled(uint256 indexed agentId, bool breached);
    event AgentSlashed(uint256 indexed agentId, uint256 penalty);
    event AgentLiquidated(uint256 indexed agentId, uint256 slashedAmount);
    event LiquidationCheckRequested(uint256 indexed agentId, uint256 indexed requestId);
    event TaskRegistered(uint256 indexed taskId, uint256 indexed agentId, address indexed client, uint256 coverageLimit);
    event ClaimPaid(uint256 indexed taskId, uint256 indexed agentId, address indexed client, uint256 payoutAmount);
    event NeuronWeightsUpdated(uint32 wComp, uint32 wUpt, uint32 wLat, uint32 wErr, uint32 bias, uint32 threshold);
    event ReputationBadgeSet(address indexed badge);
    event InsurancePoolSet(address indexed pool);
    event LeaseRequested(uint256 indexed leaseId, address indexed lessee, uint256 hardwareId, uint256 requiredBond);
    event LeaseSettled(uint256 indexed leaseId, address indexed lessee, uint256 hardwareId, bool success, uint256 payout);
    event SalaryStreamCreated(address indexed recipient);
    event StreamClaimRequested(address indexed recipient, uint256 indexed requestId);
    event StreamClaimed(address indexed recipient, uint256 amount);

    modifier onlyAdmin() {
        require(msg.sender == admin);
        _;
    }

    modifier onlyOracle() {
        require(authorizedOracles[msg.sender]);
        _;
    }

    modifier onlyAgentOperator(uint256 agentId) {
        require(_agents[agentId].operator == msg.sender);
        _;
    }

    constructor() {
        admin = msg.sender;
    }

    function setQuorumThreshold(uint32 threshold) external onlyAdmin {
        require(threshold >= 1);
        quorumThreshold = threshold;
    }

    function setReputationBadge(address badge) external onlyAdmin {
        require(address(reputationBadge) == address(0));
        reputationBadge = IReputationBadge(badge);
        emit ReputationBadgeSet(badge);
    }

    function setInsurancePool(address pool) external onlyAdmin {
        require(address(insurancePool) == address(0));
        insurancePool = IInsurancePool(pool);
        emit InsurancePoolSet(pool);
    }

    function authorizeOracle(address oracle) external onlyAdmin {
        authorizedOracles[oracle] = true;
        emit OracleAuthorized(oracle);
    }

    function authorizeUnderwriter(address underwriter) external onlyAdmin {
        authorizedUnderwriters[underwriter] = true;
        emit UnderwriterAuthorized(underwriter);
    }

    /// @notice Register a new autonomous agent/robot under a given operator.
    /// @param identityId optional AgentIdentityRegistry id (0 if not using the registry).
    function registerAgent(address operator, uint256 identityId) external onlyAdmin returns (uint256 agentId) {
        agentId = nextAgentId++;
        Agent storage a = _agents[agentId];
        a.operator = operator;
        a.registered = true;
        a.active = true;
        a.identityId = identityId;
        a.trustScore = FHE.asEuint64(500); // neutral starting score
        a.trustScoreVar = 100;
        a.liquidationThreshold = FHE.asEuint64(300);
        a.requiredBond = _deriveBond(a.trustScore, 100);
        a.delegatedBond = 0;
        a.interestAccumulated = 0;
        a.lastInterestUpdateTime = block.timestamp;

        FHE.allowThis(a.trustScore);
        FHE.allowThis(a.requiredBond);
        FHE.allowThis(a.liquidationThreshold);
        FHE.allow(a.trustScore, operator);
        FHE.allow(a.requiredBond, operator);
        FHE.allow(a.liquidationThreshold, operator);

        emit AgentRegistered(agentId, operator, identityId);
    }

    /// @notice Submit fully-encrypted telemetry for a completed task. Only
    /// authorized oracles may call this. The submission only affects the
    /// agent's score once `quorumThreshold` independent oracles have
    /// submitted within the current round.
    function submitTelemetry(
        uint256 agentId,
        externalEuint64 completionScoreA,
        externalEuint64 completionScoreB,
        externalEuint64 uptimeScore,
        externalEuint64 latencyScore,
        externalEuint64 errorScore,
        bytes calldata inputProof
    ) external onlyOracle {
        Agent storage a = _agents[agentId];
        require(a.registered && a.active);

        uint256 roundId = currentRoundId[agentId];
        require(_oracleLastRound[agentId][msg.sender] != roundId + 1);
        _oracleLastRound[agentId][msg.sender] = roundId + 1;

        euint64 compA = FHE.fromExternal(completionScoreA, inputProof);
        euint64 compB = FHE.fromExternal(completionScoreB, inputProof);
        euint64 uptime = FHE.fromExternal(uptimeScore, inputProof);
        euint64 latency = FHE.fromExternal(latencyScore, inputProof);
        euint64 errorP = FHE.fromExternal(errorScore, inputProof);

        // Compute absolute differences for anomaly detection (Completion only)
        ebool compAltB = FHE.lt(compA, compB);
        euint64 compDiff = FHE.select(compAltB, FHE.sub(compB, compA), FHE.sub(compA, compB));
        ebool compAnomaly = FHE.gt(compDiff, FHE.asEuint64(2));

        // Apply sensor fusion outlier filter
        euint64 completion = FHE.select(compAnomaly, FHE.asEuint64(0), FHE.div(FHE.add(compA, compB), 2));
        uptime = FHE.select(compAnomaly, FHE.asEuint64(0), uptime);
        latency = FHE.select(compAnomaly, FHE.asEuint64(0), latency);
        errorP = FHE.select(compAnomaly, FHE.asEuint64(10), errorP);

        PendingRound storage round = _pendingRounds[agentId];
        if (!round.initialized) {
            round.sumCompletion = completion;
            round.sumUptime = uptime;
            round.sumLatency = latency;
            round.sumError = errorP;
            round.initialized = true;
        } else {
            round.sumCompletion = FHE.add(round.sumCompletion, completion);
            round.sumUptime = FHE.add(round.sumUptime, uptime);
            round.sumLatency = FHE.add(round.sumLatency, latency);
            round.sumError = FHE.add(round.sumError, errorP);
        }
        round.count += 1;
        FHE.allowThis(round.sumCompletion);
        FHE.allowThis(round.sumUptime);
        FHE.allowThis(round.sumLatency);
        FHE.allowThis(round.sumError);

        emit TelemetrySubmitted(agentId, msg.sender, roundId);

        if (round.count >= quorumThreshold) {
            euint64 avgCompletion = FHE.div(round.sumCompletion, quorumThreshold);
            euint64 avgUptime = FHE.div(round.sumUptime, quorumThreshold);
            euint64 avgLatency = FHE.div(round.sumLatency, quorumThreshold);
            euint64 avgError = FHE.div(round.sumError, quorumThreshold);

            _applyScoreUpdate(agentId, avgCompletion, avgUptime, avgLatency, avgError);

            delete _pendingRounds[agentId];
            currentRoundId[agentId] = roundId + 1;
            emit ScoreUpdated(agentId, roundId);
        }
    }

    function _applyScoreUpdate(
        uint256 agentId,
        euint64 completion,
        euint64 uptime,
        euint64 latency,
        euint64 errorP
    ) private {
        Agent storage a = _agents[agentId];

        euint64 weightedObs = FHE.add(
            FHE.add(FHE.mul(completion, W_COMPLETION), FHE.mul(uptime, W_UPTIME)),
            FHE.mul(latency, W_LATENCY)
        );
        euint64 penalty = FHE.mul(errorP, W_ERROR);

        ebool obsUnderflow = FHE.lt(weightedObs, penalty);
        euint64 x_obs = FHE.select(obsUnderflow, FHE.asEuint64(0), FHE.sub(weightedObs, penalty));

        // Bayesian Update for variance and weights
        uint256 oldVar = a.trustScoreVar;
        uint256 newVar = (oldVar * ORACLE_VAR) / (oldVar + ORACLE_VAR);
        if (newVar < 10) {
            newVar = 10;
        }
        a.trustScoreVar = newVar;

        uint256 alpha = (ORACLE_VAR * 100) / (oldVar + ORACLE_VAR);
        uint256 beta = (oldVar * 100) / (oldVar + ORACLE_VAR);

        // Weighted FHE score update
        euint64 term1 = FHE.mul(a.trustScore, uint64(alpha));
        euint64 term2 = FHE.mul(x_obs, uint64(beta));
        euint64 newScore = FHE.div(FHE.add(term1, term2), 100);

        a.trustScore = newScore;
        a.requiredBond = _deriveBond(newScore, newVar);

        uint256 dt = block.timestamp - a.lastInterestUpdateTime;
        a.lastInterestUpdateTime = block.timestamp;
        if (dt > 0 && a.delegatedBond > 0) {
            uint256 apr = 500; // default 5% APR
            if (address(reputationBadge) != address(0)) {
                uint8 tier = reputationBadge.tierOf(agentId);
                if (tier == 3) apr = 100;
                else if (tier == 2) apr = 500;
                else if (tier == 1) apr = 2500;
            }
            uint256 interestAcc = (a.delegatedBond * apr * dt) / 8640000000;
            a.interestAccumulated += interestAcc;
        }

        euint64 totalCollateral = FHE.add(FHE.asEuint64(uint64(_clampToU64(a.postedBond))), FHE.asEuint64(uint64(_clampToU64(a.delegatedBond))));
        a.bondSufficient = FHE.ge(totalCollateral, a.requiredBond);

        FHE.allowThis(a.trustScore);
        FHE.allowThis(a.requiredBond);
        FHE.allowThis(a.bondSufficient);
        FHE.allow(a.trustScore, a.operator);
        FHE.allow(a.requiredBond, a.operator);
        // On-chain FHE Perceptron (Confidential AI Model Inference)
        euint64 positiveRisk = FHE.add(
            FHE.add(FHE.mul(latency, weightLatency), FHE.mul(errorP, weightError)),
            FHE.asEuint64(neuronBias)
        );
        euint64 negativeRisk = FHE.add(
            FHE.mul(completion, weightCompletion),
            FHE.mul(uptime, weightUptime)
        );
        
        ebool riskUnderflow = FHE.lt(positiveRisk, negativeRisk);
        euint64 neuralRisk = FHE.select(riskUnderflow, FHE.asEuint64(0), FHE.sub(positiveRisk, negativeRisk));
        ebool isNeuralBreach = FHE.gt(neuralRisk, FHE.asEuint64(maxNeuralRiskThreshold));

        ebool breachedLimit = FHE.or(FHE.lt(newScore, a.liquidationThreshold), isNeuralBreach);
        euint64 severity = FHE.sub(FHE.asEuint64(1000), newScore);

        bytes32[] memory cts = new bytes32[](2);
        cts[0] = ebool.unwrap(breachedLimit);
        cts[1] = euint64.unwrap(severity);
        
        uint256 reqId = FHE.requestDecryption(cts, this.fulfillLiquidation.selector);
        liquidationRequestAgent[reqId] = agentId;
        emit LiquidationCheckRequested(agentId, reqId);
    }

    /// @dev Confidential decision-tree: three bond tiers selected entirely
    /// under encryption via FHE.select, plus a dynamic uncertainty premium.
    function _deriveBond(euint64 score, uint256 variance) private returns (euint64) {
        ebool highTrust = FHE.ge(score, FHE.asEuint64(HIGH_TRUST_THRESHOLD));
        ebool medTrust = FHE.ge(score, FHE.asEuint64(MED_TRUST_THRESHOLD));

        euint64 baseBond = FHE.select(medTrust, FHE.asEuint64(MED_TRUST_BOND), FHE.asEuint64(LOW_TRUST_BOND));
        baseBond = FHE.select(highTrust, FHE.asEuint64(HIGH_TRUST_BOND), baseBond);

        uint256 premium = variance * PREMIUM_PER_VAR_WEI;
        return FHE.add(baseBond, FHE.asEuint64(uint64(premium)));
    }

    function _clampToU64(uint256 value) private pure returns (uint256) {
        uint256 maxU64 = type(uint64).max;
        return value > maxU64 ? maxU64 : value;
    }

    /// @notice Operator posts native-token collateral for an agent.
    function depositBond(uint256 agentId) external payable onlyAgentOperator(agentId) {
        require(msg.value > 0);
        Agent storage a = _agents[agentId];
        a.postedBond += msg.value;
        a.bondSufficient = FHE.ge(FHE.asEuint64(uint64(_clampToU64(a.postedBond))), a.requiredBond);
        FHE.allowThis(a.bondSufficient);
        FHE.allow(a.bondSufficient, a.operator);
        emit BondDeposited(agentId, msg.value, a.postedBond);
    }

    /// @notice Operator withdraws excess collateral. Confidential sufficiency
    /// should be re-checked off-chain via the relayer SDK before withdrawing,
    /// since the exact required bond stays encrypted on-chain.
    function withdrawBond(uint256 agentId, uint256 amount) external onlyAgentOperator(agentId) {
        Agent storage a = _agents[agentId];
        require(amount <= a.postedBond);
        a.postedBond -= amount;
        a.bondSufficient = FHE.ge(FHE.asEuint64(uint64(_clampToU64(a.postedBond))), a.requiredBond);
        FHE.allowThis(a.bondSufficient);
        FHE.allow(a.bondSufficient, a.operator);
        payable(msg.sender).transfer(amount);
        emit BondWithdrawn(agentId, amount);
    }

    /// @notice Grant an authorized underwriter/insurer read access to an
    /// agent's encrypted trust score, required bond, and sufficiency flag --
    /// without exposing the raw telemetry that produced them.
    function grantUnderwriterAccess(uint256 agentId, address underwriter) external onlyAgentOperator(agentId) {
        require(authorizedUnderwriters[underwriter]);
        Agent storage a = _agents[agentId];
        FHE.allow(a.trustScore, underwriter);
        FHE.allow(a.requiredBond, underwriter);
        FHE.allow(a.bondSufficient, underwriter);
    }

    /// @notice Operator opts in to publicly reveal only the *tier* (Low/Medium/High)
    /// of their agent's trust score -- never the exact score -- so a soulbound
    /// ReputationBadge can be minted/upgraded. This is a selective disclosure,
    /// not a default: the raw score stays encrypted unless the operator calls this.
    function requestTierReveal(uint256 agentId) external onlyAgentOperator(agentId) returns (uint256 requestId) {
        Agent storage a = _agents[agentId];
        ebool highTrust = FHE.ge(a.trustScore, FHE.asEuint64(HIGH_TRUST_THRESHOLD));
        ebool medTrust = FHE.ge(a.trustScore, FHE.asEuint64(MED_TRUST_THRESHOLD));
        euint64 tierCode = FHE.select(highTrust, FHE.asEuint64(3), FHE.select(medTrust, FHE.asEuint64(2), FHE.asEuint64(1)));

        bytes32[] memory cts = new bytes32[](1);
        cts[0] = euint64.unwrap(tierCode);
        requestId = FHE.requestDecryption(cts, this.fulfillTierReveal.selector);

        tierRequestAgent[requestId] = agentId;
        emit TierRevealRequested(agentId, requestId);
    }

    /// @notice Called with the Zama KMS's decrypted cleartext + proof (via the
    /// relayer SDK's public-decrypt flow) to finalize a tier reveal.
    function fulfillTierReveal(uint256 requestId, bytes memory cleartexts, bytes memory decryptionProof) external {
        FHE.checkSignatures(requestId, cleartexts, decryptionProof);

        uint64 tierCode = abi.decode(cleartexts, (uint64));
        uint256 agentId = tierRequestAgent[requestId];
        delete tierRequestAgent[requestId];

        if (address(reputationBadge) != address(0)) {
            reputationBadge.mintOrUpgrade(agentId, _agents[agentId].operator, uint8(tierCode));
        }
        emit TierRevealed(agentId, tierCode);
    }

    /// @notice An authorized oracle flags a possible SLA breach with an
    /// encrypted 0/1 signal. Nothing happens on-chain until the flag is
    /// confidentially checked and revealed via fulfillSlashCheck.
    function requestSlashCheck(
        uint256 agentId,
        externalEuint64 breachSignal,
        bytes calldata inputProof
    ) external onlyOracle returns (uint256 requestId) {
        Agent storage a = _agents[agentId];
        require(a.registered);

        euint64 signal = FHE.fromExternal(breachSignal, inputProof);
        ebool breached = FHE.eq(signal, FHE.asEuint64(1));

        bytes32[] memory cts = new bytes32[](1);
        cts[0] = ebool.unwrap(breached);
        requestId = FHE.requestDecryption(cts, this.fulfillSlashCheck.selector);

        slashRequestAgent[requestId] = agentId;
        emit SlashCheckRequested(agentId, requestId);
    }

    /// @notice Finalizes a slash check using the Zama KMS's decrypted
    /// cleartext + proof. If breached, 10% of the posted bond is slashed and
    /// forwarded to the InsurancePool (if configured) as LP yield.
    function fulfillSlashCheck(uint256 requestId, bytes memory cleartexts, bytes memory decryptionProof) external {
        FHE.checkSignatures(requestId, cleartexts, decryptionProof);

        bool breached = abi.decode(cleartexts, (bool));
        uint256 agentId = slashRequestAgent[requestId];
        delete slashRequestAgent[requestId];

        if (breached) {
            Agent storage a = _agents[agentId];
            uint256 penalty = (a.postedBond * SLASH_BPS) / 10000;
            if (penalty > 0) {
                a.postedBond -= penalty;
                a.breachCount += 1;
                a.bondSufficient = FHE.ge(FHE.asEuint64(uint64(_clampToU64(a.postedBond))), a.requiredBond);
                FHE.allowThis(a.bondSufficient);
                FHE.allow(a.bondSufficient, a.operator);

                if (address(insurancePool) != address(0)) {
                    insurancePool.receivePenalty{value: penalty}(agentId);
                }
                emit AgentSlashed(agentId, penalty);
            }
        }
        emit SlashCheckFulfilled(agentId, breached);
    }

    /// @notice Finalizes a liquidation check using the Zama KMS's decrypted
    /// cleartext + proof. If breached, the agent is deactivated and its remaining
    /// posted bond is fully slashed to the InsurancePool.
    function fulfillLiquidation(uint256 requestId, bytes memory cleartexts, bytes memory decryptionProof) external {
        FHE.checkSignatures(requestId, cleartexts, decryptionProof);

        (bool breached, uint256 severity) = abi.decode(cleartexts, (bool, uint256));
        uint256 agentId = liquidationRequestAgent[requestId];
        delete liquidationRequestAgent[requestId];

        if (breached) {
            Agent storage a = _agents[agentId];
            a.active = false;

            uint256 selfBond = a.postedBond;
            uint256 borrowedBond = a.delegatedBond;

            a.postedBond = 0;
            a.delegatedBond = 0;
            a.bondSufficient = FHE.asEbool(false);
            FHE.allowThis(a.bondSufficient);
            FHE.allow(a.bondSufficient, a.operator);

            uint256 totalBond = selfBond + borrowedBond;
            uint256 payoutAmount = 0;
            uint256 taskId = agentActiveTaskId[agentId];

            if (taskId > 0 && tasks[taskId].active) {
                payoutAmount = (tasks[taskId].coverageLimit * severity) / 1000;
                if (payoutAmount > totalBond) {
                    payoutAmount = totalBond;
                }
                
                Task storage t = tasks[taskId];
                t.active = false;
                agentActiveTaskId[agentId] = 0;
                
                if (payoutAmount > 0) {
                    payable(t.client).transfer(payoutAmount);
                    emit ClaimPaid(taskId, agentId, t.client, payoutAmount);
                }
            }

            uint256 remainder = totalBond - payoutAmount;
            if (remainder > 0) {
                if (address(insurancePool) != address(0)) {
                    insurancePool.receivePenalty{value: remainder}(agentId);
                }
                emit AgentLiquidated(agentId, remainder);
            }
        }
    }

    function getAgent(uint256 agentId)
        external
        view
        returns (address operator, bool registered, bool active, uint256 postedBond, uint256 breachCount, uint256 identityId, uint256 trustScoreVar)
    {
        Agent storage a = _agents[agentId];
        return (a.operator, a.registered, a.active, a.postedBond, a.breachCount, a.identityId, a.trustScoreVar);
    }

    function getEncryptedTrustScore(uint256 agentId) external view returns (euint64) {
        return _agents[agentId].trustScore;
    }

    function getEncryptedRequiredBond(uint256 agentId) external view returns (euint64) {
        return _agents[agentId].requiredBond;
    }

    function getEncryptedBondSufficiency(uint256 agentId) external view returns (ebool) {
        return _agents[agentId].bondSufficient;
    }

    function getDelegatedBond(uint256 agentId) external view returns (uint256) {
        return _agents[agentId].delegatedBond;
    }

    function getInterestAccumulated(uint256 agentId) external view returns (uint256) {
        return _agents[agentId].interestAccumulated;
    }

    event CreditDelegated(uint256 indexed agentId, uint256 amount);
    event InterestRepaid(uint256 indexed agentId, uint256 amount);

    function requestCreditDelegation(uint256 agentId, uint256 amount) external {
        Agent storage a = _agents[agentId];
        require(msg.sender == a.operator);
        require(a.registered && a.active);
        require(address(insurancePool) != address(0));

        insurancePool.delegateCredit(agentId, amount);

        a.delegatedBond += amount;
        euint64 totalCollateral = FHE.add(FHE.asEuint64(uint64(_clampToU64(a.postedBond))), FHE.asEuint64(uint64(_clampToU64(a.delegatedBond))));
        a.bondSufficient = FHE.ge(totalCollateral, a.requiredBond);

        FHE.allowThis(a.bondSufficient);
        FHE.allow(a.bondSufficient, a.operator);

        emit CreditDelegated(agentId, amount);
    }

    function repayInterest(uint256 agentId) external payable {
        Agent storage a = _agents[agentId];
        require(a.registered && a.active);
        require(msg.value > 0);

        if (msg.value >= a.interestAccumulated) {
            a.interestAccumulated = 0;
        } else {
            a.interestAccumulated -= msg.value;
        }

        insurancePool.repayCredit{value: msg.value}(agentId);

        emit InterestRepaid(agentId, msg.value);
    }

    function registerUnderwrittenTask(uint256 agentId, address client, uint256 coverageLimit) external returns (uint256 taskId) {
        Agent storage a = _agents[agentId];
        require(msg.sender == a.operator || msg.sender == admin);
        require(a.registered && a.active);
        require(agentActiveTaskId[agentId] == 0);

        uint256 totalCollateral = a.postedBond + a.delegatedBond;
        require(totalCollateral >= coverageLimit);

        taskId = nextTaskId++;
        Task storage t = tasks[taskId];
        t.agentId = agentId;
        t.client = client;
        t.coverageLimit = coverageLimit;
        t.active = true;

        agentActiveTaskId[agentId] = taskId;

        emit TaskRegistered(taskId, agentId, client, coverageLimit);
    }

    function deactivateAgent(uint256 agentId) external onlyAdmin {
        _agents[agentId].active = false;
    }

    function updateNeuronWeights(
        uint32 wComp,
        uint32 wUpt,
        uint32 wLat,
        uint32 wErr,
        uint32 bias,
        uint32 threshold
    ) external onlyAdmin {
        weightCompletion = wComp;
        weightUptime = wUpt;
        weightLatency = wLat;
        weightError = wErr;
        neuronBias = bias;
        maxNeuralRiskThreshold = threshold;

        emit NeuronWeightsUpdated(wComp, wUpt, wLat, wErr, bias, threshold);
    }

    function requestLeaseHardware(
        uint256 agentId,
        uint256 hardwareId,
        uint256 leaseBond
    ) external returns (uint256 leaseId) {
        Agent storage a = _agents[agentId];
        require(msg.sender == a.operator);
        require(a.registered && a.active);
        require(address(reputationBadge) != address(0));
        require(address(insurancePool) != address(0));
        require(userActiveLeaseId[msg.sender] == 0);
        
        uint8 tier = reputationBadge.tierOf(agentId);
        require(tier >= 2);

        insurancePool.delegateCredit(agentId, leaseBond);

        leaseId = nextLeaseId++;
        Lease storage l = leases[leaseId];
        l.lessee = msg.sender;
        l.agentId = agentId;
        l.hardwareId = hardwareId;
        l.requiredBond = leaseBond;
        l.startTimestamp = block.timestamp;
        l.active = true;

        userActiveLeaseId[msg.sender] = leaseId;

        emit LeaseRequested(leaseId, msg.sender, hardwareId, leaseBond);
    }

    function settleLeaseHardware(uint256 leaseId, bool success) external onlyAdmin {
        Lease storage l = leases[leaseId];
        require(l.active);

        l.active = false;
        userActiveLeaseId[l.lessee] = 0;

        uint256 payout = 0;
        if (!success) {
            payout = l.requiredBond;
            payable(admin).transfer(payout);
        } else {
            insurancePool.repayCredit{value: l.requiredBond}(l.agentId);
        }

        emit LeaseSettled(leaseId, l.lessee, l.hardwareId, success, payout);
    }

    // FHE-Stream: Confidential Salary & Yield Streaming Implementation
    function createSalaryStream(
        address recipient,
        externalEuint64 hRate,
        bytes calldata inputProof
    ) external onlyAdmin {
        require(!_salaryStreams[recipient].active);
        
        _salaryStreams[recipient].flowRate = FHE.fromExternal(hRate, inputProof);
        _salaryStreams[recipient].lastClaimBlock = block.number;
        _salaryStreams[recipient].active = true;

        FHE.allow(_salaryStreams[recipient].flowRate, recipient);
        FHE.allowThis(_salaryStreams[recipient].flowRate);

        emit SalaryStreamCreated(recipient);
    }

    function claimSalaryStream() external returns (uint256 requestId) {
        address recipient = msg.sender;
        SalaryStream storage stream = _salaryStreams[recipient];
        require(stream.active);
        require(block.number > stream.lastClaimBlock);

        uint256 blocksAccrued = block.number - stream.lastClaimBlock;
        stream.lastClaimBlock = block.number;

        euint64 accrued = FHE.mul(stream.flowRate, uint64(blocksAccrued));
        FHE.allowThis(accrued);

        bytes32[] memory cts = new bytes32[](1);
        cts[0] = euint64.unwrap(accrued);

        requestId = FHE.requestDecryption(cts, this.fulfillStreamClaim.selector);
        streamRequests[requestId] = recipient;

        emit StreamClaimRequested(recipient, requestId);
    }

    function fulfillStreamClaim(
        uint256 requestId,
        bytes memory cleartexts,
        bytes memory decryptionProof
    ) external {
        FHE.checkSignatures(requestId, cleartexts, decryptionProof);

        uint256 amount = abi.decode(cleartexts, (uint256));
        address recipient = streamRequests[requestId];
        delete streamRequests[requestId];

        if (amount > 0) {
            payable(recipient).transfer(amount);
        }

        emit StreamClaimed(recipient, amount);
    }

    receive() external payable {}
}