Aave V3 高级功能

1. E-Mode (效率模式)

1.1 概述

E-Mode 允许相关性资产(如稳定币之间)获得更高的资本效率:

普通模式                        E-Mode (稳定币)
┌──────────────┐               ┌──────────────┐
│ LTV: 80%     │               │ LTV: 97%     │
│ 清算阈值: 82.5% │  ─升级──▶   │ 清算阈值: 98%  │
│ 清算奖励: 5%  │               │ 清算奖励: 1%  │
└──────────────┘               └──────────────┘

1.2 E-Mode 类别

struct EModeCategory {
    uint16 ltv;                  // LTV (97% = 9700)
    uint16 liquidationThreshold; // 清算阈值 (98% = 9800)
    uint16 liquidationBonus;     // 清算奖励 (1% = 10100)
    address priceSource;         // 自定义价格源(可选)
    string label;                // 类别标签
}

1.3 类别示例

类别 ID标签LTV清算阈值清算奖励资产
1Stablecoins97%98%1%USDC, USDT, DAI
2ETH Correlated93%95%1%ETH, stETH, wstETH
3BTC Correlated93%95%1%WBTC, renBTC

1.4 设置用户 E-Mode

// Pool.sol
function setUserEMode(uint8 categoryId) external virtual override {
    EModeLogic.executeSetUserEMode(
        _reserves,
        _reservesList,
        _eModeCategories,
        _usersEModeCategory,
        _usersConfig[msg.sender],
        DataTypes.ExecuteSetUserEModeParams({
            reservesCount: _reservesCount,
            oracle: ADDRESSES_PROVIDER.getPriceOracle(),
            categoryId: categoryId
        })
    );
}

1.5 E-Mode 逻辑

// EModeLogic.sol
function executeSetUserEMode(
    mapping(address => DataTypes.ReserveData) storage reservesData,
    mapping(uint256 => address) storage reservesList,
    mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
    mapping(address => uint8) storage usersEModeCategory,
    DataTypes.UserConfigurationMap storage userConfig,
    DataTypes.ExecuteSetUserEModeParams memory params
) external {
    // 验证类别存在
    if (params.categoryId != 0) {
        require(
            eModeCategories[params.categoryId].liquidationThreshold != 0,
            Errors.INCONSISTENT_EMODE_CATEGORY
        );
    }
 
    uint8 prevCategoryId = usersEModeCategory[msg.sender];
    if (prevCategoryId != params.categoryId) {
        // 更新用户 E-Mode 类别
        usersEModeCategory[msg.sender] = params.categoryId;
 
        // 验证切换后健康因子仍然健康
        if (params.categoryId != 0) {
            require(
                _validateEModeCategory(
                    reservesData,
                    reservesList,
                    eModeCategories,
                    userConfig,
                    params
                ),
                Errors.INCONSISTENT_EMODE_CATEGORY
            );
        }
 
        // 检查健康因子
        (, , , , uint256 healthFactor, ) = GenericLogic.calculateUserAccountData(
            reservesData,
            reservesList,
            eModeCategories,
            DataTypes.CalculateUserAccountDataParams({
                userConfig: userConfig,
                reservesCount: params.reservesCount,
                user: msg.sender,
                oracle: params.oracle,
                userEModeCategory: params.categoryId
            })
        );
 
        require(
            healthFactor >= HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
            Errors.HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD
        );
    }
 
    emit UserEModeSet(msg.sender, params.categoryId);
}

1.6 E-Mode 效率计算

场景:用户存入 10,000 USDC,借出 USDT

普通模式:
- 最大借贷 = 10,000 × 80% = 8,000 USDT
- 有效杠杆 = 10,000 / (10,000 - 8,000) = 5x

E-Mode (稳定币):
- 最大借贷 = 10,000 × 97% = 9,700 USDT
- 有效杠杆 = 10,000 / (10,000 - 9,700) = 33x

资本效率提升: 33x / 5x = 6.6 倍

2. Isolation Mode (隔离模式)

2.1 概述

