Integrating Aave, Uniswap, and Curve into a Single DeFi Dashboard
Picture this: you have assets across a dozen DeFi protocols. To check total yield, you open three different interfaces, manually convert positions to USD, and track APY changes. That's hours every week. We build a unified dashboard: all positions, USD value, PnL, and history on one screen, updated in real time. Our engineers have 10+ years of blockchain development experience and are Ethereum-certified.
The challenge isn't the widgets—it's the integration: each protocol has its own API, data formats, and networks. Plus price feeds. We solve that and deliver a turnkey dashboard.
Which Protocols We Integrate
| Protocol | Data Type | Retrieval Method | Update Frequency |
|---|---|---|---|
| Aave v3 | Deposits, loans, APR | Subgraph | 30 sec |
| Uniswap v3 | LP positions, fees | Subgraph | 30 sec |
| Curve | Staking, LP tokens | Curve API | 1 min |
| CoinGecko | Token prices | REST API | 1 min (cached) |
| Ethereum | Balances, tokens | Multicall batched RPC | 12 sec (1 block) |
Protocol Integration and Data Collection
Why Subgraph is Faster than Direct RPC Queries?
Subgraph from The Graph indexes contract events—one query replaces thousands of RPC calls. For Aave v3, we compose a GraphQL query, retrieve userReserves, parse them, and map to the AavePosition interface. Subgraph queries are 10x faster for batch processing.
// lib/protocols/aave.ts — full listing
const AAVE_SUBGRAPH = 'https://api.thegraph.com/subgraphs/name/aave/protocol-v3';
interface AavePosition {
reserve: { symbol: string; underlyingAsset: string; decimals: number };
currentATokenBalance: string;
currentVariableDebt: string;
currentStableDebt: string;
reserveLiquidationThreshold: string;
}
export async function getAavePositions(userAddress: string): Promise<AavePosition[]> {
const query = `{
userReserves(
where: { user: "${userAddress.toLowerCase()}", currentATokenBalance_gt: "0" }
) {
reserve { symbol underlyingAsset decimals liquidityRate }
currentATokenBalance
currentVariableDebt
currentStableDebt
}
}`;
const res = await fetch(AAVE_SUBGRAPH, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query }),
next: { revalidate: 30 },
});
const { data } = await res.json();
return data.userReserves;
}
Uniswap v3 LP Positions — NFTs and Subgraph
Uniswap v3 positions are ERC-721 tokens in the NonfungiblePositionManager. Through the Subgraph, we retrieve details: tokens, tick ranges, collected fees. This allows calculating current value and unrealized profit.
// lib/protocols/uniswap-v3.ts
const UNISWAP_SUBGRAPH = 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3';
export async function getUniswapPositions(ownerAddress: string) {
const query = `{
positions(
where: { owner: "${ownerAddress.toLowerCase()}", liquidity_gt: "0" }
orderBy: id
orderDirection: desc
first: 20
) {
id
liquidity
token0 { symbol decimals }
token1 { symbol decimals }
pool { feeTier sqrtPrice tick token0Price token1Price }
depositedToken0
depositedToken1
collectedFeesToken0
collectedFeesToken1
tickLower { tickIdx }
tickUpper { tickIdx }
}
}`;
const res = await fetch(UNISWAP_SUBGRAPH, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query }),
});
const { data } = await res.json();
return data.positions;
}
How to Aggregate Data in Real Time?
We use the usePortfolio hook built on TanStack Query + Wagmi. It loads positions from Aave and Uniswap in parallel, fetches prices, and merges into a single object. A refetchInterval of 60 seconds ensures freshness, while client-side cache provides instant response when switching tabs.
// hooks/usePortfolio.ts
import { useQuery } from '@tanstack/react-query';
import { useAccount } from 'wagmi';
import { getAavePositions } from '@/lib/protocols/aave';
import { getUniswapPositions } from '@/lib/protocols/uniswap-v3';
import { getTokenPrices } from '@/lib/prices';
export function usePortfolio() {
const { address } = useAccount();
return useQuery({
queryKey: ['portfolio', address],
queryFn: async () => {
if (!address) return null;
const [aavePositions, uniswapPositions] = await Promise.all([
getAavePositions(address),
getUniswapPositions(address),
]);
const tokenIds = [
...new Set([
...aavePositions.map(p => p.reserve.symbol.toLowerCase()),
'ethereum',
]),
];
const prices = await getTokenPrices(tokenIds);
const aaveUSD = aavePositions.reduce((sum, pos) => {
const balance = Number(pos.currentATokenBalance) / 10 ** pos.reserve.decimals;
const price = prices[pos.reserve.symbol.toLowerCase()] ?? 0;
return sum + balance * price;
}, 0);
return {
aavePositions,
uniswapPositions,
aaveUSD,
prices,
};
},
enabled: !!address,
refetchInterval: 60_000,
});
}
How Data Caching Saves Money and Resources?
Without caching, every render triggers a new API call. We implemented a price cache with a 60-second TTL, reducing CoinGecko load by 20x. Savings on API limits reach 95%, amounting to about $200 per month under heavy usage. A similar cache for Subgraph: data is re-fetched no more than once every 30 seconds.
// lib/prices.ts — price cache with TTL
const COINGECKO_BASE = 'https://api.coingecko.com/api/v3';
const priceCache = new Map<string, { price: number; ts: number }>();
const CACHE_TTL = 60_000;
export async function getTokenPrices(
tokenIds: string[],
vsCurrency = 'usd',
): Promise<Record<string, number>> {
const stale = tokenIds.filter(id => {
const cached = priceCache.get(id);
return !cached || Date.now() - cached.ts > CACHE_TTL;
});
if (stale.length > 0) {
const res = await fetch(
`${COINGECKO_BASE}/simple/price?ids=${stale.join(',')}&vs_currencies=${vsCurrency}`,
);
const data = await res.json();
for (const [id, price] of Object.entries(data)) {
priceCache.set(id, { price: (price as Record<string, number>)[vsCurrency], ts: Date.now() });
}
}
return Object.fromEntries(
tokenIds.map(id => [id, priceCache.get(id)?.price ?? 0]),
);
}
Dashboard Components
// components/NetWorthCard.tsx
export function NetWorthCard() {
const { data, isLoading } = usePortfolio();
if (isLoading) return <Skeleton className="h-32" />;
const total = (data?.aaveUSD ?? 0) + (data?.uniswapUSD ?? 0);
return (
<div className="rounded-2xl border border-white/10 bg-gradient-to-br from-neutral-900 to-neutral-800 p-6">
<p className="text-sm text-neutral-400">Net Worth</p>
<p className="mt-1 text-4xl font-bold">
${total.toLocaleString('en-US', { maximumFractionDigits: 2 })}
</p>
<div className="mt-4 flex gap-6 text-sm">
<Metric label="Aave" value={data?.aaveUSD} />
<Metric label="Uniswap LP" value={data?.uniswapUSD} />
</div>
</div>
);
}
Real-time updates: prices refresh every minute via react-query refetchInterval, on-chain data every 12 seconds (one Ethereum block), Subgraph data every 30 seconds (indexing delay ~15s).
Development Process
| Stage | Duration | Result |
|---|---|---|
| Analysis and brief | 1 day | Protocol list, specification |
| Subgraph integration | 2–3 days | Data retrieval for each protocol |
| UI development | 2–4 days | Components: NetWorth, Positions, History |
| Caching and realtime | 1 day | react-query setup, price cache |
| Testing and deploy | 1–2 days | Working dashboard on Vercel or your server |
Common Pitfalls and How to Avoid Them
- N+1 query: fetching data for each pool with a separate RPC call. Solution: Multicall or Subgraph with batch queries.
- Hydration mismatch in Next.js: data obtained on the server doesn't match client data due to different request times. Fix: defer queries to the client using Wagmi.
-
Stale data: without cache, users see outdated prices. Use a 60-second TTL and
refetchIntervalto keep data fresh.
One of our clients reduced manual monitoring time from 10 hours per week to 15 minutes after implementing the dashboard—saving over $2,000 per year in labor costs.
What's Included in the Deliverable
We hand over a fully ready solution:
- Integration of selected protocols (up to 5)
- Architecture built on Next.js + TypeScript + Wagmi + TanStack Query
- Caching for prices and Subgraph data
- Responsive UI using Tailwind CSS
- Documentation for deployment and adding new protocols
- Free support for 1 month after delivery
Timelines
- MVP with one protocol (Aave) — 3-4 days
- Multi-protocol dashboard (3-4 protocols + charts + history) — 1.5-2 weeks
- Exact timelines are estimated after the brief
Order a dashboard for your portfolio. Get a consultation: contact us, and we'll prepare a proposal tailored to your protocols and requirements. Our engineers have 5+ years of DeFi experience and guarantee stable operation of the dashboard.







