hyperd.wallet.pnl
Compute realized and unrealized profit and loss for any EVM wallet across a time window. Supports FIFO, LIFO, and HCFO cost-basis methods with per-token breakdown.
Instructions
Realized + unrealized P&L for a wallet over a time window. ERC-20 + native, single chain per call. Three cost-basis methods: FIFO (default, IRS standard), LIFO (last-in-first-out), HCFO (highest-cost-first-out, tax-loss-harvesting view). Per-token breakdown with weighted-avg cost + mark-to-market. Unpriced trades and untracked-proceeds gaps surfaced honestly under coverage.* fields. v1.0 defers per-tx gas-fee USD attribution. Costs $0.05 in USDC.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| address | Yes | 0x EVM wallet address | |
| chain | No | Chain to compute P&L on. Default 'base'. | |
| from | No | Window start, unix seconds. Default = to - 30 days. Must be >= 1577836800 (2020-01-01). | |
| to | No | Window end, unix seconds. Default = now. Cannot be > now+60. | |
| method | No | Cost-basis method. Default 'fifo'. |
Implementation Reference
- src/server.ts:389-404 (registration)Tool registration using server.tool() with name 'hyperd.wallet.pnl' and the description schema
// hyperd.wallet.pnl — realized + unrealized P&L (FIFO/LIFO/HCFO) ($0.05) server.tool( "hyperd.wallet.pnl", "Realized + unrealized P&L for a wallet over a time window. ERC-20 + native, single chain per call. Three cost-basis methods: FIFO (default, IRS standard), LIFO (last-in-first-out), HCFO (highest-cost-first-out, tax-loss-harvesting view). Per-token breakdown with weighted-avg cost + mark-to-market. Unpriced trades and untracked-proceeds gaps surfaced honestly under coverage.* fields. v1.0 defers per-tx gas-fee USD attribution. Costs $0.05 in USDC.", { address: z.string().describe("0x EVM wallet address"), chain: z .enum(["base", "ethereum", "polygon", "arbitrum", "optimism", "avalanche", "bnb"]) .optional() .describe("Chain to compute P&L on. Default 'base'."), from: z.number().int().optional().describe("Window start, unix seconds. Default = to - 30 days. Must be >= 1577836800 (2020-01-01)."), to: z.number().int().optional().describe("Window end, unix seconds. Default = now. Cannot be > now+60."), method: z.enum(["fifo", "lifo", "hcfo"]).optional().describe("Cost-basis method. Default 'fifo'."), }, async (args) => asText(await paidGet("/api/wallet/pnl", args)), ); - src/server.ts:393-402 (schema)Input schema for hyperd.wallet.pnl: address (string, required), chain (enum optional, default base), from/to (unix seconds optional), method (fifo/lifo/hcfo optional, default fifo)
{ address: z.string().describe("0x EVM wallet address"), chain: z .enum(["base", "ethereum", "polygon", "arbitrum", "optimism", "avalanche", "bnb"]) .optional() .describe("Chain to compute P&L on. Default 'base'."), from: z.number().int().optional().describe("Window start, unix seconds. Default = to - 30 days. Must be >= 1577836800 (2020-01-01)."), to: z.number().int().optional().describe("Window end, unix seconds. Default = now. Cannot be > now+60."), method: z.enum(["fifo", "lifo", "hcfo"]).optional().describe("Cost-basis method. Default 'fifo'."), }, - src/server.ts:403-403 (handler)Handler: calls paidGet('/api/wallet/pnl', args) which makes an x402-signed GET request to the hyperD API, then formats response as text via asText()
async (args) => asText(await paidGet("/api/wallet/pnl", args)), - src/server.ts:79-92 (helper)paidGet helper function that constructs the URL with query params and delegates to paidRequest for x402 payment flow
async function paidGet( path: string, query: Record<string, string | number | boolean | undefined>, ): Promise<unknown> { if (!httpClient) { throw new Error(WALLET_NOT_CONFIGURED_MSG); } const url = new URL(`${API_BASE}${path}`); for (const [k, v] of Object.entries(query)) { if (v !== undefined && v !== "" && v !== null) url.searchParams.set(k, String(v)); } return paidRequest("GET", url, undefined); } - src/server.ts:155-157 (helper)asText helper function that wraps JSON response into MCP text content format
function asText(data: unknown) { return { content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }] }; }