隔离模式允许新资产在受控环境中上线,限制风险敞口:

隔离模式资产
┌────────────────────────────────────┐
│ 特点:                              │
│ • 只能作为唯一抵押品               │
│ • 只能借出稳定币                   │
│ • 有债务上限                       │
│ • 逐步提升信任度后可解除限制       │
└────────────────────────────────────┘

2.2 配置参数

struct ReserveData {
    // ... 其他字段
    uint128 isolationModeTotalDebt;  // 当前隔离模式总债务
}
 
// ReserveConfiguration 中的隔离模式参数
// bit 212-251: debt ceiling (隔离模式债务上限)

2.3 隔离模式检测

// UserConfiguration.sol
function getIsolationModeState(
    DataTypes.UserConfigurationMap storage self,
    mapping(address => DataTypes.ReserveData) storage reservesData,
    mapping(uint256 => address) storage reservesList
) internal view returns (bool, address, uint256) {
    // 遍历用户抵押品
    if (self.isEmpty()) {
        return (false, address(0), 0);
    }
 
    uint256 firstAssetId = self.getFirstAssetIdByMask(COLLATERAL_MASK);
 
    if (reservesData[reservesList[firstAssetId]].configuration.getDebtCeiling() != 0) {
        address isolationModeCollateral = reservesList[firstAssetId];
        return (
            true,
            isolationModeCollateral,
            reservesData[isolationModeCollateral].configuration.getDebtCeiling()
        );
    }
 
    return (false, address(0), 0);
}

2.4 隔离模式借贷限制

// ValidationLogic.sol (借贷验证)
function validateBorrow(...) internal view {
    // ...
 
    // 隔离模式检查
    if (params.isolationModeActive) {
        // 只允许借出标记为可在隔离模式借出的资产
        require(
            params.reserveCache.reserveConfiguration.getBorrowableInIsolation(),
            Errors.ASSET_NOT_BORROWABLE_IN_ISOLATION
        );
 
        // 检查债务上限
        require(
            reservesData[params.isolationModeCollateralAddress].isolationModeTotalDebt +
                (params.amount /
                    10 ** (params.reserveCache.reserveConfiguration.getDecimals() -
                        ReserveConfiguration.DEBT_CEILING_DECIMALS)) <=
                params.isolationModeDebtCeiling,
            Errors.DEBT_CEILING_EXCEEDED
        );
    }
}

2.5 隔离模式示例

场景:新代币 XYZ 上线,设置为隔离模式

配置:
- XYZ 债务上限: 1,000,000 USD
- XYZ LTV: 50%
- 可借出资产: 仅 USDC, USDT, DAI

用户操作:
1. 存入 1000 XYZ (价值 $10,000)
2. 最大可借: 10,000 × 50% = 5,000 USDC
3. 无法同时使用其他抵押品
4. 无法借出非稳定币资产

3. Portal (跨链桥接)

3.1 概述

Portal 允许在不同链上铸造未支持的 aToken,实现跨链流动性:

源链 (Ethereum)                    目标链 (Polygon)
┌─────────────┐                   ┌─────────────┐
│  用户资产   │                   │  用户资产   │
│  100 USDC   │                   │  0 USDC     │
└─────────────┘                   └─────────────┘
       │                                 │
       ▼                                 ▼
┌─────────────┐    Portal        ┌─────────────┐
│  aUSDC      │ ═══════════════▶ │  aUSDC      │
│  销毁       │    (跨链消息)    │  铸造       │
└─────────────┘                   └─────────────┘

3.2 Mint Unbacked

在目标链铸造未支持的 aToken:

