HardhatDeFi
15,000 USDC
View results
Submission Details
Severity: medium
Invalid

AaveDIVAWrapper::registerCollateralToken will always revert for collaterals whose symbol are not string i.e MKR

Summary

The _registerCollateralToken function is responsible for registering a collateral token and creating a wrapped version of it (wToken) to be used in the DIVA Protocol. However, it assumes that all ERC-20 tokens return string symbols, which is incorrect. Some tokens, such as MKR (MakerDAO’s token), WBTC, and others, return bytes32 instead of string, causing the function to revert unexpectedly when symbol() is called.

Vulnerability Details

function _registerCollateralToken(address _collateralToken) internal returns (address) {
// Verify that the collateral token is not yet registered.
if (_collateralTokenToWToken[_collateralToken] != address(0)) {
revert CollateralTokenAlreadyRegistered();
}
// Retrieve the aToken address associated with the provided collateral token from Aave V3. Reverts if
// the collateral token is not supported by Aave V3.
// Note: aTokens have the same number of decimals as the collateral token: https://discord.com/channels/602826299974877205/636902500041228309/1249607036417867810
address _aToken = _getAToken(_collateralToken);
if (_aToken == address(0)) {
revert UnsupportedCollateralToken();
}
IERC20Metadata _collateralTokenContract = IERC20Metadata(_collateralToken);
// Deploy a token that represents a wrapped version of the collateral token to be used as proxy collateral in DIVA Protocol.
// The symbol and name of the wToken are derived from the original collateral token, prefixed with 'w' (e.g., wUSDT or wUSDC).
// This naming convention helps in identifying the token as a wrapped version of the original collateral token.
// The wToken decimals are aligned with those of the collateral token and the aToken.
// This contract is set as the owner and has exclusive rights to mint and burn the wToken.
WToken _wTokenContract = new WToken(
@> string(abi.encodePacked("w", _collateralTokenContract.symbol())),
_collateralTokenContract.decimals(),
address(this) // wToken owner
);
address _wToken = address(_wTokenContract);
// Map collateral token to its corresponding wToken.
_collateralTokenToWToken[_collateralToken] = _wToken;
// Map wToken to its corresponding collateral token to facilitate reverse lookups.
_wTokenToCollateralToken[_wToken] = _collateralToken;
// Set unlimited approval for the wToken transfer to DIVA Protocol and the collateral token transfer to Aave V3. This setup reduces
// the need for repeated approval transactions, thereby saving on gas costs.
// The unlimited approvals are deemed safe as the `AaveDIVAWrapper` is a pass-through entity that does not hold excess wTokens or collateral tokens.
// Should a vulnerability be discovered in DIVA Protocol or Aave, users can simply stop interacting with the `AaveDIVAWrapper` contract.
//
// Note that granting an infinite allowance for wToken does not reduce the allowance on `transferFrom` as it uses a newer OpenZeppelin ERC20 implementation.
// However, this behavior may differ for collateral tokens like USDC, DAI, or WETH used in Aave. These tokens decrement the allowance with each use of
// `transferFrom`, even if an unlimited allowance is set. Consequently, though very unlikely, AaveDIVAWrapper could eventually exhaust its allowance.
// The `approveCollateralTokenForAave` function has been implemented to manually reset the allowance to unlimited.
_wTokenContract.approve(_diva, type(uint256).max);
_collateralTokenContract.approve(_aaveV3Pool, type(uint256).max);
emit CollateralTokenRegistered(_collateralToken, _wToken);
return _wToken;
}

The function above retrieves the symbol from _collateralTokenContract.symbol(), If the token returns bytes32 instead of string, calling .symbol() reverts since Solidity cannot convert bytes32 directly into a string. This is an issue because the protocol is assumed to work with all collateral tokens on Aave, but the **MKR (MakerDAO’s token), which is one of the collateral tokens on Aave(Ethereum Mainnet), returns bytes32 instead of string, the above code guarantees that the owner will never be able to register MKR as a collateral token on AaveDIVAWrapper.

Impact

The function fails entirely for affected tokens, preventing their registration as collateral.

Tools Used

Manual Review

Recommendations

Use a helper function to safely convert bytes32 to string.

