This is exactly how the Railnet REST API computes
total_rewards_in_asset on its positions endpoint. If you just need the number, call the API; this page is for integrators who want to compute it themselves or understand what the API returns.What you need from the chain
Everything comes from events the Conduit and its query registry emit, all of which are exposed by the indexer GraphQL API:
A deposit pulls assets in and pushes shares out when it settles. A redemption pulls shares in and pushes assets out. Rejected operations push the input back, so refunds take care of themselves.
The formula
Where:- balance value — the wallet’s share balance at the current share price.
- assets in — every asset amount ever pulled from the wallet, settled or not.
- assets out — every asset amount ever pushed out, settled or not, excluding amounts pushed to the fee manager (those are fees, not the user’s money).
- pending deposits / pending redemptions — one claim per in-flight operation, computed below.
Valuing an in-flight claim
For each operation that is not yet in a final state:- For a deposit: committed is the assets pulled in; delivered is the shares minted so far, valued at the current share price.
- For a redemption: committed is the shares burnt; delivered is the asset tranches paid out so far.
Pricing the redemption’s shares: use the rate at request time
The one subtle step. The burnt shares of a pending redemption must be valued at: A redemption’s value typically crystallizes at the moment the shares are burnt. An Allocation Strategy’s redemption queue, for example, stamps each queued request with a maximum payout computed at the request-time price — a hard cap. If the share price rises while the request waits in the queue, the payout does not rise with it. Valuing the claim at the current price would therefore show yield the user will never receive; it would silently evaporate when the redemption settles. Themin handles both directions:
- Price rose since the request → value at the request-time price. No phantom yield.
- Price fell since the request → value at the current price. Queues socialise losses into pending requests, so the lower value is the honest one.
min still does the right thing: the claim is marked at the request-time price, and the growth surfaces the moment the payout lands, when real flows replace the claim. The books are exact either way; the marking only decides when in-flight growth becomes visible, and it errs on the side of never showing yield before it is guaranteed. The same self-healing covers the case where no request-time price is available at all: fall back to the current price, and settlement trues everything up.
A worked example
A user deposits 5 USDC into a Conduit at a share price of 1.00, and later redeems everything.
Two things to notice:
- Rewards never dip negative while the money is in flight, and never jump from double counting. Every intermediate row reports the same +0.10 the user actually earned.
- Once the operation settles, the claim term is zero and rewards are computed purely from real flows. Whatever approximation the in-flight marking carried, the final number is exact.
Putting it together
Pseudo-code for one wallet on one Conduit, using the entities served by the indexer GraphQL API. Amounts are minimum units;value(shares, rate) converts shares to assets at a rate expressed as assets per share.
Pseudo-code