lido
Lido liquid staking protocol — stake ETH to receive stETH, wrap to wstETH for DeFi composability, manage withdrawal queue requests, read share rates and protocol state. Covers rebasing token pitfalls, 1-2 wei transfer rounding, wstETH/stETH conversion, and integration patterns for lending protocols and vaults.
Best use case
lido is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Lido liquid staking protocol — stake ETH to receive stETH, wrap to wstETH for DeFi composability, manage withdrawal queue requests, read share rates and protocol state. Covers rebasing token pitfalls, 1-2 wei transfer rounding, wstETH/stETH conversion, and integration patterns for lending protocols and vaults.
Teams using lido should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/lido/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How lido Compares
| Feature / Agent | lido | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Lido liquid staking protocol — stake ETH to receive stETH, wrap to wstETH for DeFi composability, manage withdrawal queue requests, read share rates and protocol state. Covers rebasing token pitfalls, 1-2 wei transfer rounding, wstETH/stETH conversion, and integration patterns for lending protocols and vaults.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Lido
Lido is the largest liquid staking protocol on Ethereum. Users deposit ETH and receive stETH, a rebasing token whose balance increases daily as staking rewards accrue. wstETH is the non-rebasing wrapper used in DeFi. The protocol manages a validator set, an oracle-reported share rate, and an on-chain withdrawal queue.
## What You Probably Got Wrong
- **stETH is a rebasing token** — Your stETH balance changes every day when the oracle reports. If you store a balance in a variable and check it later, it will differ. This breaks naive accounting in smart contracts. Use wstETH or track shares, not balances.
- **wstETH is NOT stETH** — wstETH is a non-rebasing ERC-20 wrapper around stETH shares. 1 wstETH != 1 stETH. The exchange rate drifts upward over time as rewards accumulate. Always convert via `stETH.getPooledEthByShares()` or `wstETH.stEthPerToken()`.
- **stETH/ETH is NOT 1:1** — There is a market rate on secondary markets (Curve, Uniswap) that can deviate from the protocol's internal rate, especially during high withdrawal demand or market stress. The 2022 depeg hit ~0.93.
- **stETH transfers lose 1-2 wei** — Due to shares-to-balance rounding, transferring your full `balanceOf` may leave 1-2 wei behind. The recipient may receive 1 wei less than `amount`. This is by design, not a bug. Never assert exact equality on stETH transfers.
- **Withdrawals are NOT instant** — The withdrawal queue processes requests in order. Finalization depends on validator exits and oracle reports. Typical wait: 1-5 days, but can be longer during high demand. You must request, wait for finalization, then claim in a separate tx.
- **Shares are the canonical unit** — stETH balances are derived from shares. `balanceOf(account) = shares[account] * totalPooledEther / totalShares`. All internal accounting uses shares. When integrating, think in shares.
- **`submit()` requires the referral address parameter** — The staking function is `submit(address _referral)` payable, not just a payable fallback. Pass `address(0)` if you have no referral.
## Quick Start
### Stake ETH via Lido (TypeScript)
```typescript
import { createPublicClient, createWalletClient, http, parseAbi, parseEther } from "viem";
import { mainnet } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
const LIDO = "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84" as const;
const LIDO_ABI = parseAbi([
"function submit(address _referral) external payable returns (uint256)",
"function balanceOf(address _account) external view returns (uint256)",
"function sharesOf(address _account) external view returns (uint256)",
"function getPooledEthByShares(uint256 _sharesAmount) external view returns (uint256)",
"function getSharesByPooledEth(uint256 _ethAmount) external view returns (uint256)",
"function getTotalPooledEther() external view returns (uint256)",
"function getTotalShares() external view returns (uint256)",
]);
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const publicClient = createPublicClient({
chain: mainnet,
transport: http(process.env.RPC_URL),
});
const walletClient = createWalletClient({
account,
chain: mainnet,
transport: http(process.env.RPC_URL),
});
async function stakeEth(amountEth: string) {
const { request } = await publicClient.simulateContract({
address: LIDO,
abi: LIDO_ABI,
functionName: "submit",
args: ["0x0000000000000000000000000000000000000000"],
value: parseEther(amountEth),
account,
});
const hash = await walletClient.writeContract(request);
const receipt = await publicClient.waitForTransactionReceipt({ hash });
if (receipt.status !== "success") throw new Error("Stake tx reverted");
return hash;
}
```
## Staking
### Submit ETH, Receive stETH (Solidity)
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface ILido {
function submit(address _referral) external payable returns (uint256 sharesAmount);
function balanceOf(address _account) external view returns (uint256);
function sharesOf(address _account) external view returns (uint256);
function getPooledEthByShares(uint256 _sharesAmount) external view returns (uint256);
function getSharesByPooledEth(uint256 _ethAmount) external view returns (uint256);
function transferShares(address _recipient, uint256 _sharesAmount) external returns (uint256);
}
contract LidoStaker {
ILido public constant LIDO = ILido(0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84);
event Staked(address indexed user, uint256 ethAmount, uint256 sharesReceived);
/// @notice Stake ETH via Lido. Returns shares minted, not stETH amount.
function stake() external payable returns (uint256 shares) {
if (msg.value == 0) revert ZeroDeposit();
shares = LIDO.submit{value: msg.value}(address(0));
emit Staked(msg.sender, msg.value, shares);
}
/// @notice Transfer stETH using shares to avoid rounding issues
/// @dev transferShares is exact — no 1-2 wei rounding loss
function transferSharesTo(address recipient, uint256 sharesAmount) external {
LIDO.transferShares(recipient, sharesAmount);
}
error ZeroDeposit();
}
```
### Wrap stETH to wstETH
wstETH holds a fixed number of stETH shares. Its balance does not rebase. Use wstETH in DeFi protocols, vaults, and any contract that stores balances.
```typescript
const WSTETH = "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0" as const;
const WSTETH_ABI = parseAbi([
"function wrap(uint256 _stETHAmount) external returns (uint256)",
"function unwrap(uint256 _wstETHAmount) external returns (uint256)",
"function getStETHByWstETH(uint256 _wstETHAmount) external view returns (uint256)",
"function getWstETHByStETH(uint256 _stETHAmount) external view returns (uint256)",
"function stEthPerToken() external view returns (uint256)",
"function tokensPerStEth() external view returns (uint256)",
]);
async function wrapSteth(stEthAmount: bigint) {
// Approve stETH spending by wstETH contract first
const approveHash = await walletClient.writeContract({
address: LIDO,
abi: parseAbi(["function approve(address spender, uint256 amount) external returns (bool)"]),
functionName: "approve",
args: [WSTETH, stEthAmount],
});
await publicClient.waitForTransactionReceipt({ hash: approveHash });
const { request } = await publicClient.simulateContract({
address: WSTETH,
abi: WSTETH_ABI,
functionName: "wrap",
args: [stEthAmount],
account,
});
const hash = await walletClient.writeContract(request);
const receipt = await publicClient.waitForTransactionReceipt({ hash });
if (receipt.status !== "success") throw new Error("Wrap tx reverted");
return hash;
}
async function unwrapWsteth(wstEthAmount: bigint) {
const { request } = await publicClient.simulateContract({
address: WSTETH,
abi: WSTETH_ABI,
functionName: "unwrap",
args: [wstEthAmount],
account,
});
const hash = await walletClient.writeContract(request);
const receipt = await publicClient.waitForTransactionReceipt({ hash });
if (receipt.status !== "success") throw new Error("Unwrap tx reverted");
return hash;
}
```
### Wrap/Unwrap in Solidity
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IWstETH {
function wrap(uint256 _stETHAmount) external returns (uint256);
function unwrap(uint256 _wstETHAmount) external returns (uint256);
function getStETHByWstETH(uint256 _wstETHAmount) external view returns (uint256);
function getWstETHByStETH(uint256 _stETHAmount) external view returns (uint256);
}
contract WstETHWrapper {
IERC20 public constant STETH = IERC20(0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84);
IWstETH public constant WSTETH = IWstETH(0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0);
/// @notice Wrap stETH to wstETH. Caller must approve this contract for stETH first.
function wrapStETH(uint256 stETHAmount) external returns (uint256 wstETHReceived) {
STETH.transferFrom(msg.sender, address(this), stETHAmount);
// Rounding may cause actual transferred amount to differ by 1-2 wei
uint256 actualBalance = STETH.balanceOf(address(this));
STETH.approve(address(WSTETH), actualBalance);
wstETHReceived = WSTETH.wrap(actualBalance);
IERC20(address(WSTETH)).transfer(msg.sender, wstETHReceived);
}
error InsufficientBalance();
}
```
## Withdrawals
Lido v2 introduced an on-chain withdrawal queue. Withdrawals mint an NFT (ERC-721) representing the request. Once finalized by the oracle, the NFT can be claimed for ETH.
### Request Withdrawal (TypeScript)
```typescript
const WITHDRAWAL_QUEUE = "0x889edC2eDab5f40e902b864aD4d7AdE8E412F9B1" as const;
const WITHDRAWAL_ABI = parseAbi([
"function requestWithdrawals(uint256[] _amounts, address _owner) external returns (uint256[])",
"function requestWithdrawalsWstETH(uint256[] _amounts, address _owner) external returns (uint256[])",
"function claimWithdrawals(uint256[] _requestIds, uint256[] _hints) external",
"function getWithdrawalStatus(uint256[] _requestIds) external view returns ((uint256 amountOfStETH, uint256 amountOfShares, address owner, uint256 timestamp, bool isFinalized, bool isClaimed)[])",
"function findCheckpointHints(uint256[] _requestIds, uint256 _firstIndex, uint256 _lastIndex) external view returns (uint256[])",
"function getLastCheckpointIndex() external view returns (uint256)",
"function getLastFinalizedRequestId() external view returns (uint256)",
]);
async function requestWithdrawal(stEthAmounts: bigint[]) {
// Approve WithdrawalQueue to spend stETH
const totalAmount = stEthAmounts.reduce((a, b) => a + b, 0n);
const approveHash = await walletClient.writeContract({
address: LIDO,
abi: parseAbi(["function approve(address spender, uint256 amount) external returns (bool)"]),
functionName: "approve",
args: [WITHDRAWAL_QUEUE, totalAmount],
});
await publicClient.waitForTransactionReceipt({ hash: approveHash });
const { request } = await publicClient.simulateContract({
address: WITHDRAWAL_QUEUE,
abi: WITHDRAWAL_ABI,
functionName: "requestWithdrawals",
args: [stEthAmounts, account.address],
account,
});
const hash = await walletClient.writeContract(request);
const receipt = await publicClient.waitForTransactionReceipt({ hash });
if (receipt.status !== "success") throw new Error("Withdrawal request reverted");
return hash;
}
```
### Check Withdrawal Status and Claim
```typescript
async function getWithdrawalStatus(requestIds: bigint[]) {
const statuses = await publicClient.readContract({
address: WITHDRAWAL_QUEUE,
abi: WITHDRAWAL_ABI,
functionName: "getWithdrawalStatus",
args: [requestIds],
});
return statuses.map((s, i) => ({
requestId: requestIds[i],
amountOfStETH: s.amountOfStETH,
isFinalized: s.isFinalized,
isClaimed: s.isClaimed,
owner: s.owner,
}));
}
async function claimWithdrawals(requestIds: bigint[]) {
const lastCheckpointIndex = await publicClient.readContract({
address: WITHDRAWAL_QUEUE,
abi: WITHDRAWAL_ABI,
functionName: "getLastCheckpointIndex",
});
const hints = await publicClient.readContract({
address: WITHDRAWAL_QUEUE,
abi: WITHDRAWAL_ABI,
functionName: "findCheckpointHints",
args: [requestIds, 1n, lastCheckpointIndex],
});
const { request } = await publicClient.simulateContract({
address: WITHDRAWAL_QUEUE,
abi: WITHDRAWAL_ABI,
functionName: "claimWithdrawals",
args: [requestIds, hints],
account,
});
const hash = await walletClient.writeContract(request);
const receipt = await publicClient.waitForTransactionReceipt({ hash });
if (receipt.status !== "success") throw new Error("Claim tx reverted");
return hash;
}
```
## Reading Protocol State
### Share Rate, Total Pooled Ether, APR Estimation
```typescript
async function getProtocolState() {
const [totalPooledEther, totalShares] = await Promise.all([
publicClient.readContract({
address: LIDO,
abi: LIDO_ABI,
functionName: "getTotalPooledEther",
}),
publicClient.readContract({
address: LIDO,
abi: LIDO_ABI,
functionName: "getTotalShares",
}),
]);
// Share rate: how much ETH one share is worth (18 decimals)
const shareRate = (totalPooledEther * 10n ** 18n) / totalShares;
return { totalPooledEther, totalShares, shareRate };
}
async function convertWstethToSteth(wstEthAmount: bigint): Promise<bigint> {
return publicClient.readContract({
address: WSTETH,
abi: WSTETH_ABI,
functionName: "getStETHByWstETH",
args: [wstEthAmount],
});
}
async function convertStethToWsteth(stEthAmount: bigint): Promise<bigint> {
return publicClient.readContract({
address: WSTETH,
abi: WSTETH_ABI,
functionName: "getWstETHByStETH",
args: [stEthAmount],
});
}
```
### Reading Share Rate in Solidity
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface ILido {
function getTotalPooledEther() external view returns (uint256);
function getTotalShares() external view returns (uint256);
function getPooledEthByShares(uint256 _sharesAmount) external view returns (uint256);
function getSharesByPooledEth(uint256 _ethAmount) external view returns (uint256);
}
contract LidoReader {
ILido public constant LIDO = ILido(0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84);
/// @notice Returns ETH value of one stETH share, scaled to 18 decimals
function getShareRate() external view returns (uint256) {
return LIDO.getPooledEthByShares(1e18);
}
/// @notice Convert stETH amount to underlying shares
function ethToShares(uint256 ethAmount) external view returns (uint256) {
return LIDO.getSharesByPooledEth(ethAmount);
}
/// @notice Convert shares to stETH amount
function sharesToEth(uint256 sharesAmount) external view returns (uint256) {
return LIDO.getPooledEthByShares(sharesAmount);
}
}
```
## DeFi Integration
### wstETH as Collateral (Aave/Compound Pattern)
When integrating wstETH in lending protocols or vaults, always use wstETH (not stETH) to avoid rebasing accounting complexity.
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IWstETH {
function getStETHByWstETH(uint256 _wstETHAmount) external view returns (uint256);
}
/// @notice Simplified vault accepting wstETH as collateral
/// @dev Uses wstETH to avoid rebasing — balanceOf is stable between oracle reports
contract WstETHVault {
IERC20 public constant WSTETH = IERC20(0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0);
IWstETH public constant WSTETH_RATE = IWstETH(0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0);
mapping(address => uint256) public deposits;
event Deposited(address indexed user, uint256 wstETHAmount);
event Withdrawn(address indexed user, uint256 wstETHAmount);
function deposit(uint256 wstETHAmount) external {
WSTETH.transferFrom(msg.sender, address(this), wstETHAmount);
deposits[msg.sender] += wstETHAmount;
emit Deposited(msg.sender, wstETHAmount);
}
function withdraw(uint256 wstETHAmount) external {
if (deposits[msg.sender] < wstETHAmount) revert InsufficientDeposit();
deposits[msg.sender] -= wstETHAmount;
WSTETH.transfer(msg.sender, wstETHAmount);
emit Withdrawn(msg.sender, wstETHAmount);
}
/// @notice Get the ETH value of a user's wstETH collateral
function getCollateralValueInEth(address user) external view returns (uint256) {
return WSTETH_RATE.getStETHByWstETH(deposits[user]);
}
error InsufficientDeposit();
}
```
### Oracle Considerations for wstETH Pricing
wstETH price = wstETH/stETH exchange rate * stETH/ETH rate * ETH/USD price. Protocols typically use:
1. **Chainlink wstETH/ETH feed** — Available on mainnet at `0x536218f9E9Eb48863970252233c8F271f554C2d0`. Combines the protocol rate with market data.
2. **On-chain rate from wstETH contract** — `wstETH.stEthPerToken()` gives the protocol exchange rate. This does NOT reflect secondary market deviations.
3. **Dual oracle approach** — Use the Chainlink feed as primary, fall back to the on-chain rate with bounds checking.
```typescript
const WSTETH_ETH_FEED = "0x536218f9E9Eb48863970252233c8F271f554C2d0" as const;
const AGGREGATOR_ABI = parseAbi([
"function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)",
"function decimals() external view returns (uint8)",
]);
async function getWstethEthPrice() {
const [roundData, decimals] = await Promise.all([
publicClient.readContract({
address: WSTETH_ETH_FEED,
abi: AGGREGATOR_ABI,
functionName: "latestRoundData",
}),
publicClient.readContract({
address: WSTETH_ETH_FEED,
abi: AGGREGATOR_ABI,
functionName: "decimals",
}),
]);
const [, answer, , updatedAt] = roundData;
if (answer <= 0n) throw new Error("Invalid wstETH/ETH price");
const now = BigInt(Math.floor(Date.now() / 1000));
// wstETH/ETH feed heartbeat: 86400s
if (now - updatedAt > 86400n) throw new Error("Stale wstETH/ETH price");
return { answer, decimals };
}
```
## Contract Addresses
> **Last verified:** 2025-05-01
### Ethereum Mainnet
| Contract | Address |
|----------|---------|
| Lido (stETH proxy) | `0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84` |
| wstETH | `0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0` |
| WithdrawalQueueERC721 | `0x889edC2eDab5f40e902b864aD4d7AdE8E412F9B1` |
| Lido Accounting Oracle | `0x852deD011285fe67063a08005c71a85690503Cee` |
| Lido Execution Layer Rewards Vault | `0x388C818CA8B9251b393131C08a736A67ccB19297` |
### wstETH on L2s
| Chain | wstETH Address |
|-------|----------------|
| Arbitrum | `0x5979D7b546E38E9Ab8F24815DCa0E57E830D4df6` |
| Optimism | `0x1F32b1c2345538c0c6f582fCB022739c4A194Ebb` |
| Base | `0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452` |
| Polygon | `0x03b54A6e9a984069379fae1a4fC4dBAE93B3bCCD` |
### Chainlink Price Feeds for stETH/wstETH
| Pair | Mainnet Address |
|------|----------------|
| wstETH/ETH | `0x536218f9E9Eb48863970252233c8F271f554C2d0` |
| stETH/ETH | `0x86392dC19c0b719886221c78AB11eb8Cf5c52812` |
| stETH/USD | `0xCfE54B5cD566aB89272946F602D76Ea879CAb4a8` |
## Error Handling
| Error / Symptom | Cause | Fix |
|-----------------|-------|-----|
| `STAKE_LIMIT` revert on `submit()` | Daily staking limit reached | Wait for next day or check `getCurrentStakeLimit()` before submitting |
| Transfer leaves 1-2 wei dust | Shares-to-balance rounding in rebasing math | Use `transferShares()` for exact share transfers; never assert exact stETH balance equality |
| `REQUEST_AMOUNT_TOO_SMALL` | Withdrawal amount below minimum (100 wei) | Ensure each withdrawal request is >= 100 wei of stETH |
| `REQUEST_AMOUNT_TOO_LARGE` | Single request exceeds max (1000 stETH) | Split large withdrawals into multiple requests of <= 1000 stETH each |
| Withdrawal claim reverts | Request not yet finalized, or already claimed | Check `getWithdrawalStatus()` — wait for `isFinalized == true`, verify `isClaimed == false` |
| `findCheckpointHints` returns empty | Invalid range for first/last index | Use `1` as first index and `getLastCheckpointIndex()` as last |
| wstETH `wrap()` returns less than expected | stETH balance changed between approval and wrap due to rebase | Approve slightly more or use the actual balance after transfer |
| `ALLOWANCE_EXCEEDED` on wrap/withdrawal | Insufficient stETH approval for wstETH or WithdrawalQueue contract | Call `approve()` with the exact or higher amount before wrap/request |
## Security Considerations
### Rebasing Accounting
stETH balances change on every oracle report (typically daily). Smart contracts that store stETH balances in mappings will have stale values. Two safe patterns:
1. **Use wstETH** — Non-rebasing. Balance is stable. This is the correct choice for vaults, collateral, and any stored-balance pattern.
2. **Track shares** — Use `sharesOf()` and `getPooledEthByShares()` instead of `balanceOf()`. Shares are the invariant unit.
### The 1-2 Wei Rounding Issue
stETH `transfer(to, amount)` converts `amount` to shares (rounding down), then converts back to balance for the recipient (rounding down again). The sender's balance decreases by `amount`, but the recipient may receive `amount - 1` or `amount - 2` wei. This is inherent to the rebasing design.
Implications:
- Never use `require(balanceAfter - balanceBefore == amount)` with stETH
- Use `transferShares()` when exact amounts matter
- Tolerance of 2 wei on stETH balance checks
### Oracle Manipulation Risks
- The stETH/ETH Chainlink feed reflects market price, which can deviate from the protocol rate during market stress
- The on-chain `stEthPerToken()` rate is controlled by the Lido oracle — it can only change once per oracle report cycle and is bounded by sanity checks
- For highest security, cross-check the Chainlink feed against the on-chain rate and revert if deviation exceeds a threshold (e.g., 5%)
```solidity
/// @notice Revert if Chainlink wstETH/ETH deviates too far from on-chain rate
function validateOracleRate(int256 chainlinkAnswer, uint8 feedDecimals) internal view {
uint256 onchainRate = IWstETH(0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0).stEthPerToken();
uint256 normalizedChainlink = feedDecimals <= 18
? uint256(chainlinkAnswer) * 10 ** (18 - feedDecimals)
: uint256(chainlinkAnswer) / 10 ** (feedDecimals - 18);
uint256 deviation = normalizedChainlink > onchainRate
? normalizedChainlink - onchainRate
: onchainRate - normalizedChainlink;
// 5% max deviation threshold
if (deviation * 100 / onchainRate > 5) revert OracleDeviation();
}
```
### Integration Checklist
1. Use wstETH (not stETH) in any contract that stores balances
2. Never assert exact stETH transfer amounts — allow 2 wei tolerance
3. Check Chainlink feed staleness with per-feed heartbeat
4. Cross-validate oracle price against on-chain rate for critical paths
5. Handle withdrawal queue delays in UX — show estimated wait time
6. Test with a forked mainnet (`anvil --fork-url`) to verify rebase behavior
## References
- [Lido Docs](https://docs.lido.fi)
- [Lido Deployed Contracts](https://docs.lido.fi/deployed-contracts/)
- [stETH Integration Guide](https://docs.lido.fi/guides/steth-integration-guide)
- [wstETH on L2s](https://docs.lido.fi/token-guides/wsteth-bridging-guide)
- [Lido GitHub](https://github.com/lidofinance)
- [Withdrawal Queue Docs](https://docs.lido.fi/contracts/withdrawal-queue-erc721)
- [Chainlink stETH/ETH Feed](https://data.chain.link/ethereum/mainnet/crypto-eth/steth-eth)Related Skills
metaplex
Complete Metaplex Protocol guide for Solana NFTs and digital assets. Covers Core (next-gen NFTs), Token Metadata, Bubblegum (compressed NFTs), Candy Machine, Genesis (token launches), MPL-Hybrid, Inscriptions, DAS API, and the Umi framework. The single source of truth for all Metaplex integrations.
megaeth
MegaETH real-time blockchain development — instant receipts, eth_sendRawTransactionSync, MegaNames, Warren sequencer, ERC-7710 delegations, and sub-millisecond storage optimization.
marginfi
Complete guide for Marginfi - Solana's decentralized lending protocol for lending, borrowing, leveraged positions(looping) and flash loans. Covers account creation, deposits, borrows, repayments, withdrawals, flash loans, and leveraged positions using the @mrgnlabs/marginfi-client-v2 SDK.
maker
MakerDAO / Sky protocol -- Maker Vaults (CDPs) for DAI minting against collateral, DAI Savings Rate (DSR), Liquidation 2.0 with Dutch auctions, MKR governance, and Sky rebranding (USDS/SKY tokens). Covers DssProxyActions, Vat core accounting, Jug stability fees, and Spark Protocol integration.
magicblock
Complete guide for MagicBlock Ephemeral Rollups - high-performance Solana execution with sub-10ms latency, gasless transactions, and Solana Plugins. Use when building real-time games, high-frequency trading, or any application requiring ultra-low latency on Solana.
lulo
Complete guide for Lulo - Solana's premier lending aggregator. Covers API integration for deposits, withdrawals, balance queries, Protected/Boosted deposits, Custom deposits, and automated yield optimization across Kamino, Drift, MarginFi, and Jupiter.
light-protocol
Complete guide for Light Protocol on Solana - includes ZK Compression for rent-free compressed tokens and PDAs using zero-knowledge proofs, and the Light Token Program for high-performance token standard (200x cheaper than SPL). Covers TypeScript SDK, JSON RPC methods, and complete integration patterns.
layerzero
LayerZero V2 cross-chain messaging — OApp framework, OFT (Omnichain Fungible Token), DVN configuration, executor setup, message options, and cross-chain deployment patterns. Covers lz-oapp contracts, EndpointV2 interface, message lifecycle, and security configuration across Ethereum, Arbitrum, Base, Optimism, and Polygon.
kamino
Complete guide for Kamino Finance - Solana's leading DeFi protocol for lending, borrowing, liquidity management, and leverage trading. Covers klend-sdk (lending), kliquidity-sdk (automated liquidity strategies), scope-sdk (oracle aggregator), multiply/leverage operations, vaults, and obligation orders.
jupiter
Comprehensive guidance for integrating Jupiter APIs (Ultra Swap, Lend, Perps, Trigger, Recurring, Tokens, Price, Portfolio, Prediction Markets, Send, Studio, Lock, Routing). Use for endpoint selection, integration flows, error handling, and production hardening.
hyperliquid
Hyperliquid perpetual futures DEX — order placement (market/limit/trigger/TWAP), position management, leverage up to 50x, WebSocket streaming, vault strategies, and L1 architecture. REST and WebSocket APIs with wallet signing authentication. Python SDK and TypeScript patterns.
hyperlane
Hyperlane permissionless interoperability — Mailbox messaging, Interchain Security Modules (ISM), Warp Routes for token bridging, hooks, interchain accounts, and permissionless deployment to any chain. Covers Hyperlane SDK, contract interfaces, and custom security configurations.