> ## Documentation Index
> Fetch the complete documentation index at: https://developers.paxoslabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# List User Positions

> Read a user share balance, position value in the vault base asset, and current exchange rate per chain

`GET /v2/amplify/userPositions` returns a user's `shareBalance`, `positionValueInBase`, `baseAssetAddress`, and `exchangeRateToBase` per chain each vault is deployed on. Use it to render a user's holdings in a portfolio or vault detail view without re-implementing the share-to-base math client-side.

`positionValueInBase` is computed as `shareBalance * exchangeRateToBase` at the accountant contract's current rate, so the value reflects up-to-the-block on-chain state.

<Info>
  `userAddress` is required as a top-level query parameter. `filter` is a
  single AIP-160-style query parameter and is optional. The flags inside the
  filter are also optional: `vaultAddress` (hex) scopes to one vault,
  `chainId` (number) scopes to one chain. With no filter, the user's position
  is returned for every live vault deployment.
</Info>

<Info>
  Some vaults are deployed as **stand-in** vaults on a chain where the canonical
  base asset isn't natively available (e.g. soohoUSDGt on Base). In that case
  the endpoint reports `baseAssetAddress` from the on-chain `accountant.base()`
  call. The endpoint transparently falls back to `accountant.base()` whenever
  the DB has no `baseAssetAddress` for that deployment.
</Info>

<Info>
  Each response includes a `tokenMetadata` map keyed by lowercase address,
  populated for the `vaultAddress` and `baseAssetAddress` returned in
  `userPositions[]`. Use it to render token symbols/decimals without making a
  separate metadata call.
</Info>

## Parameters

| Parameter     | Type     | Required | Description                                                                                                                                                                                                          |
| ------------- | -------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `userAddress` | `string` | Yes      | User wallet address (hex). Top-level query parameter — not part of the filter.                                                                                                                                       |
| `filter`      | `string` | No       | AIP-160-style filter. Optional flags: `vaultAddress` (hex), `chainId` (number). Both support `OR`. With no filter, every live vault deployment is returned. Example: `vaultAddress=0xbbbb...0001 AND chainId=84532`. |
| `pageSize`    | `number` | No       | Maximum items per page. Default: 25. Min: 1, max: 100.                                                                                                                                                               |
| `pageToken`   | `string` | No       | Opaque cursor returned as `nextPageToken` on the previous response. Omit for the first page.                                                                                                                         |

## Example

<Tabs>
  <Tab title="curl">
    ```bash theme={null}
    # Every live deployment for this user
    curl --get "https://api.paxoslabs.com/v2/amplify/userPositions" \
      --data-urlencode "userAddress=0xdEADdE9539A00Bbd9A8494f45EB38aEe89d7C001" \
      -H "x-api-key: pxl_your_key"

    # Scoped to one vault and one chain
    curl --get "https://api.paxoslabs.com/v2/amplify/userPositions" \
      --data-urlencode "userAddress=0xdEADdE9539A00Bbd9A8494f45EB38aEe89d7C001" \
      --data-urlencode "filter=vaultAddress=0xbbbb000000000000000000000000000000000001 AND chainId=84532" \
      -H "x-api-key: pxl_your_key"
    ```
  </Tab>

  <Tab title="Node.js">
    ```ts theme={null}
    const url = new URL("https://api.paxoslabs.com/v2/amplify/userPositions");
    url.searchParams.set(
      "userAddress",
      "0xdEADdE9539A00Bbd9A8494f45EB38aEe89d7C001"
    );
    // Optional: scope to one vault and chain.
    url.searchParams.set(
      "filter",
      "vaultAddress=0xbbbb000000000000000000000000000000000001 AND chainId=84532"
    );

    const resp = await fetch(url, {
      headers: { "x-api-key": process.env.AMPLIFY_API_KEY! },
    });
    const { userPositions } = await resp.json();
    for (const p of userPositions) {
      console.log(
        `${p.chainId}: ${p.shareBalance} shares = ${p.positionValueInBase} (rate ${p.exchangeRateToBase})`
      );
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import requests

    resp = requests.get(
        "https://api.paxoslabs.com/v2/amplify/userPositions",
        headers={"x-api-key": "pxl_your_key"},
        params={
            "userAddress": "0xdEADdE9539A00Bbd9A8494f45EB38aEe89d7C001",
            # filter is optional — omit to return every live deployment.
            "filter": (
                "vaultAddress=0xbbbb000000000000000000000000000000000001 "
                "AND chainId=84532"
            ),
        },
    )
    for p in resp.json()["userPositions"]:
        print(
            f"{p['chainId']}: {p['shareBalance']} shares = "
            f"{p['positionValueInBase']} (rate {p['exchangeRateToBase']})"
        )
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    // filter is optional — omit to return every live deployment.
    params := url.Values{
        "userAddress": {"0xdEADdE9539A00Bbd9A8494f45EB38aEe89d7C001"},
        "filter":      {"vaultAddress=0xbbbb000000000000000000000000000000000001 AND chainId=84532"},
    }
    req, _ := http.NewRequest("GET",
        "https://api.paxoslabs.com/v2/amplify/userPositions?"+params.Encode(),
        nil)
    req.Header.Set("x-api-key", os.Getenv("AMPLIFY_API_KEY"))
    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()
    ```
  </Tab>
</Tabs>

## Response

```json theme={null}
{
  "userPositions": [
    {
      "vaultAddress": "0xbbbb000000000000000000000000000000000001",
      "chainId": 84532,
      "shareBalance": "0.1",
      "positionValueInBase": "0.101811",
      "baseAssetAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
      "exchangeRateToBase": "1.018116"
    }
  ],
  "nextPageToken": null,
  "tokenMetadata": {
    "0xbbbb000000000000000000000000000000000001": {
      "symbol": "soohoUSDG",
      "decimals": 6
    },
    "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": {
      "symbol": "USDC",
      "decimals": 6
    }
  }
}
```

| Field                                 | Type               | Description                                                                                                 |
| ------------------------------------- | ------------------ | ----------------------------------------------------------------------------------------------------------- |
| `userPositions[].vaultAddress`        | `string`           | BoringVault contract address (lowercased)                                                                   |
| `userPositions[].chainId`             | `number`           | EVM chain ID for this entry                                                                                 |
| `userPositions[].shareBalance`        | `string` (decimal) | User share-token balance, human-readable (scaled by share decimals)                                         |
| `userPositions[].positionValueInBase` | `string` (decimal) | Position value denominated in the vault base asset (`shareBalance * exchangeRateToBase`), human-readable    |
| `userPositions[].baseAssetAddress`    | `string`           | Vault base asset contract address (lowercased); falls back to `accountant.base()` for stand-in vaults       |
| `userPositions[].exchangeRateToBase`  | `string` (decimal) | Exchange rate of one share to the base asset, at the accountant contract's current precision                |
| `nextPageToken`                       | `string \| null`   | Cursor for the next page. `null` when there are no more results.                                            |
| `tokenMetadata`                       | `object`           | Map keyed by lowercase address with `{ symbol, decimals, ... }`. Missing metadata is represented as `null`. |

## Error Responses

| Status | Meaning                                                                                                                                                        |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400    | Missing or invalid `userAddress`, invalid `filter` (invalid hex address, unknown flag, non-positive `chainId`, malformed segment), or `pageSize` outside 1–100 |
| 503    | Upstream RPC unavailable                                                                                                                                       |
