Balances and earnings
Show each customer's live positions, movement history, and exact earnings, and never show a missing value as zero.
Read balances from live positions and history from recorded movements. Each
per-wallet read takes the customer's wallet as ownerAddress, so your
backend decides which wallet a signed-in customer may see. The examples read
it from OWNER_ADDRESS, as in Deposit funds.
Set SDP_API_URL and SDP_API_KEY as shown in the
Quickstart.
Positions
GET /v1/earn/external-wallet/positions
returns the wallet's live positions.
| Field | Show it as |
|---|---|
id | The position ID. Withdrawals name it. |
label | The strategy's display name. |
shares | The wallet's live share balance. |
withdrawableShares | The shares that can leave right now. |
tokenValue | The live value in the deposit token (tokenMint). Every Earn deposit token is a USD stablecoin, so at par this reads as dollars. |
unlockTimestamp | When locked shares become withdrawable, in Unix seconds. Null or absent when nothing is locked. |
Positions come newest first, and repeat deposits into one strategy add to the same position.
- A missing live value is unavailable. When the provider read fails,
shares,withdrawableShares, andtokenValueare left out. Show "Unavailable" and disable withdrawals until a fresh read succeeds. Never show zero or an old quote. - A wallet with no positions returns an empty list, not
404. A fully withdrawn position drops out of the list. - An open withdrawal request holds shares outside the position. They
leave
sharesandtokenValueuntil the request pays out or is canceled, so show itsquotedAssetsas an expected payout. parIntermediateappears while the wallet holds an operator redemption's intermediate token outside an open request. It keeps the position open and counts toward earnings'currentValue. See Operator redemptions.
Movements
Each deposit and withdrawal is a movement. Read one with
GET /v1/earn/external-wallet/movements/{movementId},
or list a wallet's history, newest first, with
GET /v1/earn/external-wallet/movements.
Show tokenAmount in tokenMint units for both directions. On a withdrawal,
amount is the share quantity, not the payout, and tokenAmount stays null
until SDP observes the payout. confirmedAt is when the network confirmed
the movement, and settledAt is when it finalized. Label requested amounts
and expected queue payouts as such.
How a movement settles explains
each status and settlement.
Earnings
GET /v1/earn/external-wallet/earnings
returns the wallet's balance and earnings for each deposit token.
currentValue is the sum of the positions' tokenValue, plus any
intermediate tokens. earned is
currentValue + totalWithdrawn - totalDeposited, and it appears only when it
is exact. It can be negative: right after a deposit, the strategy's share
rounding can leave it slightly below zero. When it is left out,
earnedUnavailableReason says why:
| Reason | Meaning |
|---|---|
live_value_unavailable | A position's live value could not be read. |
movements_pending | A deposit or withdrawal is still settling. |
withdrawals_pending | An open withdrawal request holds shares that have not paid out yet. |
deposits_not_valued | SDP did not observe a finalized deposit's amount. |
withdrawals_not_valued | SDP did not observe a finalized withdrawal's payout. |
Show a dash, never $0, while earned is missing. Three limits apply:
- Only held positions count. A fully withdrawn position drops out of earnings but stays in the movement history.
- Outside transfers count. The live value reads the wallet's whole balance in the strategy, including shares acquired outside SDP, while deposits and withdrawals come only from SDP's records.
- It is not an accounting ledger. It gives no lifetime realized returns or daily accrual history. Reconcile against your own records for statements.
Position totals
GET /v1/earn/external-wallet/positions/summary
returns live totals across every customer wallet in your project, by token and
by strategy. It takes no ownerAddress.
Wallet addresses are personal data, so they are left out unless you pass
includeOwnerAddresses=true. Add includePositions=true to also get each
wallet's positions; it requires the address opt-in, or the request returns
400.
Never send this summary, or your API key, to a customer's browser.
Paginate every list
| List | Paging |
|---|---|
| Strategies | page and pageSize (default 20, up to 100), until you have total. |
| Positions, movements, withdrawal requests | limit (default 20, up to 100) and before, set to the previous page's nextCursor, until hasMore is false. |
Treat a missing or repeated cursor as an error: a silently short list hides a
customer's money. Earn lists differ from the shared
Pagination conventions: the catalog
returns total, page, and pageSize inside data, and the cursor lists
take their cursor as before.