This repository was archived by the owner on Jun 2, 2024. It is now read-only.
This repository was archived by the owner on Jun 2, 2024. It is now read-only.
ZanyBonzy - Lowering the gauge weight can disrupt accounting, potentially leading to both excessive fund distribution and a loss of funds. #94
Open
Description
ZanyBonzy
medium
Lowering the gauge weight can disrupt accounting, potentially leading to both excessive fund distribution and a loss of funds.
Summary
Similar issues were found by users 0xDetermination and bart1e in the Canto veRWA audit, which uses a similar gauge controller type.
Vulnerability Detail
- When the _change_gauge_weight function is called, the
points_weight[addr][next_time].bias
andtime_weight[addr]
are updated - the slope is not.
def _change_gauge_weight(addr: address, weight: uint256):
# Change gauge weight
# Only needed when testing in reality
gauge_type: int128 = self.gauge_types_[addr] - 1
old_gauge_weight: uint256 = self._get_weight(addr)
type_weight: uint256 = self._get_type_weight(gauge_type)
old_sum: uint256 = self._get_sum(gauge_type)
_total_weight: uint256 = self._get_total()
next_time: uint256 = (block.timestamp + WEEK) / WEEK * WEEK
self.points_weight[addr][next_time].bias = weight
self.time_weight[addr] = next_time
new_sum: uint256 = old_sum + weight - old_gauge_weight
self.points_sum[gauge_type][next_time].bias = new_sum
self.time_sum[gauge_type] = next_time
_total_weight = _total_weight + new_sum * type_weight - old_sum * type_weight
self.points_total[next_time] = _total_weight
self.time_total = next_time
log NewGaugeWeight(addr, block.timestamp, weight, _total_weight)
- The equation f(t) = c - mx represents the gauge's decay equation before the weight is reduced. In this equation,
m
is the slope. After the weight is reduced by an amountk
using thechange_gauge_weight
function, the equation becomes f(t) = c - k - mx The slope m remains unchanged, but the t-axis intercept changes from t1 = c/m to t2 = (c-k)/m. - Slope adjustments that should be applied to the global slope when decay reaches 0 are stored in the
changes_sum
hashmap. And is not affected by changes in gauge weight. Consequently, there's a time window t1 - t2 during which the earlier slope changes applied to the global state when user calledvote_for_gauge_weights
function remains applied even though they should have been subtracted. This in turn creates a situation in which the global weightis less than the sum of the individual gauge weights, resulting in an accounting error. - So, in the
CvgRewards
contract when thewriteStakingRewards
function invokes the_checkpoint
, which subsequently triggers thegauge_relative_weight_writes
function for the relevant time period, the calculated relative weight becomes inflated, leading to an increase in the distributed rewards. If all available rewards are distributed before the entire array is processed, the remaining users will receive no rewards." - The issue mainly arises when a gauge's weight has completely diminished to zero. This is certain to happen if a gauge with a non-zero bias, non-zero slope, and a t-intercept exceeding the current time is killed using
kill_gauge
function. - Additionally, decreasing a gauge's weight introduces inaccuracies in its decay equation, as is evident in the t-intercept.
Impact
The way rewards are calculated is broken, leading to an uneven distribution of rewards, with some users receiving too much and others receiving nothing.
Code Snippet
Tool used
Manual Review
Recommendation
Disable weight reduction, or only allow reset to 0.