// BridgeLogic.sol
function executeMintUnbacked(
    mapping(address => DataTypes.ReserveData) storage reservesData,
    mapping(uint256 => address) storage reservesList,
    DataTypes.UserConfigurationMap storage userConfig,
    address asset,
    uint256 amount,
    address onBehalfOf,
    uint16 referralCode
) external {
    DataTypes.ReserveData storage reserve = reservesData[asset];
    DataTypes.ReserveCache memory reserveCache = reserve.cache();
 
    reserve.updateState(reserveCache);
 
    // 验证
    ValidationLogic.validateUnbacked(reserveCache, amount, reserve);
 
    // 增加 unbacked 计数
    uint256 unbackedMintCap = reserveCache.reserveConfiguration.getUnbackedMintCap();
    uint256 reserveDecimals = reserveCache.reserveConfiguration.getDecimals();
 
    uint256 currentUnbacked = reserve.unbacked += amount.toUint128();
 
    require(
        currentUnbacked <= unbackedMintCap * (10 ** reserveDecimals),
        Errors.UNBACKED_MINT_CAP_EXCEEDED
    );
 
    // 铸造 aToken
    bool isFirstSupply = IAToken(reserveCache.aTokenAddress).mint(
        msg.sender,
        onBehalfOf,
        amount,
        reserveCache.nextLiquidityIndex
    );
 
    if (isFirstSupply) {
        if (
            ValidationLogic.validateAutomaticUseAsCollateral(
                reservesData,
                reservesList,
                userConfig,
                reserveCache.reserveConfiguration,
                reserveCache.aTokenAddress
            )
        ) {
            userConfig.setUsingAsCollateral(reserve.id, true);
            emit ReserveUsedAsCollateralEnabled(asset, onBehalfOf);
        }
    }
 
    emit MintUnbacked(asset, msg.sender, onBehalfOf, amount, referralCode);
}

3.3 Back Unbacked

在源链销毁资产,完成跨链:

function executeBackUnbacked(
    DataTypes.ReserveData storage reserve,
    address asset,
    uint256 amount,
    uint256 fee
) external returns (uint256) {
    DataTypes.ReserveCache memory reserveCache = reserve.cache();
 
    reserve.updateState(reserveCache);
 
    // 计算实际支持金额
    uint256 backingAmount = (amount < reserve.unbacked) ? amount : reserve.unbacked;
    uint256 feeAmount = amount.percentMul(fee);
 
    // 减少 unbacked 计数
    reserve.unbacked -= backingAmount.toUint128();
 
    // 累积费用到国库
    reserve.accruedToTreasury += (backingAmount + feeAmount)
        .rayDiv(reserveCache.nextLiquidityIndex)
        .toUint128();
 
    // 更新利率
    reserve.updateInterestRates(reserveCache, asset, backingAmount + feeAmount, 0);
 
    // 转移资产
    IERC20(asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, backingAmount + feeAmount);
 
    emit BackUnbacked(asset, msg.sender, backingAmount, feeAmount);
 
    return backingAmount;
}

3.4 Portal 费用机制

费用 = bridgeAmount × bridgeProtocolFee

费用分配:
└── 100% → 协议国库

这笔费用补偿协议承担的跨链风险

4. Flash Loan (闪电贷)

4.1 概述

闪电贷允许在单笔交易内无抵押借贷任意金额:

单笔交易
┌──────────────────────────────────────────────┐
│ 1. 借出资产                                   │
│ 2. 执行自定义逻辑                             │
│ 3. 归还资产 + 手续费                          │
│                                              │
│ 如果无法归还 → 整笔交易回滚                   │
└──────────────────────────────────────────────┘

4.2 闪电贷类型

类型函数特点
简单闪电贷flashLoanSimple单一资产
批量闪电贷flashLoan多资产,可选开仓

4.3 简单闪电贷

