From 63dd3e2d03b9743a940ac078092f965643d59b25 Mon Sep 17 00:00:00 2001 From: Satoshi Qazi Muhammed Date: Fri, 17 Jul 2026 19:30:24 -0700 Subject: [PATCH] debug: add temporary Log.e tracing to Pending Signatures pipeline Nothing renders on-device despite the multisigsApiUrl fix and a confirmed real pending approval on-chain, with zero exceptions surfacing anywhere - every runCatching in this path swallows silently with no logging. Adding Log.e at each step (account resolution, signatory gate, on-chain keys/entries, off-chain fetch, call decode, transfer parse) to find where it actually drops the result. To be removed once the real cause is found. --- .../multisig/BridgeMultisigInteractor.kt | 26 ++++++++++++++++--- .../presentation/bridge/BridgeViewModel.kt | 21 +++++++++++---- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/feature-assets/src/main/java/io/novafoundation/nova/feature_assets/domain/bridge/multisig/BridgeMultisigInteractor.kt b/feature-assets/src/main/java/io/novafoundation/nova/feature_assets/domain/bridge/multisig/BridgeMultisigInteractor.kt index a758bfa3..bae39e56 100644 --- a/feature-assets/src/main/java/io/novafoundation/nova/feature_assets/domain/bridge/multisig/BridgeMultisigInteractor.kt +++ b/feature-assets/src/main/java/io/novafoundation/nova/feature_assets/domain/bridge/multisig/BridgeMultisigInteractor.kt @@ -174,7 +174,9 @@ class RealBridgeMultisigInteractor @Inject constructor( ChainGeneses.PEZKUWI_ASSET_HUB to BridgeMultisigConstants.MULTISIG_ADDRESS, ChainGeneses.POLKADOT_ASSET_HUB to BridgeMultisigConstants.MULTISIG_ADDRESS_POLKADOT, ).flatMap { (chainGenesis, multisigAddress) -> - runCatching { getPendingApprovalsFor(chainGenesis, multisigAddress, metaAccount) }.getOrElse { emptyList() } + runCatching { getPendingApprovalsFor(chainGenesis, multisigAddress, metaAccount) } + .onFailure { android.util.Log.e("BridgeDebug", "getPendingApprovalsFor($chainGenesis) threw", it) } + .getOrElse { emptyList() } } } @@ -184,11 +186,14 @@ class RealBridgeMultisigInteractor @Inject constructor( metaAccount: MetaAccount, ): List { val chain = chainRegistry.getChain(chainGenesis) - val myAccountId = metaAccount.accountIdIn(chain)?.intoKey() ?: return emptyList() + val myAccountId = metaAccount.accountIdIn(chain)?.intoKey() + android.util.Log.e("BridgeDebug", "chain=$chainGenesis myAccountId=${myAccountId?.toHexWithPrefix()}") + if (myAccountId == null) return emptyList() // Only the 5 known bridge signatories can ever have anything to approve here - same gate // as getSignerStateFor, matching how the rest of this screen already scopes itself. val isKnownSignatory = BridgeMultisigConstants.SIGNATORIES.any { it.address.toAccountId().intoKey() == myAccountId } + android.util.Log.e("BridgeDebug", "isKnownSignatory=$isKnownSignatory") if (!isKnownSignatory) return emptyList() val multisigAccountId = multisigAddress.toAccountId().intoKey() @@ -196,17 +201,21 @@ class RealBridgeMultisigInteractor @Inject constructor( val keys = storageDataSource.query(chain.id) { runtime.metadata.bridgeMultisig().multisigs.keys(multisigAccountId) } + android.util.Log.e("BridgeDebug", "keys.size=${keys.size}") if (keys.isEmpty()) return emptyList() val entries = storageDataSource.query(chain.id) { runtime.metadata.bridgeMultisig().multisigs.entries(keys) } + android.util.Log.e("BridgeDebug", "entries.size=${entries.size}") val notYetApprovedByMe = entries.filterNot { (_, onChainMultisig) -> myAccountId in onChainMultisig.approvals } + android.util.Log.e("BridgeDebug", "notYetApprovedByMe.size=${notYetApprovedByMe.size}") if (notYetApprovedByMe.isEmpty()) return emptyList() val callHashes = notYetApprovedByMe.keys.map { it.second } val callDataByHash = fetchCallData(chain, multisigAccountId, callHashes) + android.util.Log.e("BridgeDebug", "callDataByHash=${callDataByHash.mapValues { it.value != null }}") return notYetApprovedByMe.map { (key, onChainMultisig) -> val callHash = key.second @@ -227,16 +236,25 @@ class RealBridgeMultisigInteractor @Inject constructor( ): Map { return runCatching { val globalConfig = globalConfigDataSource.getGlobalConfig() + android.util.Log.e("BridgeDebug", "multisigsApiUrl=${globalConfig.multisigsApiUrl}") val request = BridgeOffChainCallDataRequest(multisigAccountId, callHashes, chain.id) val response = bridgeMultisigOperationsApi.getCallDatas(globalConfig.multisigsApiUrl, request) + android.util.Log.e("BridgeDebug", "response nodes=${response.data.multisigOperations.nodes.size}") val runtime = chainRegistry.getRuntime(chain.id) response.data.multisigOperations.nodes.mapNotNull { node -> val hash = CallHash.fromHexOrNull(node.callHash) ?: return@mapNotNull null - val call = node.callData?.let { GenericCall.fromHexOrNull(runtime, it) } + val call = node.callData?.let { + runCatching { GenericCall.fromHexOrNull(runtime, it) } + .onFailure { e -> android.util.Log.e("BridgeDebug", "GenericCall decode failed for $it", e) } + .getOrNull() + } + android.util.Log.e("BridgeDebug", "hash=$hash callData=${node.callData} decodedCall=${call != null}") hash to call }.toMap() - }.getOrElse { emptyMap() } + } + .onFailure { android.util.Log.e("BridgeDebug", "fetchCallData threw", it) } + .getOrElse { emptyMap() } } override suspend fun submitApproval(approval: PendingBridgeApproval): Result = runCatching { diff --git a/feature-assets/src/main/java/io/novafoundation/nova/feature_assets/presentation/bridge/BridgeViewModel.kt b/feature-assets/src/main/java/io/novafoundation/nova/feature_assets/presentation/bridge/BridgeViewModel.kt index e92559a1..cae57f14 100644 --- a/feature-assets/src/main/java/io/novafoundation/nova/feature_assets/presentation/bridge/BridgeViewModel.kt +++ b/feature-assets/src/main/java/io/novafoundation/nova/feature_assets/presentation/bridge/BridgeViewModel.kt @@ -474,8 +474,10 @@ class BridgeViewModel( fun refreshPendingSignatures() { launch { - val models = bridgeMultisigInteractor.getPendingApprovals() - .mapNotNull { it.toPendingSignatureUiOrNull() } + val approvals = bridgeMultisigInteractor.getPendingApprovals() + android.util.Log.e("BridgeDebug", "refreshPendingSignatures: approvals.size=${approvals.size}") + val models = approvals.mapNotNull { it.toPendingSignatureUiOrNull() } + android.util.Log.e("BridgeDebug", "refreshPendingSignatures: models.size=${models.size}") _pendingSignatures.postValue(models) } @@ -499,15 +501,24 @@ class BridgeViewModel( * BridgeMultisigInteractor.submitApproval's own refusal to blind-sign for why this isn't * just a display-only gap: an unparseable row would have no safe "Sign" action anyway). */ private suspend fun PendingBridgeApproval.toPendingSignatureUiOrNull(): PendingSignatureModel? { - val call = call ?: return null + val call = call ?: run { + android.util.Log.e("BridgeDebug", "toPendingSignatureUiOrNull: call is null for hash=$callHash") + return null + } val assetId = if (chain.id == ChainGeneses.POLKADOT_ASSET_HUB) { BridgeMultisigConstants.POLKADOT_USDT_ASSET_ID } else { BridgeMultisigConstants.WUSDT_ASSET_ID } - val asset = chain.assetsById[assetId] ?: return null - val parsed = assetSourceRegistry.sourceFor(asset).transfers.tryParseTransfer(call, chain) ?: return null + val asset = chain.assetsById[assetId] ?: run { + android.util.Log.e("BridgeDebug", "toPendingSignatureUiOrNull: asset $assetId not found on ${chain.name}") + return null + } + val parsed = assetSourceRegistry.sourceFor(asset).transfers.tryParseTransfer(call, chain) ?: run { + android.util.Log.e("BridgeDebug", "toPendingSignatureUiOrNull: tryParseTransfer returned null, call=$call") + return null + } val decimalAmount = asset.amountFromPlanks(parsed.amount.amount) val amountText = "${NumberFormat.getNumberInstance().format(decimalAmount)} ${asset.symbol.value}"