feat(balances): v1-aligned key/token fields; rename balance to total - #138
Merged
Merged
Conversation
…otal Aligns the POST /api/v1/accounts/balances response with the v1 backend pattern (stellar/freighter-backend#319) so the extension and mobile clients no longer re-derive per-asset identity when mapping to the legacy shape (see stellar/freighter#2906 discussion). Every balance entry now carries: - key: the v1 balance-map key — "native", "CODE:ISSUER" (classic/SAC), "SYMBOL:CONTRACT_ID" (SEP-41), "POOLID:lp" (LP shares) - token: the v1 token identity — {type,code} for native, {type,code,issuer:{key}} for classic (type verbatim from the trustline) and SAC (type derived from code length; the SDK carries none), {code,issuer:{key}} without type for SEP-41 (v1 Mercury parity), and omitted for LP entries (v1 has no token there) - total: renamed from "balance"; same raw on-ledger value (v1 exposes total/available, never balance — verified against v1 source and a live prd response) available, token_id, token_type, and all per-variant fields are unchanged; minimum_balance stays the bare base reserve. Covers edge cases: >4-char SAC codes map to credit_alphanum12, nil trustline code/issuer and nil SEP-41 symbol degrade to empty key parts, mirroring the client-side fallbacks this replaces.
aristidesstaffieri
marked this pull request as ready for review
July 16, 2026 19:58
Contributor
There was a problem hiding this comment.
Pull request overview
Aligns account-balance responses with the legacy v1 wire shape.
Changes:
- Adds server-derived
keyandtokenfields. - Renames
balancetototal. - Expands mapping and wire-contract tests.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
internal/types/account_balances.go |
Defines v1-aligned response fields and token types. |
internal/types/account_balances_test.go |
Verifies the updated JSON contract. |
internal/services/account_balances_mapping.go |
Derives keys and token identities per balance variant. |
internal/services/account_balances_mapping_test.go |
Tests mappings and optional-field edge cases. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+28
to
+31
| // shares, which carry no token in v1). Total is the raw on-ledger amount and | ||
| // Available is the spendable portion (total minus the reserved amount for | ||
| // native/classic; equal to total for contract tokens and pool shares). Both | ||
| // are Stellar amount strings so JavaScript clients never lose precision. |
aditya1702
approved these changes
Jul 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Aligns the
POST /api/v1/accounts/balancesresponse with the v1 backend pattern: every balance entry now carries a server-derivedkeyandtoken, and the on-ledger amount is exposed astotalinstead ofbalance. These are exactly the fields the extension and mobile currently re-derive client-side when mapping v2 responses into the legacy v1 shape (see the discussion on stellar/freighter#2906) — with them server-provided, clients need only a generic snake_case→camelCase + string→BigNumber pass, with no per-asset identity logic to keep in sync across repos.Verified against v1 before renaming: v1 exposes
total/availableand neverbalance(confirmed in the v1 source and a live prd response), andtotalis the raw on-ledger amount — so this is a pure rename, value unchanged.Breaking wire change:
balanceis removed from balance entries. Deliberately out of scope:minimum_balancestays the bare base reserve andavailablesemantics are unchanged — the extension mapper already compensates for both.Closes #137
What's in this PR
internal/types/account_balances.go— newToken/TokenIssuertypes;BalanceBasegainskeyand optionaltoken;balance→total.internal/services/account_balances_mapping.go— per-variantkey/tokenderivation; SAC asset type derived from code length (the SDK carries none), trustline type passed through verbatim.internal/types/account_balances_test.go— wire-contract assertions forkey/token/total; forbids"balance":; SEP-41 token is typeless; LP entries carry no token.internal/services/account_balances_mapping_test.go— expectations updated; new edge cases: >4-char SAC code →credit_alphanum12, nil trustline code/issuer, nil SEP-41 symbol.Wire shape
Key formats:
native,CODE:ISSUER(classic/SAC),SYMBOL:CONTRACT_ID(SEP-41),POOLID:lp(LP shares). Example native entry:{ "key": "native", "token": { "type": "native", "code": "XLM" }, "total": "100.0000000", "available": "98.0000000", "token_id": "native", "token_type": "NATIVE", "minimum_balance": "1.5000000", ... }SEP-41 tokens get
{ "code": "<symbol>", "issuer": { "key": "<contract_id>" } }with notype, and liquidity-pool entries have akeybut notoken— both matching v1.Test plan
make unit-test,make check(fmt/vet/golangci-lint/shadow/deadcode),make buildall green locallykey/token/totalper variant and absence ofbalancekey/tokenvalues cross-checked against what the extension'smapAccountBalancesV2.ts(feat(src): balances v2 migration freighter#2906) derives todaybalance→totalrename is breakingFollowups
totaldirectly.availablealso subtractsminimum_balance(v1 subtracts only selling liabilities); revisit only if clients stop compensating.🤖 Generated with Claude Code