feat(sample-wallet): add USDC balance display on Connections screen#247
feat(sample-wallet): add USDC balance display on Connections screen#247jakubuid merged 2 commits intofeat/wc_payfrom
Conversation
- 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 finished @jakubuid's task in 2m 17s —— View job PR Review CompleteReviewed PR #247: USDC balance display feature Found 5 issue(s)Issue 1: Missing timeout configuration for balance API callsID: retrofit-missing-timeout-8a4f 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 layerID: connections-missing-error-2c9d 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 amountsID: balanceresponse-double-precision-7f1a Using Recommendation: Use @SerializedName("value")
val value: BigDecimal,
@SerializedName("price")
val price: BigDecimal,Issue 4: Unvalidated address in signing operationID: payment-address-validation-4b2c 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 operationsID: payment-missing-retry-9e3a 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 |
| import retrofit2.converter.gson.GsonConverterFactory | ||
|
|
||
| fun createBalanceApiService(): BalanceApiService { | ||
| val httpClient = OkHttpClient.Builder() |
There was a problem hiding this comment.
🤖 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) { |
There was a problem hiding this comment.
🤖 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, |
There was a problem hiding this comment.
🤖 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,
|



Summary
This PR adds USDC token balance display to the sample wallet's Connections screen, using the WalletConnect balance API.
Changes
New Features
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 --> CardsFiles Changed
BalanceApiService.kt- New Retrofit service for balance APIBalanceResponse.kt- DTOs for balance responseRetrofit.kt- Added balance API service factory with required headersConnectionsViewModel.kt- Balance fetching and filtering logicConnectionsRoute.kt- USDC balance UI sectionWeb3WalletNavGraph.kt- Wire up balance refresh on payment successPaymentRoute.kt- Added onPaymentSuccess callbackPaymentViewModel.kt- Skip intro when no info capture requiredTesting