Strategy

Lido stETH Strategies for AI Agents: Yield Stacking and Restaking

March 6, 2026 19 min read Purple Flea Research

Lido's liquid staking token — stETH — is the cornerstone of DeFi yield stacking for AI agents. From basic ETH staking to restaking via EigenLayer, Pendle yield splitting, and Curve LP compounding, this guide covers the complete stETH yield stack with Python automation patterns.

1. stETH vs wstETH Mechanics

Understanding the distinction between stETH and wstETH is fundamental before building any yield strategy. They represent the same underlying position but with very different accounting models that affect smart contract compatibility and gas efficiency.

$28B
stETH TVL
3.8%
Base Staking APR
30%
ETH Staked via Lido
10%
Protocol Fee

stETH: Rebasing Token

stETH is a rebasing ERC-20 token. Every 24 hours (at 12:00 UTC), when Ethereum validators earn consensus and execution layer rewards, stETH balances in all wallets automatically increase. If you hold 10 stETH and the daily rebase adds 0.01%, you will hold 10.001 stETH the next day without any transaction.

This means stETH balances in smart contracts that don't explicitly handle rebasing will silently accumulate tokens. Many DeFi protocols cannot accommodate this. Agents using stETH directly in automated strategies must account for balance drift between rebase events.

wstETH: Wrapped Non-Rebasing Token

wstETH wraps stETH into a non-rebasing format. Instead of balance increasing, the exchange rate (wstETH per ETH) increases over time. This makes it compatible with all DeFi protocols and much safer for agents to track positions programmatically.

from web3 import Web3

WSTETH_ADDRESS = "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"
STETH_ADDRESS = "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84"

WSTETH_ABI = [
    {"name": "wrap", "type": "function", "inputs": [{"type": "uint256"}], "outputs": [{"type": "uint256"}]},
    {"name": "unwrap", "type": "function", "inputs": [{"type": "uint256"}], "outputs": [{"type": "uint256"}]},
    {"name": "stEthPerToken", "type": "function", "inputs": [], "outputs": [{"type": "uint256"}]},
    {"name": "tokensPerStEth", "type": "function", "inputs": [], "outputs": [{"type": "uint256"}]},
]

def get_wsteth_exchange_rate(w3: Web3) -> float:
    """Get current stETH per wstETH (increases over time)."""
    wsteth = w3.eth.contract(address=WSTETH_ADDRESS, abi=WSTETH_ABI)
    rate = wsteth.functions.stEthPerToken().call()
    return rate / 10**18  # e.g., 1.1234 stETH per wstETH

def wrap_steth(w3: Web3, account, steth_amount: int) -> str:
    """Convert stETH to wstETH for DeFi protocol use."""
    wsteth = w3.eth.contract(address=WSTETH_ADDRESS, abi=WSTETH_ABI)
    tx = wsteth.functions.wrap(steth_amount).build_transaction({
        'from': account.address,
        'gas': 120000,
        'nonce': w3.eth.get_transaction_count(account.address)
    })
    signed = account.sign_transaction(tx)
    tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction)
    return tx_hash.hex()

When to Use Each

Use CaseBest TokenReason
Aave v3 collateralwstETHNative support, no rebase issues
Curve stETH/ETH poolstETHPool is built around rebasing stETH
MakerDAO vaultwstETHstETH-B vault accepts wstETH-via-proxy
EigenLayer restakingBothNative ETH restaking or wstETH LST restaking
Pendle yield splittingwstETHPendle wraps to PT/YT, wstETH preferred
Holding for staking yieldstETHDirect daily rebase; no wrap gas cost

2. Aave/Compound stETH Collateral

Using wstETH as collateral on Aave v3 allows agents to borrow USDC, DAI, or other assets against their staking position. The borrowed assets can then be deployed at yields that exceed the Aave borrow rate, creating a leveraged staking carry trade.

Aave wstETH Carry Trade Example

stETH staking APR+3.8%
wstETH collateral on AaveLTV 80%
Borrow USDC at 5.5% APY, deploy at 8.0%+2.5% on 80%
Net carry (0.8 × 2.5%)+2.0%
Total combined APR~5.8%
from web3 import Web3

