218 lines
6.8 KiB
Solidity
218 lines
6.8 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.24;
|
|
|
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
import "../interfaces/IPolicyEngine.sol";
|
|
import "../interfaces/IConfigRegistry.sol";
|
|
import "../interfaces/IVault.sol";
|
|
|
|
/**
|
|
* @title GovernanceGuard
|
|
* @notice Enforces invariants and policy checks before execution
|
|
* @dev Acts as the final gatekeeper for all system actions
|
|
*/
|
|
contract GovernanceGuard is Ownable {
|
|
IPolicyEngine public policyEngine;
|
|
IConfigRegistry public configRegistry;
|
|
IVault public vault;
|
|
|
|
// Strategy throttling
|
|
struct ThrottleConfig {
|
|
uint256 dailyCap;
|
|
uint256 monthlyCap;
|
|
uint256 dailyCount;
|
|
uint256 monthlyCount;
|
|
uint256 lastDailyReset;
|
|
uint256 lastMonthlyReset;
|
|
}
|
|
|
|
mapping(bytes32 => ThrottleConfig) private strategyThrottles;
|
|
|
|
event InvariantCheckFailed(string reason);
|
|
event PolicyCheckFailed(string reason);
|
|
event ThrottleExceeded(string strategy, string period);
|
|
|
|
modifier onlyVault() {
|
|
require(msg.sender == address(vault), "Only vault");
|
|
_;
|
|
}
|
|
|
|
constructor(
|
|
address _policyEngine,
|
|
address _configRegistry,
|
|
address _vault,
|
|
address initialOwner
|
|
) Ownable(initialOwner) {
|
|
require(_policyEngine != address(0), "Invalid policy engine");
|
|
require(_configRegistry != address(0), "Invalid config registry");
|
|
require(_vault != address(0), "Invalid vault");
|
|
|
|
policyEngine = IPolicyEngine(_policyEngine);
|
|
configRegistry = IConfigRegistry(_configRegistry);
|
|
vault = IVault(_vault);
|
|
}
|
|
|
|
/**
|
|
* @notice Verify invariants before action
|
|
* @param actionType Action type identifier
|
|
* @param actionData Action-specific data
|
|
* @return success True if all checks pass
|
|
*/
|
|
function verifyInvariants(
|
|
bytes32 actionType,
|
|
bytes memory actionData
|
|
) external view returns (bool success) {
|
|
// Policy check
|
|
(bool policyAllowed, string memory policyReason) = policyEngine.evaluateAll(actionType, actionData);
|
|
if (!policyAllowed) {
|
|
return false; // Would emit event in actual execution
|
|
}
|
|
|
|
// Position invariant check (for amortization actions)
|
|
if (actionType == keccak256("AMORTIZATION")) {
|
|
// Decode and verify position improvement
|
|
// This would decode the expected position changes and verify
|
|
// For now, return true - actual implementation would check
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @notice Check and enforce invariants (with revert)
|
|
* @param actionType Action type
|
|
* @param actionData Action data
|
|
*/
|
|
function enforceInvariants(bytes32 actionType, bytes memory actionData) external {
|
|
// Policy check
|
|
(bool policyAllowed, string memory policyReason) = policyEngine.evaluateAll(actionType, actionData);
|
|
if (!policyAllowed) {
|
|
emit PolicyCheckFailed(policyReason);
|
|
revert(string(abi.encodePacked("Policy check failed: ", policyReason)));
|
|
}
|
|
|
|
// Throttle check
|
|
if (!checkThrottle(actionType)) {
|
|
emit ThrottleExceeded(_bytes32ToString(actionType), "daily or monthly");
|
|
revert("Strategy throttle exceeded");
|
|
}
|
|
|
|
// Record throttle usage
|
|
recordThrottleUsage(actionType);
|
|
}
|
|
|
|
/**
|
|
* @notice Verify position improved (invariant check)
|
|
* @param collateralBefore Previous collateral value
|
|
* @param debtBefore Previous debt value
|
|
* @param healthFactorBefore Previous health factor
|
|
*/
|
|
function verifyPositionImproved(
|
|
uint256 collateralBefore,
|
|
uint256 debtBefore,
|
|
uint256 healthFactorBefore
|
|
) external view returns (bool) {
|
|
return vault.verifyPositionImproved(collateralBefore, debtBefore, healthFactorBefore);
|
|
}
|
|
|
|
/**
|
|
* @notice Check throttle limits
|
|
*/
|
|
function checkThrottle(bytes32 strategy) public view returns (bool) {
|
|
ThrottleConfig storage throttle = strategyThrottles[strategy];
|
|
|
|
// Reset if needed
|
|
uint256 currentDailyCount = throttle.dailyCount;
|
|
uint256 currentMonthlyCount = throttle.monthlyCount;
|
|
|
|
if (block.timestamp - throttle.lastDailyReset >= 1 days) {
|
|
currentDailyCount = 0;
|
|
}
|
|
if (block.timestamp - throttle.lastMonthlyReset >= 30 days) {
|
|
currentMonthlyCount = 0;
|
|
}
|
|
|
|
// Check limits
|
|
if (throttle.dailyCap > 0 && currentDailyCount >= throttle.dailyCap) {
|
|
return false;
|
|
}
|
|
if (throttle.monthlyCap > 0 && currentMonthlyCount >= throttle.monthlyCap) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @notice Record throttle usage
|
|
*/
|
|
function recordThrottleUsage(bytes32 strategy) internal {
|
|
ThrottleConfig storage throttle = strategyThrottles[strategy];
|
|
|
|
// Reset daily if needed
|
|
if (block.timestamp - throttle.lastDailyReset >= 1 days) {
|
|
throttle.dailyCount = 0;
|
|
throttle.lastDailyReset = block.timestamp;
|
|
}
|
|
|
|
// Reset monthly if needed
|
|
if (block.timestamp - throttle.lastMonthlyReset >= 30 days) {
|
|
throttle.monthlyCount = 0;
|
|
throttle.lastMonthlyReset = block.timestamp;
|
|
}
|
|
|
|
throttle.dailyCount++;
|
|
throttle.monthlyCount++;
|
|
}
|
|
|
|
/**
|
|
* @notice Configure throttle for a strategy
|
|
*/
|
|
function setThrottle(
|
|
bytes32 strategy,
|
|
uint256 dailyCap,
|
|
uint256 monthlyCap
|
|
) external onlyOwner {
|
|
strategyThrottles[strategy] = ThrottleConfig({
|
|
dailyCap: dailyCap,
|
|
monthlyCap: monthlyCap,
|
|
dailyCount: 0,
|
|
monthlyCount: 0,
|
|
lastDailyReset: block.timestamp,
|
|
lastMonthlyReset: block.timestamp
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @notice Update policy engine
|
|
*/
|
|
function setPolicyEngine(address newPolicyEngine) external onlyOwner {
|
|
require(newPolicyEngine != address(0), "Invalid policy engine");
|
|
policyEngine = IPolicyEngine(newPolicyEngine);
|
|
}
|
|
|
|
/**
|
|
* @notice Update config registry
|
|
*/
|
|
function setConfigRegistry(address newConfigRegistry) external onlyOwner {
|
|
require(newConfigRegistry != address(0), "Invalid config registry");
|
|
configRegistry = IConfigRegistry(newConfigRegistry);
|
|
}
|
|
|
|
/**
|
|
* @notice Helper to convert bytes32 to string
|
|
*/
|
|
function _bytes32ToString(bytes32 _bytes32) private pure returns (string memory) {
|
|
uint8 i = 0;
|
|
while (i < 32 && _bytes32[i] != 0) {
|
|
i++;
|
|
}
|
|
bytes memory bytesArray = new bytes(i);
|
|
for (i = 0; i < 32 && _bytes32[i] != 0; i++) {
|
|
bytesArray[i] = _bytes32[i];
|
|
}
|
|
return string(bytesArray);
|
|
}
|
|
}
|
|
|