Summary
The DebtToken
contract claims to align with Aave's VariableDebtToken
implementation but deviates from it by omitting a key zero-check optimization in the balanceOf
function. While this doesn't affect mathematical correctness, it represents both a gas inefficiency and a deviation from the documented specification.
Vulnerability Details
[](https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/tokens/DebtToken.sol#L17)
The DebtToken
contract explicitly states in its documentation:
* @dev This contract aligns with Aave's VariableDebtToken implementation, scaling balances by the usage index.
[](https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/tokens/DebtToken.sol#L223-L225)
However, in the DebtToken::balanceOf
implementation:
function balanceOf(address account) public view override(ERC20, IERC20) returns (uint256) {
uint256 scaledBalance = super.balanceOf(account);
return scaledBalance.rayMul(ILendingPool(_reservePool).getNormalizedDebt());
}
[](https://github.com/aave/protocol-v2/blob/ce53c4a8c8620125063168620eba0a8a92854eb8/contracts/protocol/tokenization/VariableDebtToken.sol#L75-L82)
This deviates from Aave's implementation which includes a zero check:
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));
}
Impact
The contract fails to fully align with Aave's implementation despite explicit documentation stating otherwise
While this doesn't affect the mathematical correctness of the calculations (as 0 * any number = 0), it represents a deviation from what we are implementing.
Tools Used
Manual Review
Recommendations
Modify the balanceOf
function to include the zero check, aligning with Aave's implementation:
function balanceOf(address account) public view override(ERC20, IERC20) returns (uint256) {
uint256 scaledBalance = super.balanceOf(account);
+ if (scaledBalance == 0) {
+ return 0;
+ }
return scaledBalance.rayMul(ILendingPool(_reservePool).getNormalizedDebt());
}