AAVE_V3_POOL = "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2"
WSTETH = "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"
USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"

class AaveLidoStrategy:
    """Leveraged stETH strategy via Aave v3."""

    def __init__(self, w3: Web3, account):
        self.w3 = w3
        self.account = account
        self.pool = w3.eth.contract(address=AAVE_V3_POOL, abi=AAVE_POOL_ABI)

    def supply_wsteth(self, amount: int) -> str:
        """Supply wstETH as collateral to Aave v3."""
        tx = self.pool.functions.supply(
            WSTETH, amount, self.account.address, 0
        ).build_transaction({
            'from': self.account.address,
            'gas': 280000,
            'nonce': self.w3.eth.get_transaction_count(self.account.address)
        })
        return self._sign_and_send(tx)

    def borrow_usdc(self, amount: int, rate_mode: int = 2) -> str:
        """Borrow USDC against wstETH collateral (rate_mode=2 variable)."""
        tx = self.pool.functions.borrow(
            USDC, amount, rate_mode, 0, self.account.address
        ).build_transaction({
            'from': self.account.address,
            'gas': 250000,
            'nonce': self.w3.eth.get_transaction_count(self.account.address)
        })
        return self._sign_and_send(tx)

    async def get_health_factor(self) -> float:
        """Get position health factor (>1.0 = safe)."""
        data = self.pool.functions.getUserAccountData(
            self.account.address
        ).call()
        # Returns: totalCollateralBase, totalDebtBase, availableBorrowsBase,
        #          currentLiquidationThreshold, ltv, healthFactor
        health_factor = data[5] / 10**18
        return health_factor

3. Curve stETH/ETH Pool LP

The Curve stETH/ETH pool is one of the deepest liquidity pools in DeFi and a primary venue for stETH yield stacking. LPs earn trading fees from ETH/stETH conversions plus CRV emissions from the gauge.

LP Mechanics

The pool maintains a near-1:1 balance between ETH and stETH. When LPs provide both assets, they earn:

from web3 import Web3

CURVE_STETH_POOL = "0xDC24316b9AE028F1497c275EB9192a3Ea0f67022"
CURVE_STETH_LP = "0x06325440D014e39736583c165C2963BA99fAf14E"
CURVE_STETH_GAUGE = "0x182B723a58739a9c974cFDB385ceaDb237453c28"

class CurveStEthLP:
    """Manage Curve stETH/ETH LP position."""

    def __init__(self, w3: Web3, account):
        self.w3 = w3
        self.account = account
        self.pool = w3.eth.contract(address=CURVE_STETH_POOL, abi=CURVE_POOL_ABI)
        self.gauge = w3.eth.contract(address=CURVE_STETH_GAUGE, abi=GAUGE_ABI)

    def add_liquidity(
        self,
        eth_amount: int,
        steth_amount: int,
        min_lp_out: int
    ) -> str:
        """Add ETH + stETH to Curve pool and receive LP tokens."""
        tx = self.pool.functions.add_liquidity(
            [eth_amount, steth_amount], min_lp_out
        ).build_transaction({
            'from': self.account.address,
            'value': eth_amount,
            'gas': 350000,
            'nonce': self.w3.eth.get_transaction_count(self.account.address)
        })
        return self._sign_and_send(tx)

    def stake_lp_in_gauge(self, lp_amount: int) -> str:
        """Stake LP tokens in gauge to earn CRV emissions."""
        tx = self.gauge.functions.deposit(lp_amount).build_transaction({
            'from': self.account.address,
            'gas': 180000,
            'nonce': self.w3.eth.get_transaction_count(self.account.address)
        })
        return self._sign_and_send(tx)

    def get_claimable_crv(self) -> int:
        """Get unclaimed CRV balance."""
        return self.gauge.functions.claimable_tokens(
            self.account.address
        ).call()

    def get_virtual_price(self) -> float:
        """Get LP token virtual price (increases with fees)."""
        price = self.pool.functions.get_virtual_price().call()
        return price / 10**18

Note: Consider depositing LP tokens into Convex Finance (cvxCRV) to receive boosted CRV + CVX rewards without needing to lock veCRV yourself. Convex typically boosts CRV yields by 2.5x for protocols with significant veCRV holdings.

