get_portfolio
Retrieve a wallet's complete portfolio including total USD value, all token holdings, and DeFi positions across multiple networks.
Instructions
Full portfolio breakdown for a wallet: total USD value, all token holdings, and all DeFi app positions across networks. Use this when the user wants a complete picture of what a wallet holds.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| address | Yes | Wallet address or ENS name | |
| networks | No | Networks to filter by. Supported: ethereum, base, optimism, arbitrum, polygon, bnb, avalanche, zora. Omit for all networks. |
Implementation Reference
- src/server.ts:44-66 (registration)Tool registration for 'get_portfolio' using server.registerTool with Zod input schema (address, optional networks). The handler calls fetchPortfolio(apiKey, address, resolveChainIds(networks)) and returns the result as JSON text.
// ── Tool: get_portfolio ────────────────────────────────────────────────────── server.registerTool( "get_portfolio", { description: "Full portfolio breakdown for a wallet: total USD value, all token holdings, and all DeFi app positions across networks. Use this when the user wants a complete picture of what a wallet holds.", inputSchema: { address: z.string().describe("Wallet address or ENS name"), networks: networksSchema, }, }, async ({ address, networks }) => { try { const result = await fetchPortfolio(apiKey, address, resolveChainIds(networks)); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (err) { return errorResponse(err); } }, ); - src/zapper.ts:374-397 (handler)fetchPortfolio function - the core logic invoked by the get_portfolio tool. Executes the PORTFOLIO_QUERY GraphQL against Zapper API, parses token balances and app positions, and returns a PortfolioResult with totalUSD, tokensTotalUSD, appsTotalUSD, tokens[], and apps[].
export async function fetchPortfolio( apiKey: string, address: string, chainIds?: number[], ): Promise<PortfolioResult> { const data = (await gql(apiKey, PORTFOLIO_QUERY, { addresses: [address], chainIds: chainIds ?? null, })) as { portfolioV2: { tokenBalances: { totalBalanceUSD: number; byToken: { edges: Array<{ node: GqlTokenNode }> } }; appBalances: { totalBalanceUSD: number; byApp: { edges: Array<{ node: GqlAppNode }> } }; }; }; const { tokenBalances: tb, appBalances: ab } = data.portfolioV2; return { tokensTotalUSD: tb.totalBalanceUSD ?? 0, appsTotalUSD: ab.totalBalanceUSD ?? 0, totalUSD: (tb.totalBalanceUSD ?? 0) + (ab.totalBalanceUSD ?? 0), tokens: tb.byToken.edges.map(({ node }) => parseTokenNode(node)), apps: ab.byApp.edges.map(({ node }) => parseAppNode(node)), }; } - src/zapper.ts:62-68 (schema)PortfolioResult interface - defines the return shape of fetchPortfolio: totalUSD, tokensTotalUSD, appsTotalUSD, tokens (TokenBalance[]), and apps (AppBalance[]).
export interface PortfolioResult { totalUSD: number; tokensTotalUSD: number; appsTotalUSD: number; tokens: TokenBalance[]; apps: AppBalance[]; } - src/zapper.ts:194-259 (helper)PORTFOLIO_QUERY - the GraphQL query used by fetchPortfolio. Fetches token balances (byToken) and app positions (byApp) from the Zapper portfolioV2 endpoint.
const PORTFOLIO_QUERY = ` query Portfolio($addresses: [Address!]!, $chainIds: [Int!]) { portfolioV2(addresses: $addresses, chainIds: $chainIds) { tokenBalances { totalBalanceUSD byToken(first: 50) { edges { node { symbol name balance balanceUSD price network { name } } } } } appBalances { totalBalanceUSD byApp(first: 20) { edges { node { balanceUSD app { displayName slug } network { name } positionBalances(first: 20) { edges { node { ... on AppTokenPositionBalance { type symbol balance balanceUSD tokens { ... on BaseTokenPositionBalance { symbol balance balanceUSD } } } ... on ContractPositionBalance { type balanceUSD tokens { metaType token { ... on BaseTokenPositionBalance { symbol balance balanceUSD } } } } } } } } } } } } } `; - src/server.ts:32-37 (helper)resolveChainIds helper - utility used by the get_portfolio handler to translate network name strings into numeric chain IDs based on SUPPORTED_CHAINS.
function resolveChainIds(networks?: string[]): number[] | undefined { if (!networks?.length) return undefined; return networks .map((n) => SUPPORTED_CHAINS[n.toLowerCase()]) .filter((id): id is number => id !== undefined); }