Summary
A critical issue has been identified in the AaveDIVAWrapperCore
contract, where the owner is unable to claim yield due to the AaveDIVAWrapperCore:: _getAccruedYieldPrivate
function consistently returning zero. This results in a revert with the error INVALID_AMOUNT '26'
when attempting to withdraw from Aave, as the function does not recognize any accrued yield.
Vulnerability Details
The issue originates from the AaveDIVAWrapperCore:: _getAccruedYieldPrivate
function, which calculates the accrued yield as the difference between the Aave `aToken` balance and the `wToken` supply:
function _getAccruedYieldPrivate(address _collateralToken) private view returns (uint256) {
uint256 aTokenBalance = IERC20Metadata(IAave(_aaveV3Pool).getReserveData(_collateralToken).aTokenAddress)
.balanceOf(address(this));
uint256 wTokenSupply = IERC20Metadata(_collateralTokenToWToken[_collateralToken]).totalSupply();
@> return aTokenBalance > wTokenSupply ? aTokenBalance - wTokenSupply : 0;
}
-
The function assumes yield accrues when aTokenBalance > wTokenSupply
.
-
However, due to the logic in AaveDIVAWrapperCore:: _handleTokenOperations
, every deposit supplies an equal amount of collateral to Aave and mints an equal number of wTokens, ensuring aTokenBalance == wTokenSupply
at all times.
function _handleTokenOperations(address _collateralToken, uint256 _collateralAmount, address _wToken) private {
IERC20Metadata(_collateralToken).safeTransferFrom(msg.sender, address(this), _collateralAmount);
IAave(_aaveV3Pool).supply(
_collateralToken,
@> _collateralAmount,
address(this),
0
);
@> IWToken(_wToken).mint(address(this), _collateralAmount);
}
function _claimYield(address _collateralToken, address _recipient) internal returns (uint256) {
if (_collateralTokenToWToken[_collateralToken] == address(0)) {
revert CollateralTokenNotRegistered();
}
if (_recipient == address(0)) revert ZeroAddress();
uint256 _amountReturned = IAave(_aaveV3Pool).withdraw(
_collateralToken,
@> _getAccruedYieldPrivate(_collateralToken),
_recipient
);
emit YieldClaimed(owner(), _recipient, _collateralToken, _amountReturned);
return _amountReturned;
}
https://github.com/aave-dao/aave-v3-origin/blob/ae2d19f998b421b381b85a62d79ecffbb0701501/src/contracts/protocol/libraries/helpers/Errors.sol#L35
https://github.com/aave-dao/aave-v3-origin/blob/ae2d19f998b421b381b85a62d79ecffbb0701501/src/contracts/protocol/libraries/logic/ValidationLogic.sol#L101
Impact
-
Yield Claim Failure: The owner cannot claim any yield since the function always calculates it as zero.
-
Protocol Inefficiency: The inability to access yield reduces the effectiveness of staking collateral in Aave.
In test/AaveDIVAWrapperTest.t.sol
pragma solidity ^0.8.26;
import "forge-std/Test.sol";
import {IAaveDIVAWrapper} from "../src/interfaces/IAaveDIVAWrapper.sol";
import {IAave} from "../src/interfaces/IAave.sol";
import {IDIVA} from "../src/interfaces/IDIVA.sol";
import "../src/AaveDIVAWrapper.sol";
import "../src/interfaces/IDIVA.sol";
import "lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol";
interface IUSDC {
function approve(address spender, uint256 amount) external;
function transfer(address recipient, uint256 amount) external;
function mint(address account, uint256 amount) external;
function balanceOf(address) external view returns (uint256);
function transferFrom(address sender, address recipient, uint256 amount) external;
function decimals() external view returns (uint8);
}
contract AaveDIVAWrapperTest is Test {
AaveDIVAWrapperCore wrapper;
address trader = address(0x4D8336bDa6C11BD2a805C291Ec719BaeDD10AcB9);
address blacklistedUser = address(0x0E6b8E34dC115a2848F585851AF23D99D09b8463);
address shortRecipient;
address longRecipient;
bytes32 poolId;
IAave _aavePool = IAave(0x794a61358D6845594F94dc1DB02A252b5b4814aD);
IDIVA _diva = IDIVA(0x2C9c47E7d254e493f02acfB410864b9a86c28e1D);
IUSDC USDC = IUSDC(0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359);
function setUp() public {
wrapper = new AaveDIVAWrapper(address(_diva),address(_aavePool),address(this));
shortRecipient = address(0x1);
longRecipient = address(0x2);
vm.startPrank(trader);
IUSDC(USDC).approve(address(this), type(uint256).max);
vm.stopPrank();
IUSDC(USDC).transferFrom(trader, address(this), 100* 10**USDC.decimals());
wrapper.registerCollateralToken(address(USDC));
}
modifier createPool {
USDC.approve(address(wrapper), type(uint256).max);
poolId = wrapper.createContingentPool(IAaveDIVAWrapper.PoolParams({
referenceAsset: "BTC/USD",
expiryTime: uint96(block.timestamp + 86400),
floor: 100,
inflection: 150,
cap: 200,
gradient: uint256(5 * 10 ** (USDC.decimals()-1)),
collateralAmount: 10 * 10**USDC.decimals(),
collateralToken: address(USDC),
dataProvider: address(0x11),
capacity: type(uint256).max,
longRecipient: longRecipient,
shortRecipient: shortRecipient,
permissionedERC721Token: address(0)
}));
_;
}
modifier addliquidity {
vm.startPrank(trader);
USDC.approve(address(wrapper), type(uint256).max);
wrapper.addLiquidity(poolId,100 *10**USDC.decimals() , longRecipient, shortRecipient);
vm.stopPrank();
_;
}
function testClaimYield() external createPool addliquidity{
wrapper.claimYield(address(USDC),address(this));
}
}
Run Thi Command :
forge test --mt testClaimYield --fork-url https:
Output :
├─ [46444] AaveDIVAWrapper::claimYield(0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359, 0x0E6b8E34dC115a2848F585851AF23D99D09b8463)
│ ├─ [12291] 0x794a61358D6845594F94dc1DB02A252b5b4814aD::getReserveData(0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359) [staticcall]
│ │ ├─ [11693] 0x5DFb8c777C19d3cEdcDc7398d2EeF1FB0b9b05c9::getReserveData(0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359) [delegatecall]
│ │ │ ├─ [2488] 0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb::getAddress(0x4d4f434b5f535441424c455f4445425400000000000000000000000000000000) [staticcall]
│ │ │ │ └─ ← [Return] 0x000000000000000000000000d94112b5b62d53c9402e7a60289c6810def1dc9b
│ │ │ └─ ← [Return] ReserveDataLegacy({ configuration: ReserveConfigurationMap({ data: 7237005577332262213973186942896412476039334390506525738199614521317852192076 [7.237e75] }), liquidityIndex: 1093697577431803941150626743 [1.093e27], currentLiquidityRate: 71624501664828486057718072 [7.162e25], variableBorrowIndex: 1115720259726756204254285379 [1.115e27], currentVariableBorrowRate: 103009889251849476157308009 [1.03e26], currentStableBorrowRate: 0, lastUpdateTimestamp: 1738314186 [1.738e9], id: 20, aTokenAddress: 0xA4D94019934D8333Ef880ABFFbF2FDd611C762BD, stableDebtTokenAddress: 0xd94112B5B62d53C9402e7A60289c6810dEF1dC9B, variableDebtTokenAddress: 0xE701126012EC0290822eEA17B794454d1AF8b030, interestRateStrategyAddress: 0x56076f960980d453b5B749CB6A1c4D2E4e138B1A, accruedToTreasury: 135848380 [1.358e8], unbacked: 0, isolationModeTotalDebt: 0 })
│ │ └─ ← [Return] ReserveDataLegacy({ configuration: ReserveConfigurationMap({ data: 7237005577332262213973186942896412476039334390506525738199614521317852192076 [7.237e75] }), liquidityIndex: 1093697577431803941150626743 [1.093e27], currentLiquidityRate: 71624501664828486057718072 [7.162e25], variableBorrowIndex: 1115720259726756204254285379 [1.115e27], currentVariableBorrowRate: 103009889251849476157308009 [1.03e26], currentStableBorrowRate: 0, lastUpdateTimestamp: 1738314186 [1.738e9], id: 20, aTokenAddress: 0xA4D94019934D8333Ef880ABFFbF2FDd611C762BD, stableDebtTokenAddress: 0xd94112B5B62d53C9402e7A60289c6810dEF1dC9B, variableDebtTokenAddress: 0xE701126012EC0290822eEA17B794454d1AF8b030, interestRateStrategyAddress: 0x56076f960980d453b5B749CB6A1c4D2E4e138B1A, accruedToTreasury: 135848380 [1.358e8], unbacked: 0, isolationModeTotalDebt: 0 })
│ ├─ [5306] 0xA4D94019934D8333Ef880ABFFbF2FDd611C762BD::balanceOf(AaveDIVAWrapper: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f]) [staticcall]
│ │ ├─ [4698] 0xCf85FF1c37c594a10195F7A9Ab85CBb0a03f69dE::balanceOf(AaveDIVAWrapper: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f]) [delegatecall]
│ │ │ ├─ [1419] 0x794a61358D6845594F94dc1DB02A252b5b4814aD::getReserveNormalizedIncome(0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359) [staticcall]
│ │ │ │ ├─ [867] 0x5DFb8c777C19d3cEdcDc7398d2EeF1FB0b9b05c9::getReserveNormalizedIncome(0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359) [delegatecall]
│ │ │ │ │ └─ ← [Return] 0x00000000000000000000000000000000000000000388af787984f09d5be993b7
│ │ │ │ └─ ← [Return] 0x00000000000000000000000000000000000000000388af787984f09d5be993b7
│ │ │ └─ ← [Return] 110000000 [1.1e8]
│ │ └─ ← [Return] 110000000 [1.1e8]
│ ├─ [477] WToken::totalSupply() [staticcall]
│ │ └─ ← [Return] 110000000 [1.1e8]
│ ├─ [15702] 0x794a61358D6845594F94dc1DB02A252b5b4814aD::withdraw(0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359, 0 , 0x0E6b8E34dC115a2848F585851AF23D99D09b8463)
│ │ ├─ [15131] 0x5DFb8c777C19d3cEdcDc7398d2EeF1FB0b9b05c9::withdraw(0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359, 0, 0x0E6b8E34dC115a2848F585851AF23D99D09b8463) [delegatecall]
│ │ │ ├─ [2402] 0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb::getPriceOracle() [staticcall]
│ │ │ │ └─ ← [Return] 0x000000000000000000000000b023e699f5a33916ea823a16485e259257ca8bd1
│ │ │ ├─ [6619] 0x2b22E425C1322fbA0DbF17bb1dA25d71811EE7ba::186dea44(00000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000003702d183c14bea6b53d1a60b293e9416ed749f2af0faddac5a251f02731f788fbe0000000000000000000000003c499c542cef5e3811e1192ce70d8cc03d5c335900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e6b8e34dc115a2848f585851af23d99d09b84630000000000000000000000000000000000000000000000000000000000000015000000000000000000000000b023e699f5a33916ea823a16485e259257ca8bd10000000000000000000000000000000000000000000000000000000000000000) [delegatecall]
│ │ │ │ ├─ [1024] 0xE701126012EC0290822eEA17B794454d1AF8b030::scaledTotalSupply() [staticcall]
│ │ │ │ │ ├─ [419] 0x79b5e91037AE441dE0d9e6fd3Fd85b96B83d4E93::scaledTotalSupply() [delegatecall]
│ │ │ │ │ │ └─ ← [Return] 0x000000000000000000000000000000000000000000000000000022352f711ff4
│ │ │ │ │ └─ ← [Return] 0x000000000000000000000000000000000000000000000000000022352f711ff4
│ │ │ │ ├─ [1299] 0xA4D94019934D8333Ef880ABFFbF2FDd611C762BD::scaledBalanceOf(AaveDIVAWrapper: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f]) [staticcall]
│ │ │ │ │ ├─ [691] 0xCf85FF1c37c594a10195F7A9Ab85CBb0a03f69dE::scaledBalanceOf(AaveDIVAWrapper: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f]) [delegatecall]
│ │ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000005feabf9
│ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000005feabf9
│ │ │ │ └─ ← [Revert] revert: 26
Tools Used
Recommendations