Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -644,22 +644,55 @@ struct WalletRowView: View {
let wallet: PersistentWallet
@EnvironmentObject var platformState: AppState

/// Per-wallet BLAST-synced platform-address balances. Mirrors
/// `BalanceCardView` so the summary row sees the same balance as
/// the detail view — previously the row only summed identity
/// credits, so a wallet funded purely via Platform Payment
/// addresses (no identities) showed "Empty".
@Query private var addressBalances: [PersistentPlatformAddress]

init(wallet: PersistentWallet) {
self.wallet = wallet
let walletId = wallet.walletId
_addressBalances = Query(
filter: #Predicate<PersistentPlatformAddress> { $0.walletId == walletId }
)
}

/// Identities on this wallet — via the SwiftData relationship.
/// The wallet↔identity relationship is the canonical source
/// now; no need to filter `appState.identities` by walletId.
private var identitiesForWallet: [PersistentIdentity] {
wallet.identities
}

/// 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)
}
}
Comment on lines +669 to 678

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 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 swift

Repository: 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 -20

Repository: 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.swift

Repository: 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 -300

Repository: 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.swift

Repository: 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.swift

Repository: 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.swift

Repository: 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.swift

Repository: 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.swift

Repository: 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.swift

Repository: 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.swift

Repository: 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.


private var totalCoreBalance: UInt64 {
wallet.balanceConfirmed + wallet.balanceUnconfirmed
+ wallet.balanceImmature + wallet.balanceLocked
}

/// 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
}
Comment on lines +685 to +694

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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.


private var walletIdShort: String {
let hex = wallet.walletId.prefix(6)
.map { String(format: "%02x", $0) }
Expand All @@ -680,7 +713,10 @@ struct WalletRowView: View {
}

private func formatBalance(_ amount: UInt64) -> String {
let dash = Double(amount) / 100_000_000.0
formatDash(Double(amount) / 100_000_000.0)
}

private func formatDash(_ dash: Double) -> String {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.minimumFractionDigits = 0
Expand Down Expand Up @@ -748,10 +784,10 @@ struct WalletRowView: View {
}
}
Spacer()
Text(totalCoreBalance == 0 ? "Empty" : formatBalance(totalCoreBalance))
Text(hasAnyBalance ? formatDash(combinedDashAmount) : "Empty")
.font(.subheadline)
.fontWeight(.medium)
.foregroundColor(totalCoreBalance == 0 ? .secondary : .primary)
.foregroundColor(hasAnyBalance ? .primary : .secondary)
}

// Row 1: network + created date.
Expand Down
Loading