diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountActivityStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountActivityStore.swift index 2f34460b3be4..573c02c4bdd7 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountActivityStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountActivityStore.swift @@ -202,13 +202,6 @@ class AccountActivityStore: ObservableObject, WalletObserverStore { let allTokens = await blockchainRegistry.allTokens(in: networksForAccountCoin).flatMap( \.tokens ) - (self.userAssets, self.userNFTs) = buildAssetsAndNFTs( - userNetworkAssets: allUserNetworkAssets, - tokenBalances: tokenBalanceCache, - tokenPrices: tokenPricesCache, - nftMetadata: nftMetadataCache, - btcBalances: btcBalancesCache - ) let allAccountsForCoin = await keyringService.allAccounts().accounts.filter { $0.coin == account.coin } @@ -228,8 +221,6 @@ class AccountActivityStore: ObservableObject, WalletObserverStore { ) self.isLoadingAccountFiat = true - // TODO: cleanup with balance caching with issue - // https://github.com/brave/brave-browser/issues/36764 var tokenBalances: [String: Double] = [:] if account.coin == .btc { let networkAsset = allUserNetworkAssets.first { @@ -244,12 +235,30 @@ class AccountActivityStore: ObservableObject, WalletObserverStore { tokenBalances = [btcToken.id: btcTotalBalance] } } else { - tokenBalances = await self.rpcService.fetchBalancesForTokens( - account: account, - networkAssets: allUserNetworkAssets - ) + if let accountBalances = self.assetManager.getBalances(for: nil, account: account.id) { + tokenBalances = accountBalances.reduce(into: [String: Double]()) { + let tokenId = + $1.contractAddress + $1.chainId + + $1.symbol + $1.tokenId + $0[tokenId] = Double($1.balance) ?? 0 + } + } else { + tokenBalances = await self.rpcService.fetchBalancesForTokens( + account: account, + networkAssets: allUserNetworkAssets + ) + } } tokenBalanceCache.merge(with: tokenBalances) + // update assets, NFTs, after balance fetch + guard !Task.isCancelled else { return } + (self.userAssets, self.userNFTs) = buildAssetsAndNFTs( + userNetworkAssets: allUserNetworkAssets, + tokenBalances: tokenBalanceCache, + tokenPrices: tokenPricesCache, + nftMetadata: nftMetadataCache, + btcBalances: btcBalancesCache + ) // fetch price for every user asset let prices: [String: String] = await assetRatioService.fetchPrices( @@ -272,8 +281,8 @@ class AccountActivityStore: ObservableObject, WalletObserverStore { self.accountTotalFiat = currencyFormatter.formatAsFiat(totalFiat) ?? "$0.00" self.isLoadingAccountFiat = false - guard !Task.isCancelled else { return } // update assets, NFTs, transactions after balance & price fetch + guard !Task.isCancelled else { return } (self.userAssets, self.userNFTs) = buildAssetsAndNFTs( userNetworkAssets: allUserNetworkAssets, tokenBalances: tokenBalanceCache, @@ -298,7 +307,7 @@ class AccountActivityStore: ObservableObject, WalletObserverStore { ipfsApi: ipfsApi ) nftMetadataCache.merge(with: allNFTMetadata) - + // update assets, NFTs, transactions after balance & price & metadata fetch guard !Task.isCancelled else { return } (self.userAssets, self.userNFTs) = buildAssetsAndNFTs( userNetworkAssets: allUserNetworkAssets, @@ -508,6 +517,7 @@ class AccountActivityStore: ObservableObject, WalletObserverStore { extension AccountActivityStore: WalletUserAssetDataObserver { public func cachedBalanceRefreshed() { + update() } public func userAssetUpdated() { diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountsStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountsStore.swift index dd4caca6b155..9c76e773bcec 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountsStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountsStore.swift @@ -34,7 +34,7 @@ class AccountsStore: ObservableObject, WalletObserverStore { let currencyFormatter: NumberFormatter = .usdCurrencyFormatter private typealias TokenBalanceCache = [String: [String: Double]] - /// Cache of token balances for each account. [account.cacheBalanceKey: [token.id: balance]] + /// Cache of token balances for each account. `[account.id: [token.id: balance]]` private var tokenBalancesCache: TokenBalanceCache = [:] /// Cache of prices for each token. The key is the token's `assetRatioId`. private var pricesCache: [String: String] = [:] @@ -72,6 +72,7 @@ class AccountsStore: ObservableObject, WalletObserverStore { func setupObservers() { guard !isObserving else { return } + self.userAssetManager.addUserAssetDataObserver(self) self.keyringServiceObserver = KeyringServiceObserver( keyringService: keyringService, _accountsChanged: { [weak self] in @@ -141,35 +142,69 @@ class AccountsStore: ObservableObject, WalletObserverStore { for accounts: [BraveWallet.AccountInfo], networkAssets allNetworkAssets: [NetworkAssets] ) async { + // Update BTC account balance + if accounts.contains(where: { $0.coin == .btc }) { + await withTaskGroup( + of: [String: [String: Double]].self + ) { [bitcoinWalletService] group in + for account in accounts where account.coin == .btc { + group.addTask { + let btcBalance = + await bitcoinWalletService.fetchBTCBalance( + accountId: account.accountId, + type: .total + ) ?? 0 + if let btcToken = allNetworkAssets.first(where: { + $0.network.supportedKeyrings.contains( + account.keyringId.rawValue as NSNumber + ) + })?.tokens.first { + return [account.id: [btcToken.id: btcBalance]] + } + return [:] + } + } + for await accountBTCBalances in group { + tokenBalancesCache.merge(with: accountBTCBalances) + } + } + } + // Update non-BTC account balance let balancesForAccounts = await withTaskGroup( of: TokenBalanceCache.self, body: { group in - for account in accounts { - group.addTask { - // TODO: cleanup with balance caching with issue - // https://github.com/brave/brave-browser/issues/36764 - var balancesForTokens: [String: Double] = [:] - if account.coin == .btc { - let networkAssets = allNetworkAssets.first { - $0.network.supportedKeyrings.contains(account.keyringId.rawValue as NSNumber) - } - if let btc = networkAssets?.tokens.first, - let btcBalance = await self.bitcoinWalletService.fetchBTCBalance( - accountId: account.accountId, - type: .total - ) - { - balancesForTokens = [btc.id: btcBalance] - } - } else { + for account in accounts where account.coin != .btc { + if let allTokenBalance = self.userAssetManager.getBalances( + for: nil, + account: account.id + ) { + var result: [String: Double] = [:] + for balancePerToken in allTokenBalance { + let tokenId = + balancePerToken.contractAddress + balancePerToken.chainId + + balancePerToken.symbol + balancePerToken.tokenId + result.merge(with: [ + tokenId: Double(balancePerToken.balance) ?? 0 + ]) + } + self.tokenBalancesCache.merge(with: [account.id: result]) + } else { + // 1. We have a user asset from CD but wallet has never + // fetched it's balance. Should never happen. But we will fetch its + // balance and cache it in CD. + // 2. Test Cases will come here, we will fetch balance using + // a mock `rpcService` and `bitcoinWalletService` + group.addTask { + var balancesForTokens: [String: Double] = [:] balancesForTokens = await self.rpcService.fetchBalancesForTokens( account: account, networkAssets: allNetworkAssets ) + return [account.id: balancesForTokens] } - return [account.id: balancesForTokens] } } + return await group.reduce( into: TokenBalanceCache(), { partialResult, new in @@ -292,3 +327,12 @@ class AccountsStore: ObservableObject, WalletObserverStore { tokenBalancesCache[account.id]?[tokenId] } } + +extension AccountsStore: WalletUserAssetDataObserver { + func cachedBalanceRefreshed() { + update() + } + + func userAssetUpdated() { + } +} diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AssetDetailStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AssetDetailStore.swift index 44b09d8c5247..23283d6ee988 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AssetDetailStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AssetDetailStore.swift @@ -170,6 +170,7 @@ class AssetDetailStore: ObservableObject, WalletObserverStore { func setupObservers() { guard !isObserving else { return } + self.assetManager.addUserAssetDataObserver(self) self.keyringServiceObserver = KeyringServiceObserver( keyringService: keyringService, _accountsChanged: { [weak self] in @@ -457,8 +458,6 @@ class AssetDetailStore: ObservableObject, WalletObserverStore { @MainActor group -> [AccountBalance] in for accountAssetViewModel in accountAssetViewModels { group.addTask { @MainActor in - // TODO: cleanup with balance caching with issue - // https://github.com/brave/brave-browser/issues/36764 var tokenBalance: Double? if accountAssetViewModel.account.coin == .btc { tokenBalance = await self.bitcoinWalletService.fetchBTCBalance( @@ -466,11 +465,18 @@ class AssetDetailStore: ObservableObject, WalletObserverStore { type: .total ) } else { - tokenBalance = await self.rpcService.balance( + if let assetBalancePerAccount = self.assetManager.getBalances( for: token, - in: accountAssetViewModel.account, - network: network - ) + account: accountAssetViewModel.account.id + )?.first { + tokenBalance = Double(assetBalancePerAccount.balance) + } else { + tokenBalance = await self.rpcService.balance( + for: token, + in: accountAssetViewModel.account, + network: network + ) + } } return [AccountBalance(accountAssetViewModel.account, tokenBalance)] } @@ -694,3 +700,12 @@ extension AssetDetailStore: BraveWalletBraveWalletServiceObserver { func onResetWallet() { } } + +extension AssetDetailStore: WalletUserAssetDataObserver { + func cachedBalanceRefreshed() { + update() + } + + func userAssetUpdated() { + } +} diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/NFTDetailStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/NFTDetailStore.swift index 4472ae518bcb..7435f77d1ef1 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/NFTDetailStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/NFTDetailStore.swift @@ -118,6 +118,8 @@ class NFTDetailStore: ObservableObject, WalletObserverStore { } func setupObservers() { + guard !isObserving else { return } + self.assetManager.addUserAssetDataObserver(self) self.txServiceObserver = TxServiceObserver( txService: txService, _onTransactionStatusChanged: { [weak self] txInfo in @@ -142,9 +144,7 @@ class NFTDetailStore: ObservableObject, WalletObserverStore { networkInfo = network } - if owner == nil { - updateOwner() - } + updateOwner() if nftMetadata == nil { isLoading = true @@ -182,15 +182,22 @@ class NFTDetailStore: ObservableObject, WalletObserverStore { let accounts = await keyringService.allAccounts().accounts let nftBalances: [String: Int] = await withTaskGroup( of: [String: Int].self, - body: { @MainActor [rpcService, nft] group in + body: { @MainActor [assetManager, rpcService, nft] group in for account in accounts where account.coin == nft.coin { group.addTask { @MainActor in - let balanceForToken = await rpcService.balance( + if let assetBalance = assetManager.getBalances( for: nft, - in: account, - network: network - ) - return [account.id: Int(balanceForToken ?? 0)] + account: account.id + )?.first { + return [account.id: (assetBalance.balance as NSString).integerValue] + } else { + let balanceForToken = await rpcService.balance( + for: nft, + in: account, + network: network + ) + return [account.id: Int(balanceForToken ?? 0)] + } } } return await group.reduce( @@ -217,3 +224,12 @@ class NFTDetailStore: ObservableObject, WalletObserverStore { } } } + +extension NFTDetailStore: WalletUserAssetDataObserver { + func cachedBalanceRefreshed() { + update() + } + + func userAssetUpdated() { + } +} diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/NFTStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/NFTStore.swift index 46140ad7bf85..8821114588e0 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/NFTStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/NFTStore.swift @@ -308,55 +308,80 @@ public class NFTStore: ObservableObject, WalletObserverStore { // if we're not hiding unowned or grouping by account, balance isn't needed if filters.isHidingUnownedNFTs || filters.groupBy == .accounts { let allAccounts = filters.accounts.map(\.model) - nftBalancesCache = await withTaskGroup( + + let fetchedNFTBlances = await withTaskGroup( of: [String: [String: Int]].self, - body: { @MainActor [nftBalancesCache, rpcService] group in - for nft in allUserNFTs { // for all NFTs that have not yet been fetched its balance - guard let networkForNFT = allNetworks.first(where: { $0.chainId == nft.chainId }) - else { - continue - } - group.addTask { @MainActor in - let updatedBalances = await withTaskGroup( - of: [String: Int].self, - body: { @MainActor group in - for account in allAccounts where account.coin == nft.coin { - if !forceUpdateNFTBalances, - let cachedBalance = nftBalancesCache[nft.id]?[account.id] - { // cached balance - return [account.id: cachedBalance] - } else { // no balance for this account - group.addTask { @MainActor in - let balanceForToken = await rpcService.balance( - for: nft, - in: account, - network: networkForNFT - ) - return [account.id: Int(balanceForToken ?? 0)] - } - } - } - return await group.reduce( - into: [String: Int](), - { partialResult, new in - partialResult.merge(with: new) - } + body: { @MainActor [rpcService, assetManager] group in + for nft in allUserNFTs { + if let nftBalances = assetManager.getBalances( + for: nft, + account: nil + ), + !nftBalances.isEmpty, + !forceUpdateNFTBalances + { + var result: [String: Int] = [:] + for balancePerAccount in nftBalances { + result.merge(with: [ + balancePerAccount.accountAddress: + (balancePerAccount.balance as NSString).integerValue + ]) + } + nftBalancesCache.merge(with: [nft.id: result]) + } else { + // 1. Force to fetch NFT balance + // 2. Spam NFT + // 3. We have a user asset from CD but wallet has never + // fetched it's balance. Should never happen. But we will fetch its + // balance and cache it in CD. + // 4. Test Cases will come here, we will fetch balance using + // a mock `rpcService` and `bitcoinWalletService + guard let networkForNFT = allNetworks.first(where: { $0.chainId == nft.chainId }) + else { + continue + } + group.addTask { @MainActor in + var nftBalances: [String: Int] = [:] + for account in allAccounts where account.coin == nft.coin { + var balanceForNFT: Int? + let balanceInDouble = await rpcService.balance( + for: nft, + in: account, + network: networkForNFT + ) + balanceForNFT = Int(balanceInDouble ?? 0) + nftBalances.merge(with: [account.id: balanceForNFT ?? 0]) + assetManager.updateBalance( + for: nft, + account: account.id, + balance: "\(balanceForNFT ?? 0)", + completion: nil ) } - ) - var tokenBalances = nftBalancesCache[nft.id] ?? [:] - tokenBalances.merge(with: updatedBalances) - return [nft.id: tokenBalances] + return [nft.id: nftBalances] + } } } + return await group.reduce( - into: [String: [String: Int]](), + into: [:], { partialResult, new in partialResult.merge(with: new) } ) } ) + for nft in allUserNFTs { + if let updatedBalancesForNFT = fetchedNFTBlances[nft.id] { + // if balance fetch failed that we already have cached, don't overwrite existing + if var existing = self.nftBalancesCache[nft.id] { + existing.merge(with: updatedBalancesForNFT) + self.nftBalancesCache[nft.id] = existing + } else { + self.nftBalancesCache[nft.id] = updatedBalancesForNFT + } + } + } } guard !Task.isCancelled else { return } let (userNFTGroupsWithBalance, _) = buildNFTGroupModels( diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/PortfolioStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/PortfolioStore.swift index a79d484a7741..8cfa43a5d708 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/PortfolioStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/PortfolioStore.swift @@ -314,7 +314,7 @@ public class PortfolioStore: ObservableObject, WalletObserverStore { /// Cancellable for the last running `update()` Task. private var updateTask: Task<(), Never>? private typealias TokenBalanceCache = [String: [String: Double]] - /// Cache of token balances for each account. [token.id: [account.cacheBalanceKey: balance]] + /// Cache of token balances for each account. [token.id: [account.id: balance]] private var tokenBalancesCache: TokenBalanceCache = [:] /// Cache of prices for each token. The key is the token's `assetRatioId`. private var pricesCache: [String: String] = [:] @@ -469,10 +469,14 @@ public class PortfolioStore: ObservableObject, WalletObserverStore { token: token, network: networkAssets.network, accounts: selectedAccounts.filter { - if token.coin == .fil { + switch token.coin { + case .fil, .btc, .zec: return $0.keyringId - == BraveWallet.KeyringId.keyringId(for: token.coin, on: token.chainId) - } else { + == BraveWallet.KeyringId.keyringId( + for: token.coin, + on: token.chainId + ) + default: return $0.coin == token.coin } } diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/SelectAccountTokenStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/SelectAccountTokenStore.swift index 651b219dc33e..0c46f806eecc 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/SelectAccountTokenStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/SelectAccountTokenStore.swift @@ -129,6 +129,7 @@ class SelectAccountTokenStore: ObservableObject, WalletObserverStore { func setupObservers() { guard !isObserving else { return } + self.assetManager.addUserAssetDataObserver(self) self.walletServiceObserver = WalletServiceObserver( walletService: walletService, _onDefaultBaseCurrencyChanged: { [weak self] currency in @@ -248,13 +249,26 @@ class SelectAccountTokenStore: ObservableObject, WalletObserverStore { of: TokenBalanceCache.self, body: { group in for account in allAccounts where account.coin != .btc { - group.addTask { // get balance for all tokens this account supports - let balancesForTokens: [String: Double] = await self.rpcService - .fetchBalancesForTokens( - account: account, - networkAssets: networkAssets - ) - return [account.id: balancesForTokens] + if let allTokenBalance = assetManager.getBalances(for: nil, account: account.id) { + var result: [String: Double] = [:] + for balancePerToken in allTokenBalance { + let tokenId = + balancePerToken.contractAddress + balancePerToken.chainId + + balancePerToken.symbol + balancePerToken.tokenId + result.merge(with: [ + tokenId: Double(balancePerToken.balance) ?? 0 + ]) + } + balancesForAccountsCache.merge(with: [account.id: result]) + } else { + group.addTask { // get balance for all tokens this account supports + let balancesForTokens: [String: Double] = await self.rpcService + .fetchBalancesForTokens( + account: account, + networkAssets: networkAssets + ) + return [account.id: balancesForTokens] + } } } return await group.reduce( @@ -431,3 +445,19 @@ class SelectAccountTokenStore: ObservableObject, WalletObserverStore { return accountSections } } + +extension SelectAccountTokenStore: WalletUserAssetDataObserver { + func cachedBalanceRefreshed() { + Task { @MainActor in + let allNetworks = await rpcService.allNetworksForSupportedCoins() + let allNetworkAssets = assetManager.getAllUserAssetsInNetworkAssetsByVisibility( + networks: allNetworks, + visible: true + ) + fetchAccountBalances(networkAssets: allNetworkAssets) + } + } + + func userAssetUpdated() { + } +} diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/SendTokenStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/SendTokenStore.swift index 4d7b11c9f981..4a553d770f08 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/SendTokenStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/SendTokenStore.swift @@ -226,6 +226,7 @@ public class SendTokenStore: ObservableObject, WalletObserverStore { func setupObservers() { guard !isObserving else { return } + self.assetManager.addUserAssetDataObserver(self) self.keyringServiceObserver = KeyringServiceObserver( keyringService: keyringService, _selectedWalletAccountChanged: { [weak self] _ in @@ -392,12 +393,19 @@ public class SendTokenStore: ObservableObject, WalletObserverStore { } self.btcBalances[selectedAccount.id] = btcBalances } else { - balance = await self.rpcService.balance( + if let assetBalance = self.assetManager.getBalances( for: selectedSendToken, - in: selectedAccount.address, - network: network, - decimalFormatStyle: .decimals(precision: Int(selectedSendToken.decimals)) - ) + account: selectedAccount.id + )?.first { + balance = BDouble(assetBalance.balance) + } else { + balance = await self.rpcService.balance( + for: selectedSendToken, + in: selectedAccount.address, + network: network, + decimalFormatStyle: .decimals(precision: Int(selectedSendToken.decimals)) + ) + } } if selectedSendToken.isErc721 || selectedSendToken.isNft, @@ -917,3 +925,12 @@ public class SendTokenStore: ObservableObject, WalletObserverStore { return await rpcService.fetchNFTMetadata(tokens: tokens, ipfsApi: ipfsApi) } } + +extension SendTokenStore: WalletUserAssetDataObserver { + public func cachedBalanceRefreshed() { + update() + } + + public func userAssetUpdated() { + } +} diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/SwapTokenStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/SwapTokenStore.swift index 1c05c6b5c938..777ed5a5ff91 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/SwapTokenStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/SwapTokenStore.swift @@ -244,6 +244,7 @@ public class SwapTokenStore: ObservableObject, WalletObserverStore { func setupObservers() { guard !isObserving else { return } + self.assetManager.addUserAssetDataObserver(self) self.keyringServiceObserver = KeyringServiceObserver( keyringService: keyringService, _selectedWalletAccountChanged: { [weak self] account in @@ -302,13 +303,20 @@ public class SwapTokenStore: ObservableObject, WalletObserverStore { } rpcService.network(coin: token.coin, origin: nil) { [weak self] network in - self?.rpcService.balance( + if let assetBalance = self?.assetManager.getBalances( for: token, - in: account.address, - network: network, - decimalFormatStyle: .decimals(precision: Int(token.decimals)) - ) { balance in - completion(balance) + account: account.id + )?.first(where: { $0.chainId == network.chainId }) { + completion(BDouble(assetBalance.balance)) + } else { + self?.rpcService.balance( + for: token, + in: account.address, + network: network, + decimalFormatStyle: .decimals(precision: Int(token.decimals)) + ) { balance in + completion(balance) + } } } } @@ -685,22 +693,29 @@ public class SwapTokenStore: ObservableObject, WalletObserverStore { let network = await rpcService.network(coin: accountInfo.coin, origin: nil) // Check if balance available to pay for gas - let (ethBalanceString, _, _) = await rpcService.balance( - address: accountInfo.address, - coin: network.coin, - chainId: network.chainId - ) + let ethBalance: BDouble + if let assetBalance = assetManager.getBalances( + for: network.nativeToken, + account: accountInfo.id + )?.first(where: { $0.chainId == network.chainId }) { + ethBalance = BDouble(assetBalance.balance) ?? 0 + } else { + let (ethBalanceString, _, _) = await rpcService.balance( + address: accountInfo.address, + coin: network.coin, + chainId: network.chainId + ) + let balanceFormatter = WalletAmountFormatter(decimalFormatStyle: .balance) + ethBalance = + BDouble( + balanceFormatter.decimalString( + for: ethBalanceString.removingHexPrefix, + radix: .hex, + decimals: 18 + ) ?? "" + ) ?? 0 + } let fee = gasLimit * gasPrice - let balanceFormatter = WalletAmountFormatter(decimalFormatStyle: .balance) - let ethBalance = - BDouble( - balanceFormatter.decimalString( - for: ethBalanceString.removingHexPrefix, - radix: .hex, - decimals: 18 - ) - ?? "" - ) ?? 0 if fromToken.symbol == network.symbol { if ethBalance < fee + sellAmountValue { self.state = .error(Strings.Wallet.insufficientFundsForGas) @@ -817,19 +832,27 @@ public class SwapTokenStore: ObservableObject, WalletObserverStore { // Check if balance available to pay for gas if route.fromToken.coin == .eth { - let (ethBalanceString, _, _) = await rpcService.balance( - address: accountInfo.address, - coin: network.coin, - chainId: network.chainId - ) - let ethBalance = - BDouble( - walletAmountFormatter.decimalString( - for: ethBalanceString.removingHexPrefix, - radix: .hex, - decimals: 18 - ) ?? "" - ) ?? 0 + let ethBalance: BDouble + if let assetBalance = assetManager.getBalances( + for: network.nativeToken, + account: accountInfo.id + )?.first(where: { $0.chainId == network.chainId }) { + ethBalance = BDouble(assetBalance.balance) ?? 0 + } else { + let (ethBalanceString, _, _) = await rpcService.balance( + address: accountInfo.address, + coin: network.coin, + chainId: network.chainId + ) + ethBalance = + BDouble( + walletAmountFormatter.decimalString( + for: ethBalanceString.removingHexPrefix, + radix: .hex, + decimals: 18 + ) ?? "" + ) ?? 0 + } let feeTotal: Double = step.estimate.gasCosts.reduce(Double(0)) { total, cost in total + (Double(cost.amount) ?? 0) } @@ -1215,3 +1238,16 @@ public class SwapTokenStore: ObservableObject, WalletObserverStore { } #endif } + +extension SwapTokenStore: WalletUserAssetDataObserver { + public func cachedBalanceRefreshed() { + if let token = selectedFromToken { + fetchTokenBalance(for: token) { [weak self] balance in + self?.selectedFromTokenBalance = balance + } + } + } + + public func userAssetUpdated() { + } +} diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/TransactionConfirmationStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/TransactionConfirmationStore.swift index e687349bfee5..03c1f3c02af2 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/TransactionConfirmationStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/TransactionConfirmationStore.swift @@ -210,6 +210,7 @@ public class TransactionConfirmationStore: ObservableObject, WalletObserverStore func setupObservers() { guard !isObserving else { return } + self.assetManager.addUserAssetDataObserver(self) self.txServiceObserver = TxServiceObserver( txService: txService, _onNewUnapprovedTx: { _ in @@ -457,7 +458,16 @@ public class TransactionConfirmationStore: ObservableObject, WalletObserverStore gasBalancesForChain[account.id] = availableBTCBalance } } else { - if let gasTokenBalance = await rpcService.balance(for: token, in: account, network: network) { + if let assetBalance = assetManager.getBalances( + for: token, + account: account.id + )?.first(where: { $0.chainId == network.chainId }) { + gasBalancesForChain[account.id] = Double(assetBalance.balance) ?? 0 + } else if let gasTokenBalance = await rpcService.balance( + for: token, + in: account, + network: network + ) { gasBalancesForChain[account.id] = gasTokenBalance } } @@ -993,3 +1003,16 @@ struct TransactionProviderError { let code: Int let message: String } + +extension TransactionConfirmationStore: WalletUserAssetDataObserver { + public func cachedBalanceRefreshed() { + updateTransaction( + with: activeTransaction, + shouldFetchCurrentAllowance: false, + shouldFetchGasTokenBalance: true + ) + } + + public func userAssetUpdated() { + } +} diff --git a/ios/brave-ios/Sources/BraveWallet/Extensions/RpcServiceExtensions.swift b/ios/brave-ios/Sources/BraveWallet/Extensions/RpcServiceExtensions.swift index 249dd06953e6..be47b38f6da8 100644 --- a/ios/brave-ios/Sources/BraveWallet/Extensions/RpcServiceExtensions.swift +++ b/ios/brave-ios/Sources/BraveWallet/Extensions/RpcServiceExtensions.swift @@ -293,31 +293,6 @@ extension BraveWalletJsonRpcService { } } - /// Returns the total balance for a given token for all of the given accounts - @MainActor func fetchTotalBalance( - token: BraveWallet.BlockchainToken, - network: BraveWallet.NetworkInfo, - accounts: [BraveWallet.AccountInfo] - ) async -> Double { - let balancesForAsset = await withTaskGroup( - of: [Double].self, - body: { @MainActor group in - for account in accounts { - group.addTask { @MainActor in - let balance = await self.balance( - for: token, - in: account, - network: network - ) - return [balance ?? 0] - } - } - return await group.reduce([Double](), { $0 + $1 }) - } - ) - return balancesForAsset.reduce(0, +) - } - /// Returns the total balance for a given account for all of the given network assets func fetchBalancesForTokens( account: BraveWallet.AccountInfo, diff --git a/ios/brave-ios/Sources/BraveWallet/WalletUserAssetManager.swift b/ios/brave-ios/Sources/BraveWallet/WalletUserAssetManager.swift index 568dc3a4328c..6fb834016ed3 100644 --- a/ios/brave-ios/Sources/BraveWallet/WalletUserAssetManager.swift +++ b/ios/brave-ios/Sources/BraveWallet/WalletUserAssetManager.swift @@ -115,6 +115,7 @@ public class WalletUserAssetManager: WalletUserAssetManagerType, WalletObserverS keyringServiceObserver = nil txServiceObserver = nil walletServiceObserver = nil + dataObservers.removeAllObjects() } public func setupObservers() {