Skip to content
Lattice

AaveV3AdapterLib

Git Source

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

NameTypeDescription
$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

NameTypeDescription
provideraddressAave PoolAddressesProvider.
asset_addressUnderlying asset (must equal the vault's asset).
vault_addressVault to return funds to.
rewardRecipient_addressRecipient for raw-forwarded rewards.
assetUsdFeedKeybytes32Legacy ChainlinkAdapter feed key for asset/USD. No longer used by the
net-equity valuation (now Aave-oracle-priced); stored for compatibility.
minHealthFactorWaduint256Initial 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

NameTypeDescription
<none>IAaveV3Pool

aToken

Resolves the live aToken for the configured asset.

function aToken() internal view returns (address);

Returns

NameTypeDescription
<none>address

asset

function asset() internal view returns (address);

Returns

NameTypeDescription
<none>address

vault

function vault() internal view returns (address);

Returns

NameTypeDescription
<none>address

addressesProvider

function addressesProvider() internal view returns (address);

Returns

NameTypeDescription
<none>address

rewardRecipient

function rewardRecipient() internal view returns (address);

Returns

NameTypeDescription
<none>address

operator

function operator() internal view returns (address);

Returns

NameTypeDescription
<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

NameTypeDescription
<none>uint8

minHealthFactor

function minHealthFactor() internal view returns (uint256);

Returns

NameTypeDescription
<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

NameTypeDescription
<none>bool

healthFactor

Current health factor (WAD). Aave returns type(uint256).max when there is no debt.

function healthFactor() internal view returns (uint256);

Returns

NameTypeDescription
<none>uint256

setEMode

function setEMode(uint8 categoryId) internal;

Parameters

NameTypeDescription
categoryIduint8

setMinHealthFactor

function setMinHealthFactor(uint256 minHealthFactorWad) internal;

Parameters

NameTypeDescription
minHealthFactorWaduint256

setRewardRecipient

function setRewardRecipient(address recipient) internal;

Parameters

NameTypeDescription
recipientaddress

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

NameTypeDescription
operator_address

rewardsController

function rewardsController() internal view returns (address);

Returns

NameTypeDescription
<none>address

setRewardsController

function setRewardsController(address controller) internal;

Parameters

NameTypeDescription
controlleraddress

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

NameTypeDescription
deployeduint256

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

NameTypeDescription
amountuint256
toaddress

Returns

NameTypeDescription
withdrawnuint256

_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

NameTypeDescription
poolIAaveV3Pool
asset_address
aToken_address

Returns

NameTypeDescription
cashuint256

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

NameTypeDescription
<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

NameTypeDescription
borrowAmountuint256

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

NameTypeDescription
collateralToPulluint256

Returns

NameTypeDescription
repaiduint256

_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

NameTypeDescription
<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

NameTypeDescription
recovereduint256