Skip to content

GaugeController

Curve-style gauge weight scheduler. veToken holders vote slope (bps) across gauges; weights decay weekly for emission routing.

Source: khomdev-veforge/src/GaugeController.vy (Vyper 0.4.x)

Dependencies: VotingEscrow · EmissionRouter · BribeDistributor

Implementation overview

  • Votes — user allocates slope in bps per gauge; slope from VE.get_user_slope (M5), not balanceOf
  • Weight math — per-gauge bias/slope schedules + per-type sums + type multipliers (veCRV-faithful scaling)
  • Decay — weekly walk via checkpoint / checkpoint_gauge; slope cancels at lock expiry
  • Historyuser_vote_history (≤1024/gauge) for BribeDistributor epoch snapshots
  • Kill switch — killed gauges get 0 relative weight; no new votes (A6)
flowchart LR
    VE[VotingEscrow] -->|get_user_slope| GC[GaugeController]
    User -->|vote bps| GC
    GC -->|relative weight| ER[EmissionRouter]
    GC -->|vote history| BD[BribeDistributor]
Name Role
VE VotingEscrow contract

Constants

Name Role
WEEK Schedule granularity (604800 s)
WEIGHT_VOTE_DELAY 10-day per-(user,gauge) vote cooldown
MAX_GAUGE_TYPES 16 type cap (A5)
MAX_TYPE_WEIGHT Overflow guard on type weight (A4)
MAX_BPS 10000 — vote allocation denominator
MULTIPLIER 1e18 — relative weight scaling

Roles

Role Powers
owner (2-step) Add types/gauges, type/gauge weights, kill/unkill

Events

Event When
NewType / TypeWeightUpdated Type registry / multiplier change
NewGauge / GaugeWeightUpdated Gauge added / admin weight bump
VoteForGauge User vote recorded
GaugeKilled / GaugeRevived Kill switch

Admin

add_type

GaugeController.add_type(_name, _weight=DEFAULT_TYPE_WEIGHT)

Register a new gauge type. Owner-only.

Param Type Description
_name String[64] Human-readable type label
_weight uint256 Type multiplier (1e18 = 1.0); default 1e18; may be 0

Access: Owner only.

Events: NewType.

Reverts if: n_gauge_types >= MAX_GAUGE_TYPES (16).

Source (khomdev-veforge/src/GaugeController.vy:196-215):

@external
def add_type(_name: String[64], _weight: uint256 = DEFAULT_TYPE_WEIGHT):
    ownable._check_owner()
    assert self.n_gauge_types < MAX_GAUGE_TYPES, "GC: too many types"
    type_id: int128 = self.n_gauge_types
    self.gauge_type_names[type_id] = _name
    self.n_gauge_types = type_id + 1
    if _weight != 0:
        self._change_type_weight(type_id, _weight)
    log NewType(type_id=type_id, name=_name, weight=_weight)

Example:

gc.add_type("Keep strategies", 10**18)

Cross-links: add_gauge · change_type_weight

add_gauge

GaugeController.add_gauge(_gauge, _gauge_type, _weight=0)

Add a gauge to a registered type. Owner-only.

Param Type Description
_gauge address Gauge contract address
_gauge_type int128 Type index in [0, n_gauge_types)
_weight uint256 Optional seed bias (zero slope); default 0

Access: Owner only.

Events: NewGauge.

Reverts if: zero gauge, duplicate gauge, or invalid type.

Source (khomdev-veforge/src/GaugeController.vy:218-260):

@external
def add_gauge(_gauge: address, _gauge_type: int128, _weight: uint256 = 0):
    ownable._check_owner()
    assert _gauge != empty(address), "GC: zero gauge"
    assert not self.is_gauge_added[_gauge], "GC: gauge already added"
    assert _gauge_type >= 0 and _gauge_type < self.n_gauge_types, "GC: invalid type"
    self.is_gauge_added[_gauge] = True
    self.gauge_type_of[_gauge] = _gauge_type
    self.gauges.append(_gauge)

Cross-links: add_type · vote_for_gauge_weights

change_type_weight

GaugeController.change_type_weight(_type_id, _weight)

Set the weight multiplier for a gauge type. Rescales that type's contribution to the global total. Owner-only.

Param Type Description
_type_id int128 Type index
_weight uint256 New multiplier; capped at MAX_TYPE_WEIGHT

Access: Owner only.

Events: TypeWeightUpdated.

Reverts if: invalid type or _weight > MAX_TYPE_WEIGHT.

Source (khomdev-veforge/src/GaugeController.vy:263-271, _change_type_weight 436-451):

@external
def change_type_weight(_type_id: int128, _weight: uint256):
    ownable._check_owner()
    assert _type_id >= 0 and _type_id < self.n_gauge_types, "GC: invalid type"
    self._change_type_weight(_type_id, _weight)

Cross-links: get_type_weight · get_total_weight

change_gauge_weight

GaugeController.change_gauge_weight(_gauge, _weight)

Admin override of a gauge's absolute raw bias (zero slope change). Owner-only.

