DeFi portfolio dashboard development

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
DeFi portfolio dashboard development
Medium
~1-2 weeks
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1214
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

DeFi Portfolio Dashboard Development

A user holds USDC in Aave, ETH-USDC LP in Uniswap V3, wstETH in Lido, and open perpetual position on GMX. Four different protocols, four different ways to represent positions, four different APIs or subgraphs. Dashboard task — aggregate all this in single screen with real P&L figures.

Development of such tool — not only frontend. 80% effort goes to data layer: normalization of data from different sources and correct calculation of stale/live balances.

Data sources and their specifics

On-chain direct calls vs indexers

Most accurate way to get balance — direct eth_call to contract. For token balance — balanceOf(). For Aave position — getUserAccountData(). Always current, but slow: each protocol requires separate calls, with dozens of protocols latency grows linearly.

Solution — Multicall3 (0xcA11bde05977b3631167028862bE2a173976CA11, deployed on all major EVM chains): batch of 50+ calls in one transaction. Response time — like one RPC call instead of 50.

import { multicall } from 'viem'

const results = await multicall(client, {
  contracts: [
    { address: AAVE_POOL, abi: aavePoolAbi, functionName: 'getUserAccountData', args: [userAddress] },
    { address: USDC_TOKEN, abi: erc20Abi, functionName: 'balanceOf', args: [userAddress] },
    { address: UNISWAP_POSITION_MANAGER, abi: nftAbi, functionName: 'balanceOf', args: [userAddress] },
  ]
})

For historical data (transaction history, P&L over time) direct calls don't work — need indexers.

The Graph for historical data

Uniswap, Aave, Compound, Curve, Balancer — all have official subgraphs on The Graph Network. Subgraph provides GraphQL API for querying historical events: deposits, withdrawals, swaps, liquidations.

query UserPositions($user: String!) {
  aaveV3_deposits(where: { user: $user }, orderBy: timestamp, orderDirection: desc) {
    amount
    reserve { symbol, decimals, priceInUSD }
    timestamp
  }
  aaveV3_borrows(where: { user: $user }) {
    amount
    reserve { symbol }
    currentVariableBorrowRate
  }
}

Problem: different protocol versions have different subgraphs. Aave V2 on Ethereum, Aave V3 on Polygon, Aave V3 on Arbitrum — three different subgraphs with different schemas. Normalization — main engineering task of dashboard.

Alchemy and Moralis as API-over-RPC

Alchemy API provides ready methods: getTokenBalances() returns all ERC-20 balances of address without iterating contracts. getAssetTransfers() — transfer history. This significantly simplifies initial implementation, but costs money at high load.

Moralis additionally aggregates data on NFT positions and DeFi protocol positions through their DeFi API — paid, but saves months of custom data layer development.

For MVP justified Alchemy + The Graph for key protocols. For production with tens of thousands of users — own indexer.

P&L calculation and impermanent loss

Most complex part — correct unrealized P&L calculation for LP positions.

For Uniswap V3 position — NFT with certain tickLower, tickUpper, liquidity. Current amounts of token0 and token1 depend on current sqrtPriceX96 of pool. Formula non-trivial:

function getAmountsFromLiquidity(
  sqrtPriceX96: bigint,
  sqrtRatioAX96: bigint,
  sqrtRatioBX96: bigint,
  liquidity: bigint
): [bigint, bigint] {
  if (sqrtPriceX96 <= sqrtRatioAX96) {
    // All liquidity in token0
    const amount0 = (liquidity * (sqrtRatioBX96 - sqrtRatioAX96) * Q96) 
                    / (sqrtRatioBX96 * sqrtRatioAX96)
    return [amount0, 0n]
  } else if (sqrtPriceX96 < sqrtRatioBX96) {
    const amount0 = (liquidity * (sqrtRatioBX96 - sqrtPriceX96) * Q96) 
                    / (sqrtRatioBX96 * sqrtPriceX96)
    const amount1 = (liquidity * (sqrtPriceX96 - sqrtRatioAX96)) / Q96
    return [amount0, amount1]
  } else {
    // All liquidity in token1
    const amount1 = (liquidity * (sqrtRatioBX96 - sqrtRatioAX96)) / Q96
    return [0n, amount1]
  }
}

Impermanent loss calculated as difference between current position value and value if same assets simply held from entry. For dashboard need to store entry price and initial amounts when position opened.

Multichain aggregation

Typical user active on Ethereum mainnet, Arbitrum, Polygon, Base. Dashboard should show total portfolio across chains.

Scheme: parallel requests to RPC of each chain via Promise.all(), normalize balances to USD via single price oracle. Coingecko API or DefiLlama Price API for getting current prices by token address + chain ID.

Cross-chain identity problem: user address same on all EVM chains (ECDSA), but smart contract wallet (Safe, Argent) can have different addresses on different chains on unsynchronized deployment. Need explicit multi-address mode support.

Stack and performance

Backend: Node.js + TypeScript with viem for RPC. Redis for balance cache (TTL 30 seconds for live data, 5 minutes for historical). PostgreSQL for storing historical portfolio snapshots (for building equity curve).

Frontend: React + wagmi v2 for wallet connection, Recharts or TradingView Lightweight Charts for charts, Tanstack Query for data fetching with automatic refetch every 30 seconds.

WebSocket for real-time updates: subscribe to eth_subscribe("newHeads") to trigger balance update on new block — looks live without extra poll requests.

Development process

Analytics (1–2 days). List of target protocols and chains, prioritization by popular audience use cases.

Data layer (5–7 days). Multicall aggregator, The Graph integrations for key protocols, normalization to single position schema.

Backend API (3–5 days). REST/GraphQL API for frontend, caching, portfolio history.

Frontend (5–7 days). Wallet connection, total balance view, per-protocol details, charts.

Timeline estimates

MVP with 5–7 protocols on 2–3 chains — 2–3 weeks. Full dashboard with history, IL calculation, alerts and mobile view — 6–8 weeks.