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.
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.
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.
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.
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
# 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"
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})`
);
}
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']})"
)
// 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()
Response
{
"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 |