// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/access/AccessControl.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; /** * @title CollateralToggleManager * @notice Manages Aave v3 collateral enable/disable and sub-vault operations * @dev Enables efficient batch operations for collateral management */ contract CollateralToggleManager is Ownable, AccessControl, ReentrancyGuard { bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); bytes32 public constant KERNEL_ROLE = keccak256("KERNEL_ROLE"); // Aave v3 Pool interface interface IPoolV3 { function setUserUseReserveAsCollateral( address asset, bool useAsCollateral ) external; function getUserAccountData(address user) external view returns ( uint256 totalCollateralBase, uint256 totalDebtBase, uint256 availableBorrowsBase, uint256 currentLiquidationThreshold, uint256 ltv, uint256 healthFactor ); } IPoolV3 public aavePool; address public aaveUserAccount; // Collateral status tracking mapping(address => bool) public collateralEnabled; address[] public managedAssets; event CollateralToggled(address indexed asset, bool enabled); event BatchCollateralToggled(address[] assets, bool[] enabled); event AavePoolUpdated(address oldPool, address newPool); event AaveUserAccountUpdated(address oldAccount, address newAccount); constructor( address _aavePool, address _aaveUserAccount, address initialOwner ) Ownable(initialOwner) { require(_aavePool != address(0), "Invalid Aave pool"); require(_aaveUserAccount != address(0), "Invalid Aave account"); aavePool = IPoolV3(_aavePool); aaveUserAccount = _aaveUserAccount; _grantRole(DEFAULT_ADMIN_ROLE, initialOwner); } /** * @notice Enable or disable collateral for an asset */ function toggleCollateral( address asset, bool useAsCollateral ) external onlyRole(KERNEL_ROLE) nonReentrant { require(asset != address(0), "Invalid asset"); // Update Aave aavePool.setUserUseReserveAsCollateral(asset, useAsCollateral); // Track status if (!collateralEnabled[asset] && managedAssets.length > 0) { bool alreadyTracked = false; for (uint256 i = 0; i < managedAssets.length; i++) { if (managedAssets[i] == asset) { alreadyTracked = true; break; } } if (!alreadyTracked) { managedAssets.push(asset); } } collateralEnabled[asset] = useAsCollateral; emit CollateralToggled(asset, useAsCollateral); } /** * @notice Batch toggle collateral for multiple assets */ function batchToggleCollateral( address[] calldata assets, bool[] calldata useAsCollateral ) external onlyRole(KERNEL_ROLE) nonReentrant { require(assets.length == useAsCollateral.length, "Array length mismatch"); require(assets.length > 0 && assets.length <= 20, "Invalid array length"); // Reasonable limit for (uint256 i = 0; i < assets.length; i++) { toggleCollateral(assets[i], useAsCollateral[i]); } emit BatchCollateralToggled(assets, useAsCollateral); } /** * @notice Get collateral status for an asset */ function isCollateralEnabled(address asset) external view returns (bool) { return collateralEnabled[asset]; } /** * @notice Get all managed assets */ function getManagedAssets() external view returns (address[] memory) { return managedAssets; } /** * @notice Get Aave position health factor */ function getHealthFactor() external view returns (uint256) { try aavePool.getUserAccountData(aaveUserAccount) returns ( uint256, uint256, uint256, uint256, uint256, uint256 healthFactor ) { return healthFactor; } catch { return 0; } } /** * @notice Update Aave pool */ function setAavePool(address newPool) external onlyOwner { require(newPool != address(0), "Invalid pool"); address oldPool = address(aavePool); aavePool = IPoolV3(newPool); emit AavePoolUpdated(oldPool, newPool); } /** * @notice Update Aave user account */ function setAaveUserAccount(address newAccount) external onlyOwner { require(newAccount != address(0), "Invalid account"); address oldAccount = aaveUserAccount; aaveUserAccount = newAccount; emit AaveUserAccountUpdated(oldAccount, newAccount); } /** * @notice Grant kernel role */ function grantKernel(address kernel) external onlyOwner { _grantRole(KERNEL_ROLE, kernel); } /** * @notice Revoke kernel role */ function revokeKernel(address kernel) external onlyOwner { _revokeRole(KERNEL_ROLE, kernel); } }