4. EigenLayer Restaking for Additional Yield

EigenLayer allows stETH and wstETH holders to "restake" their liquid staking tokens (LSTs) to provide cryptoeconomic security to additional protocols called Actively Validated Services (AVSs). Restakers earn additional yield on top of base Lido staking rewards.

LST Restaking via EigenLayer

Agents deposit wstETH into an EigenLayer StrategyManager contract to receive restaking points. These points eventually convert to real yield from AVS fees once AVS mainnet launches complete.

EIGENLAYER_STRATEGY_MANAGER = "0x858646372CC42E1A627fcE94aa7A7033e7CF075A"
WSTETH_STRATEGY = "0x7CA911E83dabf90C90dD3De5411a10F1A6112184"

class EigenLayerRestaker:
    """Deposit wstETH into EigenLayer for restaking yield."""

    def __init__(self, w3: Web3, account):
        self.w3 = w3
        self.account = account
        self.strategy_mgr = w3.eth.contract(
            address=EIGENLAYER_STRATEGY_MANAGER,
            abi=STRATEGY_MANAGER_ABI
        )

    def deposit_into_strategy(self, token: str, strategy: str, amount: int) -> str:
        """Deposit LST token into EigenLayer strategy."""
        tx = self.strategy_mgr.functions.depositIntoStrategy(
            strategy, token, amount
        ).build_transaction({
            'from': self.account.address,
            'gas': 220000,
            'nonce': self.w3.eth.get_transaction_count(self.account.address)
        })
        return self._sign_and_send(tx)

    def get_shares(self, strategy: str) -> int:
        """Get agent's share balance in a given strategy."""
        return self.strategy_mgr.functions.stakerStrategyShares(
            self.account.address, strategy
        ).call()

    def queue_withdrawal(
        self,
        strategies: list,
        tokens: list,
        shares: list,
        withdrawer: str,
        nonce: int
    ) -> str:
        """Queue withdrawal from EigenLayer (7-day delay)."""
        tx = self.strategy_mgr.functions.queueWithdrawal(
            strategies, tokens, shares, withdrawer, True
        ).build_transaction({
            'from': self.account.address,
            'gas': 300000,
            'nonce': nonce
        })
        return self._sign_and_send(tx)

Liquidity Warning: EigenLayer restaked positions are subject to a 7-day withdrawal delay. Agents must account for this illiquidity period in their strategy design — do not restake assets that may be needed for quick liquidation defense in other positions.

5. Pendle stETH Yield Splitting

Pendle Finance allows agents to split yield-bearing assets like wstETH into two components:

Agent Strategies with Pendle

Fixed-rate strategy: Buy PT-wstETH at a discount and hold to maturity. If 1 wstETH PT trades at 0.94 wstETH equivalent with 6 months to maturity, that's ~12% annualized fixed yield — regardless of what Lido's APR does during that period.

Yield speculation strategy: Buy YT-wstETH when you believe Ethereum staking rewards will increase (e.g., during high MEV periods or network upgrades). YT is leveraged long on yield.

LP strategy: Provide liquidity to Pendle AMMs to earn swap fees from traders splitting and combining PT/YT. Generally yields 8-15% APY including Pendle emissions.

class PendleStrategyAgent:
    """Pendle Finance integration for stETH yield optimization."""

    def __init__(self, w3: Web3, account, market_address: str):
        self.w3 = w3
        self.account = account
        self.market = w3.eth.contract(
            address=market_address, abi=PENDLE_MARKET_ABI
        )
        self.router = w3.eth.contract(
            address="0x00000000005BBB0EF59571E58418F9a4357b68A0",
            abi=PENDLE_ROUTER_ABI
        )

    def get_pt_implied_apy(self) -> float:
        """Calculate implied fixed APY from PT current price."""
        state = self.market.functions.readState(True).call()
        last_ln_implied_rate = state[4]
        implied_rate = (last_ln_implied_rate / 10**18)
        import math
        return (math.exp(implied_rate) - 1) * 100

    def should_buy_pt(
        self,
        implied_apy: float,
        current_steth_apy: float,
        yield_outlook: str = "neutral"
    ) -> bool:
        """Determine if PT purchase makes sense."""
        spread = implied_apy - current_steth_apy
        if yield_outlook == "bearish":  # expect yields to fall
            return spread > 0.5  # any premium to current yield is good
        elif yield_outlook == "bullish":
            return spread > 3.0  # need large spread to justify locking
        return spread > 1.5  # neutral: moderate spread threshold

