File size: 9,306 Bytes
03c8703
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "./CGAERegistry.sol";

/**
 * @title CGAEEscrow
 * @notice Contract management and escrow for the CGAE economy.
 *         Implements tier-gated contract assignment with budget ceiling enforcement
 *         (Theorem 1: Bounded Economic Exposure).
 *
 * @dev All economic activity is mediated through formally specified contracts.
 *      Rewards are escrowed until verification. Penalties are enforced on failure.
 */
contract CGAEEscrow {

    // -----------------------------------------------------------------------
    // Types
    // -----------------------------------------------------------------------

    enum ContractStatus {
        Open,
        Assigned,
        Completed,
        Failed,
        Expired,
        Cancelled
    }

    struct EconomicContract {
        bytes32 contractId;
        address issuer;
        address assignedAgent;
        string objective;
        bytes32 constraintsHash;   // Hash of machine-verifiable constraint set (Phi)
        string verifierSpecHash;   // 0G Storage hash or pointer to verifier specification
        uint8 minTier;
        uint256 reward;
        uint256 penalty;
        uint64 deadline;
        uint64 createdAt;
        ContractStatus status;
        string domain;
    }

    // -----------------------------------------------------------------------
    // State
    // -----------------------------------------------------------------------

    CGAERegistry public registry;

    mapping(bytes32 => EconomicContract) public contracts;
    bytes32[] public contractIds;

    // Agent -> total active penalty exposure
    mapping(address => uint256) public activeExposure;

    // Economic accounting
    uint256 public totalRewardsPaid;
    uint256 public totalPenaltiesCollected;
    uint256 public totalEscrowed;

    address public admin;

    // -----------------------------------------------------------------------
    // Events
    // -----------------------------------------------------------------------

    event ContractCreated(bytes32 indexed contractId, uint8 minTier, uint256 reward, string domain);
    event ContractAssigned(bytes32 indexed contractId, address indexed agent);
    event ContractCompleted(bytes32 indexed contractId, address indexed agent, uint256 reward);
    event ContractFailed(bytes32 indexed contractId, address indexed agent, uint256 penalty);
    event ContractExpired(bytes32 indexed contractId);

    // -----------------------------------------------------------------------
    // Constructor
    // -----------------------------------------------------------------------

    constructor(address _registry) {
        registry = CGAERegistry(_registry);
        admin = msg.sender;
    }

    // -----------------------------------------------------------------------
    // Contract Lifecycle
    // -----------------------------------------------------------------------

    /**
     * @notice Create a new contract. Issuer deposits reward as escrow.
     */
    function createContract(
        string calldata objective,
        bytes32 constraintsHash,
        string calldata verifierSpecHash,
        uint8 minTier,
        uint256 penalty,
        uint64 deadline,
        string calldata domain
    ) external payable {
        require(msg.value > 0, "Must escrow reward");
        require(minTier > 0 && minTier <= 5, "Invalid tier");
        require(deadline > block.timestamp, "Deadline must be in future");
        require(constraintsHash != bytes32(0), "Missing constraints hash");
        require(bytes(verifierSpecHash).length > 0, "Missing verifier spec");

        bytes32 contractId = keccak256(abi.encodePacked(
            msg.sender, block.timestamp, objective, contractIds.length
        ));

        contracts[contractId] = EconomicContract({
            contractId: contractId,
            issuer: msg.sender,
            assignedAgent: address(0),
            objective: objective,
            constraintsHash: constraintsHash,
            verifierSpecHash: verifierSpecHash,
            minTier: minTier,
            reward: msg.value,
            penalty: penalty,
            deadline: deadline,
            createdAt: uint64(block.timestamp),
            status: ContractStatus.Open,
            domain: domain
        });

        contractIds.push(contractId);
        totalEscrowed += msg.value;

        emit ContractCreated(contractId, minTier, msg.value, domain);
    }

    /**
     * @notice Agent accepts a contract. Enforces:
     *         1. Agent tier >= min_tier
     *         2. Agent exposure + penalty <= budget ceiling (Theorem 1)
     */
    function acceptContract(bytes32 contractId) external payable {
        EconomicContract storage c = contracts[contractId];
        require(c.status == ContractStatus.Open, "Not open");
        require(block.timestamp < c.deadline, "Past deadline");

        // Tier check
        CGAERegistry.AgentRecord memory agent = registry.getAgent(msg.sender);
        require(agent.active, "Agent not active");
        require(agent.currentTier >= c.minTier, "Tier too low");

        // Budget ceiling check (Theorem 1: Bounded Economic Exposure)
        uint256 ceiling = registry.getBudgetCeiling(agent.currentTier);
        require(
            activeExposure[msg.sender] + c.penalty <= ceiling,
            "Would exceed budget ceiling"
        );

        // Agent must deposit penalty as collateral
        require(msg.value >= c.penalty, "Insufficient penalty collateral");

        c.assignedAgent = msg.sender;
        c.status = ContractStatus.Assigned;
        activeExposure[msg.sender] += c.penalty;

        emit ContractAssigned(contractId, msg.sender);
    }

    /**
     * @notice Mark a contract as completed. Called by admin/verifier after
     *         output verification. Releases reward to agent, returns collateral.
     */
    function completeContract(bytes32 contractId) external {
        require(msg.sender == admin, "Only admin/verifier");
        EconomicContract storage c = contracts[contractId];
        require(c.status == ContractStatus.Assigned, "Not assigned");

        c.status = ContractStatus.Completed;

        // Release exposure
        activeExposure[c.assignedAgent] -= c.penalty;

        // Pay reward to agent
        uint256 reward = c.reward;
        totalEscrowed -= reward;
        totalRewardsPaid += reward;

        // Return penalty collateral + pay reward
        uint256 payout = reward + c.penalty;
        payable(c.assignedAgent).transfer(payout);

        // Record on registry
        registry.recordContractOutcome(c.assignedAgent, true, reward);

        emit ContractCompleted(contractId, c.assignedAgent, reward);
    }

    /**
     * @notice Mark a contract as failed. Penalty is forfeited.
     */
    function failContract(bytes32 contractId) external {
        require(msg.sender == admin, "Only admin/verifier");
        EconomicContract storage c = contracts[contractId];
        require(c.status == ContractStatus.Assigned, "Not assigned");

        c.status = ContractStatus.Failed;

        // Release exposure
        activeExposure[c.assignedAgent] -= c.penalty;

        // Forfeit penalty collateral
        totalPenaltiesCollected += c.penalty;

        // Return escrowed reward to issuer
        totalEscrowed -= c.reward;
        payable(c.issuer).transfer(c.reward);

        // Record on registry
        registry.recordContractOutcome(c.assignedAgent, false, c.penalty);

        emit ContractFailed(contractId, c.assignedAgent, c.penalty);
    }

    /**
     * @notice Expire contracts past deadline. Anyone can call this.
     */
    function expireContract(bytes32 contractId) external {
        EconomicContract storage c = contracts[contractId];
        require(c.status == ContractStatus.Open, "Not open");
        require(block.timestamp >= c.deadline, "Not expired yet");

        c.status = ContractStatus.Expired;
        totalEscrowed -= c.reward;
        payable(c.issuer).transfer(c.reward);

        emit ContractExpired(contractId);
    }

    // -----------------------------------------------------------------------
    // Views
    // -----------------------------------------------------------------------

    function getContract(bytes32 contractId) external view returns (EconomicContract memory) {
        return contracts[contractId];
    }

    function getContractCount() external view returns (uint256) {
        return contractIds.length;
    }

    function getExposure(address agent) external view returns (uint256) {
        return activeExposure[agent];
    }

    function getEconomicsSummary() external view returns (
        uint256 _totalRewards,
        uint256 _totalPenalties,
        uint256 _totalEscrowed,
        uint256 _contractCount
    ) {
        return (totalRewardsPaid, totalPenaltiesCollected, totalEscrowed, contractIds.length);
    }

    // -----------------------------------------------------------------------
    // Admin
    // -----------------------------------------------------------------------

    function updateAdmin(address newAdmin) external {
        require(msg.sender == admin, "Only admin");
        admin = newAdmin;
    }

    receive() external payable {}
}