Summary
The AaveDIVAWrapper can create a Contingent Pool with such long and short tokens which can be transferred only to _poolParams.permissionedERC721Token holders. It means that the long and short tokens can't be redeemed via the removeLiquidity and redeemPositionToken functions.
Vulnerability Details
The _poolParams.permissionedERC721Token can be non zero and there is no check that the AaveDIVAWrapper owns a permissionedERC721Token. At the same time if the recipients own the permissionedERC721Token the call will be successful.
function _createContingentPool(PoolParams calldata _poolParams) internal returns (bytes32) {
address _wToken = _collateralTokenToWToken[_poolParams.collateralToken];
if (_wToken == address(0)) {
revert CollateralTokenNotRegistered();
}
_handleTokenOperations(_poolParams.collateralToken, _poolParams.collateralAmount, _wToken);
bytes32 _poolId = IDIVA(_diva).createContingentPool(
IDIVA.PoolParams({
referenceAsset: _poolParams.referenceAsset,
expiryTime: _poolParams.expiryTime,
floor: _poolParams.floor,
inflection: _poolParams.inflection,
cap: _poolParams.cap,
gradient: _poolParams.gradient,
collateralAmount: _poolParams.collateralAmount,
collateralToken: _collateralTokenToWToken[_poolParams.collateralToken],
dataProvider: _poolParams.dataProvider,
capacity: _poolParams.capacity,
longRecipient: _poolParams.longRecipient,
shortRecipient: _poolParams.shortRecipient,
>> permissionedERC721Token: _poolParams.permissionedERC721Token
})
);
emit PoolIssued(_poolId);
return _poolId;
}
The long and short tokens holders can't redeem via the removeLiquidity and redeemPositionToken functions because they need to transfer the tokens to the AaveDIVAWrapper contract.
_shortTokenContract.transferFrom(msg.sender , address(this) , _positionTokenAmountToRemove);
_longTokenContract.transferFrom(msg.sender , address(this) , _positionTokenAmountToRemove);
But it is possible only when the receiver owns the permissionedERC721Token:
contracts/PermissionedPositionToken.sol:
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual override {
super._beforeTokenTransfer(from, to, amount);
if (to != address(0)) {
require(
from == address(0) || _validHolder(from),
"PositionToken: invalid sender"
);
>> require(_validHolder(to), "PositionToken: invalid recipient");
}
}
function _validHolder(address _holder) private view returns (bool) {
return IERC721(_permissionedERC721Token).balanceOf(_holder) > 0;
}
The permissionedERC721Token can avoid sending their tokens to the protocol contract because it is impossible to return them back.
Impact
The contract functionality can be unavailable for particular cases or controlled by the permissionedERC721Token contract owner.
Tools used
Manual Review
Recommendations
Consider checking if the protocol owns a permissionedERC721Token before Contingent Pool creation or always using address zero.