6. stETH/ETH Depeg Monitoring

stETH has historically traded at a small discount to ETH (0.1-0.5% normally), with severe depegs during the June 2022 crisis (stETH fell to 0.933 ETH). Agents that monitor depeg events can either arbitrage the spread or defensively reduce stETH exposure before cascading liquidations.

import asyncio
from datetime import datetime

class StEthDepegMonitor:
    """Monitor stETH/ETH peg and alert on significant deviations."""

    def __init__(self, curve_pool: Web3.eth.contract, alert_threshold: float = 0.005):
        self.pool = curve_pool
        self.alert_threshold = alert_threshold  # 0.5% depeg
        self.depeg_history = []

    def get_steth_price(self) -> float:
        """Get stETH price in ETH from Curve pool."""
        # dy for 1 stETH → ETH
        eth_out = self.pool.functions.get_dy(1, 0, 10**18).call()
        return eth_out / 10**18

    async def monitor(self, callback, poll_interval: int = 60):
        """Continuously monitor depeg, call callback on alert."""
        while True:
            price = self.get_steth_price()
            depeg = 1.0 - price
            timestamp = datetime.utcnow().isoformat()

            self.depeg_history.append({'time': timestamp, 'price': price, 'depeg': depeg})
            if len(self.depeg_history) > 1440:  # keep 24h of 1m samples
                self.depeg_history.pop(0)

            if depeg > self.alert_threshold:
                await callback({
                    'type': 'DEPEG_ALERT',
                    'price': price,
                    'depeg_pct': depeg * 100,
                    'severity': 'HIGH' if depeg > 0.02 else 'MEDIUM'
                })
            await asyncio.sleep(poll_interval)

7. Slashing Risk Assessment

Lido validators are subject to slashing if they double-sign blocks or go offline for extended periods. While Lido has not experienced a significant slashing event to date, agents must quantify this tail risk in their position sizing models.

Key slashing risk parameters agents should monitor:

Risk Sizing: For conservative agents, cap stETH allocation at 40% of total portfolio ETH. For aggressive strategies (e.g., multiple Pendle YT positions), use smaller tranches and maintain escape velocity to exit positions within 24 hours.

8. Python LidoAgent

The following LidoAgent class orchestrates the full yield stacking pipeline: stake ETH via Lido, wrap to wstETH, deploy into the optimal venue based on current market conditions, monitor health, and rebalance automatically.

import asyncio
import logging
from dataclasses import dataclass, field
from typing import Dict, Optional
from web3 import AsyncWeb3

logger = logging.getLogger('LidoAgent')

@dataclass
class LidoConfig:
    eth_to_stake: int           # in wei
    strategies: list = field(default_factory=lambda: ['curve', 'aave', 'eigenlayer'])
    rebalance_interval: int = 3600  # seconds
    depeg_exit_threshold: float = 0.01  # 1% depeg triggers exit
    health_factor_min: float = 1.3     # Aave health factor threshold
    eigenlayer_max_pct: float = 0.3    # max 30% restaked (illiquid)

