Skip to content

feat(sample-wallet): add USDC balance display on Connections screen#247

Merged
jakubuid merged 2 commits intofeat/wc_payfrom
add_balance
Jan 9, 2026
Merged

feat(sample-wallet): add USDC balance display on Connections screen#247
jakubuid merged 2 commits intofeat/wc_payfrom
add_balance

Conversation

@jakubuid
Copy link
Collaborator

@jakubuid jakubuid commented Jan 8, 2026

Summary

This PR adds USDC token balance display to the sample wallet's Connections screen, using the WalletConnect balance API.

Changes

New Features

  • USDC Balance Display: Shows USDC balances for Ethereum, Polygon, and Base chains on the main Connections screen
  • Auto-refresh on Payment: Balances automatically refresh after a successful payment
  • Payment Flow Improvement: Skip the intro screen when no information capture is required

Technical Details

flowchart LR
    subgraph API[WalletConnect RPC]
        Endpoint["/v1/account/{address}/balance"]
    end
    
    subgraph Service[BalanceApiService]
        GetBalance["getBalance(address)"]
    end
    
    subgraph VM[ConnectionsViewModel]
        Fetch[Fetch + Filter USDC]
        State["usdcBalances: StateFlow"]
    end
    
    subgraph UI[ConnectionsRoute]
        Cards[Balance Cards]
    end
    
    Endpoint --> GetBalance
    GetBalance --> Fetch
    Fetch --> State
    State --> Cards
Loading

Files Changed

  • BalanceApiService.kt - New Retrofit service for balance API
  • BalanceResponse.kt - DTOs for balance response
  • Retrofit.kt - Added balance API service factory with required headers
  • ConnectionsViewModel.kt - Balance fetching and filtering logic
  • ConnectionsRoute.kt - USDC balance UI section
  • Web3WalletNavGraph.kt - Wire up balance refresh on payment success
  • PaymentRoute.kt - Added onPaymentSuccess callback
  • PaymentViewModel.kt - Skip intro when no info capture required

Testing

  • Verified balance display on Connections screen
  • Tested balance refresh after payment completion
  • Confirmed proper filtering for supported chains (Ethereum, Polygon, Base)

- Add BalanceApiService and DTOs for WalletConnect balance API
- Display USDC balances for Ethereum, Polygon, and Base chains
- Show wallet address in balance section header
- Auto-refresh balances after successful payment
- Skip payment intro screen when no information capture required
@claude
Copy link

claude bot commented Jan 8, 2026

Claude finished @jakubuid's task in 2m 17s —— View job


PR Review Complete

Reviewed PR #247: USDC balance display feature

Found 5 issue(s)

Issue 1: Missing timeout configuration for balance API calls

ID: retrofit-missing-timeout-8a4f
File: sample/wallet/src/main/kotlin/com/reown/sample/wallet/blockchain/Retrofit.kt:12
Severity: HIGH
Category: reliability

The OkHttpClient for balance API service lacks timeout configuration. Network calls without timeouts can hang indefinitely, degrading user experience and potentially blocking UI.

Recommendation: Configure timeouts for the HTTP client:

val httpClient = OkHttpClient.Builder()
    .connectTimeout(10, TimeUnit.SECONDS)
    .readTimeout(30, TimeUnit.SECONDS)
    .writeTimeout(30, TimeUnit.SECONDS)

Issue 2: No error handling for balance fetch in UI layer

ID: connections-missing-error-2c9d
File: sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/connections/ConnectionsViewModel.kt:82
Severity: MEDIUM
Category: code_quality

Balance fetch errors are logged but not exposed to UI. Users have no visibility when balance loading fails (network issues, API errors, etc.). The catch block at line 82 silently swallows all exceptions.

Recommendation: Add error state to ViewModel:

private val _balanceError = MutableStateFlow<String?>(null)
val balanceError: StateFlow<String?> = _balanceError.asStateFlow()

// In catch block:
_balanceError.value = "Failed to load balances: ${e.message}"

Issue 3: Double value precision loss for financial amounts

ID: balanceresponse-double-precision-7f1a
File: sample/wallet/src/main/kotlin/com/reown/sample/wallet/blockchain/BalanceResponse.kt:23
Severity: HIGH
Category: code_quality

