- Fetch all DeFi positions (staked, deposited, LP, locked, rewards)
- Filter by position type and chain
- Group positions by protocol and calculate per-protocol totals
- Build a complete runnable DeFi dashboard
Prerequisites
- A Zerion API key (get one here)
- A wallet address to query
Steps
Call the positions endpoint with
filter[positions]=only_complex to get only DeFi protocol positions, excluding regular wallet tokens.Key parameters:
filter[positions]=only_complex (DeFi positions only), sort=-value (highest value first), currency=usd (also supports eur, gbp, etc.)depositstakedrewardlockedwalletonly_complex)curl -u "YOUR_API_KEY:" \
"https://api.zerion.io/v1/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/positions/?currency=usd&filter[positions]=only_complex&filter[chain_ids]=ethereum,arbitrum"
const API_KEY = process.env.ZERION_API_KEY;
const BASE_URL = "https://api.zerion.io/v1";
const headers = {
accept: "application/json",
authorization: `Basic ${btoa(API_KEY + ":")}`,
};
async function getAllDeFiPositions(address) {
const allPositions = [];
let url = `${BASE_URL}/wallets/${address}/positions/?currency=usd&filter[positions]=only_complex&sort=-value`;
while (url) {
const res = await fetch(url, { headers });
const { data, links } = await res.json();
allPositions.push(...data);
url = links.next || null;
}
return allPositions;
}
async function displayDeFiDashboard(address) {
const positions = await getAllDeFiPositions(address);
// Group by protocol
const byProtocol = {};
for (const pos of positions) {
const protocol = pos.attributes.protocol || "Unknown";
if (!byProtocol[protocol]) byProtocol[protocol] = { total: 0, positions: [] };
byProtocol[protocol].total += pos.attributes.value || 0;
byProtocol[protocol].positions.push(pos);
}
// Sort protocols by total value
const sorted = Object.entries(byProtocol).sort((a, b) => b[1].total - a[1].total);
const grandTotal = positions.reduce((sum, p) => sum + (p.attributes.value || 0), 0);
console.log(`=== DeFi OVERVIEW ===`);
console.log(`Total DeFi value: $${grandTotal.toFixed(2)}`);
console.log(`Protocols: ${sorted.length}`);
console.log(`Positions: ${positions.length}\n`);
for (const [protocol, data] of sorted) {
console.log(`--- ${protocol} ($${data.total.toFixed(2)}) ---`);
for (const pos of data.positions) {
const { name, position_type, value, quantity } = pos.attributes;
const chain = pos.relationships.chain.data.id;
console.log(
` [${position_type}] ${name} on ${chain}: ${quantity.float} (${value != null ? `$${value.toFixed(2)}` : "N/A"})`
);
}
console.log();
}
}
displayDeFiDashboard("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045");
Response fields reference
Each DeFi position includes:| Field | Description |
|---|---|
name | Human-readable position name |
protocol | Protocol name (e.g., “Aave V3”, “Lido”) |
protocol_module | Type of DeFi interaction: lending, staked, liquidity_pool, locked, rewards, farming, vesting, deposit |
position_type | Position category (deposit, staked, reward, locked) |
quantity.float | Token amount |
value | USD value of the position |
group_id | ID to group related positions (e.g., both sides of an LP) |
relationships.chain | Which blockchain the position is on |
Next steps
- Use
filter[positions]=no_filterto get both DeFi and wallet positions in a single call - Combine with the portfolio endpoint to see total value distribution by type
- Implement pagination to handle wallets with many DeFi positions