class LidoAgent:
    """Complete Lido yield stacking agent."""

    def __init__(self, w3: AsyncWeb3, account, config: LidoConfig):
        self.w3 = w3
        self.account = account
        self.config = config
        self.positions: Dict[str, int] = {}
        self.depeg_monitor = StEthDepegMonitor(alert_threshold=config.depeg_exit_threshold)

    async def initialize(self):
        """Stake ETH and distribute across strategies."""
        # Step 1: Submit ETH to Lido
        steth_amount = await self._stake_eth(self.config.eth_to_stake)
        logger.info(f"Staked {self.config.eth_to_stake/1e18:.4f} ETH, received {steth_amount/1e18:.4f} stETH")

        # Step 2: Wrap portion to wstETH
        wrap_amount = int(steth_amount * 0.7)  # keep 30% as stETH for Curve
        wsteth_amount = await self._wrap_steth(wrap_amount)

        # Step 3: Distribute across strategies
        eigenlayer_amount = int(wsteth_amount * self.config.eigenlayer_max_pct)
        aave_amount = wsteth_amount - eigenlayer_amount
        curve_steth = steth_amount - wrap_amount

        await asyncio.gather(
            self._deploy_to_aave(aave_amount),
            self._deploy_to_eigenlayer(eigenlayer_amount),
            self._deploy_to_curve(curve_steth),
        )
        logger.info("Positions initialized across all strategies")

    async def run(self):
        """Main agent loop: monitor, rebalance, and harvest."""
        asyncio.create_task(self.depeg_monitor.monitor(self._handle_depeg_alert))
        while True:
            try:
                await self._check_health()
                await self._harvest_rewards()
                await self._rebalance_if_needed()
            except Exception as e:
                logger.error(f"Agent error: {e}")
            await asyncio.sleep(self.config.rebalance_interval)

    async def _handle_depeg_alert(self, alert: dict):
        """React to stETH depeg events."""
        if alert['severity'] == 'HIGH':
            logger.warning(f"Severe depeg: stETH at {alert['price']:.4f} ETH. Exiting Aave position.")
            await self._emergency_exit_aave()
        else:
            logger.info(f"Minor depeg: {alert['depeg_pct']:.2f}%. Monitoring.")

    async def _check_health(self):
        """Check Aave health factor and top-up if needed."""
        hf = await self._get_aave_health_factor()
        if hf < self.config.health_factor_min:
            repay_amount = await self._calc_repay_to_hf(1.5)
            await self._repay_aave(repay_amount)
            logger.warning(f"Health factor {hf:.3f} below min. Repaid {repay_amount/1e6:.2f} USDC")

    async def _harvest_rewards(self):
        """Claim and compound CRV and other rewards."""
        crv_claimable = await self._get_crv_claimable()
        if crv_claimable > 100e18:  # min 100 CRV to harvest
            await self._claim_crv()
            await self._compound_crv_to_steth(crv_claimable)
            logger.info(f"Harvested and compounded {crv_claimable/1e18:.2f} CRV")

9. Full Yield Stack Analysis

Here is a complete view of the yield stack agents can build on top of stETH:

Maximum stETH Yield Stack (Annualized)

Base Lido staking APR+3.8%
Curve stETH/ETH LP fees (30% allocation)+1.2%
CRV gauge emissions (30% allocation)+0.9%
Aave wstETH supply APR (40% allocation)+0.3%
Aave USDC borrow → deploy carry (40% LTV 80%)+2.0%
EigenLayer restaking points yield (30% allocation)+0.6%
Total Blended APR (pre-gas)~8.8%

Gas costs will reduce this by 0.3-0.8% depending on position size and Ethereum gas prices. Positions under $50K equivalent may not be economical at current gas rates.

10. Risk Framework

Building a stETH yield stack exposes agents to multiple simultaneous risk vectors. A robust risk framework assigns weights to each:

RiskLikelihoodImpactMitigation
stETH depeg >5%LowHighDepeg monitor + auto-exit Aave
Lido validator slashingVery LowMediumLido insurance fund + diversify LSTs
Aave liquidationMediumHighHF monitoring + 1.4+ target HF
CRV price crashMediumLowAuto-compound CRV to stETH immediately
EigenLayer slashingLowMediumCap restaking at 30% of position
Smart contract exploitVery LowVery HighDiversify protocols; use audited contracts only

Agent Advantage: AI agents monitoring positions 24/7 at block-level granularity have a significant safety advantage over human DeFi participants. Automated health checks and instant reaction to depeg events dramatically reduce liquidation risk.

Run Your Agent on Purple Flea

Purple Flea provides financial primitives for AI agents: casino for entertainment, perpetuals for speculation, wallet for asset custody, domains for identity, faucet for test funds, and escrow for trustless payments.

Explore Purple Flea