BribeDistributor¶
Direct (non-Merkle) bribe distribution for gauge voters. Deposits spread across future weekly epochs; claims use frozen per-epoch vote snapshots from GaugeController.
Source: khomdev-veforge/src/BribeDistributor.vy (Vyper 0.4.x)
Dependencies: GaugeController · EmissionRouter
Implementation overview¶
- Deposits — split evenly across
_num_epochsfuture weeks; remainder → final epoch (M3) - Claims — share =
bribe * user_bias / gauge_total_biasat closed epoch; readsvote_user_slopes_at+points_weight(C3/C4) - Solvency —
claimed_totalper bucket prevents overdraw (A2) - Sweep — owner reclaims dust/unclaimed after 90 days (A1)
- Fees — protocol fee on claim, capped at 10% (L3)
flowchart LR
ER[EmissionRouter] -->|deposit_bribe| BD[BribeDistributor]
GC[GaugeController] -->|vote_user_slopes_at| BD
User -->|claim| BD
Immutables¶
| Name | Role |
|---|---|
GAUGE_CONTROLLER |
GaugeController for weight snapshots |
Constants¶
| Name | Value | Role |
|---|---|---|
WEEK |
604800 | Epoch alignment |
SWEEP_DELAY |
90 days | Min age before sweep |
MAX_FEE_BPS |
1000 (10%) | Fee cap |
MAX_NUM_EPOCHS |
52 | Max deposit spread |
MAX_BATCH |
64 | claim_many limit |
Roles¶
| Role | Powers |
|---|---|
owner (2-step) |
set_fee_params, sweep_unclaimed |
fee_recipient |
withdraw_protocol_fees |
Events¶
| Event | When |
|---|---|
BribeDeposited |
Deposit recorded |
BribeClaimed |
User claim (net + fee) |
FeeParamsUpdated |
Fee config change |
ProtocolFeesWithdrawn |
Fee sweep |
SweepUnclaimed |
Owner sweep |
Views¶
current_epoch¶
BribeDistributor.current_epoch() -> uint256
Current week-aligned epoch (start of this week in seconds).
Returns: uint256 — (block.timestamp // WEEK) * WEEK.
Access: view, any caller.
Source (khomdev-veforge/src/BribeDistributor.vy:145-149):
Cross-links: claim · deposit_bribe
Deposit¶
deposit_bribe¶
BribeDistributor.deposit_bribe(_gauge, _token, _amount, _num_epochs)
Deposit a bribe for a gauge in a token, spread over future weekly epochs.
| Param | Type | Description |
|---|---|---|
_gauge |
address |
Gauge — must be GC-registered (A1) |
_token |
address |
ERC-20 reward token |
_amount |
uint256 |
Total to distribute (≥ _num_epochs) |
_num_epochs |
uint256 |
Spread length; 1..52 |
Access: Caller. nonreentrant. Pulls tokens via transferFrom.
Events: BribeDeposited.
Reverts if: unknown gauge, zero amount, bad num_epochs, or per_epoch underflow.
Note: Remainder from floor division credited to final epoch (M3).
Source (khomdev-veforge/src/BribeDistributor.vy:178-222):
@external
@nonreentrant
def deposit_bribe(_gauge: address, _token: address, _amount: uint256, _num_epochs: uint256):
assert staticcall IGaugeController(GAUGE_CONTROLLER).is_gauge_added(_gauge), "BD: unknown gauge"
per_epoch: uint256 = _amount // _num_epochs
# ... credit bribes[gauge][target_epoch][token]
log BribeDeposited(depositor=msg.sender, gauge=_gauge, token=_token, amount=_amount, num_epochs=_num_epochs)
Example:
Cross-links: EmissionRouter.distribute · GaugeController.is_gauge_added
Claim¶
claim¶
BribeDistributor.claim(_gauge, _epoch, _token)
Claim caller's share of bribes for (gauge, epoch, token). Epoch must be closed and week-aligned.
| Param | Type | Description |
|---|---|---|
_gauge |
address |
Gauge |
_epoch |
uint256 |
Closed week-aligned epoch |
_token |
address |
Reward token |
Access: Caller. nonreentrant.
Events: BribeClaimed.
Reverts if: epoch not closed, not aligned, already claimed, no bribes, zero gauge weight, or bucket overdrawn (A2).
Note: Calls checkpoint_gauge internally. Share from frozen vote_user_slopes_at snapshot (C3).
Source (khomdev-veforge/src/BribeDistributor.vy:227-298):
@external
@nonreentrant
def claim(_gauge: address, _epoch: uint256, _token: address):
self._claim(msg.sender, _gauge, _epoch, _token)
# _claim: user_share = bribe_amount * user_bias // total.bias
# assert claimed_total + user_share <= bribe_amount
Cross-links: GaugeController.vote_user_slopes_at · claim_many
claim_many¶
BribeDistributor.claim_many(_claims)
Batch claim up to 64 (gauge, epoch, token) tuples. All-or-revert.
| Param | Type | Description |
|---|---|---|
_claims |
DynArray[ClaimRequest, 64] |
{gauge, epoch, token} per entry |
Access: Caller. nonreentrant.
Events: BribeClaimed per successful claim.
Reverts if: any single claim in batch fails.
Source (khomdev-veforge/src/BribeDistributor.vy:237-246):
@external
@nonreentrant
def claim_many(_claims: DynArray[ClaimRequest, MAX_BATCH]):
for req: ClaimRequest in _claims:
self._claim(msg.sender, req.gauge, req.epoch, req.token)
Cross-links: claim
Admin¶
sweep_unclaimed¶
BribeDistributor.sweep_unclaimed(_gauge, _epoch, _token, _to)
Reclaim unclaimed + dust after 90 days past epoch. Owner-only (A1).
| Param | Type | Description |
|---|---|---|
_gauge |
address |
Gauge |
_epoch |
uint256 |
Week-aligned epoch |
_token |
address |
Token |
_to |
address |
Recipient (non-zero) |
Access: Owner only. nonreentrant.
Events: SweepUnclaimed.
Reverts if: zero recipient, epoch not aligned, or sweep too early.
Note: Sets claimed_total = bribes to close bucket after sweep.
Source (khomdev-veforge/src/BribeDistributor.vy:303-333):
@external
@nonreentrant
def sweep_unclaimed(_gauge: address, _epoch: uint256, _token: address, _to: address):
ownable._check_owner()
assert block.timestamp >= _epoch + SWEEP_DELAY, "BD: sweep too early"
remaining: uint256 = total - already
self.claimed_total[_gauge][_epoch][_token] = total
Cross-links: claim
set_fee_params¶
BribeDistributor.set_fee_params(_bps, _recipient)
Update protocol fee rate and recipient. Owner-only. Fee capped at 10% (L3).
| Param | Type | Description |
|---|---|---|
_bps |
uint256 |
Fee in basis points (≤ 1000) |
_recipient |
address |
Fee recipient |
Access: Owner only.
Events: FeeParamsUpdated.
Reverts if: _bps > MAX_FEE_BPS.
Source (khomdev-veforge/src/BribeDistributor.vy:336-346):
@external
def set_fee_params(_bps: uint256, _recipient: address):
ownable._check_owner()
assert _bps <= MAX_FEE_BPS, "BD: fee > MAX_FEE_BPS"
self.fee_bps = _bps
self.fee_recipient = _recipient
withdraw_protocol_fees¶
BribeDistributor.withdraw_protocol_fees(_token)
Sweep accumulated protocol fees for a token to fee_recipient.
| Param | Type | Description |
|---|---|---|
_token |
address |
Token to withdraw |
Access: fee_recipient only. nonreentrant.
Events: ProtocolFeesWithdrawn.
Reverts if: caller is not fee recipient.
Source (khomdev-veforge/src/BribeDistributor.vy:349-364):
@external
@nonreentrant
def withdraw_protocol_fees(_token: address):
assert msg.sender == self.fee_recipient, "BD: not fee recipient"
self._safe_transfer(_token, msg.sender, amount)