Summary
Non-stakeholders can abuse the contract by simply calling decreasePosition and this ties back to my other finding where pendingStakes can get as lengthy as possible in less than 24 hours
File: LiquidationPool.sol
Vulnerability Details
When a non-stakeholder calls decreasePosition it will first loop through an unbounded array before reverting.
function decreasePosition(uint256 _tstVal, uint256 _eurosVal) external {
@> consolidatePendingStakes();
ILiquidationPoolManager(manager).distributeFees();
require(_tstVal <= positions[msg.sender].TST && _eurosVal <= positions[msg.sender].EUROs, "invalid-decr-amount");
if (_tstVal > 0) {
IERC20(TST).safeTransfer(msg.sender, _tstVal);
positions[msg.sender].TST -= _tstVal;
}
if (_eurosVal > 0) {
IERC20(EUROs).safeTransfer(msg.sender, _eurosVal);
positions[msg.sender].EUROs -= _eurosVal;
}
if (empty(positions[msg.sender])) deletePosition(positions[msg.sender]);
}
Impact
This attack vector can be useful if you want to stop the holders from decreasing their position by keeping the contract busy.
Tools Used
No tools
Recommendations
Allow the require to run first.
function decreasePosition(uint256 _tstVal, uint256 _eurosVal) external {
- consolidatePendingStakes(); // this will loop through pendingStakes array
- ILiquidationPoolManager(manager).distributeFees();
require(_tstVal <= positions[msg.sender].TST && _eurosVal <= positions[msg.sender].EUROs, "invalid-decr-amount");
+ consolidatePendingStakes(); // this will loop through pendingStakes array
+ ILiquidationPoolManager(manager).distributeFees();
if (_tstVal > 0) {
IERC20(TST).safeTransfer(msg.sender, _tstVal);