Files
no_five/contracts/core/FlashLoanRouter.sol
2025-11-20 15:35:25 -08:00

355 lines
11 KiB
Solidity

// 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);
}
}