Skip to content

feat: suffix balance/amount fields with explicit unit (Sat or Msat)#2153

Open
Anshumancanrock wants to merge 1 commit intogetAlby:masterfrom
Anshumancanrock:suffix
Open

feat: suffix balance/amount fields with explicit unit (Sat or Msat)#2153
Anshumancanrock wants to merge 1 commit intogetAlby:masterfrom
Anshumancanrock:suffix

Conversation

@Anshumancanrock
Copy link
Contributor

@Anshumancanrock Anshumancanrock commented Mar 18, 2026

Summary

Several API response fields had ambiguous names like balance, amount, fee, localBalance , with no indication of whether the value is in sats or millisats. This makes it easy for clients to misinterpret values and introduce subtle bugs.

What this PR does

Fixes #2146

Adds a new suffixed field alongside every ambiguous numeric property so the unit is always explicit:

  • balance → also returns balanceMsat
  • amount → also returns amountMsat
  • feesPaid → also returns feesPaidMsat
  • localBalance → also returns localBalanceMsat
  • sendAmount → also returns sendAmountSat
  • etc.

Summary by CodeRabbit

  • New Features
    • Extended API responses with satoshi (*Sat) and millisatoshi (*Msat) denomination fields for balances, amounts, and fees.
    • Updated frontend types to provide explicit unit variants alongside existing balance and transaction data.
    • Changes applied across app balances, channel liquidity, swap operations, transaction fees, and balance queries for all lightning client implementations.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 18, 2026

Warning

Rate limit exceeded

@Anshumancanrock has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 4 minutes and 8 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d73d26de-b04a-4e60-b07f-dd8058f7dd6a

📥 Commits

Reviewing files that changed from the base of the PR and between 477b3a0 and 144b8bc.

📒 Files selected for processing (11)
  • api/api.go
  • api/lsp.go
  • api/models.go
  • api/transactions.go
  • frontend/src/types.ts
  • lnclient/cashu/cashu.go
  • lnclient/ldk/ldk.go
  • lnclient/lnd/lnd.go
  • lnclient/models.go
  • lnclient/phoenixd/phoenixd.go
  • tests/mock_ln_client.go
📝 Walkthrough

Walkthrough

This PR systematically adds satoshi (*Sat) and millisatoshi (*Msat) denomination-specific fields to API response models and their implementations across the Go backend and TypeScript frontend. Existing fields and logic remain unchanged; new fields are derived from present values without introducing breaking changes.

Changes

Cohort / File(s) Summary
API Response Model Definitions
api/models.go, lnclient/models.go, frontend/src/types.ts
Added *Sat and *Msat field declarations to balance, swap, channel, and transaction-related types across multiple response models (e.g., App, Channel, OnchainBalanceResponse, LightningBalanceResponse, Transaction, LSPOrderResponse, SwapInfoResponse).
API Implementation
api/api.go, api/lsp.go, api/transactions.go
Populated new *Sat and *Msat fields in response payloads by deriving them from existing values: *Msat computed as base * 1000 or mirrored from existing msat fields, *Sat computed as base / 1000 or mirrored from base satoshi values. Affected endpoints: GetApp, ListApps, ListChannels, GetAutoSwapConfig, toApiSwap, GetSwapInInfo/GetSwapOutInfo, and RequestLSPOrder.
LN Client Implementations
lnclient/lnd/lnd.go, lnclient/ldk/ldk.go, lnclient/cashu/cashu.go, lnclient/phoenixd/phoenixd.go
Extended balance response population in GetOnchainBalance and GetBalances across all client implementations to include new *Sat and *Msat fields for spendable, total, reserved, and pending balance breakdowns, using consistent derivation patterns (msat = satoshi × 1000).
Test Fixtures
tests/mock_ln_client.go
Updated mock LightningBalanceResponse data to include newly added TotalSpendableSat and TotalSpendableMsat fields with consistent test values.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • rolznz
  • reneaaron

Poem

🐰 With whiskers twitching, I hop with glee,
Sat and Msat fields, for all to see!
No more confusion 'bout the denomination's size,
Balance clarity now meets each user's eyes. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely describes the main change: adding explicit unit suffixes (Sat or Msat) to balance/amount fields across the codebase.
Linked Issues check ✅ Passed The PR successfully implements all requirements from issue #2146: adds unit-suffixed fields (Sat/Msat variants) throughout API responses without breaking changes, avoids database modifications, and provides deterministic unit identification.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the stated objective of adding unit-suffixed numeric fields; no unrelated modifications to error handling, business logic, or other systems are present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
api/models.go (1)