Param Type Description
_gauge address Registered gauge
_weight uint256 New bias at next week boundary

Access: Owner only.

Events: GaugeWeightUpdated.

Reverts if: gauge not registered.

Source (khomdev-veforge/src/GaugeController.vy:274-302):

@external
def change_gauge_weight(_gauge: address, _weight: uint256):
    ownable._check_owner()
    assert self.is_gauge_added[_gauge], "GC: unknown gauge"
    # ... updates points_weight + points_sum, then _get_total()
    log GaugeWeightUpdated(gauge=_gauge, time=next_time, weight=_weight, total_weight=new_total)

Cross-links: get_gauge_weight · change_type_weight

kill_gauge

GaugeController.kill_gauge(_gauge)

Mark a gauge killed. Relative weight returns 0; new votes revert. Existing slope-decay schedules continue. Owner-only (A6).

Param Type Description
_gauge address Registered gauge

Access: Owner only.

Events: GaugeKilled.

Reverts if: gauge not registered.

Source (khomdev-veforge/src/GaugeController.vy:305-316):

@external
def kill_gauge(_gauge: address):
    ownable._check_owner()
    assert self.is_gauge_added[_gauge], "GC: unknown gauge"
    self.is_killed[_gauge] = True
    log GaugeKilled(gauge=_gauge)

Cross-links: unkill_gauge · gauge_relative_weight

unkill_gauge

GaugeController.unkill_gauge(_gauge)

Restore a killed gauge. Owner-only.

Param Type Description
_gauge address Registered gauge

Access: Owner only.

Events: GaugeRevived.

Reverts if: gauge not registered.

Source (khomdev-veforge/src/GaugeController.vy:319-327):

@external
def unkill_gauge(_gauge: address):
    ownable._check_owner()
    assert self.is_gauge_added[_gauge], "GC: unknown gauge"
    self.is_killed[_gauge] = False
    log GaugeRevived(gauge=_gauge)

Cross-links: kill_gauge

Voting

vote_for_gauge_weights

GaugeController.vote_for_gauge_weights(_gauge, _user_weight)

Allocate voting power to a gauge in basis points (0–10000). Cumulative across all gauges must not exceed 100%.

Param Type Description
_gauge address Target gauge
_user_weight uint256 BPS allocated (0 = un-vote)

Access: Caller with active VE lock. nonreentrant implied by state updates.

Events: VoteForGauge.

Reverts if: weight > 10000, unknown/killed gauge, vote cooldown (WEIGHT_VOTE_DELAY), no active lock, lock ends before next epoch, or total power > 100%.

Note: Slope from VE.get_user_slope, not balanceOf (M5). Updates per-gauge and per-type-sum schedules; schedules slope cancellation at lock expiry.

Source (khomdev-veforge/src/GaugeController.vy:332-408):

@external
def vote_for_gauge_weights(_gauge: address, _user_weight: uint256):
    assert _user_weight <= MAX_BPS, "GC: weight > 100%"
    assert not self.is_killed[_gauge], "GC: gauge is killed"
    slope_int: int128 = staticcall IVotingEscrow(VE).get_user_slope(user)
    assert slope_int > 0, "GC: no active lock"
    lock_end: uint256 = staticcall IVotingEscrow(VE).locked__end(user)
    assert lock_end > next_time, "GC: lock ends before next epoch"
    # ... apply bias/slope deltas, record VoteSnapshot
    log VoteForGauge(ts=block.timestamp, user=user, gauge=_gauge, weight=_user_weight)

Example:

gc.vote_for_gauge_weights(gauge_addr, 5000)  # 50% of slope to this gauge

Cross-links: VotingEscrow.get_user_slope · checkpoint_gauge · BribeDistributor.claim

Checkpoint

checkpoint

GaugeController.checkpoint()

Advance the global type-weighted total weight schedule to the current epoch.

Access: Any caller.

Events: None (state walk only).

Source (khomdev-veforge/src/GaugeController.vy:413-418):

@external
def checkpoint():
    self._get_total()

Cross-links: checkpoint_gauge · get_total_weight

checkpoint_gauge

GaugeController.checkpoint_gauge(_gauge)

Advance one gauge's weight schedule, refresh its type sum, then recompute global total.

Param Type Description
_gauge address Gauge to checkpoint

Access: Any caller. Idempotent — safe to call before claims.

Source (khomdev-veforge/src/GaugeController.vy:421-430):

@external
def checkpoint_gauge(_gauge: address):
    self._get_weight(_gauge)
    self._get_sum(self.gauge_type_of[_gauge])
    self._get_total()

Cross-links: BribeDistributor.claim · gauge_relative_weight_write

Views

vote_user_slopes_at

GaugeController.vote_user_slopes_at(_user, _gauge, _epoch) -> VoteSnapshot

Snapshot of (user, gauge) vote state active at _epoch. Used by BribeDistributor for frozen epoch claims.

Param Type Description
_user address Voter
_gauge address Gauge
_epoch uint256 Week-aligned timestamp

