fix(swift-sdk): show combined wallet balance on Wallets list#3537
Conversation
The wallet row used only Core balance to decide whether to display "Empty" and only summed identity credits for Platform — so a wallet funded via Platform Payment addresses (no identities) showed "Empty" even when BalanceCardView showed a non-zero Platform Balance. Mirror BalanceCardView's logic on the row: query PersistentPlatformAddress by walletId for BLAST-synced credits (falling back to identity credits), and render the combined Core+Platform total in DASH at the top-right. "Empty" is only shown when both sides are zero. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthrough
Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
|
🕓 Ready for review — 12 ahead in queue (commit 2a99e9d) |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/CoreContentView.swift (1)
685-690: Centralize duff/credit conversion constants.
100_000_000.0and100_000_000_000.0are now repeated acrossformatBalance,combinedDashAmount,formatCredits(here and in theCoreContentViewextension at lines 1014–1026). A single private constant pair (e.g.private static let duffsPerDash = 100_000_000.0,creditsPerDash = 100_000_000_000.0) would prevent future drift between the row and detail formatters.Also applies to: 715-734
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/CoreContentView.swift` around lines 685 - 690, Replace repeated numeric literals with centralized constants by adding two private static constants on CoreContentView (e.g., private static let duffsPerDash = 100_000_000.0 and private static let creditsPerDash = 100_000_000_000.0) and update all uses to reference these constants: combinedDashAmount, formatBalance, formatCredits, and the CoreContentView extension formatting methods. Ensure you use CoreContentView.duffsPerDash and .creditsPerDash (or self.) consistently so both row and detail formatters share the same conversion factors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/CoreContentView.swift`:
- Around line 685-694: The header can show "0 DASH" for tiny platform-only
balances because combinedDashAmount is formatted with formatDash (max 8 fraction
digits) while platform credits resolve to 1e‑11 DASH; update the header
formatting to handle platform-only balances by detecting the platform-only case
(totalCoreBalance == 0 && platformBalance > 0) and either (A) format using
higher precision (raise maximumFractionDigits to cover 1e‑11 when computing
combinedDashAmount for display) or (B) display the platform amount using
credits/formatCredits instead of DASH; locate and change the logic around
combinedDashAmount and hasAnyBalance to branch the header formatting accordingly
so the header is consistent with row formatting.
- Around line 669-678: platformBalance incorrectly falls back to identity
credits whenever blastBalance == 0, but must only do so if the wallet has never
synced; locate the platformBalance computed property and modify its logic to
query PersistentSyncState (e.g. access syncStates similar to BalanceCardView) to
detect whether the wallet has been synced at least once — if blastBalance > 0
return blastBalance, else if the wallet has a syncState indicating it has
previously synced return 0, otherwise fall back to summing identitiesForWallet
balances; ensure you reference the same sync state key/lookup used by
BalanceCardView so behavior is consistent.
---
Nitpick comments:
In
`@packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/CoreContentView.swift`:
- Around line 685-690: Replace repeated numeric literals with centralized
constants by adding two private static constants on CoreContentView (e.g.,
private static let duffsPerDash = 100_000_000.0 and private static let
creditsPerDash = 100_000_000_000.0) and update all uses to reference these
constants: combinedDashAmount, formatBalance, formatCredits, and the
CoreContentView extension formatting methods. Ensure you use
CoreContentView.duffsPerDash and .creditsPerDash (or self.) consistently so both
row and detail formatters share the same conversion factors.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1898d8f1-c414-43e4-a4f9-957531f7eeb5
📒 Files selected for processing (1)
packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/CoreContentView.swift
| /// Platform balance in credits: prefer BLAST address sync, fall | ||
| /// back to summing identity credits when no addresses have been | ||
| /// synced yet. | ||
| private var platformBalance: UInt64 { | ||
| identitiesForWallet.reduce(0) { $0 + UInt64(bitPattern: $1.balance) } | ||
| let blastBalance = addressBalances.reduce(UInt64(0)) { $0 + $1.balance } | ||
| if blastBalance > 0 { return blastBalance } | ||
| return identitiesForWallet.reduce(UInt64(0)) { | ||
| $0 + UInt64(bitPattern: $1.balance) | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
fd -t f BalanceCardView.swift packages/swift-sdk | xargs -I{} sh -c 'echo "=== {} ==="; cat "{}"'Repository: dashpay/platform
Length of output: 42
🏁 Script executed:
# Search for BalanceCardView references in the codebase
rg -i "BalanceCardView" packages/swift-sdk --type swiftRepository: dashpay/platform
Length of output: 459
🏁 Script executed:
# List all Swift view files in the core views directory
fd -t f "\.swift$" packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views | head -20Repository: dashpay/platform
Length of output: 1097
🏁 Script executed:
# Check the full platformBalance context in CoreContentView.swift
sed -n '650,700p' packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/CoreContentView.swiftRepository: dashpay/platform
Length of output: 1995
🏁 Script executed:
# Find and read WalletDetailView.swift, focusing on BalanceCardView
fd -t f WalletDetailView.swift packages/swift-sdk | xargs -I{} sh -c 'wc -l "{}"; echo ""; cat -n "{}"' | head -300Repository: dashpay/platform
Length of output: 13278
🏁 Script executed:
# Get the BalanceCardView struct implementation
sed -n '400,600p' packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/WalletDetailView.swiftRepository: dashpay/platform
Length of output: 7577
🏁 Script executed:
# Get the full BalanceCardView platformBalance implementation
sed -n '530,560p' packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/WalletDetailView.swiftRepository: dashpay/platform
Length of output: 1390
🏁 Script executed:
# Check if CoreContentView has any syncStates query or similar
grep -n "syncStates\|PersistentSyncState\|hasSynced" packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/CoreContentView.swiftRepository: dashpay/platform
Length of output: 42
🏁 Script executed:
# Get lines around platformBalance in BalanceCardView
sed -n '530,555p' packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/WalletDetailView.swiftRepository: dashpay/platform
Length of output: 1096
🏁 Script executed:
# Get the complete BalanceCardView struct with more lines
sed -n '500,580p' packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/WalletDetailView.swiftRepository: dashpay/platform
Length of output: 3234
🏁 Script executed:
# Get the platformBalance method in BalanceCardView
sed -n '575,610p' packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/WalletDetailView.swiftRepository: dashpay/platform
Length of output: 1384
🏁 Script executed:
# Confirm the CoreContentView platformBalance and check if syncStates exists
sed -n '650,680p' packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/CoreContentView.swiftRepository: dashpay/platform
Length of output: 1273
Implementation does not achieve parity with BalanceCardView — missing sync state tracking.
The platformBalance logic in CoreContentView (lines 669–678) falls back to identity credits whenever blastBalance == 0, but BalanceCardView only falls back when the wallet has never synced. BalanceCardView queries PersistentSyncState to distinguish "synced with zero balance" from "not yet synced", and returns zero credits in the synced-but-empty case. CoreContentView lacks this distinction, so it will incorrectly display stale identity credits on a wallet that has been BLAST-synced but now holds zero platform credits.
To match BalanceCardView, add the syncStates query and check:
♻️ Proposed change
`@Query` private var addressBalances: [PersistentPlatformAddress]
+ `@Query` private var syncStates: [PersistentSyncState]
init(wallet: PersistentWallet) {
self.wallet = wallet
let walletId = wallet.walletId
+ let networkName = wallet.network
_addressBalances = Query(
filter: `#Predicate`<PersistentPlatformAddress> { $0.walletId == walletId }
)
+ _syncStates = Query(
+ filter: `#Predicate`<PersistentSyncState> { $0.network == networkName }
+ )
}
/// Platform balance in credits: prefer BLAST address sync, fall
- /// back to summing identity credits when no addresses have been
- /// synced yet.
+ /// back to summing identity credits only when no BLAST sync
+ /// has occurred yet.
private var platformBalance: UInt64 {
let blastBalance = addressBalances.reduce(UInt64(0)) { $0 + $1.balance }
- if blastBalance > 0 { return blastBalance }
+ let hasSynced = syncStates.first.map { $0.syncHeight > 0 || $0.syncTimestamp > 0 }
+ ?? false
+ if blastBalance > 0 || hasSynced {
+ return blastBalance
+ }
return identitiesForWallet.reduce(UInt64(0)) {
$0 + UInt64(bitPattern: $1.balance)
}
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/CoreContentView.swift`
around lines 669 - 678, platformBalance incorrectly falls back to identity
credits whenever blastBalance == 0, but must only do so if the wallet has never
synced; locate the platformBalance computed property and modify its logic to
query PersistentSyncState (e.g. access syncStates similar to BalanceCardView) to
detect whether the wallet has been synced at least once — if blastBalance > 0
return blastBalance, else if the wallet has a syncState indicating it has
previously synced return 0, otherwise fall back to summing identitiesForWallet
balances; ensure you reference the same sync state key/lookup used by
BalanceCardView so behavior is consistent.
| /// Combined wallet balance expressed in DASH. Core uses 1e8 | ||
| /// duffs/DASH; Platform uses 1e11 credits/DASH. | ||
| private var combinedDashAmount: Double { | ||
| Double(totalCoreBalance) / 100_000_000.0 | ||
| + Double(platformBalance) / 100_000_000_000.0 | ||
| } | ||
|
|
||
| private var hasAnyBalance: Bool { | ||
| totalCoreBalance > 0 || platformBalance > 0 | ||
| } |
There was a problem hiding this comment.
Header may render “0 DASH” for tiny platform balances despite hasAnyBalance == true.
formatDash caps at maximumFractionDigits = 8, but Platform credits resolve to 1e‑11 DASH. Any platform-only balance under 1000 credits (≈ 1e‑8 DASH) rounds to 0 and the header shows “0 DASH” while hasAnyBalance is true — which is inconsistent with the “Empty”/non-empty intent and with row 5 (formatCredits uses %.4f and would also display 0.0000 DASH). Consider raising precision when only platform credits are present, or showing the credits unit directly:
♻️ One option
- private func formatDash(_ dash: Double) -> String {
+ private func formatDash(_ dash: Double, maxFractionDigits: Int = 8) -> String {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.minimumFractionDigits = 0
- formatter.maximumFractionDigits = 8
+ formatter.maximumFractionDigits = maxFractionDigits
if let formatted = formatter.string(from: NSNumber(value: dash)) {
return "\(formatted) DASH"
}
- return String(format: "%.8f DASH", dash)
+ return String(format: "%.\(maxFractionDigits)f DASH", dash)
}Also applies to: 787-790
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/CoreContentView.swift`
around lines 685 - 694, The header can show "0 DASH" for tiny platform-only
balances because combinedDashAmount is formatted with formatDash (max 8 fraction
digits) while platform credits resolve to 1e‑11 DASH; update the header
formatting to handle platform-only balances by detecting the platform-only case
(totalCoreBalance == 0 && platformBalance > 0) and either (A) format using
higher precision (raise maximumFractionDigits to cover 1e‑11 when computing
combinedDashAmount for display) or (B) display the platform amount using
credits/formatCredits instead of DASH; locate and change the logic around
combinedDashAmount and hasAnyBalance to branch the header formatting accordingly
so the header is consistent with row formatting.
|
✅ DashSDKFFI.xcframework built for this PR.
SwiftPM (host the zip at a stable URL, then use): .binaryTarget(
name: "DashSDKFFI",
url: "https://your.cdn.example/DashSDKFFI.xcframework.zip",
checksum: "d41895f9d84c54f252b985756226d23920420d563cb8a71269d14130cac2963d"
)Xcode manual integration:
|
Issue being fixed or feature implemented
On the Wallets tab the wallet row showed "Empty" and "No Core balance" even when the wallet had a non-zero Platform balance (visible in
BalanceCardViewon the wallet detail screen). The summary row was inconsistent with the detail card and hid funds from users.What was done?
Updated
WalletRowViewin CoreContentView.swift to mirrorBalanceCardView's balance logic:@QueryforPersistentPlatformAddressfiltered bywalletIdso the row picks up BLAST-synced address credits (with the existing identity-credit sum kept as a fallback for the pre-sync state).totalCoreBalance == 0 ? "Empty"check at the top-right with a combined Core + Platform balance rendered in DASH. "Empty" is only shown when both Core and Platform balances are zero.formatDash(_:)helper so duff- and credit-derived amounts share the same formatter (Core uses 1e8 duffs/DASH; Platform uses 1e11 credits/DASH).Row 5 ("Platform: X DASH • walletId") now also picks up BLAST-synced balances via the same
platformBalancecomputation.How Has This Been Tested?
xcodebuild -project SwiftExampleApp/SwiftExampleApp.xcodeproj -scheme SwiftExampleApp -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 17 Pro,arch=arm64' buildsucceeds.Wallet1, ID73c5dc…98c4e2) that previously showed "Empty" / "No Core balance" despite holding0.24581622 DASHon the Platform side; the row now shows the combined DASH total at the top-right.Breaking Changes
None — UI-only change in the example app.
Checklist:
For repository code-owners and collaborators only
🤖 Generated with Claude Code
Summary by CodeRabbit