Using Double for value and price fields can cause precision loss in financial calculations. Floating-point arithmetic is unsuitable for monetary values.

Recommendation: Use BigDecimal for financial values:

@SerializedName("value")
val value: BigDecimal,
@SerializedName("price")
val price: BigDecimal,

Issue 4: Unvalidated address in signing operation

ID: payment-address-validation-4b2c
File: sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/payment/PaymentViewModel.kt:304
Severity: HIGH
Category: security

The address validation at line 304 uses case-insensitive comparison but doesn't validate address format or checksum. An attacker could potentially craft a malformed address that bypasses validation.

Recommendation: Add proper address validation before comparison:

// Normalize and validate addresses
val normalizedRequested = requestedAddress.lowercase().removePrefix("0x")
val normalizedWallet = EthAccountDelegate.address.lowercase().removePrefix("0x")

if (normalizedRequested != normalizedWallet) {
    throw IllegalArgumentException("Address mismatch: requested=$requestedAddress, wallet=${EthAccountDelegate.address}")
}

Issue 5: No retry mechanism for critical payment operations

ID: payment-missing-retry-9e3a
File: sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/payment/PaymentViewModel.kt:122
Severity: MEDIUM
Category: reliability

Payment-related API calls (getRequiredPaymentActions, confirmPayment) lack retry logic. Transient network failures cause complete payment failure without user recovery options.

Recommendation: Implement retry with exponential backoff for idempotent operations:

suspend fun <T> retryWithBackoff(
    maxRetries: Int = 3,
    initialDelay: Long = 1000,
    block: suspend () -> Result<T>
): Result<T> {
    repeat(maxRetries) { attempt ->
        val result = block()
        if (result.isSuccess) return result
        if (attempt < maxRetries - 1) {
            delay(initialDelay * (1 shl attempt))
        }
    }
    return block()
}

Automated Checks

✅ No external domain URLs detected
✅ No static resource cache-control issues detected
✅ No GitHub Actions workflow security issues detected
✅ No WalletConnect Pay architecture violations detected


@jakubuid jakubuid changed the base branch from develop to feat/wc_pay January 8, 2026 17:07
import retrofit2.converter.gson.GsonConverterFactory

fun createBalanceApiService(): BalanceApiService {
val httpClient = OkHttpClient.Builder()
Copy link

Choose a reason for hiding this comment

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

🤖 Auto Review Issue: Missing timeout configuration for balance API calls

Severity: HIGH
Category: reliability
Tool: Claude Auto Review

Recommendation: Configure timeouts for the HTTP client:

val httpClient = OkHttpClient.Builder()
    .connectTimeout(10, TimeUnit.SECONDS)
    .readTimeout(30, TimeUnit.SECONDS)
    .writeTimeout(30, TimeUnit.SECONDS)

} else {
Log.e("Web3Wallet", "Failed to fetch balances: ${response.code()} - ${response.errorBody()?.string()}")
}
} catch (e: Exception) {
Copy link

Choose a reason for hiding this comment

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

🤖 Auto Review Issue: No error handling for balance fetch in UI layer

Severity: MEDIUM
Category: code_quality
Tool: Claude Auto Review

Recommendation: Add error state to ViewModel:

private val _balanceError = MutableStateFlow<String?>(null)
val balanceError: StateFlow<String?> = _balanceError.asStateFlow()

// In catch block:
_balanceError.value = "Failed to load balances: ${e.message}"

@SerializedName("address")
val address: String? = null,
@SerializedName("value")
val value: Double,
Copy link

Choose a reason for hiding this comment

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

🤖 Auto Review Issue: Double value precision loss for financial amounts

Severity: HIGH
Category: code_quality
Tool: Claude Auto Review

Recommendation: Use BigDecimal for financial values:

@SerializedName("value")
val value: BigDecimal,
@SerializedName("price")
val price: BigDecimal,

@sonarqubecloud
Copy link

sonarqubecloud bot commented Jan 8, 2026

@jakubuid jakubuid merged commit 76748d4 into feat/wc_pay Jan 9, 2026
6 of 10 checks passed
@github-actions github-actions bot locked and limited conversation to collaborators Jan 9, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant