Aave V3 代币化机制

1. 概述

Aave V3 使用三种代币来表示用户的存款和债务:

代币类型合约用途特点
aTokenAToken.sol存款凭证余额随利息自动增长
StableDebtTokenStableDebtToken.sol固定利率债务利率在借款时锁定
VariableDebtTokenVariableDebtToken.sol浮动利率债务利率随市场波动

2. aToken 详解

2.1 缩放余额机制

aToken 最核心的创新是**缩放余额(Scaled Balance)**机制:

实际余额 (Real Balance) = 缩放余额 (Scaled Balance) × 流动性指数 (Liquidity Index)

为什么使用缩放余额?

传统方式:每次利息累积都更新所有用户余额
问题:Gas 消耗巨大,不可扩展

Aave 方式:存储缩放余额,读取时乘以全局指数
优势:只需更新一个全局变量,所有用户余额自动增长

2.2 余额计算

// ScaledBalanceTokenBase.sol
abstract contract ScaledBalanceTokenBase is MintableIncentivizedERC20, IScaledBalanceToken {
 
    // 获取实际余额
    function balanceOf(address user) public view virtual override returns (uint256) {
        // 实际余额 = 缩放余额 × 当前流动性指数
        return super.balanceOf(user).rayMul(POOL.getReserveNormalizedIncome(_underlyingAsset));
    }
 
    // 获取缩放余额(存储的原始值)
    function scaledBalanceOf(address user) external view override returns (uint256) {
        return super.balanceOf(user);
    }
 
    // 获取缩放总供应量
    function scaledTotalSupply() public view virtual override returns (uint256) {
        return super.totalSupply();
    }
 
    // 获取实际总供应量
    function totalSupply() public view virtual override returns (uint256) {
        uint256 currentSupplyScaled = super.totalSupply();
        if (currentSupplyScaled == 0) {
            return 0;
        }
        return currentSupplyScaled.rayMul(POOL.getReserveNormalizedIncome(_underlyingAsset));
    }
}

2.3 铸造流程

// AToken.sol
function mint(
    address caller,
    address onBehalfOf,
    uint256 amount,
    uint256 index
) external virtual override onlyPool returns (bool) {
    // 计算缩放金额
    uint256 amountScaled = amount.rayDiv(index);
    require(amountScaled != 0, Errors.INVALID_MINT_AMOUNT);
 
    // 铸造缩放后的代币
    _mint(onBehalfOf, amountScaled.toUint128());
 
    // 发射事件(显示实际金额)
    emit Transfer(address(0), onBehalfOf, amount);
    emit Mint(caller, onBehalfOf, amount, amountScaled, index);
 
    // 返回是否为首次存款
    return scaledBalanceOf(onBehalfOf) == amountScaled;
}

铸造示例

用户存入: 100 USDC
当前流动性指数: 1.05 (1.05e27)

缩放金额 = 100 / 1.05 = 95.24 aUSDC (存储)
实际余额 = 95.24 × 1.05 = 100 aUSDC (显示)

一年后,流动性指数变为 1.10:
实际余额 = 95.24 × 1.10 = 104.76 aUSDC
收益 = 4.76 USDC (自动累积)

2.4 销毁流程

function burn(
    address from,
    address receiverOfUnderlying,
    uint256 amount,
    uint256 index
) external virtual override onlyPool {
    // 计算缩放金额
    uint256 amountScaled = amount.rayDiv(index);
    require(amountScaled != 0, Errors.INVALID_BURN_AMOUNT);
 
    // 销毁 aToken
    _burn(from, amountScaled.toUint128());
 
    // 转移底层资产给接收者
    if (receiverOfUnderlying != address(this)) {
        IERC20(_underlyingAsset).safeTransfer(receiverOfUnderlying, amount);
    }
 
    emit Transfer(from, address(0), amount);
    emit Burn(from, receiverOfUnderlying, amount, amountScaled, index);
}

2.5 转账处理

aToken 支持标准 ERC-20 转账:

function _transfer(
    address from,
    address to,
    uint256 amount,
    bool validate
) internal {
    // 计算缩放金额
    uint256 index = POOL.getReserveNormalizedIncome(_underlyingAsset);
    uint256 fromBalanceBefore = super.balanceOf(from).rayMul(index);
    uint256 toBalanceBefore = super.balanceOf(to).rayMul(index);
 
    // 执行转账
    super._transfer(from, to, amount.rayDiv(index).toUint128());
 
    // 更新抵押品状态
    if (validate) {
        POOL.finalizeTransfer(_underlyingAsset, from, to, amount, fromBalanceBefore, toBalanceBefore);
    }
 
    emit BalanceTransfer(from, to, amount.rayDiv(index), index);
}