88-106: ⚠️ Potential issue | 🟡 Minor

App still ships ambiguous sat fields.

Line 98 already knows this value is sats (MaxAmountSat), but it is still serialized as maxAmount, and budgetUsage on Line 99 is still unitless too. Adding balanceMsat makes this payload only partially explicit; clients will still need special cases for two common app amounts. Add maxAmountSat/budgetUsageSat aliases or rename the JSON tags.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/models.go` around lines 88 - 106, The App struct exposes ambiguous sat
fields; change the JSON tags on MaxAmountSat and BudgetUsage to explicit sat
names (e.g., update MaxAmountSat `json:"maxAmount"` → `json:"maxAmountSat"` and
BudgetUsage `json:"budgetUsage"` → `json:"budgetUsageSat"`) or add alias fields
if backward compatibility is required so clients receive explicit sat units;
update any code that marshals/unmarshals App to use the new JSON keys
(references: struct App, fields MaxAmountSat and BudgetUsage, and consider
BalanceMsat consistency).
🧹 Nitpick comments (1)
lnclient/models.go (1)

196-199: Avoid publishing new ambiguous MPP balance fields.

Lines 196-199 add NextMaxSpendableMPP and NextMaxReceivableMPP right next to the explicit *MPPMsat fields. Since api/models.go:365 aliases lnclient.BalancesResponse, these tags go straight into the public HTTP schema, and the newly expanded surface is ambiguous again. Prefer exposing only the explicit-unit keys, or deprecate the unsuffixed aliases immediately.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lnclient/models.go` around lines 196 - 199, The new unsuffixed balance fields
NextMaxSpendableMPP and NextMaxReceivableMPP are ambiguous and will be published
via the lnclient.BalancesResponse alias used in api/models.go:365; remove or
hide those two fields from the public JSON surface by deleting them from
lnclient/models.go (or changing their struct tags to json:"-" and adding a
deprecation comment) and keep only the explicit-unit fields
NextMaxSpendableMPPMsat and NextMaxReceivableMPPMsat; ensure any internal code
referencing NextMaxSpendableMPP/NextMaxReceivableMPP is updated to use the
explicit-suffixed fields or converted appropriately.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@api/models.go`:
- Around line 88-106: The App struct exposes ambiguous sat fields; change the
JSON tags on MaxAmountSat and BudgetUsage to explicit sat names (e.g., update
MaxAmountSat `json:"maxAmount"` → `json:"maxAmountSat"` and BudgetUsage
`json:"budgetUsage"` → `json:"budgetUsageSat"`) or add alias fields if backward
compatibility is required so clients receive explicit sat units; update any code
that marshals/unmarshals App to use the new JSON keys (references: struct App,
fields MaxAmountSat and BudgetUsage, and consider BalanceMsat consistency).

---

Nitpick comments:
In `@lnclient/models.go`:
- Around line 196-199: The new unsuffixed balance fields NextMaxSpendableMPP and
NextMaxReceivableMPP are ambiguous and will be published via the
lnclient.BalancesResponse alias used in api/models.go:365; remove or hide those
two fields from the public JSON surface by deleting them from lnclient/models.go
(or changing their struct tags to json:"-" and adding a deprecation comment) and
keep only the explicit-unit fields NextMaxSpendableMPPMsat and
NextMaxReceivableMPPMsat; ensure any internal code referencing
NextMaxSpendableMPP/NextMaxReceivableMPP is updated to use the explicit-suffixed
fields or converted appropriately.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ee4d1014-7010-4769-8e4f-65d22dd8d189

📥 Commits

Reviewing files that changed from the base of the PR and between 956c841 and 46c3b07.

📒 Files selected for processing (11)
  • api/api.go
  • api/lsp.go
  • api/models.go
  • api/transactions.go
  • frontend/src/types.ts
  • lnclient/cashu/cashu.go
  • lnclient/ldk/ldk.go
  • lnclient/lnd/lnd.go
  • lnclient/models.go
  • lnclient/phoenixd/phoenixd.go
  • tests/mock_ln_client.go

return nil, err
}
response.Balance = balance
response.BalanceMsat = balance
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if we have an addition field for Sat too. That way the client can choose whether it wants to use the sat or msat field. The existing e.g. Balance field is unclear if it's sats or msats.

@rolznz
Copy link
Contributor

rolznz commented Mar 19, 2026

Thanks for the PR! I think there are some inconsistencies currently which need to be resolved. And we need to carefully review to ensure there are no breaking changes on the API level, only additions.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
api/api.go (1)

1214-1228: ⚠️ Potential issue | 🟠 Major

Update Cashu and Phoenixd GetBalances to populate all OnchainBalanceResponse fields.

The Cashu and Phoenixd implementations are incomplete. They only populate 4 fields (Spendable, SpendableSat, Total, TotalSat) with zeros and hardcoded values, while the OnchainBalanceResponse struct defines 15 fields. LDK and LND correctly populate all fields including SpendableMsat, TotalMsat, Reserved*, PendingBalances*, and InternalBalances.

This creates API inconsistency across backends. Per the interface design pattern, all four backend implementations (LDK, LND, Phoenixd, Cashu) must maintain consistent return values for the LNClient interface.

  • Cashu (lnclient/cashu/cashu.go, lines 270-275): Missing SpendableMsat, TotalMsat, Reserved*, PendingBalances*, InternalBalances
  • Phoenixd (lnclient/phoenixd/phoenixd.go, lines 116-121): Missing SpendableMsat, TotalMsat, Reserved*, PendingBalances*, InternalBalances

Update both implementations to return the complete struct with meaningful values where applicable, following the LDK and LND patterns.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/api.go` around lines 1214 - 1228, The Cashu and Phoenixd GetBalances
implementations are returning only Spendable, SpendableSat, Total and TotalSat;
update the GetBalances functions in the cashu and phoenixd LN client
implementations (the GetBalances methods in lnclient/cashu/cashu.go and
lnclient/phoenixd/phoenixd.go) to populate the full OnchainBalanceResponse
struct (include SpendableMsat, TotalMsat, ReservedOnchain, ReservedOnchainSat,
ReservedOnchainMsat, PendingBalancesOnchain, PendingBalancesOnchainSat,
PendingBalancesOnchainMsat, InternalBalances, InternalBalancesSat,
InternalBalancesMsat) following the same semantics used in LDK/LND: convert sat
-> msat (msat = sat*1000) for msat fields, fill Reserved*/Pending*/Internal*
with meaningful values (zero where not applicable) and ensure consistency
between sat and msat fields and between Spendable/Total and their sat/msat
counterparts so the LNClient GetBalances contract is met.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@api/api.go`:
- Around line 1214-1228: The Cashu and Phoenixd GetBalances implementations are
returning only Spendable, SpendableSat, Total and TotalSat; update the
GetBalances functions in the cashu and phoenixd LN client implementations (the
GetBalances methods in lnclient/cashu/cashu.go and
lnclient/phoenixd/phoenixd.go) to populate the full OnchainBalanceResponse
struct (include SpendableMsat, TotalMsat, ReservedOnchain, ReservedOnchainSat,
ReservedOnchainMsat, PendingBalancesOnchain, PendingBalancesOnchainSat,
PendingBalancesOnchainMsat, InternalBalances, InternalBalancesSat,
InternalBalancesMsat) following the same semantics used in LDK/LND: convert sat
-> msat (msat = sat*1000) for msat fields, fill Reserved*/Pending*/Internal*
with meaningful values (zero where not applicable) and ensure consistency
between sat and msat fields and between Spendable/Total and their sat/msat
counterparts so the LNClient GetBalances contract is met.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 52dc374b-226a-4014-9da9-be41349de925

📥 Commits

Reviewing files that changed from the base of the PR and between 46c3b07 and e2aa9b0.

📒 Files selected for processing (12)
  • PR_DESCRIPTION.md
  • api/api.go
  • api/lsp.go
  • api/models.go
  • api/transactions.go
  • frontend/src/types.ts
  • lnclient/cashu/cashu.go
  • lnclient/ldk/ldk.go
  • lnclient/lnd/lnd.go
  • lnclient/models.go
  • lnclient/phoenixd/phoenixd.go
  • tests/mock_ln_client.go
✅ Files skipped from review due to trivial changes (2)
  • PR_DESCRIPTION.md
  • frontend/src/types.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • api/transactions.go
  • lnclient/phoenixd/phoenixd.go
  • tests/mock_ln_client.go
  • lnclient/cashu/cashu.go
  • lnclient/models.go
  • api/models.go

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
lnclient/cashu/cashu.go (1)

280-291: Explicitly set receivable *Sat fields instead of relying on zero-values.

TotalReceivableSat, NextMaxReceivableSat, and NextMaxReceivableMPPSat are currently implicit. Please set them explicitly for consistency and future maintainability.

Proposed patch
 		Lightning: lnclient.LightningBalanceResponse{
 			TotalSpendable:           balance,
 			TotalSpendableSat:        balance / 1000,
 			TotalSpendableMsat:       balance,
 			TotalReceivable:          0,
+			TotalReceivableSat:       0,
 			TotalReceivableMsat:      0,
 			NextMaxSpendable:         balance,
 			NextMaxSpendableSat:      balance / 1000,
 			NextMaxSpendableMsat:     balance,
 			NextMaxReceivable:        0,
+			NextMaxReceivableSat:     0,
 			NextMaxReceivableMsat:    0,
 			NextMaxSpendableMPP:      balance,
 			NextMaxSpendableMPPSat:   balance / 1000,
 			NextMaxSpendableMPPMsat:  balance,
 			NextMaxReceivableMPP:     0,
+			NextMaxReceivableMPPSat:  0,
 			NextMaxReceivableMPPMsat: 0,
 		},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lnclient/cashu/cashu.go` around lines 280 - 291, The struct literal in
