-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathPolicyLib.sol
More file actions
194 lines (170 loc) · 7.17 KB
/
PolicyLib.sol
File metadata and controls
194 lines (170 loc) · 7.17 KB
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
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import {IAggregatorV3} from "../interfaces/IAggregatorV3.sol";
/// @notice Policy configuration for an AI agent registered with SentinelGuardian
struct AgentPolicy {
uint256 maxTransactionValue; // Max value per single tx (wei)
uint256 maxDailyVolume; // Max cumulative value per day (wei)
uint256 maxMintAmount; // Max mint amount per tx (token units) — prevents infinite mint
uint256 rateLimit; // Max actions per window
uint256 rateLimitWindow; // Window duration in seconds
address[] approvedContracts; // Whitelist of allowed target contracts
bytes4[] blockedFunctions; // Blacklist of forbidden function selectors
bool requireMultiAiConsensus; // Whether multi-AI evaluation is required
bool isActive; // Whether this policy is enabled
address reserveFeed; // Chainlink AggregatorV3 for Proof of Reserves (address(0) = skip)
uint256 minReserveRatio; // Required collateralization in basis points (10000 = 100%)
uint256 maxStaleness; // Max age of reserve feed data in seconds (0 = no check)
}
/// @notice Parameters for a full policy check
struct CheckParams {
address target;
bytes4 funcSig;
uint256 value;
uint256 mintAmount;
uint256 actionCount;
uint256 windowStart;
uint256 currentTime;
uint256 cumulativeMints; // Total mints so far (for PoR check)
uint256 dailyVolume; // Accumulated daily volume so far
}
/// @title PolicyLib — Pure policy validation logic for SentinelGuardian
library PolicyLib {
/// @notice Check if transaction value is within the agent's limit
/// @dev 0 = no per-tx value limit configured (consistent with other check functions)
function checkValue(
AgentPolicy storage policy,
uint256 value
) internal view returns (bool, string memory) {
if (policy.maxTransactionValue == 0) {
return (true, ""); // No per-tx value limit configured
}
if (value > policy.maxTransactionValue) {
return (false, "Value exceeds max transaction limit");
}
return (true, "");
}
/// @notice Check if target contract is on the agent's whitelist
function checkTarget(
AgentPolicy storage policy,
address target
) internal view returns (bool, string memory) {
if (policy.approvedContracts.length == 0) {
return (true, ""); // No whitelist = all allowed
}
for (uint256 i = 0; i < policy.approvedContracts.length; i++) {
if (policy.approvedContracts[i] == target) {
return (true, "");
}
}
return (false, "Target contract not approved");
}
/// @notice Check if function selector is blocked
function checkFunction(
AgentPolicy storage policy,
bytes4 funcSig
) internal view returns (bool, string memory) {
for (uint256 i = 0; i < policy.blockedFunctions.length; i++) {
if (policy.blockedFunctions[i] == funcSig) {
return (false, "Function signature is blocked");
}
}
return (true, "");
}
/// @notice Check if agent has exceeded its rate limit within the current window
function checkRateLimit(
AgentPolicy storage policy,
uint256 actionCount,
uint256 windowStart,
uint256 currentTime
) internal view returns (bool, string memory) {
if (policy.rateLimit == 0) {
return (true, ""); // No rate limit configured
}
// If window has expired, the count will be reset — passes
if (currentTime >= windowStart + policy.rateLimitWindow) {
return (true, "");
}
if (actionCount >= policy.rateLimit) {
return (false, "Rate limit exceeded");
}
return (true, "");
}
/// @notice Check if daily volume would exceed the agent's limit
function checkDailyVolume(
AgentPolicy storage policy,
uint256 currentDailyVolume,
uint256 value
) internal view returns (bool, string memory) {
if (policy.maxDailyVolume == 0) {
return (true, ""); // No daily volume limit configured
}
if (currentDailyVolume + value > policy.maxDailyVolume) {
return (false, "Daily volume limit exceeded");
}
return (true, "");
}
/// @notice Check if mint amount is within the agent's cap
function checkMintAmount(
AgentPolicy storage policy,
uint256 mintAmount
) internal view returns (bool, string memory) {
if (policy.maxMintAmount == 0) {
return (true, ""); // No mint cap configured
}
if (mintAmount > policy.maxMintAmount) {
return (false, "Mint amount exceeds cap");
}
return (true, "");
}
/// @notice Check if reserves are sufficient to back the proposed mint (Chainlink PoR)
function checkReserves(
AgentPolicy storage policy,
uint256 mintAmount,
uint256 cumulativeMints
) internal view returns (bool, string memory) {
if (policy.reserveFeed == address(0)) {
return (true, ""); // No reserve feed configured — skip
}
if (mintAmount == 0) {
return (true, ""); // Not a mint operation — skip
}
IAggregatorV3 feed = IAggregatorV3(policy.reserveFeed);
(, int256 reserves,, uint256 updatedAt,) = feed.latestRoundData();
if (reserves <= 0) {
return (false, "Reserve feed returned non-positive value");
}
// Staleness check — reject if feed data is too old
if (policy.maxStaleness > 0 && block.timestamp > updatedAt + policy.maxStaleness) {
return (false, "Reserve feed data is stale");
}
// Check: reserves >= (cumulativeMints + mintAmount) * minReserveRatio / 10000
uint256 totalAfterMint = cumulativeMints + mintAmount;
uint256 requiredReserves = (totalAfterMint * policy.minReserveRatio) / 10000;
if (uint256(reserves) < requiredReserves) {
return (false, "Insufficient reserves to back mint");
}
return (true, "");
}
/// @notice Run all policy checks in sequence. Returns on first failure.
function checkAll(
AgentPolicy storage policy,
CheckParams memory p
) internal view returns (bool, string memory) {
(bool passed, string memory reason) = checkValue(policy, p.value);
if (!passed) return (false, reason);
(passed, reason) = checkTarget(policy, p.target);
if (!passed) return (false, reason);
(passed, reason) = checkFunction(policy, p.funcSig);
if (!passed) return (false, reason);
(passed, reason) = checkRateLimit(policy, p.actionCount, p.windowStart, p.currentTime);
if (!passed) return (false, reason);
(passed, reason) = checkDailyVolume(policy, p.dailyVolume, p.value);
if (!passed) return (false, reason);
(passed, reason) = checkMintAmount(policy, p.mintAmount);
if (!passed) return (false, reason);
(passed, reason) = checkReserves(policy, p.mintAmount, p.cumulativeMints);
if (!passed) return (false, reason);
return (true, "");
}
}