Description
The AaveDIVAWrapper contract assumes consistent token behavior over time but lacks protection against upgradeable tokens (like USDC, USDT) changing their core functionality through upgrades. While these changes won't affect mid-transaction operations, they can break protocol assumptions between transactions.
Current vulnerable implementation:
https://github.com/Cyfrin/2025-01-diva/blob/main/contracts/src/AaveDIVAWrapperCore.sol#L423
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);
}
Impact
Token upgrades could break protocol in several ways:
Added transfer fees break 1:1 collateral ratio
New transfer restrictions prevent operation completion
Blocklist features could lock protocol funds
Modified balance mechanics break accounting
Changed requirements invalidate protocol assumptions
Proof of Concept
contract MockUSDC {
bool public feeEnabled;
uint256 public constant FEE = 100;
function upgrade() external {
feeEnabled = true;
}
function transfer(address to, uint256 amount) external returns (bool) {
if (feeEnabled) {
uint256 fee = amount * FEE / 10000;
_transfer(msg.sender, to, amount - fee);
return true;
}
_transfer(msg.sender, to, amount);
return true;
}
}
Recommended Fix
contract AaveDIVAWrapperCore {
struct TokenBehavior {
bool hasFee;
bool hasRestrictions;
bytes32 implementationHash;
uint256 lastCheck;
}
mapping(address => TokenBehavior) public tokenBehaviors;
error TokenBehaviorChanged(address token);
error TokenImplementationChanged(address token);
function validateTokenBehavior(address token) internal {
TokenBehavior storage behavior = tokenBehaviors[token];
bytes32 currentImpl = getImplementationHash(token);
if (currentImpl != behavior.implementationHash) {
behavior.implementationHash = currentImpl;
_handleImplementationChange(token);
}
if (hasFee(token) != behavior.hasFee) {
revert TokenBehaviorChanged(token);
}
}
function _handleTokenOperations(
address _collateralToken,
uint256 _collateralAmount,
address _wToken
) private {
validateTokenBehavior(_collateralToken);
uint256 balanceBefore = IERC20(_collateralToken).balanceOf(address(this));
IERC20Metadata(_collateralToken).safeTransferFrom(
msg.sender,
address(this),
_collateralAmount
);
uint256 received = IERC20(_collateralToken).balanceOf(address(this)) -
balanceBefore;
if (received != _collateralAmount) {
revert UnexpectedTokenBehavior(_collateralToken, _collateralAmount, received);
}
IAave(_aaveV3Pool).supply(_collateralToken, received, address(this), 0);
IWToken(_wToken).mint(address(this), received);
}
}
The fix ensures protocol safety against token upgrades while maintaining functionality with trusted tokens.