EmissionRouter¶
Phase 3 governance spine — receives TARE emission slice from SurplusSplitter and fans it to gauge voters via BribeDistributor.
Source: khomdev-veforge/src/EmissionRouter.vy (Vyper 0.4.x)
Dependencies: GaugeController · BribeDistributor · SurplusSplitter · split_lib
Implementation overview¶
- Input — TARE balance on contract (from SurplusSplitter.split)
- Split — proportional by
gauge_relative_weight_write; sub-threshold slices skipped via split_lib.weighted_distribution - Output —
BribeDistributor.deposit_bribeper gauge over owner-setnum_epochs - Recovery — owner can
recoveronly after 180 days with no emission
flowchart LR
SS[SurplusSplitter] -->|TARE| ER[EmissionRouter]
ER -->|gauge_relative_weight_write| GC[GaugeController]
ER -->|deposit_bribe| BD[BribeDistributor]
Voter -->|claim| BD
Immutables¶
| Name | Role |
|---|---|
gauge_controller |
GaugeController |
bribe_distributor |
BribeDistributor |
reward_token |
TARE (or emission token) |
Constants¶
| Name | Value | Role |
|---|---|---|
MAX_EMISSION_GAUGES |
32 | Max configured gauges |
MAX_NUM_EPOCHS |
52 | Max bribe spread |
DEFAULT_NUM_EPOCHS |
4 | Initial spread |
RECOVERY_DELAY |
180 days | Min idle before recover |
Roles¶
| Role | Powers |
|---|---|
owner (2-step) |
set_gauges, set_num_epochs, recover |
| Anyone | distribute |
Events¶
| Event | When |
|---|---|
GaugesUpdated |
Gauge list changed |
NumEpochsSet |
Spread updated |
Distributed |
Emission executed |
Recovered |
Stuck funds recovered |
Admin¶
set_gauges¶
EmissionRouter.set_gauges(_gauges)
Set gauges that receive emissions. Each must be GC-registered. Owner-only.
| Param | Type | Description |
|---|---|---|
_gauges |
DynArray[address, 32] |
Gauge addresses |
Access: Owner only.
Events: GaugesUpdated.
Reverts if: zero address or unknown gauge in GC.
Source (khomdev-veforge/src/EmissionRouter.vy:114-128):
@external
def set_gauges(_gauges: DynArray[address, MAX_EMISSION_GAUGES]):
ownable._check_owner()
for i: uint256 in range(MAX_EMISSION_GAUGES):
assert staticcall IGaugeController(gauge_controller).is_gauge_added(_gauges[i]), "router: unknown gauge"
self.gauges = _gauges
Cross-links: GaugeController.add_gauge · n_gauges
set_num_epochs¶
EmissionRouter.set_num_epochs(_num_epochs)
Set weekly spread for every gauge's slice on distribute. Owner-only (prevents griefing via permissionless distribute).
| Param | Type | Description |
|---|---|---|
_num_epochs |
uint256 |
Spread length; 1..52 |
Access: Owner only.
Events: NumEpochsSet.
Reverts if: _num_epochs out of range.
Source (khomdev-veforge/src/EmissionRouter.vy:131-142):
@external
def set_num_epochs(_num_epochs: uint256):
ownable._check_owner()
assert _num_epochs >= 1 and _num_epochs <= MAX_NUM_EPOCHS, "router: bad num_epochs"
self.num_epochs = _num_epochs
Cross-links: distribute · BribeDistributor.deposit_bribe
Apply¶
distribute¶
EmissionRouter.distribute()
Permissionless. Split contract TARE balance across configured gauges by relative weight; fund each via deposit_bribe.
Access: Any caller. nonreentrant.
Events: Distributed (when any gauge funded).
Reverts if: empty gauge list.
No-op when: zero balance, all-zero weights, or every slice below num_epochs.
Note: Uses gauge_relative_weight_write + split_lib.weighted_distribution. Resets last_emission_at on actual emission.
Source (khomdev-veforge/src/EmissionRouter.vy:147-217):
@external
@nonreentrant
def distribute():
w: uint256 = extcall IGaugeController(gauge_controller).gauge_relative_weight_write(self.gauges[i], block.timestamp)
amounts, dust = split_lib.weighted_distribution(weights, total, bal, epochs)
extcall IBribeDistributor(bribe_distributor).deposit_bribe(gauge, reward_token, amount_g, epochs)
if distributed > 0:
self.last_emission_at = block.timestamp
Example:
Cross-links: GaugeController.gauge_relative_weight_write · BribeDistributor.deposit_bribe
Escape hatch¶
recover¶
EmissionRouter.recover(_to)
Recover reward-token balance. Owner-only, only after 180 days since last actual emission.
| Param | Type | Description |
|---|---|---|
_to |
address |
Recipient (non-zero) |
Access: Owner only. nonreentrant.
Events: Recovered.
Reverts if: zero recipient, too soon since last emission, or zero balance.
Note: Successful distribute resets recovery clock — owner cannot pull while emissions flow.
Source (khomdev-veforge/src/EmissionRouter.vy:222-240):
@external
@nonreentrant
def recover(_to: address):
ownable._check_owner()
assert block.timestamp >= self.last_emission_at + RECOVERY_DELAY, "router: too soon"
assert extcall IERC20(reward_token).transfer(_to, bal), "router: recover transfer failed"
Views¶
n_gauges¶
EmissionRouter.n_gauges() -> uint256
Number of configured emission gauges.
Returns: uint256 — length of gauges array.
Access: view, any caller.
Source (khomdev-veforge/src/EmissionRouter.vy:245-249):