2.6 EIP-712 Permit

aToken 支持无 Gas 授权(签名授权):

bytes32 public constant PERMIT_TYPEHASH =
    keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)');
 
function permit(
    address owner,
    address spender,
    uint256 value,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
) external override {
    require(owner != address(0), Errors.ZERO_ADDRESS_NOT_VALID);
    require(block.timestamp <= deadline, Errors.INVALID_EXPIRATION);
 
    uint256 currentValidNonce = _nonces[owner];
    bytes32 digest = keccak256(
        abi.encodePacked(
            '\x19\x01',
            DOMAIN_SEPARATOR(),
            keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline))
        )
    );
 
    require(owner == ecrecover(digest, v, r, s), Errors.INVALID_SIGNATURE);
 
    _nonces[owner] = currentValidNonce + 1;
    _approve(owner, spender, value);
}

3. StableDebtToken (固定利率债务)

3.1 特点

  • 利率锁定:借款时利率固定,不随市场波动
  • 不可转移:禁用 transfer 和 transferFrom
  • 独立计息:每个用户有独立的利率和时间戳

3.2 数据结构

contract StableDebtToken is DebtTokenBase, IncentivizedERC20, IStableDebtToken {
    // 用户状态:additionalData 存储用户的固定利率
    // 继承自 IncentivizedERC20 的 _userState mapping
 
    // 用户借款时间戳
    mapping(address => uint40) internal _timestamps;
 
    // 全局平均固定利率
    uint128 internal _avgStableRate;
 
    // 固定债务总量
    uint128 internal _totalSupply;
}

3.3 余额计算

固定利率债务使用复利计算:

function balanceOf(address account) public view virtual override returns (uint256) {
    uint256 accountBalance = super.balanceOf(account);
    if (accountBalance == 0) {
        return 0;
    }
 
    // 获取用户的固定利率
    uint256 stableRate = _userState[account].additionalData;
 
    // 计算复利
    uint256 cumulatedInterest = MathUtils.calculateCompoundedInterest(
        stableRate,
        _timestamps[account]
    );
 
    // 当前债务 = 本金 × 复利因子
    return accountBalance.rayMul(cumulatedInterest);
}

3.4 铸造(借款)

function mint(
    address user,
    address onBehalfOf,
    uint256 amount,
    uint256 rate
) external override onlyPool returns (bool, uint256, uint256) {
    MintLocalVars memory vars;
 
    // 获取用户当前债务
    vars.previousBalance = super.balanceOf(onBehalfOf);
    vars.currentBalance = _calcUserStableBalance(vars.previousBalance, onBehalfOf);
 
    // 计算新的平均利率
    vars.nextStableRate = _calculateNewStableRate(
        vars.previousBalance,
        _userState[onBehalfOf].additionalData,
        amount,
        rate
    );
 
    // 更新用户利率
    _userState[onBehalfOf].additionalData = vars.nextStableRate.toUint128();
 
    // 更新时间戳
    _timestamps[onBehalfOf] = uint40(block.timestamp);
 
    // 更新总供应量和全局平均利率
    vars.previousSupply = _totalSupply;
    vars.currentAvgStableRate = _avgStableRate;
    vars.nextSupply = _totalSupply = (vars.previousSupply + amount).toUint128();
 
    vars.nextAvgStableRate = _calculateNextAvgStableRate(
        vars.previousSupply,
        vars.currentAvgStableRate,
        vars.currentBalance + amount,
        vars.nextStableRate
    );
    _avgStableRate = vars.nextAvgStableRate.toUint128();
 
    // 铸造债务代币
    _mint(onBehalfOf, (vars.currentBalance + amount).toUint128());
 
    emit Transfer(address(0), onBehalfOf, vars.currentBalance + amount);
    emit Mint(
        user,
        onBehalfOf,
        vars.currentBalance + amount,
        vars.currentBalance,
        vars.nextStableRate,
        vars.nextAvgStableRate,
        vars.nextSupply
    );
 
    return (vars.previousBalance == 0, vars.nextSupply, vars.nextAvgStableRate);
}