// FlashLoanLogic.sol
function executeFlashLoanSimple(
    DataTypes.ReserveData storage reserve,
    DataTypes.FlashLoanSimpleParams memory params
) external {
    DataTypes.ReserveCache memory reserveCache = reserve.cache();
    reserve.updateState(reserveCache);
 
    // 验证
    ValidationLogic.validateFlashloanSimple(reserveCache);
 
    // 计算手续费
    uint256 totalPremium = params.amount.percentMul(params.flashLoanPremiumTotal);
 
    // 转移资产给借款人
    IAToken(reserveCache.aTokenAddress).transferUnderlyingTo(
        params.receiverAddress,
        params.amount
    );
 
    // 执行用户逻辑
    require(
        IFlashLoanSimpleReceiver(params.receiverAddress).executeOperation(
            params.asset,
            params.amount,
            totalPremium,
            msg.sender,
            params.params
        ),
        Errors.INVALID_FLASHLOAN_EXECUTOR_RETURN
    );
 
    // 验证归还
    uint256 amountPlusPremium = params.amount + totalPremium;
    IERC20(params.asset).safeTransferFrom(
        params.receiverAddress,
        reserveCache.aTokenAddress,
        amountPlusPremium
    );
 
    // 手续费分配
    _handleFlashLoanRepayment(
        reserve,
        reserveCache,
        params.flashLoanPremiumToProtocol,
        totalPremium
    );
 
    emit FlashLoan(
        params.receiverAddress,
        msg.sender,
        params.asset,
        params.amount,
        DataTypes.InterestRateMode.NONE,
        totalPremium,
        params.referralCode
    );
}

4.4 批量闪电贷

function executeFlashLoan(
    mapping(address => DataTypes.ReserveData) storage reservesData,
    mapping(uint256 => address) storage reservesList,
    mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
    mapping(address => DataTypes.UserConfigurationMap) storage usersConfig,
    DataTypes.FlashLoanParams memory params
) external {
    FlashLoanLocalVars memory vars;
 
    ValidationLogic.validateFlashloan(reservesData, params.assets, params.amounts);
 
    vars.receiver = IFlashLoanReceiver(params.receiverAddress);
 
    // 转移所有资产给借款人
    for (vars.i = 0; vars.i < params.assets.length; vars.i++) {
        vars.currentAmount = params.amounts[vars.i];
        vars.currentAsset = params.assets[vars.i];
        vars.currentATokenAddress = reservesData[vars.currentAsset].aTokenAddress;
 
        vars.currentPremium = params.interestRateModes[vars.i] ==
            DataTypes.InterestRateMode.NONE
            ? vars.currentAmount.percentMul(vars.totalPremium)
            : 0;
 
        vars.flashloanPremiums[vars.i] = vars.currentPremium;
 
        IAToken(vars.currentATokenAddress).transferUnderlyingTo(
            params.receiverAddress,
            vars.currentAmount
        );
    }
 
    // 执行用户逻辑
    require(
        vars.receiver.executeOperation(
            params.assets,
            params.amounts,
            vars.flashloanPremiums,
            msg.sender,
            params.params
        ),
        Errors.INVALID_FLASHLOAN_EXECUTOR_RETURN
    );
 
    // 处理归还或开仓
    for (vars.i = 0; vars.i < params.assets.length; vars.i++) {
        vars.currentAsset = params.assets[vars.i];
        vars.currentAmount = params.amounts[vars.i];
 
        if (params.interestRateModes[vars.i] == DataTypes.InterestRateMode.NONE) {
            // 完全归还
            _handleFlashLoanRepayment(...)
        } else {
            // 开仓:保留借贷,创建债务头寸
            BorrowLogic.executeBorrow(...)
        }
    }
}

4.5 闪电贷接收者接口

interface IFlashLoanSimpleReceiver {
    function executeOperation(
        address asset,
        uint256 amount,
        uint256 premium,
        address initiator,
        bytes calldata params
    ) external returns (bool);
}
 
interface IFlashLoanReceiver {
    function executeOperation(
        address[] calldata assets,
        uint256[] calldata amounts,
        uint256[] calldata premiums,
        address initiator,
        bytes calldata params
    ) external returns (bool);
}

4.6 闪电贷费用

总费用 = 借款金额 × flashLoanPremiumTotal

费用分配:
├── LP 收益 = 总费用 × (1 - protocolFee%)
└── 协议收入 = 总费用 × protocolFee%

典型参数:
- flashLoanPremiumTotal: 0.09% (9 bps)
- protocolFee: 30%

4.7 闪电贷用例

