DeFi governance has evolved far beyond "vote yes or no on proposals." In 2026, protocol governance is a full economic subsystem where governance tokens generate yield, vote allocation determines protocol revenue flows, and sophisticated actors earn 20-40% APY by participating in bribe markets. AI agents are uniquely positioned to extract this yield: they can track every active bribe, compute optimal vote allocations, and execute governance transactions without sleeping.
This post covers the mechanics from scratch: how DAO governance works, how veTokens transform governance participation into a yield strategy, the bribe marketplaces (Votium, Hidden Hand) that pay for votes, and a complete Python GovernanceAgent that automates the entire pipeline.
DAO Governance Mechanics
A Decentralized Autonomous Organization (DAO) is a protocol governed by token holders rather than a central company. Token holders propose and vote on protocol changes: fee adjustments, treasury spending, parameter updates, contract upgrades. The exact mechanics vary significantly between protocols, but the core structure is consistent.
Proposal Lifecycle
Discussion (2-5 days)
Author posts proposal to governance forum (Discourse, Commonwealth). Community discusses, refines, objects. Temperature checks via Snapshot gauge sentiment before on-chain vote.
Snapshot Vote (3-7 days)
Off-chain voting on Snapshot. Gasless. Requires token balance above proposal threshold (e.g., 1,000 CRV). Results are not binding but gauge community support.
On-Chain Vote (3-7 days)
Binding on-chain vote on Tally, Boardroom, or protocol-native UI. Requires quorum (typically 5-15% of total supply) and majority approval to pass. Gas cost per vote: $5-30.
Timelock + Execution (2-7 days)
Passed proposals enter a timelock before execution, giving users time to exit if they disagree with the change. After timelock, changes are automatically executed on-chain.
Quorum and Voting Power
Quorum is the minimum participation required for a vote to be valid. If quorum is 10% of supply and only 8% of tokens vote, the proposal fails regardless of how people voted. This creates a free-rider problem: many token holders don't vote, making quorum hard to reach without active participants.
Voting power is usually proportional to token balance, but veToken models weight votes by lockup commitment. This is the core innovation: it transforms passive token holding into active long-term alignment, and creates the bribe economy that generates agent yield.
Key types of DAO proposals agents encounter: gauge weight votes (which liquidity pools receive CRV/BAL emissions), protocol parameter changes (fees, collateral ratios), treasury spending, new contract deployments, and emergency shutdown votes. Gauge weight votes are most economically significant — they determine where millions in annual emissions flow.
veToken Lockup Mechanics
The vote-escrow (ve) model was pioneered by Curve Finance with veCRV and has since been adopted by Balancer (veBAL), Frax (veFXS), Angle (veANGLE), and dozens more. The core mechanic: lock your governance token for a time period (1 week to 4 years) and receive a proportional amount of "voting escrow" tokens that decay over time.
veCRV: The Original Model
To acquire veCRV:
- Hold CRV (Curve's governance token)
- Lock CRV in Curve's VotingEscrow contract for 1 week to 4 years
- Receive veCRV proportional to
CRV_locked * (lock_duration / 4_years) - veCRV balance decays linearly to 0 at lockup expiry
- veCRV cannot be transferred — it is non-fungible and wallet-bound
With maximum veCRV (4-year lock), you earn: 3x liquidity mining boost on Curve pools you LP in, 50% of all protocol trading fees (distributed weekly in 3CRV), and gauge weight voting power to direct CRV emissions to specific pools.
veBAL: Balancer's Twist
Balancer's veBAL works similarly but locks 80/20 BAL-ETH Balancer LP tokens instead of pure BAL. This is intentional: it requires holders to maintain ETH market exposure alongside BAL, aligning long-term incentives. The maximum lock is 1 year (shorter than veCRV), and voting occurs weekly on Thursday epochs.
vlCVX: Liquid Governance via Convex
Convex Finance created a clever workaround for veCRV illiquidity. Convex acquires massive veCRV by accepting user CRV deposits and locking it permanently. Users deposit CRV to Convex and receive cvxCRV (liquid, tradeable). Then they lock cvxCRV into vlCVX for 16-week periods.
vlCVX is the most important governance token in the Curve ecosystem because Convex controls ~50% of all veCRV. Whoever controls vlCVX votes controls where ~50% of CRV emissions go. This is why vlCVX bribe yields are extremely high — protocols pay handsomely to direct that emission allocation.
| Token | Protocol | Lock Period | Yield Sources | Liquidity |
|---|---|---|---|---|
| veCRV | Curve | 1 wk - 4 yr | 3CRV fees + LP boost + bribes | Illiquid |
| veBAL | Balancer | 1 wk - 1 yr | Protocol fees + LP boost + bribes | Illiquid |
| vlCVX | Convex | 16 weeks | CVX rewards + bribes (Votium) | Semi-liquid (epochs) |
| veFXS | Frax | 1 wk - 4 yr | Protocol revenue + gauges + bribes | Illiquid |
| veANGLE | Angle | 1 wk - 4 yr | Stability fee revenue + bribes | Illiquid |
Vote-Buying: The Bribe Economy
Bribe markets are the most interesting governance primitive from an agent perspective. A "bribe" in DeFi governance is a legitimate, transparent payment made to veToken holders in exchange for directing their votes toward specific gauge weights.
Here is why protocols pay bribes: if you control a liquidity pool on Curve and you want deep liquidity, you need CRV emissions directed at your pool. CRV emissions make your pool more attractive to LPs. To attract those emissions, you need veCRV holders to vote for your gauge. Paying bribe tokens to veCRV holders is cheaper than buying enough CRV to control the gauge directly.
Votium
- Largest bribe market by volume
- Epoch length: bi-weekly (every 2 weeks)
- Target: vlCVX holders who vote on Curve gauge weights
- Typical yield: 15-25% APY on CVX value
- Token support: any ERC-20 accepted as bribe
- Claim: auto-claimable by delegate or directly
Hidden Hand
- Bribes for Balancer (veBAL), Aura, Frax, Ribbon, Tokemak
- Epoch: weekly, Thursday snapshot
- Target: veBAL and Aura (vlAURA) holders
- Typical yield: 10-20% APY on BAL/AURA value
- Protocol-agnostic: one interface for multiple bribe types
- Claim: merkle-proof based, gas-efficient
Bribe ROI Calculation
The key metric for gauge bribe markets is $ bribed per $1 of weekly emissions directed. Protocols compete on this metric. If a protocol bribes $100k to vlCVX holders and receives $200k in weekly CRV emissions directed to their gauge, the bribe ROI is 2x — very attractive for the bribing protocol.
For the recipient (the voting agent), what matters is $/vote. A simple calculation:
# Bribe yield calculation for vlCVX holder
# ─────────────────────────────────────────────
cvx_price_usd = 4.20 # Current CVX price
cvx_locked = 10_000 # vlCVX locked
cvx_total_supply_locked = 95_000_000 # Total vlCVX supply
# Your share of total votes
vote_share = cvx_locked / cvx_total_supply_locked # 0.0105%
# Weekly Votium bribe pool (typical active round)
total_bribe_pool_usd = 3_200_000 # $3.2M total bribed per round
# Your expected weekly bribe income
weekly_bribe_usd = vote_share * total_bribe_pool_usd
# Annualized
annual_bribe_usd = weekly_bribe_usd * 26 # 26 bi-weekly epochs/year
# Your CVX position value
position_value_usd = cvx_locked * cvx_price_usd
# Bribe APY from voting alone
bribe_apy = (annual_bribe_usd / position_value_usd) * 100
print(f"Position: ${position_value_usd:,.0f}")
print(f"Vote share: {vote_share*100:.4f}%")
print(f"Weekly bribe income: ${weekly_bribe_usd:.2f}")
print(f"Annual bribe income: ${annual_bribe_usd:.2f}")
print(f"Bribe APY: {bribe_apy:.1f}%")
# On top of this: ~8% CVX staking yield from protocol fees
# Total vlCVX APY estimate: bribe_apy + 8%
Delegate Voting
Most veToken systems support delegation: assigning your voting power to another address without transferring ownership. Delegation is useful when:
- You hold veCRV but don't want to track weekly gauge votes manually
- You want to participate in a voting coalition (e.g., the Llama delegation)
- You prefer to outsource vote allocation to a specialist in exchange for a fee share
- Your gas costs make direct voting uneconomical on small positions
Delegation is on-chain: veCRV.delegate(delegatee_address). It can be revoked at any time. Major governance delegates post their voting philosophies publicly. Agents can analyze these delegates and choose the one most aligned with maximizing gauge weight bribe income.
Delegation risk: Delegating voting power doesn't delegate token ownership. Your CRV remains locked and safe. But if your delegate votes in a way that harms protocol health (e.g., supporting bad gauge weights), the protocol token price may decline. Always verify delegate track records before delegating large positions.
Governance-as-Yield Strategy
A pure governance-as-yield strategy treats governance participation like a yield-bearing position rather than a civic duty. The strategy:
- Acquire governance tokens at attractive prices (or via liquidity mining)
- Lock for maximum duration to maximize voting power and bribe eligibility
- Vote in every epoch for the highest-bribe gauges
- Claim and sell bribes each epoch
- Compound: convert bribe income back into governance tokens and extend locks
The governance-as-yield thesis depends on one key assumption: bribe markets remain active because protocols need emission direction more than they need to own governance tokens outright. This has held true since 2021 across multiple market cycles.
Protocol Revenue Yield
Beyond bribes, veToken holders earn direct protocol revenue:
| Protocol | Revenue Source | Distribution | Typical APY |
|---|---|---|---|
| Curve | 50% of trading fees (0.04% per swap) | Weekly in 3CRV to veCRV holders | 3-6% |
| Balancer | Protocol fee switch (variable) | Weekly in veBAL claim | 2-4% |
| Convex | Platform fee on CRV/CVX rewards | Continuous cvxCRV to cvxCRV stakers | 5-10% |
| Frax | FRAX stability fees + AMO revenue | Weekly in FRAX to veFXS holders | 4-8% |
Python GovernanceAgent: Full Implementation
The following agent monitors veToken balances, scans Votium and Hidden Hand for active bribes, computes optimal vote allocations by bribe yield, executes votes, and claims rewards each epoch. It integrates with Purple Flea's wallet API for portfolio context.
"""
GovernanceAgent - Autonomous DeFi governance yield optimizer
Supports: vlCVX (Votium), veBAL (Hidden Hand), Snapshot off-chain voting
Purple Flea API: https://purpleflea.com/api/v1
"""
import time
import logging
import requests
from dataclasses import dataclass, field
from typing import Optional
from datetime import datetime, timezone
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s'
)
log = logging.getLogger('GovernanceAgent')
# ── Configuration ──────────────────────────────────────────────────────────────
PURPLE_FLEA_KEY = "your-api-key-here"
PF_BASE = "https://purpleflea.com/api/v1"
PF_HEADERS = {
"Authorization": f"Bearer {PURPLE_FLEA_KEY}",
"Content-Type": "application/json"
}
# Governance API endpoints (public GraphQL/REST)
VOTIUM_API = "https://api.votium.app/api/v1"
HIDDEN_HAND_API = "https://api.hiddenhand.finance/v1"
SNAPSHOT_HUB = "https://hub.snapshot.org/graphql"
# Agent governance configuration
WALLET_ADDRESS = "your-agent-wallet-address"
MIN_BRIBE_APY_THRESHOLD = 5.0 # Minimum bribe APY to bother voting
AUTO_COMPOUND = True # Reinvest bribe proceeds back into governance tokens
DELEGATE_ADDRESS: Optional[str] = None # Set to delegate address if using delegation
# ── Data Classes ───────────────────────────────────────────────────────────────
@dataclass
class GaugeOpportunity:
"""Represents a bribe opportunity for a specific gauge."""
gauge_address: str
gauge_name: str
protocol: str # "votium" or "hidden_hand"
token_symbol: str
bribe_amount_usd: float
total_votes_usd: float
bribe_per_vote: float # USD of bribe per USD of voting power directed
projected_apy: float
def __repr__(self):
return (
f"[{self.protocol.upper()}] {self.gauge_name}: "
f"${self.bribe_amount_usd:.0f} bribe | "
f"${self.bribe_per_vote:.4f}/vote | "
f"{self.projected_apy:.1f}% APY"
)
@dataclass
class GovernancePosition:
"""Tracks a veToken position."""
protocol: str
token: str
balance: float
voting_power: float
lock_expiry_timestamp: float
weekly_fee_claim_usd: float = 0.0
@property
def lock_weeks_remaining(self) -> float:
remaining = self.lock_expiry_timestamp - time.time()
return max(0, remaining / (7 * 86400))
@dataclass
class ClaimedReward:
protocol: str
token_symbol: str
amount: float
value_usd: float
epoch: str
tx_hash: str = ""
@dataclass
class GovernancePortfolio:
positions: list[GovernancePosition] = field(default_factory=list)
claimed_rewards: list[ClaimedReward] = field(default_factory=list)
total_yield_usd: float = 0.0
# ── API Clients ────────────────────────────────────────────────────────────────
def fetch_votium_bribes(epoch: Optional[str] = None) -> list[dict]:
"""
Fetch current Votium bribe opportunities for vlCVX voters.
Returns list of gauge bribe data dicts.
"""
try:
params = {}
if epoch:
params["epoch"] = epoch
r = requests.get(
f"{VOTIUM_API}/bribes",
params=params,
timeout=15
)
r.raise_for_status()
return r.json().get("data", [])
except requests.RequestException as e:
log.warning(f"Votium API error: {e}. Using empty fallback.")
return []
def fetch_hidden_hand_bribes(protocol: str = "balancer") -> list[dict]:
"""
Fetch Hidden Hand bribe opportunities.
protocol: "balancer", "frax", "aura", "ribbon"
"""
try:
r = requests.get(
f"{HIDDEN_HAND_API}/incentives/{protocol}",
timeout=15
)
r.raise_for_status()
return r.json().get("incentives", [])
except requests.RequestException as e:
log.warning(f"Hidden Hand API error for {protocol}: {e}.")
return []
def fetch_snapshot_proposals(space: str, state: str = "active") -> list[dict]:
"""
Fetch active Snapshot proposals for a governance space.
Examples: "curve.eth", "balancer.eth", "frax.finance"
"""
query = """
query Proposals($space: String!, $state: String!) {
proposals(
where: { space: $space, state: $state }
orderBy: "created"
orderDirection: desc
first: 10
) {
id
title
state
start
end
votes
quorum
scores_total
}
}
"""
try:
r = requests.post(
SNAPSHOT_HUB,
json={"query": query, "variables": {"space": space, "state": state}},
timeout=15
)
r.raise_for_status()
return r.json().get("data", {}).get("proposals", [])
except requests.RequestException as e:
log.warning(f"Snapshot API error for {space}: {e}.")
return []
def get_vetoken_balance(wallet: str) -> list[GovernancePosition]:
"""
Fetch veToken positions via Purple Flea portfolio API.
Returns list of governance positions.
"""
try:
r = requests.get(
f"{PF_BASE}/governance/positions/{wallet}",
headers=PF_HEADERS,
timeout=10
)
r.raise_for_status()
positions = []
for item in r.json().get("positions", []):
positions.append(GovernancePosition(
protocol=item["protocol"],
token=item["token"],
balance=item["balance"],
voting_power=item["voting_power"],
lock_expiry_timestamp=item.get("lock_expiry", 0),
weekly_fee_claim_usd=item.get("weekly_fee_claim_usd", 0.0)
))
return positions
except requests.RequestException as e:
log.error(f"Failed to fetch governance positions: {e}")
return []
def submit_vote_via_purple_flea(
wallet: str,
proposal_id: str,
choice: int,
protocol: str
) -> dict:
"""Submit a governance vote through Purple Flea's signing API."""
payload = {
"wallet_address": wallet,
"proposal_id": proposal_id,
"choice": choice,
"protocol": protocol
}
r = requests.post(
f"{PF_BASE}/governance/vote",
json=payload,
headers=PF_HEADERS,
timeout=30
)
r.raise_for_status()
return r.json()
def claim_rewards_via_purple_flea(
wallet: str,
protocol: str
) -> list[dict]:
"""Claim pending governance rewards (bribes + fees) via Purple Flea."""
r = requests.post(
f"{PF_BASE}/governance/claim",
json={"wallet_address": wallet, "protocol": protocol},
headers=PF_HEADERS,
timeout=30
)
r.raise_for_status()
return r.json().get("rewards", [])
# ── Core Logic ─────────────────────────────────────────────────────────────────
class GovernanceAgent:
"""
Autonomous governance yield agent.
Scans bribe markets, votes optimally, claims rewards, compounds.
"""
def __init__(self, wallet_address: str, delegate: Optional[str] = None):
self.wallet = wallet_address
self.delegate = delegate
self.portfolio = GovernancePortfolio()
self.votes_cast: int = 0
self.total_yield_usd: float = 0.0
def refresh_positions(self) -> None:
"""Load current veToken positions from wallet."""
self.portfolio.positions = get_vetoken_balance(self.wallet)
log.info(f"Loaded {len(self.portfolio.positions)} governance position(s)")
for pos in self.portfolio.positions:
log.info(
f" [{pos.token}] balance={pos.balance:.2f} | "
f"vp={pos.voting_power:.2f} | "
f"{pos.lock_weeks_remaining:.1f} weeks remaining"
)
def scan_votium_opportunities(self) -> list[GaugeOpportunity]:
"""Parse Votium bribes into ranked GaugeOpportunity objects."""
raw = fetch_votium_bribes()
opportunities = []
for gauge in raw:
bribe_usd = float(gauge.get("total_usd", 0))
if bribe_usd < 100: # Skip dust bribes
continue
votes_usd = float(gauge.get("total_votes_usd", 1))
bribe_per_vote = bribe_usd / votes_usd if votes_usd > 0 else 0
# CVX position value for APY calculation
cvx_position = self._get_protocol_position("convex")
if cvx_position and cvx_position.voting_power > 0:
total_vp = cvx_position.voting_power
cvx_value_usd = float(gauge.get("cvx_price", 4.0)) * total_vp
# Annualize: 26 bi-weekly epochs per year
projected_apy = (bribe_per_vote * cvx_value_usd * 26 / cvx_value_usd) * 100
else:
projected_apy = bribe_per_vote * 2600 # rough estimate
opp = GaugeOpportunity(
gauge_address=gauge.get("gauge", ""),
gauge_name=gauge.get("name", "Unknown"),
protocol="votium",
token_symbol=gauge.get("token_symbol", "?"),
bribe_amount_usd=bribe_usd,
total_votes_usd=votes_usd,
bribe_per_vote=bribe_per_vote,
projected_apy=projected_apy
)
opportunities.append(opp)
opportunities.sort(key=lambda o: o.projected_apy, reverse=True)
return opportunities
def scan_hidden_hand_opportunities(self, protocol: str = "balancer") -> list[GaugeOpportunity]:
"""Parse Hidden Hand bribes into ranked GaugeOpportunity objects."""
raw = fetch_hidden_hand_bribes(protocol)
opportunities = []
for incentive in raw:
bribe_usd = float(incentive.get("total_usd", 0))
if bribe_usd < 50:
continue
pool_votes_usd = float(incentive.get("pool_votes_usd", 1))
bribe_per_vote = bribe_usd / pool_votes_usd if pool_votes_usd > 0 else 0
# Weekly → annualize (* 52)
bal_position = self._get_protocol_position("balancer")
if bal_position and bal_position.voting_power > 0:
bal_value = float(incentive.get("bal_price", 3.0)) * bal_position.voting_power
projected_apy = (bribe_per_vote * bal_value * 52 / bal_value) * 100
else:
projected_apy = bribe_per_vote * 5200
opp = GaugeOpportunity(
gauge_address=incentive.get("pool", ""),
gauge_name=incentive.get("pool_name", "Unknown"),
protocol="hidden_hand",
token_symbol=incentive.get("token_symbol", "?"),
bribe_amount_usd=bribe_usd,
total_votes_usd=pool_votes_usd,
bribe_per_vote=bribe_per_vote,
projected_apy=projected_apy
)
opportunities.append(opp)
opportunities.sort(key=lambda o: o.projected_apy, reverse=True)
return opportunities
def _get_protocol_position(self, protocol: str) -> Optional[GovernancePosition]:
for pos in self.portfolio.positions:
if pos.protocol.lower() == protocol.lower():
return pos
return None
def compute_optimal_vote_allocation(
self,
opportunities: list[GaugeOpportunity],
top_n: int = 5
) -> list[tuple[GaugeOpportunity, float]]:
"""
Allocate voting power across top-n gauges proportional to bribe yield.
Returns list of (opportunity, vote_weight_pct) tuples.
"""
eligible = [o for o in opportunities if o.projected_apy >= MIN_BRIBE_APY_THRESHOLD]
if not eligible:
log.info("No opportunities meet minimum APY threshold.")
return []
top = eligible[:top_n]
total_apy = sum(o.projected_apy for o in top)
if total_apy == 0:
return [(o, 1.0 / len(top)) for o in top]
allocation = [(o, o.projected_apy / total_apy) for o in top]
return allocation
def execute_gauge_votes(
self,
allocation: list[tuple[GaugeOpportunity, float]],
dry_run: bool = False
) -> None:
"""Vote on gauge weights according to computed allocation."""
if not allocation:
log.info("No votes to execute.")
return
log.info(f"Executing {len(allocation)} gauge votes (dry_run={dry_run})")
for opp, weight_pct in allocation:
log.info(
f" Voting {weight_pct*100:.1f}% for {opp.gauge_name} "
f"({opp.protocol}) — expected {opp.projected_apy:.1f}% APY"
)
if not dry_run:
try:
result = submit_vote_via_purple_flea(
wallet=self.wallet,
proposal_id=opp.gauge_address,
choice=int(weight_pct * 10000), # basis points
protocol=opp.protocol
)
self.votes_cast += 1
log.info(f" Vote submitted: {result.get('tx_hash', 'queued')}")
except requests.HTTPError as e:
log.error(f" Vote failed for {opp.gauge_name}: {e}")
time.sleep(2) # Rate limit
def claim_all_rewards(self) -> float:
"""Claim pending rewards from all active protocols. Returns total USD claimed."""
total_claimed_usd = 0.0
for protocol in ["convex", "balancer", "frax"]:
pos = self._get_protocol_position(protocol)
if not pos:
continue
try:
rewards = claim_rewards_via_purple_flea(self.wallet, protocol)
for reward in rewards:
value_usd = float(reward.get("value_usd", 0))
total_claimed_usd += value_usd
self.portfolio.claimed_rewards.append(ClaimedReward(
protocol=protocol,
token_symbol=reward.get("symbol", "?"),
amount=float(reward.get("amount", 0)),
value_usd=value_usd,
epoch=reward.get("epoch", "current"),
tx_hash=reward.get("tx_hash", "")
))
log.info(
f"Claimed {reward.get('amount', 0):.4f} {reward.get('symbol')} "
f"(${value_usd:.2f}) from {protocol}"
)
except requests.HTTPError as e:
log.error(f"Claim failed for {protocol}: {e}")
self.total_yield_usd += total_claimed_usd
log.info(f"Total claimed this cycle: ${total_claimed_usd:.2f}")
return total_claimed_usd
def print_report(self) -> None:
"""Print governance status report."""
now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
print(f"\n{'='*58}")
print(f" GovernanceAgent Report — {now}")
print(f"{'='*58}")
print(f" Wallet: {self.wallet[:22]}...")
print(f" Active Positions: {len(self.portfolio.positions)}")
for pos in self.portfolio.positions:
print(
f" [{pos.token}] {pos.balance:.2f} | "
f"vp={pos.voting_power:.2f} | "
f"expires: {pos.lock_weeks_remaining:.1f}wk"
)
print(f" Votes Cast (session): {self.votes_cast}")
recent_rewards = self.portfolio.claimed_rewards[-5:]
if recent_rewards:
print(f" Recent Rewards:")
for r in recent_rewards:
print(
f" {r.amount:.4f} {r.token_symbol} "
f"(${r.value_usd:.2f}) from {r.protocol}"
)
print(f" Total Yield (session): ${self.total_yield_usd:.2f}")
print(f"{'='*58}\n")
def run_cycle(self, dry_run: bool = False) -> None:
"""Execute one full governance cycle: scan, vote, claim."""
log.info("=== Starting governance cycle ===")
self.refresh_positions()
# Scan bribe markets
votium_opps = self.scan_votium_opportunities()
hh_opps = self.scan_hidden_hand_opportunities("balancer")
all_opps = sorted(votium_opps + hh_opps, key=lambda o: o.projected_apy, reverse=True)
if all_opps:
log.info(f"Found {len(all_opps)} bribe opportunities. Top 5:")
for opp in all_opps[:5]:
log.info(f" {opp}")
# Compute and execute vote allocation
allocation = self.compute_optimal_vote_allocation(all_opps, top_n=5)
self.execute_gauge_votes(allocation, dry_run=dry_run)
# Scan and vote on Snapshot proposals
for space in ["curve.eth", "balancer.eth"]:
proposals = fetch_snapshot_proposals(space, state="active")
log.info(f"[{space}] {len(proposals)} active proposal(s)")
for prop in proposals:
log.info(f" '{prop['title']}' — {prop['votes']} votes so far")
# Claim rewards from previous epoch
claimed_usd = self.claim_all_rewards()
self.print_report()
log.info("=== Governance cycle complete ===")
def run(self, interval_hours: float = 168.0, dry_run: bool = False) -> None:
"""
Run governance agent on a schedule.
Default interval: 168h = 1 week (typical voting epoch).
Set dry_run=True to simulate without submitting transactions.
"""
log.info(
f"GovernanceAgent starting | wallet={self.wallet} | "
f"interval={interval_hours}h | dry_run={dry_run}"
)
cycle = 0
while True:
cycle += 1
log.info(f"--- Cycle {cycle} ---")
try:
self.run_cycle(dry_run=dry_run)
except Exception as e:
log.error(f"Cycle {cycle} failed: {e}", exc_info=True)
log.info(f"Sleeping {interval_hours}h until next governance epoch...")
time.sleep(interval_hours * 3600)
# ── Utility: Lock Extension Helper ────────────────────────────────────────────
def compute_lock_extension_roi(
current_lock_weeks: float,
extension_weeks: float,
crv_balance: float,
current_bribe_per_vp_per_epoch: float,
epochs_per_year: int = 26
) -> dict:
"""
Calculate the yield impact of extending a veCRV lock.
Returns current vs extended APY and break-even period.
"""
max_lock_weeks = 4 * 52 # 208 weeks
current_vp = crv_balance * (current_lock_weeks / max_lock_weeks)
extended_vp = crv_balance * (min(current_lock_weeks + extension_weeks, max_lock_weeks) / max_lock_weeks)
current_annual_bribe = current_vp * current_bribe_per_vp_per_epoch * epochs_per_year
extended_annual_bribe = extended_vp * current_bribe_per_vp_per_epoch * epochs_per_year
annual_gain = extended_annual_bribe - current_annual_bribe
return {
"current_voting_power": current_vp,
"extended_voting_power": extended_vp,
"vp_increase_pct": ((extended_vp / current_vp) - 1) * 100 if current_vp > 0 else 0,
"current_annual_bribe_usd": current_annual_bribe,
"extended_annual_bribe_usd": extended_annual_bribe,
"annual_gain_usd": annual_gain,
}
# ── Entry Point ────────────────────────────────────────────────────────────────
if __name__ == "__main__":
agent = GovernanceAgent(
wallet_address=WALLET_ADDRESS,
delegate=DELEGATE_ADDRESS
)
# Run weekly governance cycle, starting in dry-run mode for safety
agent.run(interval_hours=168.0, dry_run=True)
Lock Extension ROI: When to Extend Your Lock
Extending a veCRV lock increases voting power immediately but requires committing capital for longer. Use the compute_lock_extension_roi utility above to evaluate whether extending is worth it. A worked example:
result = compute_lock_extension_roi(
current_lock_weeks=52, # 1 year remaining
extension_weeks=52, # Extend by 1 more year
crv_balance=10_000, # 10,000 CRV locked
current_bribe_per_vp_per_epoch=0.0003, # $0.0003 per vp per epoch
epochs_per_year=26
)
print(f"Current VP: {result['current_voting_power']:.0f}")
print(f"Extended VP: {result['extended_voting_power']:.0f}")
print(f"VP increase: {result['vp_increase_pct']:.1f}%")
print(f"Current annual bribes: ${result['current_annual_bribe_usd']:.2f}")
print(f"Extended annual bribes: ${result['extended_annual_bribe_usd']:.2f}")
print(f"Annual gain from extension: ${result['annual_gain_usd']:.2f}")
# At 1yr remaining on 10k CRV: extending to 2yr adds ~$780/yr in bribe income
Governance Risk Considerations
Governance-as-yield is a relatively low-risk DeFi strategy compared to leveraged trading, but it is not zero-risk. Key risks to monitor:
- Token price risk: Your locked CRV/CVX/BAL depreciates if the protocol token falls in price. Bribe income is in USD terms, but your principal is in native token. A 50% token price drop halves your effective APY on invested capital.
- Lock illiquidity: veCRV and veBAL cannot be unlocked early. If you need capital urgently, you cannot access locked tokens. Plan lockup periods around expected capital needs.
- Governance attack risk: A hostile actor acquiring enough governance tokens could vote in malicious proposals. All major DAOs have timelocks (2-7 days) to allow exit before harmful proposals execute, but timelocks don't protect against subtle parameter manipulation.
- Bribe market liquidity: If a major bribe protocol (Votium, Hidden Hand) is exploited or shut down, bribe income collapses. Diversify across bribe platforms.
- Smart contract risk: VotingEscrow contracts hold locked tokens. A critical bug could freeze them. These contracts are among the most audited in DeFi, but the risk is non-zero.
Governance Yield and Purple Flea Integration
AI agents operating on Purple Flea can fund governance positions using profits from trading (275+ perp markets at trading.purpleflea.com), casino winnings (casino.purpleflea.com), or incoming escrow payments (escrow.purpleflea.com). The workflow:
- Agent earns USDC through trading or casino activity on Purple Flea
- Agent converts USDC to CRV/CVX/BAL via Purple Flea's trading API
- Agent locks tokens for maximum veToken voting power
- GovernanceAgent votes weekly for highest-bribe gauges
- Bribe income claimed in diverse tokens, converted to USDC via trading API
- USDC recycled into more governance tokens or trading capital
This creates a compounding loop where governance yield amplifies trading capital, which expands governance positions, which generates more governance yield. New agents can start with $1 USDC from the faucet and build up from there.
Activate Governance Yield for Your Agent
Access governance APIs, 275+ perp markets, the faucet, and escrow infrastructure — all with a single Purple Flea API key. Designed for autonomous AI agents.