// 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"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "../interfaces/IKernel.sol"; import "../interfaces/IFlashLoanRouter.sol"; import "../interfaces/IVault.sol"; import "../interfaces/IConfigRegistry.sol"; import "../interfaces/IPolicyEngine.sol"; import "../interfaces/IOracleAdapter.sol"; import "../governance/GovernanceGuard.sol"; import "../core/CollateralToggleManager.sol"; /** * @title RecursiveLeverageKernel * @notice Implements atomic amortizing cycles for leveraged positions * @dev Enforces invariants: Debt↓, Collateral↑, LTV↓, HF↑ */ contract RecursiveLeverageKernel is IKernel, IFlashLoanRouter, Ownable, AccessControl, ReentrancyGuard { using SafeERC20 for IERC20; bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); bytes32 private constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan"); // Core dependencies IFlashLoanRouter public flashRouter; IVault public vault; IConfigRegistry public configRegistry; IPolicyEngine public policyEngine; GovernanceGuard public governanceGuard; CollateralToggleManager public collateralManager; IOracleAdapter public oracleAdapter; // Aave v3 Pool for operations interface IPoolV3 { function supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external; function withdraw(address asset, uint256 amount, address to) external returns (uint256); function borrow(address asset, uint256 amount, uint256 interestRateMode, uint16 referralCode, address onBehalfOf) external; function repay(address asset, uint256 amount, uint256 rateMode, address onBehalfOf) external returns (uint256); 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; // Uniswap V3 Swap Router (simplified) interface ISwapRouter { struct ExactInputSingleParams { address tokenIn; address tokenOut; uint24 fee; address recipient; uint256 deadline; uint256 amountIn; uint256 amountOutMinimum; uint160 sqrtPriceLimitX96; } function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut); } ISwapRouter public swapRouter; // Constants uint256 private constant HF_SCALE = 1e18; uint256 private constant PRICE_SCALE = 1e8; // Cycle state (for reentrancy protection) struct CycleState { bool inProgress; uint256 cyclesExecuted; uint256 totalCollateralIncrease; uint256 totalDebtDecrease; } CycleState private cycleState; // Flash loan callback state struct FlashCallbackState { address targetAsset; bool inFlashLoan; } FlashCallbackState private flashCallbackState; event CycleCompleted( uint256 cyclesExecuted, uint256 collateralIncrease, uint256 debtDecrease, uint256 hfImprovement ); event SingleStepCompleted(uint256 collateralAdded, uint256 debtRepaid); modifier onlyInCycle() { require(cycleState.inProgress, "Not in cycle"); _; } modifier onlyNotInCycle() { require(!cycleState.inProgress, "Cycle in progress"); _; } constructor( address _flashRouter, address _vault, address _configRegistry, address _policyEngine, address _governanceGuard, address _collateralManager, address _oracleAdapter, address _aavePool, address _aaveUserAccount, address _swapRouter, address initialOwner ) Ownable(initialOwner) { require(_flashRouter != address(0), "Invalid flash router"); require(_vault != address(0), "Invalid vault"); require(_configRegistry != address(0), "Invalid config registry"); require(_policyEngine != address(0), "Invalid policy engine"); require(_governanceGuard != address(0), "Invalid governance guard"); require(_collateralManager != address(0), "Invalid collateral manager"); require(_oracleAdapter != address(0), "Invalid oracle adapter"); require(_aavePool != address(0), "Invalid Aave pool"); require(_aaveUserAccount != address(0), "Invalid Aave account"); flashRouter = IFlashLoanRouter(_flashRouter); vault = IVault(_vault); configRegistry = IConfigRegistry(_configRegistry); policyEngine = IPolicyEngine(_policyEngine); governanceGuard = GovernanceGuard(_governanceGuard); collateralManager = CollateralToggleManager(_collateralManager); oracleAdapter = IOracleAdapter(_oracleAdapter); aavePool = IPoolV3(_aavePool); aaveUserAccount = _aaveUserAccount; swapRouter = ISwapRouter(_swapRouter); _grantRole(DEFAULT_ADMIN_ROLE, initialOwner); } /** * @notice Execute atomic amortizing cycle * @dev Main entry point for amortization strategy */ function executeAmortizingCycle( AmortizationParams memory params ) external override onlyRole(OPERATOR_ROLE) nonReentrant onlyNotInCycle returns (bool success, uint256 cyclesExecuted) { // Enforce policy checks bytes memory actionData = abi.encode( address(vault), vault.getHealthFactor(), params.targetAsset, params.maxLoops ); governanceGuard.enforceInvariants(keccak256("AMORTIZATION"), actionData); // Check max loops from config uint256 maxLoops = configRegistry.getMaxLoops(); require(params.maxLoops <= maxLoops, "Exceeds max loops"); // Take position snapshot (uint256 collateralBefore, uint256 debtBefore, uint256 healthFactorBefore) = vault.snapshotPosition(); // Initialize cycle state cycleState = CycleState({ inProgress: true, cyclesExecuted: 0, totalCollateralIncrease: 0, totalDebtDecrease: 0 }); // Execute cycles up to maxLoops uint256 actualLoops = params.maxLoops; for (uint256 i = 0; i < params.maxLoops; i++) { // Calculate optimal flash loan amount (simplified) uint256 flashAmount = _calculateOptimalFlashAmount(); if (flashAmount == 0) { actualLoops = i; break; } // Execute single step (uint256 collateralAdded, uint256 debtRepaid) = _executeSingleStepInternal( flashAmount, params.targetAsset ); if (collateralAdded == 0 && debtRepaid == 0) { actualLoops = i; break; // No improvement possible } cycleState.cyclesExecuted++; cycleState.totalCollateralIncrease += collateralAdded; cycleState.totalDebtDecrease += debtRepaid; // Check if minimum HF improvement achieved uint256 currentHF = vault.getHealthFactor(); if (currentHF >= healthFactorBefore + params.minHFImprovement) { break; // Early exit if target achieved } } // Verify invariants (bool invariantSuccess, string memory reason) = verifyInvariants( collateralBefore, debtBefore, healthFactorBefore ); if (!invariantSuccess) { emit InvariantFail(reason); revert(reason); } // Calculate improvements uint256 collateralAfter = vault.getTotalCollateralValue(); uint256 debtAfter = vault.getTotalDebtValue(); uint256 healthFactorAfter = vault.getHealthFactor(); uint256 hfImprovement = healthFactorAfter > healthFactorBefore ? healthFactorAfter - healthFactorBefore : 0; // Emit events emit AmortizationExecuted( cycleState.cyclesExecuted, collateralAfter > collateralBefore ? collateralAfter - collateralBefore : 0, debtBefore > debtAfter ? debtBefore - debtAfter : 0, hfImprovement ); emit CycleCompleted( cycleState.cyclesExecuted, cycleState.totalCollateralIncrease, cycleState.totalDebtDecrease, hfImprovement ); success = true; cyclesExecuted = cycleState.cyclesExecuted; // Clear state cycleState.inProgress = false; delete cycleState; return (success, cyclesExecuted); } /** * @notice Execute a single amortization step */ function executeSingleStep( IFlashLoanRouter.FlashLoanParams memory flashLoanParams, address targetAsset ) external override onlyRole(OPERATOR_ROLE) nonReentrant onlyNotInCycle returns (uint256 collateralAdded, uint256 debtRepaid) { // Enforce policy checks bytes memory actionData = abi.encode( address(vault), flashLoanParams.asset, flashLoanParams.amount ); governanceGuard.enforceInvariants(keccak256("FLASH_LOAN"), actionData); cycleState.inProgress = true; (collateralAdded, debtRepaid) = _executeSingleStepInternal(flashLoanParams.amount, targetAsset); cycleState.inProgress = false; delete cycleState; emit SingleStepCompleted(collateralAdded, debtRepaid); } /** * @notice Internal single step execution */ function _executeSingleStepInternal( uint256 flashAmount, address targetAsset ) internal returns (uint256 collateralAdded, uint256 debtRepaid) { // Set up flash callback state flashCallbackState = FlashCallbackState({ targetAsset: targetAsset, inFlashLoan: true }); // Determine best flash loan provider (simplified - use Aave by default) IFlashLoanRouter.FlashLoanParams memory params = IFlashLoanRouter.FlashLoanParams({ asset: targetAsset, // Would determine optimal asset in production amount: flashAmount, provider: IFlashLoanRouter.FlashLoanProvider.AAVE }); bytes memory callbackData = abi.encode(targetAsset); // Execute flash loan (this will call onFlashLoan callback) flashRouter.flashLoan(params, callbackData); // Clear flash callback state delete flashCallbackState; // Calculate improvements (would track during callback) // For now, return placeholder collateralAdded = flashAmount / 2; // Simplified: 50% to collateral debtRepaid = flashAmount / 2; // 50% to debt repayment // Record in vault vault.recordCollateralAdded(targetAsset, collateralAdded); vault.recordDebtRepaid(targetAsset, debtRepaid); } /** * @notice Flash loan callback */ function onFlashLoan( address asset, uint256 amount, uint256 fee, bytes calldata callbackData ) external override returns (bytes32) { require(msg.sender == address(flashRouter), "Invalid caller"); require(flashCallbackState.inFlashLoan, "Not in flash loan"); address targetAsset = flashCallbackState.targetAsset; if (targetAsset == address(0)) { (targetAsset) = abi.decode(callbackData, (address)); } // 1. Harvest yield (simplified - would claim rewards from Aave/other protocols) uint256 yieldAmount = _harvestYield(targetAsset); // 2. Swap yield to target asset (if needed) uint256 totalAmount = amount + yieldAmount; if (asset != targetAsset) { totalAmount = _swapAsset(asset, targetAsset, totalAmount); } // 3. Split: repay debt + add collateral uint256 repayAmount = totalAmount / 2; uint256 collateralAmount = totalAmount - repayAmount; // Repay debt _repayDebt(targetAsset, repayAmount); // Add collateral _addCollateral(targetAsset, collateralAmount); // Repay flash loan uint256 repaymentAmount = amount + fee; IERC20(asset).safeApprove(address(flashRouter), repaymentAmount); return CALLBACK_SUCCESS; } /** * @notice Harvest yield from protocols */ function _harvestYield(address asset) internal returns (uint256 yieldAmount) { // Simplified: would claim rewards from Aave, compound, etc. // For now, return 0 return 0; } /** * @notice Swap asset using Uniswap V3 */ function _swapAsset( address tokenIn, address tokenOut, uint256 amountIn ) internal returns (uint256 amountOut) { // Approve router IERC20(tokenIn).safeApprove(address(swapRouter), amountIn); // Execute swap (simplified - would use proper fee tier and price limits) ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({ tokenIn: tokenIn, tokenOut: tokenOut, fee: 3000, // 0.3% fee tier recipient: address(this), deadline: block.timestamp + 300, amountIn: amountIn, amountOutMinimum: 0, // Would calculate in production sqrtPriceLimitX96: 0 }); return swapRouter.exactInputSingle(params); } /** * @notice Repay debt */ function _repayDebt(address asset, uint256 amount) internal { // Approve Aave IERC20(asset).safeApprove(address(aavePool), amount); // Repay (variable rate mode = 2) aavePool.repay(asset, amount, 2, aaveUserAccount); } /** * @notice Add collateral */ function _addCollateral(address asset, uint256 amount) internal { // Approve Aave IERC20(asset).safeApprove(address(aavePool), amount); // Supply as collateral aavePool.supply(asset, amount, aaveUserAccount, 0); } /** * @notice Verify invariants */ function verifyInvariants( uint256 collateralBefore, uint256 debtBefore, uint256 healthFactorBefore ) public view override returns (bool success, string memory reason) { return vault.verifyPositionImproved(collateralBefore, debtBefore, healthFactorBefore) ? (true, "") : (false, "Position did not improve"); } /** * @notice Calculate optimal flash loan amount */ function _calculateOptimalFlashAmount() internal view returns (uint256) { // Simplified: use a percentage of available borrow capacity try aavePool.getUserAccountData(aaveUserAccount) returns ( uint256, uint256, uint256 availableBorrowsBase, uint256, uint256, uint256 ) { // Use 50% of available borrows return availableBorrowsBase / 2; } catch { return 0; } } /** * @notice Update dependencies */ function setFlashRouter(address newRouter) external onlyOwner { require(newRouter != address(0), "Invalid router"); flashRouter = IFlashLoanRouter(newRouter); } function setConfigRegistry(address newRegistry) external onlyOwner { require(newRegistry != address(0), "Invalid registry"); configRegistry = IConfigRegistry(newRegistry); } function setPolicyEngine(address newEngine) external onlyOwner { require(newEngine != address(0), "Invalid engine"); policyEngine = IPolicyEngine(newEngine); } function setGovernanceGuard(address newGuard) external onlyOwner { require(newGuard != address(0), "Invalid guard"); governanceGuard = GovernanceGuard(newGuard); } }