Initial commit
This commit is contained in:
187
contracts/governance/policies/PolicyFlashVolume.sol
Normal file
187
contracts/governance/policies/PolicyFlashVolume.sol
Normal file
@@ -0,0 +1,187 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
import "../IPolicyModule.sol";
|
||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
|
||||
/**
|
||||
* @title PolicyFlashVolume
|
||||
* @notice Policy module that limits flash loan volume per time period
|
||||
* @dev Prevents excessive flash loan usage
|
||||
*/
|
||||
contract PolicyFlashVolume is IPolicyModule, Ownable {
|
||||
string public constant override name = "FlashVolume";
|
||||
|
||||
bool private _enabled = true;
|
||||
|
||||
// Time period for volume tracking (e.g., 1 day = 86400 seconds)
|
||||
uint256 public periodDuration = 1 days;
|
||||
|
||||
// Volume limits per period
|
||||
mapping(address => uint256) public assetVolumeLimit; // Per asset limit
|
||||
uint256 public globalVolumeLimit = type(uint256).max; // Global limit
|
||||
|
||||
// Volume tracking
|
||||
struct VolumePeriod {
|
||||
uint256 volume;
|
||||
uint256 startTime;
|
||||
uint256 endTime;
|
||||
}
|
||||
|
||||
mapping(address => mapping(uint256 => VolumePeriod)) private assetVolumes; // asset => periodId => VolumePeriod
|
||||
mapping(uint256 => VolumePeriod) private globalVolumes; // periodId => VolumePeriod
|
||||
|
||||
event VolumeLimitUpdated(address indexed asset, uint256 oldLimit, uint256 newLimit);
|
||||
event GlobalVolumeLimitUpdated(uint256 oldLimit, uint256 newLimit);
|
||||
event PeriodDurationUpdated(uint256 oldDuration, uint256 newDuration);
|
||||
|
||||
modifier onlyEnabled() {
|
||||
require(_enabled, "Policy disabled");
|
||||
_;
|
||||
}
|
||||
|
||||
constructor(address initialOwner) Ownable(initialOwner) {}
|
||||
|
||||
/**
|
||||
* @notice Check if module is enabled
|
||||
*/
|
||||
function isEnabled() external view override returns (bool) {
|
||||
return _enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Enable or disable the module
|
||||
*/
|
||||
function setEnabled(bool enabled) external override onlyOwner {
|
||||
_enabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Evaluate policy for proposed action
|
||||
* @param actionType Action type (FLASH_LOAN, etc.)
|
||||
* @param actionData Encoded action data: (asset, amount)
|
||||
*/
|
||||
function evaluate(
|
||||
bytes32 actionType,
|
||||
bytes memory actionData
|
||||
) external view override onlyEnabled returns (PolicyDecision memory) {
|
||||
if (actionType != keccak256("FLASH_LOAN")) {
|
||||
return PolicyDecision({
|
||||
allowed: true,
|
||||
reason: ""
|
||||
});
|
||||
}
|
||||
|
||||
(address asset, uint256 amount) = abi.decode(actionData, (address, uint256));
|
||||
|
||||
// Get current period
|
||||
uint256 periodId = getCurrentPeriodId();
|
||||
|
||||
// Check asset-specific limit
|
||||
if (assetVolumeLimit[asset] > 0) {
|
||||
VolumePeriod storage assetPeriod = assetVolumes[asset][periodId];
|
||||
uint256 newVolume = assetPeriod.volume + amount;
|
||||
|
||||
if (newVolume > assetVolumeLimit[asset]) {
|
||||
return PolicyDecision({
|
||||
allowed: false,
|
||||
reason: "Asset volume limit exceeded"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check global limit
|
||||
if (globalVolumeLimit < type(uint256).max) {
|
||||
VolumePeriod storage globalPeriod = globalVolumes[periodId];
|
||||
uint256 newVolume = globalPeriod.volume + amount;
|
||||
|
||||
if (newVolume > globalVolumeLimit) {
|
||||
return PolicyDecision({
|
||||
allowed: false,
|
||||
reason: "Global volume limit exceeded"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return PolicyDecision({
|
||||
allowed: true,
|
||||
reason: ""
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Record flash loan volume
|
||||
*/
|
||||
function recordVolume(address asset, uint256 amount) external {
|
||||
uint256 periodId = getCurrentPeriodId();
|
||||
|
||||
// Update asset volume
|
||||
VolumePeriod storage assetPeriod = assetVolumes[asset][periodId];
|
||||
if (assetPeriod.startTime == 0) {
|
||||
assetPeriod.startTime = block.timestamp;
|
||||
assetPeriod.endTime = block.timestamp + periodDuration;
|
||||
}
|
||||
assetPeriod.volume += amount;
|
||||
|
||||
// Update global volume
|
||||
VolumePeriod storage globalPeriod = globalVolumes[periodId];
|
||||
if (globalPeriod.startTime == 0) {
|
||||
globalPeriod.startTime = block.timestamp;
|
||||
globalPeriod.endTime = block.timestamp + periodDuration;
|
||||
}
|
||||
globalPeriod.volume += amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Set volume limit for an asset
|
||||
*/
|
||||
function setAssetVolumeLimit(address asset, uint256 limit) external onlyOwner {
|
||||
require(asset != address(0), "Invalid asset");
|
||||
uint256 oldLimit = assetVolumeLimit[asset];
|
||||
assetVolumeLimit[asset] = limit;
|
||||
emit VolumeLimitUpdated(asset, oldLimit, limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Set global volume limit
|
||||
*/
|
||||
function setGlobalVolumeLimit(uint256 limit) external onlyOwner {
|
||||
uint256 oldLimit = globalVolumeLimit;
|
||||
globalVolumeLimit = limit;
|
||||
emit GlobalVolumeLimitUpdated(oldLimit, limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Set period duration
|
||||
*/
|
||||
function setPeriodDuration(uint256 duration) external onlyOwner {
|
||||
require(duration > 0, "Invalid duration");
|
||||
uint256 oldDuration = periodDuration;
|
||||
periodDuration = duration;
|
||||
emit PeriodDurationUpdated(oldDuration, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Get current period ID
|
||||
*/
|
||||
function getCurrentPeriodId() public view returns (uint256) {
|
||||
return block.timestamp / periodDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Get current period volume for an asset
|
||||
*/
|
||||
function getAssetPeriodVolume(address asset) external view returns (uint256) {
|
||||
uint256 periodId = getCurrentPeriodId();
|
||||
return assetVolumes[asset][periodId].volume;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Get current period global volume
|
||||
*/
|
||||
function getGlobalPeriodVolume() external view returns (uint256) {
|
||||
uint256 periodId = getCurrentPeriodId();
|
||||
return globalVolumes[periodId].volume;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user