// 清算套利示例
contract FlashLiquidator is IFlashLoanSimpleReceiver {
    IPool public pool;
    ISwapRouter public router;
 
    function liquidateWithFlashLoan(
        address collateralAsset,
        address debtAsset,
        address user,
        uint256 debtToCover
    ) external {
        // 发起闪电贷借出债务资产
        pool.flashLoanSimple(
            address(this),
            debtAsset,
            debtToCover,
            abi.encode(collateralAsset, user),
            0
        );
    }
 
    function executeOperation(
        address asset,
        uint256 amount,
        uint256 premium,
        address initiator,
        bytes calldata params
    ) external override returns (bool) {
        (address collateralAsset, address user) = abi.decode(params, (address, address));
 
        // 批准池子使用资产
        IERC20(asset).approve(address(pool), amount);
 
        // 执行清算,获得抵押品
        pool.liquidationCall(collateralAsset, asset, user, amount, false);
 
        // 出售抵押品获得债务资产
        uint256 collateralReceived = IERC20(collateralAsset).balanceOf(address(this));
        IERC20(collateralAsset).approve(address(router), collateralReceived);
 
        router.exactInputSingle(ISwapRouter.ExactInputSingleParams({
            tokenIn: collateralAsset,
            tokenOut: asset,
            fee: 3000,
            recipient: address(this),
            deadline: block.timestamp,
            amountIn: collateralReceived,
            amountOutMinimum: amount + premium,
            sqrtPriceLimitX96: 0
        }));
 
        // 批准归还
        IERC20(asset).approve(address(pool), amount + premium);
 
        return true;
    }
}

5. 预言机哨兵

5.1 概述

预言机哨兵用于 L2 网络,在序列器故障时保护协议:

正常状态                         序列器故障
┌──────────────┐               ┌──────────────┐
│ 借贷: ✅ 允许  │               │ 借贷: ❌ 暂停  │
│ 清算: ✅ 允许  │  ───故障───▶  │ 清算: ❌ 暂停  │
└──────────────┘               └──────────────┘

5.2 实现

// PriceOracleSentinel.sol
contract PriceOracleSentinel is IPriceOracleSentinel {
    ISequencerOracle public sequencerOracle;
    uint256 public gracePeriod;
 
    function isBorrowAllowed() external view override returns (bool) {
        return _isUpAndGracePeriodPassed();
    }
 
    function isLiquidationAllowed() external view override returns (bool) {
        return _isUpAndGracePeriodPassed();
    }
 
    function _isUpAndGracePeriodPassed() internal view returns (bool) {
        (bool isSequencerUp, uint256 lastUpdateTimestamp) = sequencerOracle.latestRoundData();
 
        // 序列器必须在线
        if (!isSequencerUp) {
            return false;
        }
 
        // 必须过了优雅期
        if (block.timestamp - lastUpdateTimestamp < gracePeriod) {
            return false;
        }
 
        return true;
    }
}

5.3 使用场景

Arbitrum/Optimism 等 L2 网络:

1. 序列器正常运行 → 所有操作正常
2. 序列器故障 → 借贷和清算暂停
3. 序列器恢复 → 等待优雅期(如 1 小时)
4. 优雅期结束 → 恢复正常操作

优雅期的目的:
- 让用户有时间补充抵押品
- 避免在价格不确定时被清算

6. 功能对比总结

功能目的风险/限制
E-Mode提高相关资产资本效率只能使用同类资产
Isolation新资产安全上线债务上限、单一抵押
Portal跨链流动性桥接风险、费用
Flash Loan无抵押即时借贷必须同交易归还
SentinelL2 网络保护故障时功能受限

7. 小结

Aave V3 的高级功能显著提升了协议的灵活性和安全性:

  1. E-Mode:稳定币间借贷效率提升 6 倍以上
  2. Isolation Mode:新资产可安全测试,逐步建立信任
  3. Portal:打破流动性碎片化,实现多链统一
  4. Flash Loan:支持复杂的 DeFi 策略和套利
  5. Oracle Sentinel:L2 网络额外保护层

下一章将详细讲解套利机会和 MEV 策略。