DeFi Position Tracker
Monitor and analyze DeFi positions across protocols and chains. Track LP (liquidity provider) positions, staking rewards, yield farming returns, impermanent...
Like a lobster shell, security has layers — review code before you run it.
License
SKILL.md
DeFi Position Tracker
Monitor LP positions, staking rewards, and yield farming across protocols. Calculate impermanent loss, track cost basis, and feed structured data to crypto-tax-agent for tax reporting.
Supported Protocols
| Category | Protocols |
|---|---|
| DEX LP | Uniswap v2/v3, Curve, Balancer, Velodrome, PancakeSwap |
| Lending | Aave v2/v3, Compound v2/v3, Euler, Morpho |
| Liquid Staking | Lido (stETH), Rocket Pool (rETH), Frax (sfrxETH) |
| Yield Farming | Convex, Yearn, Beefy, Pendle |
| Bridges/xChain | LayerZero positions, Stargate LPs |
Supported Chains
Ethereum, Arbitrum, Optimism, Base, Polygon, BSC, Avalanche, Fantom, Solana (via Birdeye/Helius).
Core Workflows
1. Full Portfolio Snapshot
Pull all active DeFi positions for a wallet address using DeBank Pro API (most comprehensive):
# DeBank Pro API — full protocol positions
curl -s "https://pro-openapi.debank.com/v1/user/all_complex_protocol_list?id=0xYOUR_WALLET&chain_ids=eth,arb,op,base,matic" \
-H "AccessKey: YOUR_DEBANK_API_KEY" | jq '.[] | {protocol: .name, net_usd_value: .net_usd_value, positions: .portfolio_item_list}'
Free alternative — Zapper API:
curl -s "https://api.zapper.xyz/v2/balances?addresses[]=0xYOUR_WALLET&networks[]=ethereum&networks[]=arbitrum" \
-H "Authorization: Basic $(echo -n ':YOUR_ZAPPER_KEY' | base64)"
2. Impermanent Loss Calculator
Formula:
IL% = 2 * sqrt(price_ratio) / (1 + price_ratio) - 1
Where price_ratio = current_price / entry_price for the volatile asset vs stable.
Python implementation:
import math
def impermanent_loss(entry_price: float, current_price: float) -> float:
"""
Calculate impermanent loss percentage for a 50/50 LP position.
Args:
entry_price: Price of volatile asset at LP entry (in terms of stable)
current_price: Current price of volatile asset
Returns:
IL as a decimal (negative = loss). Multiply by 100 for percentage.
Example:
entry_price = 2000 # ETH at entry
current_price = 3000 # ETH now
il = impermanent_loss(2000, 3000)
# Returns ~-0.0203 → -2.03% IL
"""
price_ratio = current_price / entry_price
il = (2 * math.sqrt(price_ratio)) / (1 + price_ratio) - 1
return il
def lp_position_pnl(
token0_qty: float, token1_qty: float,
token0_entry: float, token1_entry: float,
token0_current: float, token1_current: float,
fees_earned_usd: float = 0.0
) -> dict:
"""
Full P&L for an LP position including IL and fees.
Returns:
Dict with: hodl_value, lp_value, il_usd, fees_earned, net_pnl
"""
hodl_value = (token0_qty * token0_current) + (token1_qty * token1_current)
lp_value = _calculate_lp_value(
token0_qty, token1_qty,
token0_entry, token1_entry,
token0_current, token1_current
)
il_usd = lp_value - hodl_value
net_pnl = il_usd + fees_earned_usd
return {
"hodl_value_usd": hodl_value,
"lp_value_usd": lp_value,
"il_usd": il_usd,
"il_pct": il_usd / hodl_value if hodl_value else 0,
"fees_earned_usd": fees_earned_usd,
"net_pnl_usd": net_pnl,
"net_pnl_pct": net_pnl / hodl_value if hodl_value else 0,
}
def _calculate_lp_value(t0_qty, t1_qty, t0_entry, t1_entry, t0_cur, t1_cur):
"""Compute constant-product AMM LP value at current prices."""
k = t0_qty * t1_qty # invariant
# At current prices: t0_new = sqrt(k * t1_cur / t0_cur) — wait, recalc with entry ratio
entry_value = (t0_qty * t0_entry) + (t1_qty * t1_entry)
price_ratio = t0_cur / t0_entry
# AMM rebalances: each side = sqrt(initial_product * price_ratio)
t0_new = math.sqrt(k * t0_cur / t1_cur)
t1_new = math.sqrt(k * t1_cur / t0_cur)
return (t0_new * t0_cur) + (t1_new * t1_cur)
Uniswap v3 LP (concentrated liquidity): Uniswap v3 IL is range-dependent. Use the official SDK or Revert Finance API:
# Revert Finance — v3 position analytics
curl "https://api.revert.finance/v1/position?position_id=YOUR_NFT_ID&chain_id=1"
3. Cost Basis Tracking Per Position
Track entry prices and quantities for accurate P&L and tax reporting:
from dataclasses import dataclass
from datetime import datetime
from typing import List
@dataclass
class LPEntry:
"""Single LP entry event (add liquidity)."""
timestamp: datetime
protocol: str
chain: str
pool: str
token0_symbol: str
token0_qty: float
token0_price_usd: float
token1_symbol: str
token1_qty: float
token1_price_usd: float
tx_hash: str
gas_cost_usd: float = 0.0
@property
def cost_basis_usd(self) -> float:
return (self.token0_qty * self.token0_price_usd +
self.token1_qty * self.token1_price_usd +
self.gas_cost_usd)
@dataclass
class LPExit:
"""LP exit event (remove liquidity)."""
timestamp: datetime
protocol: str
chain: str
pool: str
token0_qty_returned: float
token0_price_usd: float
token1_qty_returned: float
token1_price_usd: float
fees_token0: float
fees_token1: float
tx_hash: str
gas_cost_usd: float = 0.0
@property
def proceeds_usd(self) -> float:
return (self.token0_qty_returned * self.token0_price_usd +
self.token1_qty_returned * self.token1_price_usd +
self.fees_token0 * self.token0_price_usd +
self.fees_token1 * self.token1_price_usd -
self.gas_cost_usd)
IRS treatment (current guidance):
- Adding liquidity: typically not a taxable event, but track cost basis
- LP fees earned: ordinary income at time of receipt (FMV)
- Removing liquidity: capital gain/loss (proceeds - cost basis)
- Staking rewards: ordinary income at FMV when received
4. Staking Rewards Tracker
# Pull staking reward history via The Graph (Lido example)
curl -X POST "https://api.thegraph.com/subgraphs/name/lidofinance/lido" \
-H "Content-Type: application/json" \
-d '{
"query": "{ totalRewards(where: {account: \"0xYOUR_WALLET\"}, orderBy: block, orderDirection: desc, first: 100) { id totalRewards totalFee block blockTime } }"
}'
Aave interest accrual:
# aToken balance change = interest earned
# Use Aave subgraph to get historical balance snapshots
curl -X POST "https://api.thegraph.com/subgraphs/name/aave/protocol-v3" \
-H "Content-Type: application/json" \
-d '{
"query": "{ userReserves(where: {user: \"0xYOUR_WALLET\"}) { reserve { symbol } currentATokenBalance scaledATokenBalance } }"
}'
5. Multi-Chain Aggregation
Using Moralis Web3 Data API (no API key for public endpoints):
# ETH mainnet DeFi positions
curl "https://deep-index.moralis.io/api/v2.2/0xYOUR_WALLET/defi/positions?chain=eth" \
-H "X-API-Key: YOUR_MORALIS_KEY"
# Arbitrum positions
curl "https://deep-index.moralis.io/api/v2.2/0xYOUR_WALLET/defi/positions?chain=arbitrum" \
-H "X-API-Key: YOUR_MORALIS_KEY"
Using cast (Foundry) for on-chain reads:
# Check Uniswap v3 position NFT owner (verify position still active)
cast call 0xC36442b4a4522E871399CD717aBDD847Ab11FE88 \
"ownerOf(uint256)(address)" YOUR_NFT_ID \
--rpc-url https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY
# Get Aave v3 account health factor
cast call 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2 \
"getUserAccountData(address)(uint256,uint256,uint256,uint256,uint256,uint256)" \
0xYOUR_WALLET --rpc-url YOUR_RPC
6. Portfolio Summary Output
Standard JSON schema for handoff to crypto-tax-agent and treasury dashboards:
{
"snapshot_date": "2026-03-15T18:00:00Z",
"wallet": "0x...",
"total_value_usd": 125430.00,
"positions": [
{
"id": "uniswap-v3-eth-usdc-500-12345",
"protocol": "Uniswap v3",
"chain": "ethereum",
"pool": "ETH/USDC 0.05%",
"type": "lp",
"nft_id": 12345,
"token0": { "symbol": "ETH", "qty": 1.5, "price_usd": 3000, "value_usd": 4500 },
"token1": { "symbol": "USDC", "qty": 4500, "price_usd": 1.0, "value_usd": 4500 },
"total_value_usd": 9000,
"cost_basis_usd": 8800,
"unrealized_pnl_usd": 200,
"fees_earned_usd": 145.50,
"il_usd": -42.10,
"il_pct": -0.0047,
"entry_date": "2026-01-10T00:00:00Z",
"in_range": true
},
{
"id": "lido-steth-deposit-20240101",
"protocol": "Lido",
"chain": "ethereum",
"type": "liquid_staking",
"token": { "symbol": "stETH", "qty": 5.0, "price_usd": 3010, "value_usd": 15050 },
"cost_basis_usd": 14500,
"staking_rewards_usd": 380.00,
"apy_current": 3.8,
"entry_date": "2025-06-01T00:00:00Z"
}
],
"summary": {
"total_cost_basis_usd": 89000,
"total_unrealized_pnl_usd": 36430,
"total_fees_earned_usd_ytd": 2140,
"total_staking_rewards_usd_ytd": 890,
"total_il_usd": -520,
"net_yield_usd_ytd": 3030
}
}
Tax Handoff to crypto-tax-agent
Export in crypto-tax-agent's expected format:
def export_for_tax_agent(positions: list, year: int) -> dict:
"""
Generate tax-ready export for crypto-tax-agent consumption.
Produces:
- Income events: staking rewards, LP fees (ordinary income)
- Disposal events: LP exits (capital gains/losses)
- Cost basis lots: FIFO/HIFO tracking per position
"""
income_events = []
disposal_events = []
for pos in positions:
# Fee income events (ordinary income when earned)
for reward in pos.get("reward_events", []):
income_events.append({
"date": reward["timestamp"],
"type": "defi_income",
"subtype": reward["type"], # "lp_fee" | "staking_reward" | "yield"
"asset": reward["symbol"],
"qty": reward["qty"],
"fmv_usd": reward["price_usd"],
"income_usd": reward["qty"] * reward["price_usd"],
"protocol": pos["protocol"],
"tx_hash": reward["tx_hash"]
})
# LP exits (capital events)
for exit_event in pos.get("exit_events", []):
disposal_events.append({
"date": exit_event["timestamp"],
"type": "lp_exit",
"protocol": pos["protocol"],
"cost_basis_usd": exit_event["cost_basis_usd"],
"proceeds_usd": exit_event["proceeds_usd"],
"gain_loss_usd": exit_event["proceeds_usd"] - exit_event["cost_basis_usd"],
"holding_period_days": exit_event["holding_period_days"],
"is_long_term": exit_event["holding_period_days"] >= 365,
"tx_hash": exit_event["tx_hash"]
})
return {
"tax_year": year,
"generated_at": datetime.utcnow().isoformat(),
"income_events": income_events,
"disposal_events": disposal_events,
"total_income_usd": sum(e["income_usd"] for e in income_events),
"total_realized_gain_usd": sum(
e["gain_loss_usd"] for e in disposal_events if e["gain_loss_usd"] > 0
),
"total_realized_loss_usd": sum(
e["gain_loss_usd"] for e in disposal_events if e["gain_loss_usd"] < 0
),
}
Monitoring & Alerts
Aave Health Factor Monitoring
HEALTH_FACTOR_THRESHOLDS = {
"critical": 1.1, # Alert immediately — liquidation imminent
"warning": 1.3, # Alert — add collateral or reduce debt
"caution": 1.5, # Notify — monitor closely
}
def check_health_factor(wallet: str, rpc_url: str) -> dict:
"""
Check Aave v3 health factor for liquidation risk.
Returns alert level and recommended action.
"""
# Use cast or web3.py to call getUserAccountData
# Returns: totalCollateralBase, totalDebtBase, availableBorrowsBase,
# currentLiquidationThreshold, ltv, healthFactor
pass
LP Out-of-Range Detection (Uniswap v3)
# Check if v3 position is still in range (earning fees)
# currentTick within [tickLower, tickUpper] = in range
cast call 0xC36442b4a4522E871399CD717aBDD847Ab11FE88 \
"positions(uint256)(uint96,address,address,address,uint24,int24,int24,uint128,uint256,uint256,uint128,uint128)" \
YOUR_NFT_ID --rpc-url YOUR_RPC
Data Sources Reference
| Tool | Best For | Cost |
|---|---|---|
| DeBank Pro API | Most comprehensive, all protocols | $99/mo |
| Zapper API | Good free tier, Ethereum + L2 | Free tier available |
| Zerion API | Clean data, portfolio-focused | Freemium |
| Moralis | Multi-chain, developer-friendly | Freemium |
| The Graph | Protocol-specific subgraphs | Free |
| Revert Finance | Uniswap v3 concentrated LP analytics | Free |
| Alchemy/Infura | Raw RPC calls | Freemium |
Common Workflows
Monthly portfolio review:
- Pull snapshot via DeBank/Zapper
- Run IL calculator on all LP positions
- Flag any Aave/Compound positions below HF 1.5
- Flag any v3 positions out of range
- Export income events (fees, staking rewards) to crypto-tax-agent
- Generate markdown summary for treasury dashboard
Pre-tax-season export (Q1):
- Pull all 2025 transactions for tracked wallets
- Classify: income events vs capital events
- Calculate cost basis (FIFO default, HIFO optional)
- Reconcile staking rewards (ordinary income)
- Hand off structured JSON to crypto-tax-agent for 8949/Schedule D
New position entry:
- Record entry tx_hash, block, prices at entry
- Calculate cost basis (token values + gas)
- Store in position ledger
- Set monitoring thresholds (IL%, HF, range status)
Not For This Skill
- Executing trades or transactions — use a wallet/trading skill
- on-chain-payroll — PTIN-backed Moltlaunch service, not ClawHub
- NFT portfolio tracking — different data model and APIs
- CEX holdings (Coinbase, Kraken) — use a CEX API skill or crypto-tax-agent directly
- Real-time price ticker — use a price feed skill
- Bridging or swapping assets — use a transaction execution skill
Files
1 totalComments
Loading comments…
