DeFi protocols spend millions on token buybacks. Most execute them the dumbest way possible: fixed amounts on fixed schedules, fully predictable, trivially front-run, buying hardest exactly when they should be buying least.
Aerodrome showed there's a better way. Their Public Goods Fund runs what they call a "programmatic market-aware buyback model", and the numbers back it up: 150M+ AERO acquired and max-locked, 17% of circulating supply absorbed, executed across varying market conditions without blowing up slippage or telegraphing entries. When AERO dropped to $0.40, the PGF stepped in with 940K tokens. When the market was running, they pulled back. Simple in theory, hard to execute well.
This project is an open-source implementation of that idea. A buyback execution engine that reads market conditions and adapts.
The engine runs a loop. Each cycle:
-
Classifies the market regime. Is the token in accumulation (nobody's paying attention), markup (price is running), distribution (smart money is exiting), or markdown (everything's dumping)? It figures this out from price trend, volume behavior, volatility, and divergence signals.
-
Adjusts the buyback budget. Accumulation regime? Buy aggressively, you're getting cheap fills in quiet markets. Markup? Pull back, the market is doing your job. Distribution? Nearly pause, because absorbing sells at tops is lighting money on fire. Markdown? Buy steadily, the protocol has an infinite time horizon.
-
Targets a VWAP discount. The engine only executes when the current price is below a rolling 30-day VWAP minus a discount that scales with volatility. No chasing.
-
Splits orders across exchanges. Routes proportionally based on liquidity depth and fill quality. Prefers CoW Protocol for larger chunks because batch auctions give you MEV protection for free. Adds timing jitter so execution isn't predictable.
-
Logs everything. Regime classification with signal breakdown, budget adjustment rationale, routing decisions, fill quality. Full auditability.
A protocol spending $50M/year on buybacks is running a treasury operation. The difference between smart execution and naive execution compounds every single day. Consider:
Naive approach: buy $137K/day, every day, same time, same venue. Bots see it coming. You're buying in markup when the market would reprice itself anyway. You're barely buying in accumulation when liquidity is cheapest. You get front-run constantly. Maybe 15-30% of your budget is wasted on slippage and adverse selection.
Market-aware approach: the same $50M/year, but concentrated in periods where your marginal dollar has the most impact. Accumulation phases where you're the only buyer. Prices below VWAP where you're getting a structural discount. Split across venues with MEV protection. Same budget, much better execution.
Aerodrome figured this out. Aave is spending $50M/year on buybacks. Uniswap just activated its fee switch with $590M in the treasury. Every protocol with a buyback program needs this.
The regime classifier is built on pluggable scoring functions. Four ship by default: price trend (SMA slope), volume trend (short vs. long window), volatility (realized vol), and divergence (price-volume disagreement). Each scores every regime independently, gets weighted, and the classifier picks the winner.
The interface is simple. Want to add funding rate data? Whale wallet concentration? Governance proposal sentiment? Implement ScoreFunc, pass it to the classifier. No changes to the engine, router, or anything else. The scoring function is the unit of extensibility.
CoW Protocol is the primary venue. Batch auctions mean your orders get coincidence-of-wants matching and MEV protection without paying for it. The exchange layer is a Go interface, and the router handles splitting across multiple venues weighted by liquidity depth.
The architecture supports any exchange that can quote liquidity and fill an order. CoW ships as the real implementation. A mock exchange ships for offline testing and demos. Hyperliquid and 1inch are stubbed out for when you need CLOB depth or aggregator routing.
The regime determines how aggressively the engine spends relative to the daily budget baseline:
| Regime | Multiplier | Rationale |
|---|---|---|
| Accumulation | 1.5-2.0x | Low attention, cheap liquidity. Buy hard. |
| Markup | 0.3-0.5x | Market is doing your job. Don't chase. |
| Distribution | 0.0-0.2x | Absorbing sells at tops is wasteful. |
| Markdown | 0.8-1.0x | Protocol has infinite horizon. Buy the fear. |
On a $50M/year budget, the daily baseline is ~$137K. In accumulation that becomes ~$240K. In distribution it drops to ~$14K. Same annual spend, radically different allocation.
Requires Go 1.21+ and CGO (for SQLite).
go build -o buyback ./cmd/buyback# Full cycle — classify regime, calculate budget, simulate execution
./buyback run --config configs/example.yaml --dry-run
# Just check what regime a token is in
./buyback regime --token AAVE --quote USDC
# See the execution plan without doing anything
./buyback plan --config configs/example.yaml
# Run continuously, evaluating every 15 minutes
./buyback run --config configs/example.yaml --interval 15m --dry-runEverything is config-driven. One YAML file. See configs/example.yaml for the full annotated reference.
| Field | Description |
|---|---|
token |
Asset to buy back (e.g., "UNI") |
coingecko_id |
CoinGecko coin ID for market data (e.g., "uniswap") |
quote_asset |
Asset used to pay (e.g., "USDC") |
token_address |
ERC-20 contract address of the buy token |
quote_asset_address |
ERC-20 contract address of the sell/quote token |
annual_budget_usd |
Total annual buyback budget in USD |
regime_multipliers |
Per-regime spending multiplier ranges |
base_discount |
Target VWAP discount (0.02 = 2%) |
vol_scaling_factor |
How much volatility widens the discount |
max_slippage_bps |
Maximum slippage in basis points |
order_split_count |
Number of sub-orders per cycle |
execution_jitter |
Timing randomness (0.0–1.0) |
min_execution_size |
Smallest order size in USD |
prefer_batch_auction |
Prefer batch-auction venues (CoW Protocol) |
exchange_weight_overrides |
Manual routing weight overrides |
coingecko_api_url |
CoinGecko API base URL (default: public API; set to pro endpoint if you have a key) |
cow_api_url |
CoW Protocol API base URL (chain-specific: /mainnet, /xdai, /sepolia) |
wallet_address |
Ethereum address for exchange interactions |
db_path |
SQLite database path for persistence |
Every cycle, order, fill, and regime classification is persisted to SQLite. Query it from the CLI:
# Execution history for the last 7 days
./buyback history --token AAVE --since 7d
# Regime classification history
./buyback regimes --token AAVE --last 30
# Fill quality stats — average slippage, fill rate, MEV saved
./buyback stats --token AAVE --since 30dWhat a single cycle looks like with --dry-run:
[REGIME] token=AAVE regime=accumulation confidence=0.10
signals: price_trend=markup, volume_trend=accumulation, volatility=accumulation, divergence=distribution
[BUDGET] daily=136986.30 regime_mult=1.7500 liquidity_score=1.0000 adjusted=239726.03
[VWAP] 30d_vwap=181.2345 discount_target=0.0315 target_price=178.3827 current_price=100.0000 status=BELOW_TARGET
[ROUTE] exchanges=[cow:68%, dex:32%] reason="cow preferred for batch auction, 1.3x boost"
[FILL] exchange=cow amount_usd=80371.30 avg_price=0.9995
[FILL] exchange=dex amount_usd=71917.81 avg_price=0.9992
[FILL] exchange=cow amount_usd=80371.30 avg_price=0.9995
[SUMMARY] cycle_total=232660.42 target=239726.03 fill_rate=97.05% avg_slippage_bps=0
The engine classified accumulation (low vol, declining volume), applied a 1.75x multiplier to the daily budget, confirmed price was below the VWAP discount target, routed 68% to CoW (batch auction preference) and 32% to the secondary venue, and filled 97% of the adjusted budget.