142 lines
4.6 KiB
Solidity
142 lines
4.6 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.24;
|
|
|
|
import "../IPolicyModule.sol";
|
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
|
|
/**
|
|
* @title PolicyLiquiditySpread
|
|
* @notice Policy module that validates liquidity spreads
|
|
* @dev Ensures sufficient liquidity depth for operations
|
|
*/
|
|
contract PolicyLiquiditySpread is IPolicyModule, Ownable {
|
|
string public constant override name = "LiquiditySpread";
|
|
|
|
bool private _enabled = true;
|
|
|
|
// Maximum acceptable spread (basis points, e.g., 50 = 0.5%)
|
|
uint256 public maxSpreadBps = 50; // 0.5%
|
|
uint256 private constant BPS_SCALE = 10000;
|
|
|
|
// Minimum liquidity depth required (in USD, scaled by 1e8)
|
|
mapping(address => uint256) public minLiquidityDepth;
|
|
|
|
// Interface for checking liquidity
|
|
interface ILiquidityChecker {
|
|
function getLiquidityDepth(address asset) external view returns (uint256);
|
|
function getSpread(address asset, uint256 amount) external view returns (uint256);
|
|
}
|
|
|
|
ILiquidityChecker public liquidityChecker;
|
|
|
|
event MaxSpreadUpdated(uint256 oldSpread, uint256 newSpread);
|
|
event MinLiquidityDepthUpdated(address indexed asset, uint256 oldDepth, uint256 newDepth);
|
|
event LiquidityCheckerUpdated(address oldChecker, address newChecker);
|
|
|
|
modifier onlyEnabled() {
|
|
require(_enabled, "Policy disabled");
|
|
_;
|
|
}
|
|
|
|
constructor(address initialOwner, address _liquidityChecker) Ownable(initialOwner) {
|
|
liquidityChecker = ILiquidityChecker(_liquidityChecker);
|
|
}
|
|
|
|
/**
|
|
* @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 (SWAP, FLASH_LOAN, etc.)
|
|
* @param actionData Encoded action data: (asset, amount, spreadBps, liquidityDepth)
|
|
*/
|
|
function evaluate(
|
|
bytes32 actionType,
|
|
bytes memory actionData
|
|
) external view override onlyEnabled returns (PolicyDecision memory) {
|
|
// For swaps and flash loans, check liquidity
|
|
if (actionType != keccak256("SWAP") && actionType != keccak256("FLASH_LOAN")) {
|
|
return PolicyDecision({
|
|
allowed: true,
|
|
reason: ""
|
|
});
|
|
}
|
|
|
|
(address asset, uint256 amount, uint256 spreadBps, uint256 liquidityDepth) =
|
|
abi.decode(actionData, (address, uint256, uint256, uint256));
|
|
|
|
// Check spread
|
|
if (spreadBps > maxSpreadBps) {
|
|
return PolicyDecision({
|
|
allowed: false,
|
|
reason: "Spread too high"
|
|
});
|
|
}
|
|
|
|
// Check minimum liquidity depth
|
|
uint256 requiredDepth = minLiquidityDepth[asset];
|
|
if (requiredDepth > 0 && liquidityDepth < requiredDepth) {
|
|
return PolicyDecision({
|
|
allowed: false,
|
|
reason: "Insufficient liquidity depth"
|
|
});
|
|
}
|
|
|
|
// Additional check: ensure liquidity depth is sufficient for the amount
|
|
// Rule: liquidity should be at least 2x the operation amount
|
|
if (liquidityDepth < amount * 2) {
|
|
return PolicyDecision({
|
|
allowed: false,
|
|
reason: "Liquidity depth insufficient for operation size"
|
|
});
|
|
}
|
|
|
|
return PolicyDecision({
|
|
allowed: true,
|
|
reason: ""
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @notice Update maximum spread
|
|
*/
|
|
function setMaxSpread(uint256 newSpreadBps) external onlyOwner {
|
|
require(newSpreadBps <= BPS_SCALE, "Invalid spread");
|
|
uint256 oldSpread = maxSpreadBps;
|
|
maxSpreadBps = newSpreadBps;
|
|
emit MaxSpreadUpdated(oldSpread, newSpreadBps);
|
|
}
|
|
|
|
/**
|
|
* @notice Update minimum liquidity depth for an asset
|
|
*/
|
|
function setMinLiquidityDepth(address asset, uint256 depth) external onlyOwner {
|
|
require(asset != address(0), "Invalid asset");
|
|
uint256 oldDepth = minLiquidityDepth[asset];
|
|
minLiquidityDepth[asset] = depth;
|
|
emit MinLiquidityDepthUpdated(asset, oldDepth, depth);
|
|
}
|
|
|
|
/**
|
|
* @notice Update liquidity checker contract
|
|
*/
|
|
function setLiquidityChecker(address newChecker) external onlyOwner {
|
|
require(newChecker != address(0), "Invalid checker");
|
|
address oldChecker = address(liquidityChecker);
|
|
liquidityChecker = ILiquidityChecker(newChecker);
|
|
emit LiquidityCheckerUpdated(oldChecker, newChecker);
|
|
}
|
|
}
|
|
|