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.