diff --git a/feature-wallet-impl/src/main/java/io/novafoundation/nova/feature_wallet_impl/data/network/blockchain/assets/balances/utility/NativeAssetBalance.kt b/feature-wallet-impl/src/main/java/io/novafoundation/nova/feature_wallet_impl/data/network/blockchain/assets/balances/utility/NativeAssetBalance.kt index 7f9df0c1..c394ebdb 100644 --- a/feature-wallet-impl/src/main/java/io/novafoundation/nova/feature_wallet_impl/data/network/blockchain/assets/balances/utility/NativeAssetBalance.kt +++ b/feature-wallet-impl/src/main/java/io/novafoundation/nova/feature_wallet_impl/data/network/blockchain/assets/balances/utility/NativeAssetBalance.kt @@ -1,7 +1,6 @@ package io.novafoundation.nova.feature_wallet_impl.data.network.blockchain.assets.balances.utility import android.util.Log -import io.novafoundation.nova.common.data.network.runtime.binding.AccountInfo import io.novafoundation.nova.common.data.network.runtime.binding.bindList import io.novafoundation.nova.common.data.network.runtime.binding.bindNumber import io.novafoundation.nova.common.data.network.runtime.binding.castToDictEnum @@ -18,6 +17,7 @@ import io.novafoundation.nova.core_db.dao.LockDao import io.novafoundation.nova.core_db.model.BalanceHoldLocal import io.novafoundation.nova.feature_account_api.domain.model.MetaAccount import io.novafoundation.nova.feature_wallet_api.data.cache.AssetCache +import io.novafoundation.nova.feature_wallet_api.data.cache.bindAccountInfoOrDefault import io.novafoundation.nova.feature_wallet_api.data.cache.updateAsset import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.assets.balances.AssetBalance import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.assets.balances.BalanceSyncUpdate @@ -155,13 +155,13 @@ class NativeAssetBalance( // the caller, FullSyncPaymentUpdater.syncAsset(), wraps this whole call in a single retryWhen boundary meant // to catch and retry exactly these failures. Swallowing here would make that retry boundary never trigger. // - // Uses the typed remoteStorage.subscribe { metadata.system.account... } DSL (same as this class's own - // subscribeAccountBalanceUpdatePoint() and PooledBalanceUpdater/BalanceLocksUpdater) instead of a raw - // subscriptionBuilder.subscribe(key) call: on chains with several other assets/updaters already sharing - // the same SharedRequestsBuilder (e.g. Pezkuwi Asset Hub's 5 statemine assets + nomination-pools updater), - // the raw form's System.Account subscription was silently never reaching the wire - no exception, no data, - // ever - while every other asset on the same chain synced fine. The typed DSL is what every other caller - // on a busy shared connection already uses successfully. + // NOTE (2026-07-09): a prior attempt switched this to the typed remoteStorage.subscribe { metadata.system + // .account... } DSL, on the theory that it would fix HEZ silently never syncing on Pezkuwi Asset Hub (see + // git history). That attempt made things categorically worse - all assets across all 3 Pezkuwi chains + // stopped syncing, with logs showing what looked like cross-chain key contamination on the shared + // connection. Reverted back to the raw subscriptionBuilder.subscribe(key) form here, which is not broken + // for any OTHER native asset on any OTHER chain - only Pezkuwi Asset Hub's HEZ specifically. That narrower + // bug is still open; do not re-attempt the DSL swap without first understanding why it caused contamination. override suspend fun startSyncingBalance( chain: Chain, chainAsset: Chain.Asset, @@ -169,18 +169,21 @@ class NativeAssetBalance( accountId: AccountId, subscriptionBuilder: SharedRequestsBuilder ): Flow { - return remoteStorage.subscribe(chain.id, subscriptionBuilder) { - metadata.system.account.observeWithRaw(accountId) - }.map { change -> - val accountInfo = change.value ?: AccountInfo.empty() - val assetChanged = assetCache.updateAsset(metaAccount.id, chain.utilityAsset, accountInfo) + val runtime = chainRegistry.getRuntime(chain.id) - if (assetChanged) { - BalanceSyncUpdate.CauseFetchable(change.at!!) - } else { - BalanceSyncUpdate.NoCause + val key = runtime.metadata.system().storage("Account").storageKey(runtime, accountId) + + return subscriptionBuilder.subscribe(key) + .map { change -> + val accountInfo = bindAccountInfoOrDefault(change.value, runtime) + val assetChanged = assetCache.updateAsset(metaAccount.id, chain.utilityAsset, accountInfo) + + if (assetChanged) { + BalanceSyncUpdate.CauseFetchable(change.block) + } else { + BalanceSyncUpdate.NoCause + } } - } } private fun bindBalanceHolds(dynamicInstance: Any?): List? {