ChainScore Labs
All Guides

How Portfolio Aggregators Track DeFi Positions

LABS

How Portfolio Aggregators Track DeFi Positions

Chainscore © 2025

Core Concepts of DeFi Position Tracking

Understanding the fundamental data structures and protocols that enable portfolio aggregators to compile a unified view of user assets and liabilities across decentralized finance.

On-Chain Position Discovery

Position discovery involves scanning public blockchain data to identify assets owned or controlled by a user's wallet address.

  • Aggregators query token balances from ERC-20, ERC-721, and other token standard contracts.
  • They inspect liquidity pool positions by checking staking or LP token contracts like Uniswap V3's NonfungiblePositionManager.
  • This process is foundational, as it maps the raw, scattered on-chain state to a user's portfolio.

Yield Source Integration

Yield sources are the specific smart contracts where capital is deployed to generate returns, requiring aggregators to understand their unique accounting logic.

  • Examples include lending pools (Aave, Compound), automated vault strategies (Yearn), and liquid staking tokens (Lido's stETH).
  • Aggregators must decode position data like supplied/borrowed amounts, health factors, and accrued rewards.
  • Accurate integration is critical for calculating real-time APY and net asset value.

Cross-Chain State Reconciliation

State reconciliation is the process of unifying position data from multiple, isolated blockchain networks into a single coherent view.

  • Aggregators use indexers or RPC nodes for each supported chain (Ethereum, Arbitrum, Polygon).
  • They must handle chain-specific nuances like gas token native balances and bridge-wrapped assets.
  • This solves the fragmentation problem, allowing users to see their total DeFi exposure across ecosystems.

Portfolio Valuation Engine

The valuation engine assigns real-time fiat values to on-chain positions by sourcing accurate price data.

  • It pulls prices from decentralized oracles (Chainlink) and decentralized exchange liquidity pools.
  • For illiquid or LP positions, it calculates value using pool reserves and impermanent loss models.
  • This transforms raw token amounts into actionable financial metrics like total net worth and asset allocation.

Risk Parameter Analysis

Risk analysis involves evaluating the financial health and exposure of DeFi positions beyond simple balances.

  • It calculates metrics like collateralization ratios for loans, concentration risk, and protocol smart contract risk scores.
  • For leveraged positions, it assesses liquidation price thresholds based on oracle prices.
  • This provides users with proactive insights to manage portfolio risk and avoid liquidation events.

Transaction Intent Decoding

Intent decoding interprets pending transactions and historical activity to understand position changes and user behavior.

  • Aggregators parse transaction calldata to identify actions like swaps, deposits, or claims.
  • This allows for features like projected portfolio state post-transaction and historical performance tracking.
  • It connects on-chain actions directly to their impact on the user's financial position.

The Data Retrieval and Processing Flow

The multi-stage process aggregators use to source, parse, and normalize on-chain data into a unified portfolio view.

1

Identify and Index Target Addresses

Aggregators first locate all relevant user-controlled addresses across chains.

Detailed Instructions

The process begins by identifying every address associated with a user's wallet. This is not limited to a single chain. Aggregators use deterministic address derivation from a master seed phrase to find all EOA and smart contract wallet addresses (like Gnosis Safes) a user controls. For EVM chains, this involves deriving addresses for derivation paths like m/44'/60'/0'/0/0. They then query indexing services (like The Graph) or node RPCs to get a list of contract creations from these addresses. The goal is to build a complete map of all user-owned addresses across supported networks like Ethereum, Arbitrum, and Polygon before fetching asset data.

  • Sub-step 1: Derive all potential EOA addresses from the user's seed phrase using standard BIP-44/32 paths.
  • Sub-step 2: Query blockchain explorers or indexers for smart contract deployments originating from those EOAs.
  • Sub-step 3: Compile a final list of all address targets (EOAs and smart contracts) per network for data fetching.
javascript
// Example using ethers.js to derive an address path const wallet = ethers.Wallet.fromMnemonic(mnemonic, `m/44'/60'/0'/0/0`); console.log(wallet.address); // Target address for Ethereum mainnet

Tip: For smart contract wallets, the aggregator must also track the factory contract used for deployment to correctly interpret subsequent interactions.

2

Fetch Raw On-Chain State Data

Query blockchain nodes and subgraphs to retrieve token balances and contract states.

Detailed Instructions

With target addresses known, the aggregator performs parallel RPC calls to full nodes and decentralized indexing protocols. For native token balances, it uses simple eth_getBalance calls. For ERC-20, ERC-721, and LP token balances, it calls the standard balanceOf(address) function on each token contract. For complex DeFi positions (e.g., lending pools, yield vaults), it queries the specific protocol smart contracts to get user share data, like getUserAccountData(address) on Aave or userInfo(poolId, userAddress) on a MasterChef contract. Aggregators heavily rely on multi-chain RPC providers (like Alchemy, Infura) and subgraphs to batch these requests efficiently and avoid rate limits.

  • Sub-step 1: Batch RPC calls for native balances and simple ERC-20 balanceOf views across all addresses.
  • Sub-step 2: Query protocol-specific subgraphs for historical deposits, withdrawals, and current positions.
  • Sub-step 3: Call complex getter functions on DeFi smart contracts to retrieve stake amounts, collateral ratios, and pending rewards.
bash
# Example curl to call balanceOf via an Ethereum RPC curl -X POST https://eth-mainnet.g.alchemy.com/v2/your-api-key \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_call","params":[{"to": "0xdac17f958d2ee523a2206206994597c13d831ec7", "data": "0x70a08231000000000000000000000000USER_ADDRESS_HERE"}, "latest"],"id":1}'

Tip: Use multicall contracts (like MakerDAO's Multicall3) to aggregate hundreds of static calls into a single RPC request, drastically reducing latency and load.

3

Decode and Normalize Contract Data

Transform raw call responses into structured, chain-agnostic asset objects.

Detailed Instructions

The raw hexadecimal data from RPC calls and subgraph entries must be decoded using contract ABIs. This step converts contract-specific representations into a standardized internal model. For example, a Uniswap V3 LP position returns a tuple containing liquidity, feeGrowthInside0LastX128, and token amounts; this must be decoded and translated into underlying token balances. Aggregators maintain a protocol adapter library, where each adapter knows how to parse data for a specific contract (e.g., parseAaveV3Data, parseUniswapV3Position). A critical task is price normalization, where all token amounts are converted to a common unit (like 18 decimals) and valued in a reference currency (USD, ETH).

  • Sub-step 1: Apply the correct contract ABI to decode the raw data field from each RPC response into JavaScript objects or Python dictionaries.
  • Sub-step 2: Use protocol-specific adapter logic to calculate the user's actual claimable underlying assets from the decoded data (e.g., converting staked LP shares to token amounts).
  • Sub-step 3: Normalize all token quantities to a standard decimal basis (e.g., Wei to Ether) and fetch current market prices from an oracle for valuation.
solidity
// Example of a typical Uniswap V3 NonfungiblePositionManager position struct struct Position { uint96 nonce; address operator; address token0; address token1; uint24 fee; int24 tickLower; int24 tickUpper; uint128 liquidity; // The key value representing the user's share uint256 feeGrowthInside0LastX128; uint256 feeGrowthInside1LastX128; uint128 tokensOwed0; uint128 tokensOwed1; }

Tip: Maintain an internal registry of token decimals and symbols to avoid repeated on-chain calls for this metadata, updating it periodically.

4

Calculate Risk Metrics and Aggregate Totals

Compute portfolio-level statistics like TVL, asset allocation, and protocol exposure.

Detailed Instructions

After normalizing all asset data, the aggregator performs financial calculations to generate the final portfolio dashboard. The core metric is Total Value Locked (TVL), calculated by summing the USD value of all assets. It then computes asset allocation percentages across token types (e.g., Stablecoins 40%, Blue-Chip 30%, LP Positions 30%). For risk assessment, it calculates protocol concentration (e.g., "60% of debt is on Aave") and health factors for leveraged positions, which may involve querying liquidation thresholds from lending protocols. Aggregators also track estimated annual yield by summing the projected returns from staking, lending, and LP fees based on current on-chain rates.

  • Sub-step 1: Sum the USD value of all normalized token balances to compute the user's total portfolio value (TVL).
  • Sub-step 2: Group assets by protocol (Aave, Uniswap, Compound) and token category to calculate allocation percentages.
  • Sub-step 3: For leveraged positions, fetch the current collateral factor and liquidation threshold from the protocol to compute and display the health factor.
javascript
// Pseudocode for calculating portfolio allocation const totalValue = assets.reduce((sum, asset) => sum + asset.usdValue, 0); const allocation = assets.map(asset => ({ name: asset.symbol, percentage: (asset.usdValue / totalValue) * 100 }));

Tip: Health factors below 1.0 indicate an immediately liquidatable position. Aggregators often highlight these in red and may provide alerts.

5

Cache Results and Handle Updates

Implement caching strategies and real-time listeners to keep the portfolio view fresh.

Detailed Instructions

To ensure performance and freshness, aggregators implement sophisticated caching and update mechanisms. Time-series databases (like InfluxDB) or key-value stores (Redis) cache computed portfolio snapshots, indexed by user address and timestamp. For real-time updates, they subscribe to blockchain events via WebSocket connections to node providers. Listeners are set up for common events like Transfer, Deposit, and Withdraw from the user's tracked addresses and relevant protocol contracts. Upon detecting an event, the system invalidates the cache for that specific user and chain, triggering a partial re-fetch of the affected data. This event-driven architecture balances low latency with computational efficiency, avoiding full portfolio rescans on every block.

  • Sub-step 1: Store the final normalized portfolio object in a cache with a TTL (e.g., 30 seconds) to serve fast API responses.
  • Sub-step 2: Subscribe to transfer and specific protocol events for all tracked user addresses using eth_subscribe over WebSocket.
  • Sub-step 3: On receiving a relevant event, invalidate the cached portfolio for that user and initiate a targeted data refresh for the impacted protocol or token.
javascript
// Example of setting up a WebSocket subscription for Transfer events const Web3 = require('web3'); const web3 = new Web3('wss://eth-mainnet.ws.alchemyapi.io/v2/your-key'); const subscription = web3.eth.subscribe('logs', { address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC contract topics: [web3.utils.sha3('Transfer(address,address,uint256)')] }, (error, log) => { if (!error) handleTransferEvent(log); });

Tip: Use a bloom filter on the backend to quickly check if an incoming event log involves any address in your system's watchlist before processing.

Tracking Methods by Protocol Type

On-Chain Position Tracking

Aggregators track lending positions by querying the user's collateral balance and debt position directly from the protocol's smart contracts. For protocols like Aave and Compound, this involves reading from specific user mapping structures, such as userReservesData or getAccountLiquidity(). The key challenge is handling interest accrual, which requires calculating the current compounded interest based on the last update timestamp and the reserve's liquidity index.

Key Data Points

  • Collateral Assets: The type and amount of tokens supplied as collateral, often in aTokens or cTokens.
  • Borrowed Assets: The type and amount of tokens the user has borrowed against their collateral.
  • Health Factor / Collateral Factor: A critical risk metric determining how close a position is to liquidation.
  • Variable vs Stable Debt: Distinguishing between debt types with different interest rate models.

Implementation Example

To fetch a user's Aave V3 position, an aggregator calls getUserReservesData() on the Pool contract, which returns an array of structs containing all necessary state. The aggregator must then convert the scaled balances back to underlying amounts using the reserve's current liquidity index.

Comparison of Data Sources and Indexers

Comparison of data sourcing methods for DeFi portfolio tracking.

FeatureOn-Chain Indexer (e.g., The Graph)Centralized API (e.g., Covalent)Self-Hosted Node

Data Freshness

~1-2 block latency

~5-30 second latency

Sub-block latency

Historical Data Depth

Full chain history via subgraphs

Limited by API tier (e.g., 30 days free)

Full history from node archive

Query Complexity

Custom GraphQL, complex event filtering

REST API, predefined endpoints

Direct RPC calls, requires custom logic

Cost Structure

GRT query fees, decentralized network

API key tier (free to enterprise)

Infrastructure & engineering overhead

Supported Chains

40+ via subgraph deployments

100+ blockchains

Any chain with node client

Data Reliability

Depends on subgraph syncing health

High SLA (e.g., 99.9%), managed service

Depends on node stability & sync

Customization

High (define schema & mappings)

Low (use provided data models)

Maximum (direct chain interaction)

Calculating Portfolio Value and Performance

Process for aggregating and analyzing the value and returns of a decentralized finance portfolio.

1

Aggregate Raw Position Data

Collect token balances and staking positions from multiple protocols.

Detailed Instructions

The first step is to query on-chain data to compile a raw inventory of all assets. This involves calling the balanceOf function on ERC-20 token contracts for each wallet address across supported networks like Ethereum, Arbitrum, and Polygon. For staked or supplied assets, the aggregator must also interact with protocol-specific contracts (e.g., Aave's aToken contracts, Compound's cToken contracts, or Uniswap V3 NFT positions) to retrieve the user's share of the liquidity pool or lending market.

  • Sub-step 1: For each connected wallet, fetch the list of token addresses from a maintained registry or the user's transaction history.
  • Sub-step 2: Use multicall contracts to batch RPC requests for balanceOf and totalSupply functions to improve efficiency.
  • Sub-step 3: For liquidity positions, call the nonfungiblePositionManager contract (0xC36442b4a4522E871399CD717aBDD847Ab11FE88) to get details like token IDs, liquidity amounts, and fee tiers.
javascript
// Example: Batch balance check using ethers.js and a Multicall contract const multiCallContract = new ethers.Contract(multicallAddress, multicallABI, provider); const calls = tokenAddresses.map(addr => ({ target: addr, callData: tokenInterface.encodeFunctionData('balanceOf', [userAddress]) })); const results = await multiCallContract.aggregate(calls);

Tip: Use a fallback RPC provider to ensure data availability during network congestion.

2

Fetch Real-Time Token Prices

Resolve the USD value of all native and ERC-20 tokens.

Detailed Instructions

With raw token amounts known, the next step is to price them. Aggregators typically use decentralized oracle networks or aggregated price feeds from DEX liquidity. For major assets, Chainlink oracles (e.g., ETH/USD at 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419) provide reliable data. For long-tail or LP tokens, the price must be derived from their underlying reserves in a liquidity pool, calculated as (reserve0 * price0 + reserve1 * price1) / totalSupply.

  • Sub-step 1: For standard tokens, query price feeds from an on-chain oracle or an off-chain API aggregator like CoinGecko, caching results to minimize rate limits.
  • Sub-step 2: For LP tokens (e.g., Uniswap V2 pair tokens), call getReserves() on the pool contract and price each reserve token recursively.
  • Sub-step 3: For yield-bearing tokens (aTokens, cTokens), calculate the underlying asset amount by multiplying the balance by the exchange rate current from the protocol.
solidity
// Example: Calculating Uniswap V2 LP token price (conceptual Solidity) function getLPPrice(address pair) public view returns (uint256) { (uint112 reserve0, uint112 reserve1,) = IUniswapV2Pair(pair).getReserves(); uint256 price0 = getTokenPrice(token0); uint256 price1 = getTokenPrice(token1); uint256 totalValue = (reserve0 * price0) + (reserve1 * price1); return totalValue / IERC20(pair).totalSupply(); }

Tip: Always use the minimum price from multiple trusted sources to avoid manipulation when calculating portfolio value.

3

Calculate Portfolio Metrics

Compute total value, allocation, and unrealized P&L.

Detailed Instructions

This step synthesizes the priced data into actionable metrics. The Total Portfolio Value is the sum of all token balances multiplied by their USD prices. Asset Allocation is calculated as (asset value / total portfolio value) * 100. Unrealized Profit and Loss for each position is determined by comparing the current value to the cost basis, which must be tracked historically via ingested transaction data or user-provided inputs.

  • Sub-step 1: Sum the USD value of all wallet balances, staked assets, and LP positions to get the total portfolio value.
  • Sub-step 2: For each asset, divide its value by the total to determine its percentage allocation within the portfolio.
  • Sub-step 3: For P&L, subtract the average cost basis (total spent acquiring the asset) from the current value. A positive result is an unrealized gain.
javascript
// Example: Calculating basic portfolio metrics const totalValue = pricedPositions.reduce((sum, pos) => sum + pos.currentValueUSD, 0); const allocations = pricedPositions.map(pos => ({ symbol: pos.symbol, allocation: (pos.currentValueUSD / totalValue) * 100 })); const unrealizedPNL = pricedPositions.map(pos => pos.currentValueUSD - pos.costBasisUSD);

Tip: For accurate P&L, implement FIFO (First-In, First-Out) or specific identification accounting methods to track cost basis across multiple transactions.

4

Analyze Performance Over Time

Measure returns across different timeframes and calculate annualized metrics.

Detailed Instructions

Performance analysis requires historical snapshots of portfolio value. Aggregators store daily or hourly value records to enable time-series analysis. Time-Weighted Return (TWR) is calculated to isolate the manager's performance by removing the impact of deposits and withdrawals. The formula involves calculating returns for each sub-period between external cash flows and then linking them geometrically. Annual Percentage Yield (APY) for staked positions is often pulled directly from protocol contracts or calculated based on recent reward emissions.

  • Sub-step 1: Query stored historical portfolio values for the desired timeframe (e.g., 1 day, 7 days, 30 days ago).
  • Sub-step 2: For TWR, identify all deposit/withdrawal events, calculate the return for each period between them, and chain the periods: TWR = [(1 + R1) * (1 + R2) ...] - 1.
  • Sub-step 3: For yield positions, call contracts like Aave's getReserveData or Compound's supplyRatePerBlock to get current variable APY.
python
# Example: Simplified Time-Weighted Return calculation (Python pseudocode) def calculate_twr(portfolio_values, cash_flows): # portfolio_values: list of (timestamp, value) # cash_flows: list of (timestamp, amount) where positive is deposit periods = split_periods_by_cash_flows(portfolio_values, cash_flows) period_returns = [] for start_val, end_val in periods: period_return = (end_val - start_val) / start_val period_returns.append(period_return) twr = 1 for r in period_returns: twr *= (1 + r) return twr - 1

Tip: Use a consistent timezone (preferably UTC) and snapshot interval for comparable historical performance data.

Technical Challenges and Solutions

The infrastructure for tracking DeFi portfolios must solve complex technical problems to deliver accurate, real-time data. This section details the primary engineering hurdles and the solutions implemented by leading aggregators.

Data Indexing and Synchronization

On-chain data indexing requires parsing transactions, event logs, and state changes across multiple blockchains. Aggregators use specialized indexers or subgraphs to track interactions with thousands of smart contracts. Real-time synchronization is critical for position accuracy, especially during high volatility or network congestion, where delayed data can lead to incorrect portfolio valuations.

Protocol Integration Complexity

Each DeFi protocol has unique contract architectures and financial logic. Aggregators must build and maintain individual adapters for lending pools, AMMs, yield vaults, and staking contracts. This involves decoding complex interactions, such as Uniswap V3 concentrated liquidity positions or Compound's cToken exchange rates, to correctly calculate user holdings and accrued interest.

Handling Cross-Chain Assets

Users hold assets on multiple chains via bridges and wrapped tokens. Aggregators must map canonical and wrapped assets (e.g., WETH on Arbitrum to mainnet ETH) and track bridge liabilities. This requires maintaining a unified identity system across chains and reconciling balances, which is complicated by differing block times and the risk of bridge hacks affecting total value locked (TVL) calculations.

Pricing Oracles and Valuation

Accurate portfolio valuation depends on reliable price oracles. Aggregators aggregate data from DEX pools, Chainlink, and other sources, applying safeguards against manipulation. They must handle illiquid or long-tail assets with wide spreads, and correctly price LP tokens based on pool reserves. Mispricing can significantly distort perceived net worth and risk metrics.

Performance and Scalability

Scanning thousands of addresses across dozens of protocols is computationally intensive. Solutions include caching strategies, sharded data processing, and efficient RPC node management to avoid rate limits. The system must scale during market surges without degrading user experience, ensuring sub-second updates for active traders while managing infrastructure costs.

Security and Data Integrity

Aggregators are trusted sources for financial data, making security paramount. They implement signature verification for user data, audit their contract integrations, and run anomaly detection on indexed data. Protecting against front-running of portfolio queries and ensuring the integrity of the data pipeline are continuous challenges to prevent providing misleading information to users.

SECTION-FAQ

Frequently Asked Questions

Ready to Start Building?

Let's bring your Web3 vision to life.

From concept to deployment, ChainScore helps you architect, build, and scale secure blockchain solutions.