3.5 平均利率计算

function _calculateNextAvgStableRate(
    uint256 previousTotalSupply,
    uint256 previousAvgStableRate,
    uint256 newAmount,
    uint256 newRate
) internal pure returns (uint256) {
    // 加权平均: (旧总量 × 旧利率 + 新金额 × 新利率) / 新总量
    return (previousTotalSupply.rayMul(previousAvgStableRate) + newAmount.rayMul(newRate))
        .rayDiv(previousTotalSupply + newAmount);
}

4. VariableDebtToken (浮动利率债务)

4.1 特点

  • 利率浮动:利率随市场供需变化
  • 缩放机制:与 aToken 类似使用缩放余额
  • 不可转移:禁用 transfer 和 transferFrom

4.2 余额计算

function balanceOf(address user) public view virtual override returns (uint256) {
    uint256 scaledBalance = super.balanceOf(user);
    if (scaledBalance == 0) {
        return 0;
    }
    // 实际债务 = 缩放余额 × 当前可变借贷指数
    return scaledBalance.rayMul(POOL.getReserveNormalizedVariableDebt(_underlyingAsset));
}

4.3 铸造(借款)

function mint(
    address user,
    address onBehalfOf,
    uint256 amount,
    uint256 index
) external override onlyPool returns (bool, uint256) {
    // 检查金额
    if (user != onBehalfOf) {
        _decreaseBorrowAllowance(onBehalfOf, user, amount);
    }
 
    // 计算缩放金额
    uint256 amountScaled = amount.rayDiv(index);
    require(amountScaled != 0, Errors.INVALID_MINT_AMOUNT);
 
    // 铸造
    uint256 scaledBalance = super.balanceOf(onBehalfOf);
    _mint(onBehalfOf, amountScaled.toUint128());
 
    uint256 newScaledBalance = scaledBalance + amountScaled;
 
    emit Transfer(address(0), onBehalfOf, amount);
    emit Mint(user, onBehalfOf, amount, amountScaled, index);
 
    return (scaledBalance == 0, newScaledBalance);
}

4.4 销毁(还款)

function burn(
    address from,
    uint256 amount,
    uint256 index
) external override onlyPool returns (uint256) {
    uint256 amountScaled = amount.rayDiv(index);
    require(amountScaled != 0, Errors.INVALID_BURN_AMOUNT);
 
    _burn(from, amountScaled.toUint128());
 
    uint256 newScaledBalance = super.balanceOf(from);
 
    emit Transfer(from, address(0), amount);
    emit Burn(from, amount, amountScaled, index);
 
    return newScaledBalance;
}

5. 信用委托机制

5.1 概述

信用委托允许用户将借贷额度授权给第三方,而无需转移抵押品:

Alice (存款人) --授权--> Bob (借款人)
    │                      │
    └──── 信用额度 ─────────┘

5.2 实现

// DebtTokenBase.sol
abstract contract DebtTokenBase {
    // 借贷授权额度
    mapping(address => mapping(address => uint256)) internal _borrowAllowances;
 
    // 授权借贷额度
    function approveDelegation(address delegatee, uint256 amount) external override {
        _borrowAllowances[msg.sender][delegatee] = amount;
        emit BorrowAllowanceDelegated(msg.sender, delegatee, _getUnderlyingAssetAddress(), amount);
    }
 
    // 查询借贷额度
    function borrowAllowance(address fromUser, address toUser)
        external view override returns (uint256)
    {
        return _borrowAllowances[fromUser][toUser];
    }
 
    // 消耗借贷额度
    function _decreaseBorrowAllowance(
        address delegator,
        address delegatee,
        uint256 amount
    ) internal {
        uint256 newAllowance = _borrowAllowances[delegator][delegatee] - amount;
        _borrowAllowances[delegator][delegatee] = newAllowance;
        emit BorrowAllowanceDelegated(delegator, delegatee, _getUnderlyingAssetAddress(), newAllowance);
    }
}

5.3 使用场景

场景 1:机构借贷

主账户 A 有大量抵押品
├── 授权子账户 B 借贷 100,000 USDC
├── 授权子账户 C 借贷 50,000 USDC
└── 债务由主账户 A 承担

场景 2:DeFi 协议集成

用户授权 Yield Protocol 使用其借贷额度
├── Protocol 代用户借贷
├── 执行策略操作
└── 利润返还用户

6. 禁用转账

