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), notbalanceOf - 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 - History —
user_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]
Immutables / links¶
| 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:
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:
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):
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: uint256 — MULTIPLIER * 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):
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):