ARD: Staking Rewards Optimizer
Architectural Overview
Pattern
Multi-Source Data Aggregation with Optimization Engine
The skill aggregates staking data from multiple sources, calculates normalized metrics, runs optimization algorithms, and produces actionable recommendations.
Architecture Diagram
┌─────────────────────────────────────────────────────────────────────┐
│ STAKING REWARDS OPTIMIZER │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ DeFiLlama │ │StakingRewards│ │ CoinGecko │ │
│ │ Pools API │ │ API │ │ Prices API │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ └─────────────┬─────┴───────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ DATA AGGREGATOR │ │
│ │ - Fetch staking pools by protocol/chain │ │
│ │ - Normalize APY/APR across sources │ │
│ │ - Cache with TTL for rate limiting │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────┼─────────────┐ │
│ ▼ ▼ ▼ │
│ ┌───────────┐ ┌───────────┐ ┌───────────────┐ │
│ │ Metrics │ │ Risk │ │ Optimizer │ │
│ │Calculator │ │ Assessor │ │ Engine │ │
│ └───────────┘ └───────────┘ └───────────────┘ │
│ │ │ │ │
│ └─────────────┼─────────────┘ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ FORMATTER │ │
│ │ - Table format with rankings │ │
│ │ - JSON for programmatic use │ │
│ │ - Detailed recommendations report │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Workflow
1. Parse Request
├── Extract target assets (ETH, ATOM, DOT, etc.)
├── Parse current positions if provided
└── Identify risk tolerance and constraints
2. Fetch Data
├── Query DeFiLlama for liquid staking pools
├── Query StakingRewards for native staking
├── Get current prices for USD calculations
└── Apply rate limiting and caching
3. Calculate Metrics
├── Normalize APY/APR across protocols
├── Calculate net yield after fees
├── Project returns for time horizons
└── Generate risk scores
4. Optimize (if positions provided)
├── Run allocation algorithm
├── Apply user constraints
└── Generate rebalancing recommendations
5. Format Output
├── Generate comparison tables
├── Create risk assessments
└── Produce actionable recommendations
Progressive Disclosure Strategy
Level 1: Quick Summary
Top Staking Opportunities for ETH:
1. Rocket Pool (rETH): 4.2% APY - Low Risk
2. Lido (stETH): 4.0% APY - Low Risk
3. Frax (sfrxETH): 5.1% APY - Medium Risk
Level 2: Comparison Table
Full table with all metrics: APY, fees, lock-up, risk score, TVL
Level 3: Detailed Analysis
- Risk breakdown per protocol
- Fee structure analysis
- Implementation guide
- Position projections
Tool Permission Strategy
Allowed Tools
allowed-tools: Read, Write, Edit, Grep, Glob, Bash(crypto:staking-*)
Scoped Access
Read: Config files, reference documentationWrite: Output reports, cache filesEdit: Update local configurationsBash(crypto:staking-*): Run staking analysis scripts only
Rationale
- No network tools needed - scripts handle API calls
- Scoped Bash prevents arbitrary command execution
- File access limited to plugin directory context
Directory Structure
skills/optimizing-staking-rewards/
├── PRD.md # Product requirements
├── ARD.md # This document
├── SKILL.md # Core instructions
├── scripts/
│ ├── staking_optimizer.py # Main CLI entry point
│ ├── staking_fetcher.py # API data fetching
│ ├── metrics_calculator.py # Yield and cost calculations
│ ├── risk_assessor.py # Protocol risk scoring
│ ├── optimizer_engine.py # Allocation optimization
│ └── formatters.py # Output formatting
├── references/
│ ├── errors.md # Error handling guide
│ ├── examples.md # Usage examples
│ └── implementation.md # Implementation details
└── config/
└── settings.yaml # Configuration options
API Integration Architecture
DeFiLlama Pools API
# Endpoint: https://yields.llama.fi/pools
# Filter for staking pools
def fetch_staking_pools():
response = requests.get("https://yields.llama.fi/pools")
pools = response.json()["data"]
# Filter for staking-related pools
staking_keywords = ["staking", "liquid staking", "lsd", "stake"]
staking_pools = [
p for p in pools
if any(kw in p.get("project", "").lower() or
kw in p.get("symbol", "").lower()
for kw in staking_keywords)
or p.get("project") in KNOWN_STAKING_PROTOCOLS
]
return staking_pools
Known Staking Protocols (DeFiLlama)
| Protocol | Project Key | Assets |
|---|---|---|
| Lido | lido | stETH, stSOL, stMATIC |
| Rocket Pool | rocket-pool | rETH |
| Frax | frax-ether | sfrxETH |
| Marinade | marinade-finance | mSOL |
| Ankr | ankr | ankrETH |
| Coinbase | coinbase-wrapped-staked-eth | cbETH |
| Benqi | benqi-staked-avax | sAVAX |
StakingRewards API (Alternative Source)
# For native staking rates
# https://www.stakingrewards.com/api/
NATIVE_STAKING = {
"ethereum": {"apr": 4.0, "unbonding": "variable"},
"cosmos": {"apr": 19.0, "unbonding": "21 days"},
"polkadot": {"apr": 15.0, "unbonding": "28 days"},
"solana": {"apr": 7.0, "unbonding": "~2 days"},
"avalanche": {"apr": 8.0, "unbonding": "2 weeks"},
"near": {"apr": 9.5, "unbonding": "36-48 hours"},
}
Data Flow Architecture
Input Processing
User Request: "Compare staking for 10 ETH"
│
▼
┌───────────────┐
│ Parse Request │
└───────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌────────┐ ┌──────────┐ ┌─────────┐
│ Asset │ │ Amount │ │ Options │
│ "ETH" │ │ 10 │ │ {} │
└────────┘ └──────────┘ └─────────┘
Data Aggregation
def aggregate_staking_data(asset: str) -> List[StakingOption]:
"""
Aggregate staking options from multiple sources.
"""
options = []
# 1. Liquid staking from DeFiLlama
liquid_pools = fetch_defillama_pools(asset)
for pool in liquid_pools:
options.append(StakingOption(
protocol=pool["project"],
type="liquid",
apy=pool["apy"],
tvl=pool["tvlUsd"],
source="defillama"
))
# 2. Native staking rates
if asset.upper() in NATIVE_STAKING:
native = NATIVE_STAKING[asset.upper()]
options.append(StakingOption(
protocol=f"{asset} Native Staking",
type="native",
apy=native["apr"],
unbonding=native["unbonding"],
source="native"
))
# 3. Sort by APY descending
options.sort(key=lambda x: x.apy, reverse=True)
return options
Metrics Calculation
@dataclass
class StakingMetrics:
gross_apy: float # Advertised APY
protocol_fee: float # Protocol's cut
net_apy: float # After protocol fees
gas_cost_usd: float # Estimated staking gas
effective_apy: float # Net APY minus gas amortized
risk_score: int # 1-10 (10 = safest)
tvl_usd: float # Total value locked
unbonding_days: int # Lock-up period
projected_1y: float # Value after 1 year
def calculate_metrics(option, amount_usd, gas_price_gwei=30):
"""Calculate comprehensive metrics for a staking option."""
# Protocol fees (varies by protocol)
PROTOCOL_FEES = {
"lido": 0.10, # 10% of rewards
"rocket-pool": 0.14, # 14% of rewards
"frax-ether": 0.10, # 10% of rewards
"native": 0.0, # No protocol fee
}
fee_rate = PROTOCOL_FEES.get(option.protocol_key, 0.10)
net_apy = option.apy * (1 - fee_rate)
# Gas cost for staking transaction (~100k gas for liquid staking)
gas_cost_eth = (100_000 * gas_price_gwei) / 1e9
gas_cost_usd = gas_cost_eth * get_eth_price()
# Effective APY accounting for gas (amortized over 1 year)
if amount_usd > 0:
gas_drag = (gas_cost_usd / amount_usd) * 100
effective_apy = net_apy - gas_drag
else:
effective_apy = net_apy
return StakingMetrics(
gross_apy=option.apy,
protocol_fee=fee_rate,
net_apy=net_apy,
gas_cost_usd=gas_cost_usd,
effective_apy=max(0, effective_apy),
risk_score=calculate_risk_score(option),
tvl_usd=option.tvl,
unbonding_days=option.unbonding_days,
projected_1y=amount_usd * (1 + net_apy / 100)
)
Risk Scoring
def calculate_risk_score(option) -> int:
"""
Calculate risk score from 1-10 (10 = safest).
Factors:
- Audit status (30%)
- Time in production (25%)
- TVL size (20%)
- Protocol reputation (15%)
- Validator diversity (10%)
"""
score = 10.0
# Audit status
if not option.audited:
score -= 3.0
elif option.audit_age_months > 12:
score -= 1.0
# Time in production
if option.age_months < 3:
score -= 3.0
elif option.age_months < 12:
score -= 1.5
# TVL size (larger = safer)
if option.tvl_usd < 10_000_000:
score -= 2.0
elif option.tvl_usd < 100_000_000:
score -= 1.0
# Protocol reputation
REPUTABLE_PROTOCOLS = {"lido", "rocket-pool", "coinbase", "frax-ether"}
if option.protocol_key in REPUTABLE_PROTOCOLS:
score += 0.5
# Validator diversity (for liquid staking)
if option.validator_count and option.validator_count < 10:
score -= 1.0
return max(1, min(10, round(score)))
Error Handling Strategy
Error Categories
| Category | Examples | Recovery Strategy |
|---|---|---|
| API Errors | Timeout, rate limit | Retry with backoff, use cache |
| Data Errors | Invalid APY, missing fields | Skip record, log warning |
| User Input | Invalid asset, bad format | Clear error message |
| Calculation | Division by zero, overflow | Return safe default |
Implementation
class StakingError(Exception):
"""Base exception for staking optimizer."""
pass
class DataFetchError(StakingError):
"""Failed to fetch data from source."""
pass
class InvalidAssetError(StakingError):
"""Asset not supported."""
pass
def safe_fetch_with_retry(url, max_retries=3, timeout=15):
"""Fetch with exponential backoff."""
for attempt in range(max_retries):
try:
response = requests.get(url, timeout=timeout)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
if attempt == max_retries - 1:
raise DataFetchError(f"Failed after {max_retries} attempts: {e}")
time.sleep(2 ** attempt) # 1, 2, 4 seconds
Composability & Stacking
Works With
- market-price-tracker: Get current asset prices for USD calculations
- gas-fee-optimizer: Optimize staking transaction timing
- crypto-portfolio-tracker: Track staking positions over time
- defi-yield-optimizer: Compare staking vs other DeFi yields
Integration Pattern
# Example: Combined staking + yield analysis
def comprehensive_yield_analysis(asset, amount):
# Get staking options
staking = staking_optimizer.analyze(asset, amount)
# Get LP/farming options (from yield optimizer)
farming = yield_optimizer.analyze(asset, amount)
# Combine and rank
all_options = staking + farming
all_options.sort(key=lambda x: x.risk_adjusted_return, reverse=True)
return all_options
Performance Considerations
Caching Strategy
CACHE_TTL = {
"staking_pools": 300, # 5 minutes (APYs change slowly)
"prices": 60, # 1 minute (prices change fast)
"gas_prices": 30, # 30 seconds (gas is volatile)
"protocol_metadata": 3600, # 1 hour (rarely changes)
}
@lru_cache(maxsize=100)
def get_cached_pools(cache_key: str) -> List[dict]:
"""Memory-cached pool data."""
pass
Rate Limiting
| Source | Limit | Strategy |
|---|---|---|
| DeFiLlama | ~100/min | Batch requests, cache aggressively |
| CoinGecko | 30/min | Cache prices for 1 minute |
| StakingRewards | Varies | Use fallback static data |
Testing Strategy
Unit Tests
def test_apy_conversion():
"""Test APR to APY conversion."""
assert apr_to_apy(10, 365) == pytest.approx(10.52, rel=0.01)
assert apy_to_apr(10.52, 365) == pytest.approx(10, rel=0.01)
def test_risk_score_boundaries():
"""Risk score should be 1-10."""
for option in MOCK_OPTIONS:
score = calculate_risk_score(option)
assert 1 <= score <= 10
def test_metrics_calculation():
"""Test comprehensive metrics calculation."""
metrics = calculate_metrics(MOCK_LIDO_OPTION, 10000)
assert metrics.net_apy < metrics.gross_apy # Fees reduce yield
assert metrics.effective_apy <= metrics.net_apy # Gas reduces further
Integration Tests
def test_defillama_integration():
"""Test real DeFiLlama API."""
pools = fetch_defillama_pools("ETH")
assert len(pools) > 0
assert all("apy" in p for p in pools)
def test_end_to_end():
"""Test full analysis flow."""
result = analyze_staking("ETH", amount=10)
assert "recommendations" in result
assert len(result["options"]) > 0
Security Considerations
Data Validation
- Validate APY ranges (0-1000%)
- Sanitize protocol names
- Verify TVL is positive
No Private Data
- No wallet connections
- No private key handling
- No transaction signing
Output Safety
- Escape special characters in output
- Validate file paths for writing
- No arbitrary code execution