Phase 3 — veForge Gauge Spine

// GAUGE VOTES THAT STEER THE MONEY · NEVER TOUCH IT · VYPER 0.4 · LIVE ON SEPOLIA

ve-governance has one failure mode nobody likes to say out loud: if a vote can move a parameter, and that parameter touches funds, a vote can touch funds. Phase 3 wires veForge gauge weights into two real levers — where Keep deploys capital and where TARE surplus emissions go — without ever letting a vote do anything directly. Every vote result is read by a small permissionless router that can take exactly one bounded action, on value the protocol already released.

Vyper 0.4VotingEscrowGaugeController Permissionless RoutersHypothesisSepolia
⚠ Sepolia testnet · fuzzed · self-reviewed · NOT audited. Curve-style gauge weights activate at the next weekly epoch (Thu 00:00 UTC); until that boundary the routers correctly read zero weight and no-op.
“No vote, keeper, or admin may move a solvent user’s collateral, mint on a bad price, or alter live debt.”
3Permissionless Routers
1.0→1.4sTARE PPS · one tx
80/20Savings / Emissions
≤30%Emission Hard Cap

Router 1 — gauge weights → Keep allocations

GaugeWeightRouter is the spine. Anyone can poke() it. It reads each strategy's gauge weight, normalizes the set to basis points summing to the vault's deployable budget, and calls Keep's set_allocations — the only function it ever calls.

Router 2 — surplus → savings, emissions as a junior slice

SurplusSplitter becomes the engine's savings_vault: it receives the surplus skim and divides it — a senior slice to sTARE, a capped slice to emissions.

Router 3 — emissions → gauges → voters

EmissionRouter splits its TARE pot across gauges by relative weight and funds each gauge's bucket in the already-hardened BribeDistributor via deposit_bribe. Voters claim pro-rata.

Invariants, fuzzed

Each router ships with a Hypothesis stateful machine driving the full pipeline — locks, votes, kills, time-travel, funding, apply — asserting:

Totals: GaugeWeightRouter 15+1 · SurplusSplitter 14+1 · EmissionRouter 22+1 — all green, all compile clean.

Traced on-chain

We fed the SurplusSplitter 50 TARE and called the permissionless split(). On-chain result: 40 TARE landed in sTARE — its share price stepped from 1.0000 to 1.4000 in one transaction — and 10 TARE routed to the emission sink. Exactly the 80/20 the cap promises; the splitter held nothing afterward. A self-review drove four hardening fixes before deploy (owner-set emission schedule, delayed 180-day escape hatch, reentrancy guard, buffer-bounds check), all re-fuzzed.

Sepolia addresses (chain 11155111)

ContractAddress
sTARE0xCc7A41B795b433d41481980b9c5b84e34541107d
SurplusSplitter0x21e9f5D5F43e1Ffc438A0c548C86B4ADAa2d05ec
VotingEscrow (veTARE)0x2a62b14E973009dCA6110c76BAda2d1D90429f7B
GaugeController0xA28c124827bbe1e6d5fce394d04F7Dff8550c369
GaugeWeightRouter0x718C6a2D2c3737ee85084837dD730B3dabb0e227
BribeDistributor0x64452025DbDbaA862C8A7137deB05A1FC11316B3
EmissionRouter0xE6527f01B2488BAcBe4A5b30f898D5f17014c23A