Skip to content

bug: profiler perp-positions --table renders raw JSON blob instead of per-position rows #448

Description

@Nicolai1205

Summary

nansen research profiler perp-positions --table renders a single data column containing a raw JSON blob instead of one row per position. Every other profiler subcommand flattens correctly into tabular rows. --pretty works fine.

Reproduction

nansen research profiler perp-positions --address 0x06d8a1f8a10d5814dc1f904fbe5790ee9f7eca0c --table

Actual output:

data
──────────────────────────────
{"asset_positions":[{"position ...

Expected output (one row per position):

token_symbol │ size  │ entry_price_usd │ position_value_usd │ unrealized_pnl_usd │ leverage_value
─────────────┼───────┼─────────────────┼────────────────────┼────────────────────┼───────────────
HYPE         │ 0.31  │ 72.169          │ 22.64              │ +0.27              │ 2
BTC          │ 0.000 │ 72830.0         │ 22.55              │ -0.02              │ 2
XRP          │ 17.0  │ 1.3019          │ 22.17              │ +0.04              │ 2

Root cause

The API response for perp-positions returns a non-standard shape: the data lives under result.data.data.asset_positions[].position rather than the flat result.data.data[] array the table formatter expects. The formatter encounters a single object at result.data.data, can't iterate it as an array, and falls back to serialising the whole thing as a string in a data column.

All other profiler endpoints return data.data as a flat array — this is the only one with a nested structure.

Proposed fix

Add a response transform in the addressPerpPositions method (or in the CLI handler) to normalise the response before it reaches the formatter:

// src/api.js
async addressPerpPositions(params = {}) {
  const { address, filters = {}, orderBy } = params;
  const result = await this.request('/api/v1/profiler/perp-positions', {
    address,
    filters,
    order_by: orderBy
  });
  // Flatten asset_positions into a standard data array for table rendering
  if (result?.data?.data?.asset_positions) {
    result.data.data = result.data.data.asset_positions.map(p => ({
      ...p.position,
      position_type: p.position_type
    }));
  }
  return result;
}

Why this fix works

Normalising the response to a flat array at the API layer means the existing table formatter works without modification — consistent with every other profiler subcommand. --pretty output is unaffected (the transform only fires when data.data.asset_positions exists).

Discovered via

Dogfooding nansen-cli perp commands v1.31.1.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions