Skip to content

feat: per-token on-chain balance breakdown on GET /balance - #262

Merged
ifsantana merged 2 commits into
mainfrom
feat/onchain-balance-breakdown-256
Aug 14, 2026
Merged

feat: per-token on-chain balance breakdown on GET /balance#262
ifsantana merged 2 commits into
mainfrom
feat/onchain-balance-breakdown-256

Conversation

@ifsantana

Copy link
Copy Markdown
Collaborator

Summary

  • Adds a per-token on-chain balance breakdown to GET /api/v1/accounts/{id}/balance and the MCP getBalance tool, alongside the existing fiat amount — additive, no combined total, no cross-unit arithmetic.
  • Touches all 5 modules on the dependency chain: core (BalanceCalculator.computeOnChain), application/infrastructure (Balance.onChainBalances, GetBalanceService), api/sdk-kotlin (OnChainBalanceResponse), mcp (BalanceResult.onChainBalances).
  • Docs: README ### Query balance section and docs/mcp-server.md's BalanceResult signature.

Why: accounts here are chart-of-accounts buckets, not wallets — nothing prevents a single account from receiving both FiatEntry and OnChainEntry postings, and the README's own pitch assumes it. GET /balance was silently reporting 0 for the on-chain side with no indication anything was missing.

Design (confirmed during planning):

  • No single combined total — a token amount and a fiat amount are not fungible units, and nothing else in the codebase treats a stablecoin as fiat-equivalent outside one hardcoded travel-rule threshold table.
  • Grouped by token only (not token+chain) — net across all chains.
  • Additive API — existing BalanceResponse fields are untouched.

Closes #256

Test plan

  • ./mvnw verify — full reactor, all 8 modules, BUILD SUCCESS
  • Live: an account watched by the Alchemy webhook holding a 2.5 USDC OnChainEntry (posted earlier in E2E testing) now returns amount: 0 (fiat, unchanged) alongside onChainBalances: [{token: USDC, amount: 2.5}]
  • idem-examples StablecoinOnChainExample still runs clean against the running instance

Accounts are chart-of-accounts buckets, not wallets — nothing prevents a
single account receiving both FiatEntry and OnChainEntry postings, and the
README's own pitch assumes it ("a single transaction can contain a PIX
debit and a USDC credit on Base"). GET /balance previously reported 0 for
the on-chain side with no indication anything was missing.

Adds a per-token on-chain breakdown alongside the existing fiat amount:
- core: BalanceCalculator.computeOnChain() nets debits/credits per
  StablecoinToken across all chains, mirroring the existing fiat compute()
- application/infrastructure: Balance.onChainBalances, wired through
  GetBalanceService
- api/sdk-kotlin: additive OnChainBalanceResponse field on BalanceResponse
- mcp: getBalance tool result carries the same breakdown for agent callers

No single combined total — fiat and on-chain amounts are never summed;
they are not fungible units and nothing else in the codebase treats a
stablecoin as fiat-equivalent outside one hardcoded travel-rule threshold
table. Additive API change: existing BalanceResponse fields are untouched.

Verified live: an account watched by the Alchemy webhook holding a 2.5
USDC OnChainEntry now returns amount=0 (fiat, unchanged) alongside
onChainBalances=[{token: USDC, amount: 2.5}].

Signed-off-by: ifsantana <flaubert165@gmail.com>
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing

@ifsantana
ifsantana merged commit fe8a0ae into main Aug 14, 2026
1 check passed
ifsantana pushed a commit that referenced this pull request Aug 16, 2026
PR #262 added core.ledger.OnChainBalance and BalanceCalculator.computeOnChain()
for the per-token on-chain balance breakdown, but docs/domain-model.md — the
reference spec for the core module — never mentioned it.

Signed-off-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: per-token on-chain balance breakdown on GET /balance

1 participant