Returns: VoteSnapshot{epoch, slope, end, power}. Empty struct if no snapshot at or before _epoch.

Access: view, any caller.

Source (khomdev-veforge/src/GaugeController.vy:628-659):

@external
@view
def vote_user_slopes_at(_user: address, _gauge: address, _epoch: uint256) -> VoteSnapshot:
    # binary search user_vote_history for latest epoch <= _epoch
    return self.user_vote_history[_user][_gauge][lo]

Cross-links: BribeDistributor.claim · vote_for_gauge_weights

vote_user_history_length

GaugeController.vote_user_history_length(_user, _gauge) -> uint256

Number of recorded vote snapshots for a (user, gauge) pair.

Param Type Description
_user address Voter
_gauge address Gauge

Returns: uint256 — snapshot count (max 1024).

Access: view, any caller.

Source (khomdev-veforge/src/GaugeController.vy:662-666):

@external
@view
def vote_user_history_length(_user: address, _gauge: address) -> uint256:
    return len(self.user_vote_history[_user][_gauge])

gauge_relative_weight

GaugeController.gauge_relative_weight(_gauge, _time=block.timestamp) -> uint256

Gauge weight as fraction of type-weighted total, scaled by 1e18. Reads checkpointed state — call checkpoint_gauge first for latest values.

Param Type Description
_gauge address Gauge
_time uint256 Query timestamp (week-aligned internally)

Returns: uint256MULTIPLIER * type_weight * gauge_bias / total_weight. Returns 0 if killed or total is 0.

Access: view, any caller.

Source (khomdev-veforge/src/GaugeController.vy:671-709):

@external
@view
def gauge_relative_weight(_gauge: address, _time: uint256 = block.timestamp) -> uint256:
    return self._relative_weight(_gauge, _time)

@internal
@view
def _relative_weight(_gauge: address, _time: uint256) -> uint256:
    if self.is_killed[_gauge]:
        return 0
    return MULTIPLIER * type_weight * gauge_bias // total_weight

Cross-links: gauge_relative_weight_write · EmissionRouter.distribute

gauge_relative_weight_write

GaugeController.gauge_relative_weight_write(_gauge, _time=block.timestamp) -> uint256

Same as gauge_relative_weight but checkpoints gauge + total before read.

Param Type Description
_gauge address Gauge
_time uint256 Query timestamp

Returns: uint256 — relative weight at _time.

Access: Any caller (mutates checkpoint state).

Source (khomdev-veforge/src/GaugeController.vy:683-694):

@external
def gauge_relative_weight_write(_gauge: address, _time: uint256 = block.timestamp) -> uint256:
    self._get_weight(_gauge)
    self._get_total()
    return self._relative_weight(_gauge, _time)

Cross-links: EmissionRouter.distribute · GaugeWeightRouter.poke

get_gauge_weight

GaugeController.get_gauge_weight(_gauge) -> uint256

Latest checkpointed raw bias for a gauge (not type-scaled).

Param Type Description
_gauge address Gauge

Returns: uint256 — raw bias from points_weight.

Access: view, any caller.

Source (khomdev-veforge/src/GaugeController.vy:712-716):

@external
@view
def get_gauge_weight(_gauge: address) -> uint256:
    return self.points_weight[_gauge][self.time_weight[_gauge]].bias

get_type_weight

GaugeController.get_type_weight(_type_id) -> uint256

Latest checkpointed weight multiplier for a gauge type.

Param Type Description
_type_id int128 Type index

Returns: uint256 — type multiplier at latest checkpoint.

Access: view, any caller.

Source (khomdev-veforge/src/GaugeController.vy:719-723):

@external
@view
def get_type_weight(_type_id: int128) -> uint256:
    return self.points_type_weight[_type_id][self.time_type_weight[_type_id]]

get_weights_sum_per_type

GaugeController.get_weights_sum_per_type(_type_id) -> uint256

Latest checkpointed summed raw bias for all gauges in a type.

Param Type Description
_type_id int128 Type index

Returns: uint256 — per-type sum bias.

Access: view, any caller.

Source (khomdev-veforge/src/GaugeController.vy:726-730):

@external
@view
def get_weights_sum_per_type(_type_id: int128) -> uint256:
    return self.points_sum[_type_id][self.time_sum[_type_id]].bias

get_total_weight

GaugeController.get_total_weight() -> uint256

Latest checkpointed type-weighted global total (Σ type_weight * sum_bias).

Returns: uint256 — global total at latest checkpoint.

Access: view, any caller.

Source (khomdev-veforge/src/GaugeController.vy:733-737):

@external
@view
def get_total_weight() -> uint256:
    return self.points_total[self.time_total]

n_gauges

GaugeController.n_gauges() -> uint256

Number of registered gauges.

Returns: uint256 — length of gauges array.

Access: view, any caller.

Source (khomdev-veforge/src/GaugeController.vy:740-744):

@external
@view
def n_gauges() -> uint256:
    return len(self.gauges)