SDK Adapters
29 standalone Python adapters reading DeFi protocol smart contracts via JSON-RPC batch calls through archive RPC. Zero pip dependencies. Covers Aave, Morpho, Pendle, Euler, and 25 more.
The Yield Engine discovers positions across 15 chains. SDK adapters are the modules that do the actual contract reads — each one knows its protocol's quirks, correct APY fields, and data traps.
How adapters work
Each SDK adapter is a standalone Python module that queries protocol smart contracts directly via JSON-RPC. No pip dependencies -- stdlib only. No cached APIs, no third-party aggregator data. The number you see is computed from the contract state that exists right now.
HALT_RPC_UNAVAILABLE rather than falling back to cached or third-party data.
Protocol coverage
| Protocol | Category | Chains |
|---|---|---|
| Aave V3 | Lending | Ethereum, Arbitrum, Base, Optimism, Polygon |
| Morpho Blue | Curated lending | Ethereum, Base |
| Pendle | Yield tokenization | Ethereum, Arbitrum |
| Euler | Lending | Ethereum |
| Fluid | Lending | Ethereum |
| Compound | Lending | Ethereum, Base, Arbitrum |
| Curve / Convex | DEX LP / CRV boost | Ethereum, Arbitrum |
| Ethena | Synthetic dollar | Ethereum |
| Lido / Rocket Pool | Liquid staking | Ethereum |
| EigenLayer | Restaking | Ethereum |
| Beefy | Auto-compounding | 25+ chains |
| Yearn V3 | Yield aggregator | Ethereum |
| Jito / Kamino | Staking / Lending | Solana |
| Hyperliquid | Perp DEX | HyperEVM |
| + 15 more adapters across lending, DEX LP, perps, and restaking | ||
APY field mapping
Every protocol reports APY differently. Adapters normalize everything into a single effective_apy column:
net_apyTrap: raw
apy can be 50x higher than net yield
apy_implied (PT)Trap: No single
apy field exists at all
protocol_yieldTrap: Different meaning per entry type
Trap: Many pools report $0 TVL — garbage data
apyTrap: Can spike 500%+ on single-day data
supply_apyTrap: Usually correct, but verify reward tokens
Adapter anatomy
Every adapter follows the same structure:
class AaveAdapter:
"""Direct contract reads for Aave V3 markets."""
POOL_ADDRESS = "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2"
CHAINS = ["ethereum", "arbitrum", "base", "optimism", "polygon"]
def fetch_pools(self, chain: str) -> list[dict]:
# 1. Multicall getReservesList()
# 2. For each reserve: getReserveData()
# 3. Compute supply_apy from liquidity rate
# 4. Return normalized pool objects
...
def compute_apy(self, liquidity_rate: int) -> float:
# RAY = 10**27, convert on-chain rate to annualized %
return ((1 + liquidity_rate / 10**27) ** 31536000 - 1) * 100 eth_call.
Never sequential calls — always batched. Results are saved to DB after each batch, not at end.
Adapters produce raw position data. Before it reaches your board, every position goes through the verification pipeline — exploit cross-reference, TVL checks, APY consistency, and contract verification.