quantity
mirrors the outcome tokens your vault holds on-chain (trades, splits,
merges and redemptions all move it); cost basis and P&L are computed
from your trade, split and merge history.
Reading positions
positions — the response is an object, not a bare
array. Every stats metric carries { value, status }; status is
unavailable when a metric could not be computed exactly, so branch on it
rather than trusting a 0. nextCursor / hasMore page it; omit limit and cursor to get the
full list in one call.
Field semantics
-
quantity— outcome shares your vault holds on-chain for this outcome (decimal string, up to 6 decimals, trailing zeros trimmed:"250","250.5"). It is net of the in-kind BUY fee and is moved by splits, merges and redemptions as well as trades. When a market resolves YES, every YES share redeems for 1 USDC, so this number is also your maximum payout if the outcome wins. -
avgCost— weighted average cost per share held (decimal USDC): the cash you paid divided by the shares you actually received.null, withaccountingStatus: "unavailable", when the cost basis cannot be computed exactly. -
currentPrice— current price of this outcome token (decimal string);null, withpricingStatus: "unavailable", when no price is available. -
realizedPnl— P&L already booked: on each SELL, the proceeds minus the sell fee and the cost of the shares sold; once a resolved position has been redeemed (or has lost), also its payout minus its cost. -
unrealizedPnl—markValue − costBasisatcurrentPrice;nullwhen the price or the cost basis is unavailable. -
feesPaidUsdc— lifetime trading fees paid on this (market, outcome) in USDC (decimal string,"0"when none). BUY fees are taken in shares and converted to USDC face value; SELL fees are USDC. It is already reflected in the other figures — a BUY fee shows up as a higheravgCost(fewer shares received for the same cash), and a SELL fee is deducted fromrealizedPnl— so don’t subtract it again; it is reported for display and fee reconciliation. -
feesPaidShares— lifetime outcome-token fees taken on BUY legs (decimal string,"0"when none; same scale asquantity). The in-kind haircut: you matched N shares and received N −feesPaidShareson chain. -
feesPaidSharesPrice— the price the BUY-leg token fee was valued at, sofeesPaidUsdccan be split into its in-kind and cash halves:It is a share-weighted average across the position’s BUY legs, and it is notfeesPaidUsdc / feesPaidShares— that identity breaks as soon as the position has a SELL-leg cash fee.nullwhen no token fee was ever taken;feesPaidSharesis then"0", sotokenFeeUsdis 0 and all offeesPaidUsdcis cash. Branch on the null rather than evaluating the formula through it — any wallet that has only ever sold has that shape.
On MINT fills (both sides buying, settled via
splitPosition)
these three fields are computed from the backend fee ledger at the
matched price, which can differ from the amount actually taken on
chain. Treat them as indicative rather than chain-exact for those
fills; /api/me/fees carries the per-fill rows behind them.Redeeming a resolved position
With auto-redeem on (the default), the platform redeems voided-market refunds and winnings of at least 1 USDC per market into your vault within a few minutes, and those positions turnredeemed without a call from you. Whatever is still
redeemable — smaller winnings, or anything while auto-redeem is off —
you claim yourself. Auto-redeem also waits while
negRiskLinkagePending is true. The default open view lists
neither kind: pass status=redeemable to list what is still to claim
and status=redeemed for what has been claimed.
Once a market resolves, six fields drive the claim flow — plus the
negRiskLinkagePending fail-safe flag below. They are all on the
/api/me/positions response; redeemable is the gate.
Pick the redeem call from the data, not from the market’s name — but check
negRiskLinkagePending first, because it is the one case where negRisk
being null does not mean “binary”:
negRiskLinkagePending == true→ no redeem call is correct yet. See the warning below.- else
negRisk == null→ the binary path (conditionId). - else → the neg-risk path (
negRisk.marketId,negRisk.questionIndex).
Split / Merge
Availability: split / merge endpoints are available via partner
onboarding. The flow requires dual-signature calls to the user’s
vault.
Split — USDC → full outcome set
amount is human-readable USDC. On a neg-risk market also send
outcomeIndex (the outcome whose YES + NO pair to mint). The response
carries signedOpId, the backend co-signature backendSig, and the
exact payload the vault verifies: kind, collateralToken,
conditionId, partition, amount (6-decimal units), salt and
deadline. Sign that SplitPosition typed data with the vault owner’s
key, then before deadline either post { signedOpId, ownerSig, backendSig }
to POST /api/me/positions/op-submit (the platform broadcasts it and
pays the gas) or call vault.splitPosition(kind, collateralToken, conditionId, partition, amount, salt, deadline, ownerSig, backendSig)
yourself. Typed-data layout and domain:
Vault contract reference.
Result: 100 USDC → 100 YES + 100 NO (binary).
Merge — full outcome set → USDC
Symmetric:POST /api/vault/merge-signature with the same body, sign
MergePositions, then submit the same way (or call
vault.mergePositions(...)). Burns 1 YES + 1 NO, returns 1 USDC.
Both operations are dual-sig, state-machine-driven, and audited.