Summary
The function _registerCollateralToken handles collateral tokens registration, It's using "ERC20.approve" to approves the Aave V3 contract to transfer the collateralToken registered in _handleTokenOperations.
However, some ERC20s on some chains don't return a value.
The most popular example is USDT and USDC on the main net, and as the docs mention it should be compatible on any EVM chain and will support USDT:
the current implementation of the approve function does not account for tokens like USDT, contradicting the protocol's intentions.
Therefore _registerCollateralToken will never work on the EVM Chain or other related chain and tokens.
Vulnerability Details
In the _registerCollateralToken:115 , approve function is used instead of safeApprove. USDT on the main net doesn't return a value, https://etherscan.io/token/0xdac17f958d2ee523a2206206994597c13d831ec7#code. This includes USDC which should work in the protocol.
This behavior causes the approve function to revert when interacting with these tokens
function _registerCollateralToken(address _collateralToken) internal returns (address) {
if (_collateralTokenToWToken[_collateralToken] != address(0)) {
revert CollateralTokenAlreadyRegistered();
}
address _aToken = _getAToken(_collateralToken);
if (_aToken == address(0)) {
revert UnsupportedCollateralToken();
}
IERC20Metadata _collateralTokenContract = IERC20Metadata(_collateralToken);
WToken _wTokenContract = new WToken(
string(abi.encodePacked("w", _collateralTokenContract.symbol())),
_collateralTokenContract.decimals(),
address(this)
);
address _wToken = address(_wTokenContract);
_collateralTokenToWToken[_collateralToken] = _wToken;
_wTokenToCollateralToken[_wToken] = _collateralToken;
_wTokenContract.approve(_diva, type(uint256).max);
_collateralTokenContract.approve(_aaveV3Pool, type(uint256).max);
emit CollateralTokenRegistered(_collateralToken, _wToken);
return _wToken;
}
The specific part of the function is highlighted here;
_collateralTokenContract.approve(_aaveV3Pool, type(uint256).max);
emit CollateralTokenRegistered(_collateralToken, _wToken);
Internal pre-conditions
The protocol uses approve calls in _registerCollateralToken .
There is no consideration or error handling for approve calls when interacting with USDT, USDC.
External pre-conditions
The protocol operates on EVM-compatible chains where these tokens are prevalent.
Impact
Attack Path
-
Owner wants to use USDT or USDC as the colateralToken.
-
_registerCollateralToken that include approve call revert, causing the protocol to fail in providing the intended functionality.
-
The protocol becomes unusable with ERC20 tokens like USDT AND USDC, a widely used stablecoin.
-
Breaks the intended functionality of the protocol.
-
This breaks a criticial function in the protocol and causes the `_registerCollateralToken()' to fail.
POC
pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import "../src/AaveDIVAWrapper.sol";
import "../src/mocks/MockERC20.sol";
import "../src/interfaces/IAave.sol";
import "../src/interfaces/IDIVA.sol";
contract AaveDiverWrapperTest is Test {
AaveDIVAWrapper aaveDIVAWrapper;
IAave aaveEth = IAave(address(0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2));
IDIVA DIVAEth = IDIVA(address(0x2C9c47E7d254e493f02acfB410864b9a86c28e1D));
MockERC20 collateralTokenUSDTEth =
MockERC20(address(0xdAC17F958D2ee523a2206206994597C13D831ec7));
address owner = makeAddr("owner");
function setUp() public {
aaveDIVAWrapper = new AaveDIVAWrapper(
address(DIVAEth),
address(aaveEth),
owner
);
}
function testFailApproveFailWIthONCHAINUSDT() public {
vm.prank(owner);
aaveDIVAWrapper.registerCollateralToken(address(collateralTokenUSDTEth));
}
}
run the above code in an empty test file with forge t --fork-url https://ethereum.rpc.subquery.network/public -vvvvv
│ ├─ [26953] 0xdAC17F958D2ee523a2206206994597C13D831ec7::approve(0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2, 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77])
│ │ ├─ emit Approval(owner: AaveDIVAWrapper: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], spender: 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2, value: 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77])
│ │ ├─ storage changes:
│ │ │ @ 0x14eeb4b4c3069a8bb270c7ef3e4915e3192424d4f11f50bed308a86d7a3635cc: 0 → 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
│ │ └─ ← [Stop]
│ ├─ storage changes:
│ │ @ 4: 1 → 2
│ │ @ 0xf4f699a1ef35e64890828cc562d137cceeab28af8455567ef14b6ec385c8e441: 0 → 0x000000000000000000000000104fbc016f4bb334d775a19e8a6510109ac63e00
│ │ @ 0xbc93b95fb13b8fd819fbf60078dbe0264f9841ed2d94ee86afa3a1c5655d236b: 0 → 0x000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7
│ └─ ← [Revert] EvmError: Revert
└─ ← [Revert] EvmError: Revert
it can be seen above, after the approval for DIVA is done successfully, the Approval for AAve fail and cause the whole function to revert,
Tools Used
Manual Review
Recommendations
Use safeApprove instead of approve
- _collateralTokenContract.approve(_aaveV3Pool, type(uint256).max);
+ _collateralTokenContract.safeApprove(_aaveV3Pool, type(uint256).max);
emit CollateralTokenRegistered(_collateralToken, _wToken);