fix: retry Tron balance polling on failure instead of permanently dying

Real user on a fresh install reported TRX and USDT-TRC20 never
appearing anywhere in the Tokens list (not "zero balance", genuinely
absent - including from the multi-chain USDT chain picker). Traced
the full pipeline; every layer up to and including
TypeBasedAssetSourceRegistry, ChainRegistry, address derivation, and
TronGridApi wiring was already correct.

Root cause: pollingBalanceFlow() had no retry around fetch() - any
single transient failure (DNS hiccup, timeout, momentary connectivity
loss during app cold start) threw out of the `while(true)` loop,
killing the flow permanently. The collector
(FullSyncPaymentUpdater.syncAsset()) only logs and gives up on
failure, it doesn't resubscribe. Since the asset's first balance
write never happens, AssetCache never inserts a DB row for it, and
every UI surface (main list, multi-chain picker, search) reads
exclusively via an INNER JOIN that requires that row to exist - so
the asset is invisible everywhere, permanently, with zero
user-visible error.

Swallow fetch() failures and retry on the next interval instead of
letting them escape the loop - one bad poll no longer blackholes the
asset for the rest of the app session.

Also fixes a related but separate gap found while tracing this:
RealSecretsMetaAccount.multiChainEncryptionIn() had no isTronBased
branch (only isEthereumBased), unlike DefaultMetaAccount's
hasAccountIn/accountIdIn which already handle Tron correctly. This
didn't cause the missing-tokens bug, but would have broken "export
account" (JSON key backup) for Tron - fixed by routing Tron through
MultiChainEncryption.Ethereum, matching what
RealTronTransactionService already does for actual transaction
signing (same secp256k1 keypair).
This commit is contained in:
2026-07-08 11:33:54 -07:00
parent 15aa33000b
commit 21d2896493
2 changed files with 18 additions and 3 deletions
@@ -49,7 +49,9 @@ class RealSecretsMetaAccount(
hasChainAccountIn(chain.id) -> {
val cryptoType = chainAccounts.getValue(chain.id).cryptoType ?: return null
if (chain.isEthereumBased) {
// Tron reuses the same secp256k1 keypair/signing as Ethereum - see
// RealTronTransactionService's use of Signer.sign(MultiChainEncryption.Ethereum, ...).
if (chain.isEthereumBased || chain.isTronBased) {
MultiChainEncryption.Ethereum
} else {
MultiChainEncryption.substrateFrom(cryptoType)
@@ -58,6 +60,8 @@ class RealSecretsMetaAccount(
chain.isEthereumBased -> MultiChainEncryption.Ethereum
chain.isTronBased -> MultiChainEncryption.Ethereum
else -> substrateCryptoType?.let(MultiChainEncryption.Companion::substrateFrom)
}
}