Description:
Decentralized Finance (DeFi) protocol contract providing Liquidity, Factory functionality.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/strategies/AaveStrategy.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "../interfaces/IAavePool.sol";
import "../interfaces/IAaveDataProvider.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "../PermissionsManager.sol";
/// @title AaveStrategy
/// @notice Flexible Aave lending strategy with dynamic asset support and permissions
contract AaveStrategy {
/*//////////////////////////////////////////////////////////////
IMMUTABLES
//////////////////////////////////////////////////////////////*/
IAavePool public immutable AAVE_POOL;
IAaveDataProvider public immutable AAVE_DATA_PROVIDER;
PermissionsManager public immutable PERMISSIONS;
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
uint16 public constant REFERRAL_CODE = 0;
uint256 public constant VARIABLE_DEBT = 2;
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event AssetSupplied(
address indexed asset,
uint256 amount,
address indexed onBehalfOf
);
event AssetWithdrawn(
address indexed asset,
uint256 amount,
address indexed to
);
event AssetBorrowed(
address indexed asset,
uint256 amount,
address indexed onBehalfOf
);
event AssetRepaid(
address indexed asset,
uint256 amount,
address indexed onBehalfOf
);
event EmodeSet(address indexed user, uint8 categoryId);
/*//////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
error InvalidAsset();
error InvalidAmount();
error TransferFailed();
/*//////////////////////////////////////////////////////////////
MODIFIERS
//////////////////////////////////////////////////////////////*/
/// @notice Restricts function access to authorized callers only
modifier onlyAuthorized() {
if (!PERMISSIONS.isAuthorized(msg.sender)) {
revert("Unauthorized caller");
}
_;
}
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
/// @notice Deploy Aave strategy with protocol addresses and permissions
/// @param permissions Address of the permissions manager contract
/// @param aavePool Aave lending pool address
/// @param aaveDataProvider Aave protocol data provider address
constructor(
address permissions,
address aavePool,
address aaveDataProvider
) {
require(permissions != address(0), "Invalid permissions");
require(aavePool != address(0), "Invalid pool");
require(aaveDataProvider != address(0), "Invalid data provider");
PERMISSIONS = PermissionsManager(permissions);
AAVE_POOL = IAavePool(aavePool);
AAVE_DATA_PROVIDER = IAaveDataProvider(aaveDataProvider);
}
/*//////////////////////////////////////////////////////////////
LENDING OPERATIONS
//////////////////////////////////////////////////////////////*/
/// @notice Supply any asset to Aave
/// @param asset Asset address to supply
/// @param amount Amount to supply
/// @param onBehalfOf Address to supply on behalf of
function supply(
address asset,
uint256 amount,
address onBehalfOf
) external onlyAuthorized returns (uint256) {
if (asset == address(0)) revert InvalidAsset();
if (amount == 0) revert InvalidAmount();
IERC20(asset).transferFrom(msg.sender, address(this), amount);
IERC20(asset).approve(address(AAVE_POOL), amount);
AAVE_POOL.supply(asset, amount, onBehalfOf, REFERRAL_CODE);
emit AssetSupplied(asset, amount, onBehalfOf);
return amount;
}
/// @notice Borrow any asset from Aave
/// @param asset Asset address to borrow
/// @param amount Amount to borrow
/// @param onBehalfOf Address to borrow on behalf of
function borrow(
address asset,
uint256 amount,
address onBehalfOf
) external onlyAuthorized returns (uint256) {
if (asset == address(0)) revert InvalidAsset();
if (amount == 0) revert InvalidAmount();
AAVE_POOL.borrow(
asset,
amount,
VARIABLE_DEBT,
REFERRAL_CODE,
onBehalfOf
);
// Transfer borrowed amount to caller
bool success = IERC20(asset).transfer(msg.sender, amount);
if (!success) revert TransferFailed();
emit AssetBorrowed(asset, amount, onBehalfOf);
return amount;
}
/// @notice Set eMode category for the calling contract
/// @param categoryId eMode category ID (27 for PT-USDe Stablecoins Nov 2025)
/// @dev This sets eMode for the caller (msg.sender) - used when contract opens positions in its own name
function setContractEMode(uint8 categoryId) external onlyAuthorized {
AAVE_POOL.setUserEMode(categoryId);
emit EmodeSet(msg.sender, categoryId);
}
/*//////////////////////////////////////////////////////////////
VIEW FUNCTIONS
//////////////////////////////////////////////////////////////*/
/// @notice Get user account data from Aave
/// @param user User address to check
function getUserAccountData(
address user
)
external
view
returns (
uint256 totalCollateral,
uint256 totalDebt,
uint256 availableBorrows,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
)
{
return AAVE_POOL.getUserAccountData(user);
}
/// @notice Get reserve configuration for an asset
/// @param asset Asset address to check
function getAssetLTV(address asset) external view returns (uint256) {
if (asset == address(0)) revert InvalidAsset();
(, uint256 ltv, , , , , , , , ) = AAVE_POOL.getConfiguration(asset);
return ltv;
}
/// @notice Get reserve caps for an asset (supply cap and borrow cap)
/// @param asset Asset address to check
/// @return borrowCap The borrow cap in asset units
/// @return supplyCap The supply cap in asset units
function getReserveCaps(
address asset
) external view returns (uint256 borrowCap, uint256 supplyCap) {
if (asset == address(0)) revert InvalidAsset();
(borrowCap, supplyCap) = AAVE_DATA_PROVIDER.getReserveCaps(asset);
// Convert to asset units
uint8 assetDecimals = IERC20Metadata(asset).decimals();
borrowCap = borrowCap * 10 ** assetDecimals;
supplyCap = supplyCap * 10 ** assetDecimals;
}
/// @notice Get current total supply for an asset (total aTokens)
/// @param asset Asset address to check
/// @return totalSupply Current total supply of the asset in Aave
function getTotalSupply(
address asset
) external view returns (uint256 totalSupply) {
if (asset == address(0)) revert InvalidAsset();
return AAVE_DATA_PROVIDER.getATokenTotalSupply(asset);
}
/// @notice Get the debt token address for an asset
/// @param asset The asset address
/// @return The variable debt token address
function getDebtToken(address asset) external view returns (address) {
(, , address variableDebtToken) = AAVE_DATA_PROVIDER
.getReserveTokensAddresses(asset);
return variableDebtToken;
}
/// @notice Get the aToken address for an asset
/// @param asset The asset address
/// @return The aToken address
function getAToken(address asset) external view returns (address) {
(address aToken, , ) = AAVE_DATA_PROVIDER.getReserveTokensAddresses(
asset
);
return aToken;
}
}
"
},
"src/interfaces/IAavePool.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/// @title IAavePool
/// @notice Interface for Aave V3 Pool contract
interface IAavePool {
/// @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
/// @param asset The address of the underlying asset to supply
/// @param amount The amount to be supplied
/// @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user wants to receive them on his own wallet, or a different address if the beneficiary of aTokens is a different wallet
/// @param referralCode Code used to register the integrator originating the operation, for potential rewards from the Aave protocol
function supply(
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode
) external;
/// @notice Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower already supplied enough collateral
/// @param asset The address of the underlying asset to borrow
/// @param amount The amount to be borrowed
/// @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable
/// @param referralCode Code used to register the integrator originating the operation, for potential rewards from the Aave protocol
/// @param onBehalfOf The address of the user who will receive the debt. Should be the address of the borrower itself calling the function if he wants to borrow against his own collateral, or the address of the credit delegator if he has been given credit delegation allowance to msg.sender
function borrow(
address asset,
uint256 amount,
uint256 interestRateMode,
uint16 referralCode,
address onBehalfOf
) external;
/// @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned
/// @param asset The address of the borrowed underlying asset previously borrowed
/// @param amount The amount to repay
/// - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
/// @param interestRateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
/// @param onBehalfOf The address of the user who will get his debt reduced/removed. Should be the address of the user calling the function if he wants to reduce/remove his own debt, or the address of any other other borrower whose debt should be removed
/// @return The final amount repaid
function repay(
address asset,
uint256 amount,
uint256 interestRateMode,
address onBehalfOf
) external returns (uint256);
/// @notice Allows suppliers to enable/disable a specific supplied asset as collateral
/// @param asset The address of the underlying asset supplied
/// @param useAsCollateral True if the user wants to use the supply as collateral, false otherwise
function setUserUseReserveAsCollateral(
address asset,
bool useAsCollateral
) external;
/// @notice Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1
/// @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
/// @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
/// @param user The address of the borrower getting liquidated
/// @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
/// @param receiveAToken True if the liquidators wants to receive the collateral aTokens, `false` if he wants to receive the underlying collateral asset directly
function liquidationCall(
address collateralAsset,
address debtAsset,
address user,
uint256 debtToCover,
bool receiveAToken
) external;
/// @notice Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
/// @param asset The address of the underlying asset to withdraw
/// @param amount The underlying amount to be withdrawn
/// - Send the value type(uint256).max in order to withdraw the whole aToken balance
/// @param to The address that will receive the underlying, same as msg.sender if the user wants to receive it on his own wallet, or a different address if the beneficiary is a different wallet
/// @return The final amount withdrawn
function withdraw(
address asset,
uint256 amount,
address to
) external returns (uint256);
/// @notice Returns the user account data across all the reserves
/// @param user The address of the user
/// @return totalCollateralBase The total collateral of the user in the base currency used by the price feed
/// @return totalDebtBase The total debt of the user in the base currency used by the price feed
/// @return availableBorrowsBase The borrowing power left of the user in the base currency used by the price feed
/// @return currentLiquidationThreshold The liquidation threshold of the user
/// @return ltv The loan to value of the user
/// @return healthFactor The current health factor of the user
function getUserAccountData(
address user
)
external
view
returns (
uint256 totalCollateralBase,
uint256 totalDebtBase,
uint256 availableBorrowsBase,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
);
/// @notice Returns the configuration of the reserve
/// @param asset The address of the underlying asset of the reserve
/// @return decimals The decimals of the reserve
/// @return ltv The ltv of the reserve
/// @return liquidationThreshold The liquidationThreshold of the reserve
/// @return liquidationBonus The liquidationBonus of the reserve
/// @return reserveFactor The reserveFactor of the reserve
/// @return usageAsCollateralEnabled True if the usage as collateral is enabled, false otherwise
/// @return borrowingEnabled True if borrowing is enabled, false otherwise
/// @return stableBorrowRateEnabled True if stable borrow rate is enabled, false otherwise
/// @return isActive True if the reserve is active, false otherwise
/// @return isFrozen True if the reserve is frozen, false otherwise
function getConfiguration(
address asset
)
external
view
returns (
uint256 decimals,
uint256 ltv,
uint256 liquidationThreshold,
uint256 liquidationBonus,
uint256 reserveFactor,
bool usageAsCollateralEnabled,
bool borrowingEnabled,
bool stableBorrowRateEnabled,
bool isActive,
bool isFrozen
);
/// @notice Returns the caps parameters of the reserve
/// @param asset The address of the underlying asset of the reserve
/// @return borrowCap The borrow cap of the reserve in asset units
/// @return supplyCap The supply cap of the reserve in asset units
function getReserveCaps(
address asset
) external view returns (uint256 borrowCap, uint256 supplyCap);
/// @notice Returns the reserve data
/// @param asset The address of the underlying asset of the reserve
/// @return unbacked The amount of unbacked tokens
/// @return accruedToTreasuryScaled The accrued to treasury scaled
/// @return totalAToken The total amount of aTokens
/// @return totalStableDebt The total stable debt amount
/// @return totalVariableDebt The total variable debt amount
/// @return liquidityRate The current liquidity rate
/// @return variableBorrowRate The current variable borrow rate
/// @return stableBorrowRate The current stable borrow rate
/// @return averageStableBorrowRate The current average stable borrow rate
/// @return liquidityIndex The current liquidity index
/// @return variableBorrowIndex The current variable borrow index
/// @return lastUpdateTimestamp The last update timestamp
function getReserveData(
address asset
)
external
view
returns (
uint256 unbacked,
uint256 accruedToTreasuryScaled,
uint256 totalAToken,
uint256 totalStableDebt,
uint256 totalVariableDebt,
uint256 liquidityRate,
uint256 variableBorrowRate,
uint256 stableBorrowRate,
uint256 averageStableBorrowRate,
uint256 liquidityIndex,
uint256 variableBorrowIndex,
uint40 lastUpdateTimestamp
);
/// @notice Sets the user eMode category
/// @param categoryId The category id
function setUserEMode(uint8 categoryId) external;
/// @notice Returns the addresses provider
/// @return The addresses provider
function ADDRESSES_PROVIDER() external view returns (address);
}
"
},
"src/interfaces/IAaveDataProvider.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/// @title IAaveDataProvider
/// @notice Interface for Aave Protocol Data Provider
interface IAaveDataProvider {
/// @notice Returns the borrow and supply caps for an asset
/// @param asset The address of the underlying asset
/// @return borrowCap The borrow cap in asset units (0 = no cap)
/// @return supplyCap The supply cap in asset units (0 = no cap)
function getReserveCaps(
address asset
) external view returns (uint256 borrowCap, uint256 supplyCap);
/// @notice Returns the total supply of aTokens for a given asset
/// @param asset The address of the underlying asset
/// @return totalSupply The total supply of aTokens
function getATokenTotalSupply(
address asset
) external view returns (uint256 totalSupply);
/// @notice Returns the token addresses for a given asset
/// @param asset The address of the underlying asset
/// @return aTokenAddress The address of the aToken
/// @return stableDebtTokenAddress The address of the stable debt token
/// @return variableDebtTokenAddress The address of the variable debt token
function getReserveTokensAddresses(
address asset
)
external
view
returns (
address aTokenAddress,
address stableDebtTokenAddress,
address variableDebtTokenAddress
);
}
"
},
"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
},
"lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity >=0.6.2;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
"
},
"src/PermissionsManager.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/// @title PermissionsManager
/// @notice Manages admin and whitelisted caller permissions for the lending system
contract PermissionsManager {
/*//////////////////////////////////////////////////////////////
STORAGE
//////////////////////////////////////////////////////////////*/
/// @notice The admin address who can manage permissions
address public admin;
/// @notice Mapping of whitelisted callers
mapping(address => bool) public whitelistedCallers;
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event AdminUpdated(address indexed oldAdmin, address indexed newAdmin);
event CallerWhitelisted(address indexed caller);
event CallerRemovedFromWhitelist(address indexed caller);
/*//////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
error OnlyAdmin();
error UnauthorizedCaller();
error AdminZeroAddress();
error CallerAlreadyWhitelisted();
error CallerNotWhitelisted();
/*//////////////////////////////////////////////////////////////
MODIFIERS
//////////////////////////////////////////////////////////////*/
/// @notice Restricts function access to admin only
modifier onlyAdmin() {
if (msg.sender != admin) revert OnlyAdmin();
_;
}
/// @notice Restricts function access to whitelisted callers only
modifier onlyAuthorized() {
if (!whitelistedCallers[msg.sender]) revert UnauthorizedCaller();
_;
}
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
/// @notice Constructor sets the initial admin
/// @param _admin The initial admin address
constructor(address _admin) {
if (_admin == address(0)) revert AdminZeroAddress();
admin = _admin;
// Admin is automatically whitelisted
whitelistedCallers[_admin] = true;
emit AdminUpdated(address(0), _admin);
emit CallerWhitelisted(_admin);
}
/*//////////////////////////////////////////////////////////////
ADMIN FUNCTIONS
//////////////////////////////////////////////////////////////*/
/// @notice Updates the admin address
/// @param newAdmin The new admin address
function updateAdmin(address newAdmin) external onlyAdmin {
if (newAdmin == address(0)) revert AdminZeroAddress();
address oldAdmin = admin;
admin = newAdmin;
// Automatically whitelist new admin
whitelistedCallers[newAdmin] = true;
emit AdminUpdated(oldAdmin, newAdmin);
emit CallerWhitelisted(newAdmin);
}
/// @notice Adds a caller to the whitelist
/// @param caller The address to whitelist
function addWhitelistedCaller(address caller) external onlyAdmin {
if (whitelistedCallers[caller]) revert CallerAlreadyWhitelisted();
whitelistedCallers[caller] = true;
emit CallerWhitelisted(caller);
}
/// @notice Removes a caller from the whitelist
/// @param caller The address to remove from whitelist
function removeWhitelistedCaller(address caller) external onlyAdmin {
if (!whitelistedCallers[caller]) revert CallerNotWhitelisted();
if (caller == admin) revert OnlyAdmin(); // Cannot remove admin from whitelist
whitelistedCallers[caller] = false;
emit CallerRemovedFromWhitelist(caller);
}
/*//////////////////////////////////////////////////////////////
VIEW FUNCTIONS
//////////////////////////////////////////////////////////////*/
/// @notice Checks if a caller is authorized (whitelisted)
/// @param caller The address to check
/// @return True if the caller is whitelisted
function isAuthorized(address caller) external view returns (bool) {
return whitelistedCallers[caller];
}
/// @notice Gets the current admin address
/// @return The admin address
function getAdmin() external view returns (address) {
return admin;
}
}
"
}
},
"settings": {
"remappings": [
"@pendle/core-v2/=lib/pendle-core-v2-public/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"aave-v3-core/=lib/aave-v3-core/",
"halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
"morpho-blue/=lib/morpho-blue/",
"pendle-core-v2-public/=lib/pendle-core-v2-public/contracts/"
],
"optimizer": {
"enabled": true,
"runs": 10000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "paris",
"viaIR": true
}
}}
Submitted on: 2025-09-22 12:40:29
Comments
Log in to comment.
No comments yet.