cashu.go initializes multiple receivable and spendable fields but leaves the sat
variants implicit; explicitly add TotalReceivableSat, NextMaxReceivableSat, and
NextMaxReceivableMPPSat (set them to 0) alongside the existing fields (e.g.,
TotalReceivable, NextMaxReceivable, NextMaxReceivableMPP) so the initialization
is consistent and future-proof when editing functions like the
constructor/initializer that builds this balance object.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@api/lsp.go`:
- Around line 84-86: invoiceAmount was truncated to whole sats before computing
InvoiceAmountMsat, causing loss of sub-sat precision; instead preserve the
original decoded millisat amount (e.g., decodedMsat or invoiceMsatDecoded
captured from the decoder) and use that for the InvoiceAmountMsat field while
keeping InvoiceAmount and InvoiceAmountSat set to the truncated sats variable
(invoiceAmount). Update the struct assignment where InvoiceAmountMsat is set
(the InvoiceAmountMsat field in the code near the InvoiceAmount/InvoiceAmountSat
assignments) to use the preserved decoded msat variable rather than
invoiceAmount*1000.

---

Nitpick comments:
In `@lnclient/cashu/cashu.go`:
- Around line 280-291: The struct literal in cashu.go initializes multiple
receivable and spendable fields but leaves the sat variants implicit; explicitly
add TotalReceivableSat, NextMaxReceivableSat, and NextMaxReceivableMPPSat (set
them to 0) alongside the existing fields (e.g., TotalReceivable,
NextMaxReceivable, NextMaxReceivableMPP) so the initialization is consistent and
future-proof when editing functions like the constructor/initializer that builds
this balance object.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 53521dc9-b094-4d49-b9b0-f4ee44d15025

📥 Commits

Reviewing files that changed from the base of the PR and between e2aa9b0 and 477b3a0.

📒 Files selected for processing (11)
  • api/api.go
  • api/lsp.go
  • api/models.go
  • api/transactions.go
  • frontend/src/types.ts
  • lnclient/cashu/cashu.go
  • lnclient/ldk/ldk.go
  • lnclient/lnd/lnd.go
  • lnclient/models.go
  • lnclient/phoenixd/phoenixd.go
  • tests/mock_ln_client.go
✅ Files skipped from review due to trivial changes (2)
  • tests/mock_ln_client.go
  • lnclient/lnd/lnd.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • lnclient/phoenixd/phoenixd.go
  • lnclient/models.go
  • lnclient/ldk/ldk.go
  • api/api.go
  • frontend/src/types.ts

@Anshumancanrock
Copy link
Contributor Author

@rolznz Thanks for the review. I’ve addressed the inconsistencies by adding explicit Sat/Msat companion fields for ambiguous amount/balance values, while keeping existing fields unchanged for backward compatibility. I also rechecked the mapping paths to ensure this is API-additive only with no intended breaking changes.

I can also add explicit regression tests for the new Sat/Msat fields if you want extra safety before merge.

@rolznz rolznz requested a review from im-adithya March 24, 2026 04:15
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.

suffix balances with "Sat" or "Msat"

2 participants