AaveV3AdapterLib
Title: AaveV3AdapterLib
Authors: David Dada <daveproxy80@gmail.com> (https://github.com/dadadave80), Modified from Aave V3 (https://github.com/aave-dao/aave-v3-origin)
All logic for the Aave v3 adapter facet. Supply leg values 1:1 via aToken.balanceOf;
leverage leg values net equity via Aave's OWN price oracle (single self-consistent source
with getUserAccountData, no cross-oracle drift). Reentrancy-gated, pause/emergency-aware,
shortfall-honest.
Functions
aaveV3AdapterStorage
function aaveV3AdapterStorage() internal pure returns (AaveV3AdapterStorage storage $);Returns
| Name | Type | Description |
|---|---|---|
| $ | AaveV3AdapterStorage |
__AaveV3Adapter_init
Initializes the Aave adapter. Must run inside a pre/postInitializer block, after AccessControl is initialized.
function __AaveV3Adapter_init(
address provider,
address asset_,
address vault_,
address rewardRecipient_,
bytes32 assetUsdFeedKey,
uint256 minHealthFactorWad
) internal;Parameters
| Name | Type | Description |
|---|---|---|
| provider | address | Aave PoolAddressesProvider. |
| asset_ | address | Underlying asset (must equal the vault's asset). |
| vault_ | address | Vault to return funds to. |
| rewardRecipient_ | address | Recipient for raw-forwarded rewards. |
| assetUsdFeedKey | bytes32 | Legacy ChainlinkAdapter feed key for asset/USD. No longer used by the net-equity valuation (now Aave-oracle-priced); stored for compatibility. |
| minHealthFactorWad | uint256 | Initial HF floor (>= 1e18). |
registerInterface
Registers IProtocolAdapter + IAaveV3Adapter for ERC-165 discovery.
function registerInterface() internal;_pool
Re-resolves the live Pool from the provider on every call (picks up proxy upgrades).
function _pool() internal view returns (IAaveV3Pool);Returns
| Name | Type | Description |
|---|---|---|
| <none> | IAaveV3Pool |
aToken
Resolves the live aToken for the configured asset.
function aToken() internal view returns (address);Returns
| Name | Type | Description |
|---|---|---|
| <none> | address |
asset
function asset() internal view returns (address);Returns
| Name | Type | Description |
|---|---|---|
| <none> | address |
vault
function vault() internal view returns (address);Returns
| Name | Type | Description |
|---|---|---|
| <none> | address |
addressesProvider
function addressesProvider() internal view returns (address);Returns
| Name | Type | Description |
|---|---|---|
| <none> | address |
rewardRecipient
function rewardRecipient() internal view returns (address);Returns
| Name | Type | Description |
|---|---|---|
| <none> | address |
operator
function operator() internal view returns (address);Returns
| Name | Type | Description |
|---|---|---|
| <none> | address |
_checkOperator
Reverts ProtocolAdapterUnauthorized unless the caller is the wired operator. Placed at
the very top of deploy/withdraw/harvest — BEFORE the reentrancy guard — so an
unauthorized call never leaves the guard latched. Zero operator ⇒ always reverts.
function _checkOperator() private view;eModeCategory
function eModeCategory() internal view returns (uint8);Returns
| Name | Type | Description |
|---|---|---|
| <none> | uint8 |
minHealthFactor
function minHealthFactor() internal view returns (uint256);Returns
| Name | Type | Description |
|---|---|---|
| <none> | uint256 |
isPaused
True when the adapter is paused or emergency-stopped. deploy() checks this so a
paused adapter cannot brick the manager's rebalance() (it reverts, the manager catches
it and emits StrategyDeployFailed, and the pass-2 allocation's bare transfer stays idle
here, counted in NAV — safe).
function isPaused() internal view returns (bool);Returns
| Name | Type | Description |
|---|---|---|
| <none> | bool |
healthFactor
Current health factor (WAD). Aave returns type(uint256).max when there is no debt.
function healthFactor() internal view returns (uint256);Returns
| Name | Type | Description |
|---|---|---|
| <none> | uint256 |
setEMode
function setEMode(uint8 categoryId) internal;Parameters
| Name | Type | Description |
|---|---|---|
| categoryId | uint8 |
setMinHealthFactor
function setMinHealthFactor(uint256 minHealthFactorWad) internal;Parameters
| Name | Type | Description |
|---|---|---|
| minHealthFactorWad | uint256 |
setRewardRecipient
function setRewardRecipient(address recipient) internal;Parameters
| Name | Type | Description |
|---|---|---|
| recipient | address |
setOperator
Sets the authorized operator for deploy/withdraw/harvest (admin-only). Rejects
address(0) so the trio cannot be opened to an unauthenticated default.
function setOperator(address operator_) internal;Parameters
| Name | Type | Description |
|---|---|---|
| operator_ | address |
rewardsController
function rewardsController() internal view returns (address);Returns
| Name | Type | Description |
|---|---|---|
| <none> | address |
setRewardsController
function setRewardsController(address controller) internal;Parameters
| Name | Type | Description |
|---|---|---|
| controller | address |
deploy
Sweeps the adapter's idle asset balance into Aave via Pool.supply.
Reentrancy-gated. Reverts if paused/stopped or if there is nothing to deploy. Uses
exact-amount forceApprove to the freshly-resolved Pool (never infinite).
function deploy() internal returns (uint256 deployed);Returns
| Name | Type | Description |
|---|---|---|
| deployed | uint256 |
withdraw
Withdraws up to amount of the asset to to, returning the REAL amount.
Reentrancy-gated. Spends the adapter's undeployed idle first (it is counted in
totalAssetsManaged), then calls Pool.withdraw for the remainder, capped at both the aToken
balance and the reserve's available cash (see _availableCash), and honestly reports the
balance delta to to. During a utilisation spike the recall therefore takes what the reserve
can pay instead of reverting the calling rebalance() (#271); with no cash it skips the Pool.
A delivery short of amount is an honest partial recall: the unpaid remainder is still
supplied and counted, so the StrategyManager accepts it and a later rebalance finishes it. The
manager reverts with StrategyManagerWithdrawShortfall only when value is lost.
function withdraw(uint256 amount, address to) internal returns (uint256 withdrawn);Parameters
| Name | Type | Description |
|---|---|---|
| amount | uint256 | |
| to | address |
Returns
| Name | Type | Description |
|---|---|---|
| withdrawn | uint256 |
_availableCash
The reserve's cash available to withdraw: the underlying held by the aToken, which is where
Aave keeps a reserve's liquidity (Pool.withdraw pays out through
IAToken.transferUnderlyingTo).
On Aave v3.1+ the Pool also debits the reserve's virtual balance and reverts when a withdrawal
exceeds it. A direct transfer to the aToken lifts the raw balance but not the virtual one, so the
cash is the lower of the two. A v3.0 Pool has no getVirtualUnderlyingBalance; the low-level
staticcall treats a failed or short read as "no virtual balance" and uses the raw balance alone.
A v3.1–v3.3 reserve with virtual accounting switched off (GHO, which cannot be supplied) reports
zero, so this adapter must not be configured for one.
function _availableCash(IAaveV3Pool pool, address asset_, address aToken_) private view returns (uint256 cash);Parameters
| Name | Type | Description |
|---|---|---|
| pool | IAaveV3Pool | |
| asset_ | address | |
| aToken_ | address |
Returns
| Name | Type | Description |
|---|---|---|
| cash | uint256 |
totalAssetsManaged
Total assets managed (vault accounting / share price input).
Supply-only: the 1:1 aToken balance. When debt exists (leverage), Task 8 replaces this with oracle-priced net equity. Rewards are NEVER counted here.
Idle leg (NAV-gap fix): in BOTH branches we add the adapter's idle underlying
balance. IVaultCore.allocateToStrategy pushes vault funds here with a bare ERC-20
transfer that does NOT call deploy(). Inside rebalance() the StrategyManager calls
deploy() in the same transaction (#221), so the funds sit idle in the adapter only when
that deploy fails (paused adapter or pool, supply cap) or when the vault allocates outside
a rebalance, until the next rebalance or operator deploy(). Counting that idle keeps the
vault's share price flat across the allocate→deploy window (otherwise NAV would understate
by the idle amount, letting a depositor mint cheap shares and redeem after deploy()
re-materializes it).
No double-count: the idle is in NEITHER the vault's idle (it was transferred out) NOR the
aToken/net-equity position, and after deploy() idle→~0 while the position grows by the
same amount — so the sum is invariant across deploy. Unencumbered idle underlying is
equity either way, so it is added to the levered net-equity return too.
function totalAssetsManaged() internal view returns (uint256);Returns
| Name | Type | Description |
|---|---|---|
| <none> | uint256 |
lever
Borrows borrowAmount of the asset (variable rate) then re-supplies it, looping
leverage. Reverts if the resulting health factor would fall below the floor.
Operator-gated (same authorization class as deploy/withdraw/harvest): leverage is a
keeper-cadence position action run by the StrategyManager, not a config change. Ungated, a
permissionless caller could force leverage up at a chosen moment (risk/timing griefing —
push toward liquidation / fight the keeper). The operator check is FIRST, before the
reentrancy guard, so an unauthorized call never leaves the guard latched.
function lever(uint256 borrowAmount) internal;Parameters
| Name | Type | Description |
|---|---|---|
| borrowAmount | uint256 |
delever
Withdraws collateralToPull of the asset from supply and repays that much debt,
decreasing leverage. Returns the amount of debt actually repaid.
Operator-gated, matching lever (see its note): a permissionless caller could otherwise
force-unwind the position at a chosen moment (realize loss / fight the keeper). The
operator check is FIRST, before the reentrancy guard.
function delever(uint256 collateralToPull) internal returns (uint256 repaid);Parameters
| Name | Type | Description |
|---|---|---|
| collateralToPull | uint256 |
Returns
| Name | Type | Description |
|---|---|---|
| repaid | uint256 |
_assertHealthFactorAboveFloor
Reverts ProtocolAdapterHealthFactorBreached if the live HF is below the floor.
function _assertHealthFactorAboveFloor() internal view;_netEquityAssets
Oracle-priced net equity (collateral − debt) expressed in asset units.
Aave reports collateral/debt in its OWN base currency (USD, 8 decimals) via
getUserAccountData. We convert the net base amount back to asset units using AAVE'S OWN
price oracle (PoolAddressesProvider.getPriceOracle().getAssetPrice(asset)), which returns
the asset price in the SAME 8-decimal base. Numerator and denominator therefore come from a
single self-consistent source — no cross-oracle divergence (a Chainlink heartbeat/depeg/
mis-registered feed key can no longer silently mis-state the levered NAV). NEVER reads spot.
Single-asset looping: collateral and debt are the same token, so the conversion divides by
the one Aave price and equity is invariant under a price move (only the spread matters).
Note: the legacy _assetUsdFeedKey (Lattice ChainlinkAdapter) is NO LONGER read here and is
now unused for net-equity. The storage field is retained (append-only/upgrade-safety); a
future migration may drop it if nothing else consumes it.
function _netEquityAssets() internal view returns (uint256);Returns
| Name | Type | Description |
|---|---|---|
| <none> | uint256 |
harvest
Claims all Aave incentive rewards for the adapter's aToken and forwards each reward
token RAW to the configured recipient. Zero-claim / fee-on-transfer safe; never
reverts the rebalance/withdraw path. Rewards are not part of totalAssetsManaged().
function harvest() internal;emergencyWithdraw
Fully exits the Aave position and returns all recovered assets to the vault.
Admin-only. Callable while paused/stopped (it is the escape hatch). If the position is levered, repays debt from withdrawn collateral until debt is cleared, then withdraws the remaining collateral to the vault. Reentrancy-gated. Reports the real amount sent.
function emergencyWithdraw() internal returns (uint256 recovered);Returns
| Name | Type | Description |
|---|---|---|
| recovered | uint256 |