债务代币禁用标准 ERC-20 转账:

// DebtTokenBase.sol
function transfer(address, uint256) external virtual override returns (bool) {
    revert(Errors.OPERATION_NOT_SUPPORTED);
}
 
function allowance(address, address) external view virtual override returns (uint256) {
    revert(Errors.OPERATION_NOT_SUPPORTED);
}
 
function approve(address, uint256) external virtual override returns (bool) {
    revert(Errors.OPERATION_NOT_SUPPORTED);
}
 
function transferFrom(address, address, uint256) external virtual override returns (bool) {
    revert(Errors.OPERATION_NOT_SUPPORTED);
}
 
function increaseAllowance(address, uint256) external virtual override returns (bool) {
    revert(Errors.OPERATION_NOT_SUPPORTED);
}
 
function decreaseAllowance(address, uint256) external virtual override returns (bool) {
    revert(Errors.OPERATION_NOT_SUPPORTED);
}

为什么禁用?

  • 债务是个人责任,不能转移
  • 防止逃避清算
  • 维护协议安全性

7. 代币对比

7.1 三种代币对比

特性aTokenStableDebtTokenVariableDebtToken
用途存款凭证固定利率债务浮动利率债务
可转移✅ 是❌ 否❌ 否
余额计算缩放余额 × 指数本金 × 复利缩放余额 × 指数
利率类型存款利率固定借贷利率浮动借贷利率
利息累积线性复利复合复利复合复利
Permit✅ 支持❌ 不支持❌ 不支持

7.2 余额计算对比

aToken:
  realBalance = scaledBalance × liquidityIndex

StableDebtToken:
  realDebt = principal × compoundedInterest(userRate, timeDelta)

VariableDebtToken:
  realDebt = scaledBalance × variableBorrowIndex

7.3 Gas 消耗对比

操作aTokenStableDebtTokenVariableDebtToken
mint~50,000~60,000~50,000
burn~40,000~50,000~40,000
transfer~45,000N/AN/A
balanceOf~3,000~5,000~3,000

8. 激励机制

8.1 IncentivizedERC20

所有代币都继承自 IncentivizedERC20,支持外部激励:

abstract contract IncentivizedERC20 is Context, IERC20Detailed {
    // 激励控制器
    IRewardsController internal _rewardsController;
 
    // 用户状态
    mapping(address => UserState) internal _userState;
 
    struct UserState {
        uint128 balance;          // 缩放余额
        uint128 additionalData;   // 附加数据(StableDebtToken 用于存储利率)
    }
 
    // 在余额变化时通知激励控制器
    function _mint(address account, uint128 amount) internal virtual {
        uint256 oldTotalSupply = _totalSupply;
        _totalSupply = oldTotalSupply + amount;
 
        uint128 oldAccountBalance = _userState[account].balance;
        _userState[account].balance = oldAccountBalance + amount;
 
        IRewardsController rewardsControllerLocal = _rewardsController;
        if (address(rewardsControllerLocal) != address(0)) {
            rewardsControllerLocal.handleAction(account, oldTotalSupply, oldAccountBalance);
        }
    }
 
    function _burn(address account, uint128 amount) internal virtual {
        uint256 oldTotalSupply = _totalSupply;
        _totalSupply = oldTotalSupply - amount;
 
        uint128 oldAccountBalance = _userState[account].balance;
        _userState[account].balance = oldAccountBalance - amount;
 
        IRewardsController rewardsControllerLocal = _rewardsController;
        if (address(rewardsControllerLocal) != address(0)) {
            rewardsControllerLocal.handleAction(account, oldTotalSupply, oldAccountBalance);
        }
    }
}

8.2 激励类型

  • 流动性挖矿:持有 aToken 获得额外代币奖励
  • 借贷激励:借贷特定资产获得奖励
  • 安全模块:质押 stkAAVE 获得安全激励

9. 小结

机制实现方式优势
缩放余额存储除以指数的值自动复利,低 Gas
信用委托borrowAllowance mapping灵活的借贷授权
禁用转账重写 transfer 函数债务不可逃避
激励集成IncentivizedERC20支持外部激励

关键公式

aToken 余额 = scaledBalance × liquidityIndex
稳定债务 = principal × (1 + rate)^time
可变债务 = scaledBalance × variableBorrowIndex

下一章将讲解 E-Mode、隔离模式、Portal 和闪电贷等高级功能。