Initial commit

This commit is contained in:
Test User
2025-11-20 15:35:25 -08:00
commit bfbe3ee8b7
59 changed files with 7187 additions and 0 deletions

View File

@@ -0,0 +1,176 @@
// 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);
}
}

View File

@@ -0,0 +1,318 @@
// 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 "../interfaces/IVault.sol";
import "../interfaces/IOracleAdapter.sol";
/**
* @title DBISInstitutionalVault
* @notice Institutional vault representing a leveraged DeFi position
* @dev Tracks collateral and debt across multiple assets, enforces invariants
*/
contract DBISInstitutionalVault is IVault, Ownable, AccessControl, ReentrancyGuard {
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
bytes32 public constant KERNEL_ROLE = keccak256("KERNEL_ROLE");
IOracleAdapter public oracleAdapter;
// Aave v3 Pool interface (simplified)
interface IPoolV3 {
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; // Address of the Aave position
// Internal position tracking (for non-Aave assets)
struct AssetPosition {
uint256 collateral; // Amount deposited as collateral
uint256 debt; // Amount borrowed
}
mapping(address => AssetPosition) private assetPositions;
address[] private trackedAssets;
// Constants
uint256 private constant HF_SCALE = 1e18;
uint256 private constant PRICE_SCALE = 1e8;
constructor(
address _oracleAdapter,
address _aavePool,
address _aaveUserAccount,
address initialOwner
) Ownable(initialOwner) {
require(_oracleAdapter != address(0), "Invalid oracle");
require(_aavePool != address(0), "Invalid Aave pool");
require(_aaveUserAccount != address(0), "Invalid Aave account");
oracleAdapter = IOracleAdapter(_oracleAdapter);
aavePool = IPoolV3(_aavePool);
aaveUserAccount = _aaveUserAccount;
_grantRole(DEFAULT_ADMIN_ROLE, initialOwner);
}
/**
* @notice Get total collateral value in USD (scaled by 1e8)
*/
function getTotalCollateralValue() public view override returns (uint256) {
uint256 total = 0;
// Get Aave collateral
try aavePool.getUserAccountData(aaveUserAccount) returns (
uint256 totalCollateralBase,
uint256,
uint256,
uint256,
uint256,
uint256
) {
// Aave returns in base currency (USD, scaled by 1e8)
total += totalCollateralBase;
} catch {}
// Add non-Aave collateral
for (uint256 i = 0; i < trackedAssets.length; i++) {
address asset = trackedAssets[i];
AssetPosition storage pos = assetPositions[asset];
if (pos.collateral > 0) {
try oracleAdapter.convertAmount(asset, pos.collateral, address(0)) returns (uint256 value) {
total += value;
} catch {}
}
}
return total;
}
/**
* @notice Get total debt value in USD (scaled by 1e8)
*/
function getTotalDebtValue() public view override returns (uint256) {
uint256 total = 0;
// Get Aave debt
try aavePool.getUserAccountData(aaveUserAccount) returns (
uint256,
uint256 totalDebtBase,
uint256,
uint256,
uint256,
uint256
) {
// Aave returns in base currency (USD, scaled by 1e8)
total += totalDebtBase;
} catch {}
// Add non-Aave debt
for (uint256 i = 0; i < trackedAssets.length; i++) {
address asset = trackedAssets[i];
AssetPosition storage pos = assetPositions[asset];
if (pos.debt > 0) {
try oracleAdapter.convertAmount(asset, pos.debt, address(0)) returns (uint256 value) {
total += value;
} catch {}
}
}
return total;
}
/**
* @notice Get current health factor (scaled by 1e18)
*/
function getHealthFactor() public view override returns (uint256) {
// Try to get from Aave first (most accurate)
try aavePool.getUserAccountData(aaveUserAccount) returns (
uint256,
uint256,
uint256,
uint256,
uint256,
uint256 healthFactor
) {
return healthFactor;
} catch {}
// Fallback: calculate manually
uint256 collateralValue = getTotalCollateralValue();
uint256 debtValue = getTotalDebtValue();
if (debtValue == 0) {
return type(uint256).max; // Infinite health factor if no debt
}
// Health Factor = (Collateral * Liquidation Threshold) / Debt
// Simplified: use 80% LTV as threshold
return (collateralValue * 80e18 / 100) / debtValue;
}
/**
* @notice Get current LTV (Loan-to-Value ratio, scaled by 1e18)
*/
function getLTV() public view override returns (uint256) {
uint256 collateralValue = getTotalCollateralValue();
if (collateralValue == 0) {
return 0;
}
uint256 debtValue = getTotalDebtValue();
return (debtValue * HF_SCALE) / collateralValue;
}
/**
* @notice Record addition of collateral
*/
function recordCollateralAdded(address asset, uint256 amount) external override onlyRole(KERNEL_ROLE) {
require(asset != address(0), "Invalid asset");
require(amount > 0, "Invalid amount");
if (assetPositions[asset].collateral == 0 && assetPositions[asset].debt == 0) {
trackedAssets.push(asset);
}
assetPositions[asset].collateral += amount;
emit CollateralAdded(asset, amount);
}
/**
* @notice Record repayment of debt
*/
function recordDebtRepaid(address asset, uint256 amount) external override onlyRole(KERNEL_ROLE) {
require(asset != address(0), "Invalid asset");
require(amount > 0, "Invalid amount");
require(assetPositions[asset].debt >= amount, "Debt insufficient");
assetPositions[asset].debt -= amount;
emit DebtRepaid(asset, amount);
}
/**
* @notice Take a position snapshot for invariant checking
*/
function snapshotPosition()
external
override
onlyRole(KERNEL_ROLE)
returns (
uint256 collateralBefore,
uint256 debtBefore,
uint256 healthFactorBefore
)
{
collateralBefore = getTotalCollateralValue();
debtBefore = getTotalDebtValue();
healthFactorBefore = getHealthFactor();
}
/**
* @notice Verify position improved (invariant check)
* @dev Enforces: Debt↓ OR Collateral↑ OR HF↑
*/
function verifyPositionImproved(
uint256 collateralBefore,
uint256 debtBefore,
uint256 healthFactorBefore
) external view override returns (bool success) {
uint256 collateralAfter = getTotalCollateralValue();
uint256 debtAfter = getTotalDebtValue();
uint256 healthFactorAfter = getHealthFactor();
// Emit snapshot event (best effort)
// Note: Events can't be emitted from view functions in Solidity
// This would be done in the calling contract
// Check invariants:
// 1. Debt decreased OR
// 2. Collateral increased OR
// 3. Health factor improved
bool debtDecreased = debtAfter < debtBefore;
bool collateralIncreased = collateralAfter > collateralBefore;
bool hfImproved = healthFactorAfter > healthFactorBefore;
// All three must improve for strict amortization
return debtDecreased && collateralIncreased && hfImproved;
}
/**
* @notice Grant operator role
*/
function grantOperator(address operator) external onlyOwner {
_grantRole(OPERATOR_ROLE, operator);
}
/**
* @notice Grant kernel role
*/
function grantKernel(address kernel) external onlyOwner {
_grantRole(KERNEL_ROLE, kernel);
}
/**
* @notice Revoke operator role
*/
function revokeOperator(address operator) external onlyOwner {
_revokeRole(OPERATOR_ROLE, operator);
}
/**
* @notice Revoke kernel role
*/
function revokeKernel(address kernel) external onlyOwner {
_revokeRole(KERNEL_ROLE, kernel);
}
/**
* @notice Update oracle adapter
*/
function setOracleAdapter(address newOracle) external onlyOwner {
require(newOracle != address(0), "Invalid oracle");
oracleAdapter = IOracleAdapter(newOracle);
}
/**
* @notice Update Aave pool
*/
function setAavePool(address newPool) external onlyOwner {
require(newPool != address(0), "Invalid pool");
aavePool = IPoolV3(newPool);
}
/**
* @notice Update Aave user account
*/
function setAaveUserAccount(address newAccount) external onlyOwner {
require(newAccount != address(0), "Invalid account");
aaveUserAccount = newAccount;
}
/**
* @notice Get asset position
*/
function getAssetPosition(address asset) external view returns (uint256 collateral, uint256 debt) {
AssetPosition storage pos = assetPositions[asset];
return (pos.collateral, pos.debt);
}
/**
* @notice Get all tracked assets
*/
function getTrackedAssets() external view returns (address[] memory) {
return trackedAssets;
}
}

View File

@@ -0,0 +1,354 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../interfaces/IFlashLoanRouter.sol";
/**
* @title FlashLoanRouter
* @notice Multi-provider flash loan aggregator
* @dev Routes flash loans to Aave, Balancer, Uniswap V3, or DAI flash mint
*/
contract FlashLoanRouter is IFlashLoanRouter, Ownable, ReentrancyGuard {
using SafeERC20 for IERC20;
// Callback interface
interface IFlashLoanReceiver {
function onFlashLoan(
address asset,
uint256 amount,
uint256 fee,
bytes calldata data
) external returns (bytes32);
}
// Aave v3 Pool
interface IPoolV3 {
function flashLoanSimple(
address receiverAddress,
address asset,
uint256 amount,
bytes calldata params,
uint16 referralCode
) external;
}
// Balancer Vault
interface IBalancerVault {
function flashLoan(
address recipient,
address[] memory tokens,
uint256[] memory amounts,
bytes memory userData
) external;
}
// Uniswap V3 Pool
interface IUniswapV3Pool {
function flash(
address recipient,
uint256 amount0,
uint256 amount1,
bytes calldata data
) external;
}
// DAI Flash Mint
interface IDaiFlashMint {
function flashMint(
address receiver,
uint256 amount,
bytes calldata data
) external;
}
// Provider addresses
address public aavePool;
address public balancerVault;
address public daiFlashMint;
// Constants
bytes32 public constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan");
uint16 public constant AAVE_REFERRAL_CODE = 0;
// Flash loan state
struct FlashLoanState {
address caller;
FlashLoanProvider provider;
bytes callbackData;
bool inProgress;
}
FlashLoanState private flashLoanState;
event ProviderAddressUpdated(FlashLoanProvider provider, address oldAddress, address newAddress);
modifier onlyInFlashLoan() {
require(flashLoanState.inProgress, "Not in flash loan");
_;
}
modifier onlyNotInFlashLoan() {
require(!flashLoanState.inProgress, "Flash loan in progress");
_;
}
constructor(
address _aavePool,
address _balancerVault,
address _daiFlashMint,
address initialOwner
) Ownable(initialOwner) {
aavePool = _aavePool;
balancerVault = _balancerVault;
daiFlashMint = _daiFlashMint;
}
/**
* @notice Execute a flash loan
*/
function flashLoan(
FlashLoanParams memory params,
bytes memory callbackData
) external override nonReentrant onlyNotInFlashLoan {
require(params.asset != address(0), "Invalid asset");
require(params.amount > 0, "Invalid amount");
// Determine provider if needed (for liquidity-weighted selection)
FlashLoanProvider provider = params.provider;
// Set flash loan state
flashLoanState = FlashLoanState({
caller: msg.sender,
provider: provider,
callbackData: callbackData,
inProgress: true
});
// Execute flash loan based on provider
if (provider == FlashLoanProvider.AAVE) {
_flashLoanAave(params.asset, params.amount);
} else if (provider == FlashLoanProvider.BALANCER) {
_flashLoanBalancer(params.asset, params.amount);
} else if (provider == FlashLoanProvider.UNISWAP) {
_flashLoanUniswap(params.asset, params.amount);
} else if (provider == FlashLoanProvider.DAI_FLASH) {
_flashLoanDai(params.amount);
} else {
revert("Invalid provider");
}
// Clear state
flashLoanState.inProgress = false;
delete flashLoanState;
}
/**
* @notice Execute multi-asset flash loan
*/
function flashLoanBatch(
FlashLoanParams[] memory params,
bytes memory callbackData
) external override nonReentrant onlyNotInFlashLoan {
require(params.length > 0, "Empty params");
require(params.length <= 10, "Too many assets"); // Reasonable limit
// For simplicity, execute sequentially
// In production, could optimize for parallel execution
for (uint256 i = 0; i < params.length; i++) {
flashLoan(params[i], callbackData);
}
}
/**
* @notice Aave v3 flash loan
*/
function _flashLoanAave(address asset, uint256 amount) internal {
require(aavePool != address(0), "Aave pool not set");
emit FlashLoanInitiated(asset, amount, FlashLoanProvider.AAVE);
bytes memory params = abi.encode(flashLoanState.caller, flashLoanState.callbackData);
IPoolV3(aavePool).flashLoanSimple(
address(this),
asset,
amount,
params,
AAVE_REFERRAL_CODE
);
}
/**
* @notice Balancer flash loan
*/
function _flashLoanBalancer(address asset, uint256 amount) internal {
require(balancerVault != address(0), "Balancer vault not set");
emit FlashLoanInitiated(asset, amount, FlashLoanProvider.BALANCER);
address[] memory tokens = new address[](1);
tokens[0] = asset;
uint256[] memory amounts = new uint256[](1);
amounts[0] = amount;
bytes memory userData = abi.encode(flashLoanState.caller, flashLoanState.callbackData);
IBalancerVault(balancerVault).flashLoan(address(this), tokens, amounts, userData);
}
/**
* @notice Uniswap V3 flash loan
*/
function _flashLoanUniswap(address asset, uint256 amount) internal {
// Uniswap V3 requires pool address - simplified here
// In production, would need to determine pool from asset pair
revert("Uniswap V3 flash loan not fully implemented");
}
/**
* @notice DAI flash mint
*/
function _flashLoanDai(uint256 amount) internal {
require(daiFlashMint != address(0), "DAI flash mint not set");
emit FlashLoanInitiated(address(0), amount, FlashLoanProvider.DAI_FLASH); // DAI address
bytes memory data = abi.encode(flashLoanState.caller, flashLoanState.callbackData);
IDaiFlashMint(daiFlashMint).flashMint(address(this), amount, data);
}
/**
* @notice Aave flash loan callback
*/
function executeOperation(
address asset,
uint256 amount,
uint256 premium,
bytes calldata params
) external returns (bool) {
require(msg.sender == aavePool, "Invalid caller");
require(flashLoanState.inProgress, "Not in flash loan");
(address receiver, bytes memory callbackData) = abi.decode(params, (address, bytes));
// Calculate total repayment
uint256 totalRepayment = amount + premium;
// Call receiver callback
bytes32 result = IFlashLoanReceiver(receiver).onFlashLoan(
asset,
amount,
premium,
callbackData
);
require(result == CALLBACK_SUCCESS, "Callback failed");
// Repay flash loan
IERC20(asset).safeApprove(aavePool, totalRepayment);
emit FlashLoanRepaid(asset, totalRepayment);
return true;
}
/**
* @notice Balancer flash loan callback
*/
function receiveFlashLoan(
address[] memory tokens,
uint256[] memory amounts,
uint256[] memory feeAmounts,
bytes memory userData
) external {
require(msg.sender == balancerVault, "Invalid caller");
require(flashLoanState.inProgress, "Not in flash loan");
require(tokens.length == 1, "Single asset only");
(address receiver, bytes memory callbackData) = abi.decode(userData, (address, bytes));
address asset = tokens[0];
uint256 amount = amounts[0];
uint256 fee = feeAmounts[0];
// Call receiver callback
bytes32 result = IFlashLoanReceiver(receiver).onFlashLoan(
asset,
amount,
fee,
callbackData
);
require(result == CALLBACK_SUCCESS, "Callback failed");
// Repay flash loan
uint256 totalRepayment = amount + fee;
IERC20(asset).safeApprove(balancerVault, totalRepayment);
emit FlashLoanRepaid(asset, totalRepayment);
}
/**
* @notice Get available liquidity from Aave
*/
function getAvailableLiquidity(
address asset,
FlashLoanProvider provider
) external view override returns (uint256) {
if (provider == FlashLoanProvider.AAVE) {
// Query Aave liquidity (simplified)
// In production, would query Aave Pool's available liquidity
return type(uint256).max; // Placeholder
} else if (provider == FlashLoanProvider.BALANCER) {
// Query Balancer liquidity
return type(uint256).max; // Placeholder
} else if (provider == FlashLoanProvider.DAI_FLASH) {
// DAI flash mint has no limit
return type(uint256).max;
}
return 0;
}
/**
* @notice Get flash loan fee
*/
function getFlashLoanFee(
address asset,
uint256 amount,
FlashLoanProvider provider
) external view override returns (uint256) {
if (provider == FlashLoanProvider.AAVE) {
// Aave v3: 0.05% premium
return (amount * 5) / 10000;
} else if (provider == FlashLoanProvider.BALANCER) {
// Balancer: variable fee
return (amount * 1) / 10000; // 0.01% placeholder
} else if (provider == FlashLoanProvider.DAI_FLASH) {
// DAI flash mint: 0% fee (plus gas cost)
return 0;
}
return 0;
}
/**
* @notice Update provider addresses
*/
function setAavePool(address newPool) external onlyOwner {
address oldPool = aavePool;
aavePool = newPool;
emit ProviderAddressUpdated(FlashLoanProvider.AAVE, oldPool, newPool);
}
function setBalancerVault(address newVault) external onlyOwner {
address oldVault = balancerVault;
balancerVault = newVault;
emit ProviderAddressUpdated(FlashLoanProvider.BALANCER, oldVault, newVault);
}
function setDaiFlashMint(address newFlashMint) external onlyOwner {
address oldFlashMint = daiFlashMint;
daiFlashMint = newFlashMint;
emit ProviderAddressUpdated(FlashLoanProvider.DAI_FLASH, oldFlashMint, newFlashMint);
}
}

View File

@@ -0,0 +1,482 @@
// 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);
}
}