function _safeSymbol(IERC20Metadata token) internal view returns (string memory) {
(bool success, bytes memory data) = address(token).staticcall(
abi.encodeWithSignature("symbol()")
);
if (success && data.length > 0) {
if (data.length == 32) {
// Convert bytes32 to string
return _bytes32ToString(abi.decode(data, (bytes32)));
} else {
return abi.decode(data, (string));
}
}
return "UNKNOWN"; // Default if symbol retrieval fails
}
function _bytes32ToString(bytes32 _bytes32) internal pure returns (string memory) {
uint8 i = 0;
while (i < 32 && _bytes32[i] != 0) {
i++;
}
bytes memory bytesArray = new bytes(i);
for (uint8 j = 0; j < i; j++) {
bytesArray[j] = _bytes32[j];
}
return string(bytesArray);
}
function _registerCollateralToken(address _collateralToken) internal returns (address) {
// Verify that the collateral token is not yet registered.
if (_collateralTokenToWToken[_collateralToken] != address(0)) {
revert CollateralTokenAlreadyRegistered();
}
// Retrieve the aToken address associated with the provided collateral token from Aave V3. Reverts if
// the collateral token is not supported by Aave V3.
// Note: aTokens have the same number of decimals as the collateral token: https://discord.com/channels/602826299974877205/636902500041228309/1249607036417867810
address _aToken = _getAToken(_collateralToken);
if (_aToken == address(0)) {
revert UnsupportedCollateralToken();
}
IERC20Metadata _collateralTokenContract = IERC20Metadata(_collateralToken);
// Deploy a token that represents a wrapped version of the collateral token to be used as proxy collateral in DIVA Protocol.
// The symbol and name of the wToken are derived from the original collateral token, prefixed with 'w' (e.g., wUSDT or wUSDC).
// This naming convention helps in identifying the token as a wrapped version of the original collateral token.
// The wToken decimals are aligned with those of the collateral token and the aToken.
// This contract is set as the owner and has exclusive rights to mint and burn the wToken.
- WToken _wTokenContract = new WToken(
- string(abi.encodePacked("w", _collateralTokenContract.symbol())),
- _collateralTokenContract.decimals(),
- address(this) // wToken owner
- );
+ string memory tokenSymbol = _safeSymbol(_collateralTokenContract);
+ WToken _wTokenContract = new WToken(
+ string(abi.encodePacked("w", tokenSymbol)),
+ _collateralTokenContract.decimals(),
+ address(this)
+ );
address _wToken = address(_wTokenContract);
// Map collateral token to its corresponding wToken.
_collateralTokenToWToken[_collateralToken] = _wToken;
// Map wToken to its corresponding collateral token to facilitate reverse lookups.
_wTokenToCollateralToken[_wToken] = _collateralToken;
// Set unlimited approval for the wToken transfer to DIVA Protocol and the collateral token transfer to Aave V3. This setup reduces
// the need for repeated approval transactions, thereby saving on gas costs.
// The unlimited approvals are deemed safe as the `AaveDIVAWrapper` is a pass-through entity that does not hold excess wTokens or collateral tokens.
// Should a vulnerability be discovered in DIVA Protocol or Aave, users can simply stop interacting with the `AaveDIVAWrapper` contract.
//
// Note that granting an infinite allowance for wToken does not reduce the allowance on `transferFrom` as it uses a newer OpenZeppelin ERC20 implementation.
// However, this behavior may differ for collateral tokens like USDC, DAI, or WETH used in Aave. These tokens decrement the allowance with each use of
// `transferFrom`, even if an unlimited allowance is set. Consequently, though very unlikely, AaveDIVAWrapper could eventually exhaust its allowance.
// The `approveCollateralTokenForAave` function has been implemented to manually reset the allowance to unlimited.
_wTokenContract.approve(_diva, type(uint256).max);
_collateralTokenContract.approve(_aaveV3Pool, type(uint256).max);
emit CollateralTokenRegistered(_collateralToken, _wToken);
return _wToken;
}
Updates

Lead Judging Commences

bube Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Appeal created

josh4324 Submitter
9 months ago
bube Lead Judge
9 months ago
bube Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Known issue

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.