Summary
getLockPosition() uses cached power
Vulnerability Details
We have the following function to get a lock:
function getLockPosition(address account) external view override returns (LockPosition memory) {
LockManager.Lock memory userLock = _lockState.getLock(account);
return LockPosition({ amount: userLock.amount, end: userLock.end, power: balanceOf(account) });
}
In voting escrows, power must decay. However, here we simply call balanceOf() which has not been inherited, thus we simply use the balance of the user without applying any decay. Since a lot of functions call that function, this will cause completely wrong state and calculations.
We can see how it is supposed to be done:
def balanceOf(addr: address, _t: uint256 = block.timestamp) -> uint256:
"""
@notice Get the current voting power for `msg.sender`
@dev Adheres to the ERC20 `balanceOf` interface for Aragon compatibility
@param addr User wallet address
@param _t Epoch time to return voting power at
@return User voting power
"""
_epoch: uint256 = self.user_point_epoch[addr]
if _epoch == 0:
return 0
else:
last_point: Point = self.user_point_history[addr][_epoch]
last_point.bias -= last_point.slope * convert(_t - last_point.ts, int128)
if last_point.bias < 0:
last_point.bias = 0
return convert(last_point.bias, uint256)
Impact
Completely wrong state, calculations and so on.
Tools Used
Manual Review
Recommendations
Override the function with the appropriate functionality such as decay.