diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/FiltersDisplaySettingsView.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/FiltersDisplaySettingsView.swift index 996ff023cba..af6ff1a6f32 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/FiltersDisplaySettingsView.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/FiltersDisplaySettingsView.swift @@ -88,7 +88,7 @@ struct Filters { Preferences.Wallet.nonSelectedAccountsFilter.value = accounts .filter({ !$0.isSelected }) - .map(\.model.address) + .map(\.model.filterAccountKey) Preferences.Wallet.nonSelectedNetworksFilter.value = networks .filter({ !$0.isSelected }) diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountActivityStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountActivityStore.swift index c5eea3d53a3..90296ac3e25 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountActivityStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountActivityStore.swift @@ -44,6 +44,7 @@ class AccountActivityStore: ObservableObject, WalletObserverStore { private let blockchainRegistry: BraveWalletBlockchainRegistry private let solTxManagerProxy: BraveWalletSolanaTxManagerProxy private let ipfsApi: IpfsAPI + private let bitcoinWalletService: BraveWalletBitcoinWalletService private let assetManager: WalletUserAssetManagerType /// Cache for storing `BlockchainToken`s that are not in user assets or our token registry. /// This could occur with a dapp creating a transaction. @@ -75,6 +76,7 @@ class AccountActivityStore: ObservableObject, WalletObserverStore { blockchainRegistry: BraveWalletBlockchainRegistry, solTxManagerProxy: BraveWalletSolanaTxManagerProxy, ipfsApi: IpfsAPI, + bitcoinWalletService: BraveWalletBitcoinWalletService, userAssetManager: WalletUserAssetManagerType ) { self.account = account @@ -88,6 +90,7 @@ class AccountActivityStore: ObservableObject, WalletObserverStore { self.blockchainRegistry = blockchainRegistry self.solTxManagerProxy = solTxManagerProxy self.ipfsApi = ipfsApi + self.bitcoinWalletService = bitcoinWalletService self.assetManager = userAssetManager self._isSwapSupported = .init( wrappedValue: account.coin == .eth || account.coin == .sol @@ -223,10 +226,25 @@ class AccountActivityStore: ObservableObject, WalletObserverStore { ) self.isLoadingAccountFiat = true - let tokenBalances = await self.rpcService.fetchBalancesForTokens( - account: account, - networkAssets: allUserNetworkAssets - ) + var tokenBalances: [String: Double] = [:] + if account.coin == .btc { + let networkAsset = allUserNetworkAssets.first { + $0.network.supportedKeyrings.contains(account.keyringId.rawValue as NSNumber) + } + if let btc = networkAsset?.tokens.first, + let btcBalance = await self.bitcoinWalletService.fetchBTCBalance( + accountId: account.accountId + ) + { + tokenBalances = [btc.id: btcBalance] + } + } else { + tokenBalances = await self.rpcService.fetchBalancesForTokens( + account: account, + networkAssets: allUserNetworkAssets + ) + tokenBalanceCache.merge(with: tokenBalances) + } tokenBalanceCache.merge(with: tokenBalances) // fetch price for every user asset diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountsStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountsStore.swift index ae6d66bfd8c..9c5f9726b27 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountsStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountsStore.swift @@ -33,7 +33,7 @@ class AccountsStore: ObservableObject, WalletObserverStore { let currencyFormatter: NumberFormatter = .usdCurrencyFormatter - /// Cache of token balances for each account. [account.address: [token.id: balance]] + /// Cache of token balances for each account. [account.cacheBalanceKey: [token.id: balance]] private var tokenBalancesCache: [String: [String: Double]] = [:] /// Cache of prices for each token. The key is the token's `assetRatioId`. private var pricesCache: [String: String] = [:] @@ -42,6 +42,7 @@ class AccountsStore: ObservableObject, WalletObserverStore { private let rpcService: BraveWalletJsonRpcService private let walletService: BraveWalletBraveWalletService private let assetRatioService: BraveWalletAssetRatioService + private let bitcoinWalletService: BraveWalletBitcoinWalletService private let userAssetManager: WalletUserAssetManagerType private var keyringServiceObserver: KeyringServiceObserver? @@ -56,12 +57,14 @@ class AccountsStore: ObservableObject, WalletObserverStore { rpcService: BraveWalletJsonRpcService, walletService: BraveWalletBraveWalletService, assetRatioService: BraveWalletAssetRatioService, + bitcoinWalletService: BraveWalletBitcoinWalletService, userAssetManager: WalletUserAssetManagerType ) { self.keyringService = keyringService self.rpcService = rpcService self.walletService = walletService self.assetRatioService = assetRatioService + self.bitcoinWalletService = bitcoinWalletService self.userAssetManager = userAssetManager self.setupObservers() } @@ -143,11 +146,25 @@ class AccountsStore: ObservableObject, WalletObserverStore { body: { group in for account in accounts { group.addTask { - let balancesForTokens: [String: Double] = await self.rpcService.fetchBalancesForTokens( - account: account, - networkAssets: allNetworkAssets - ) - return [account.address: balancesForTokens] + 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 + ) + { + balancesForTokens = [btc.id: btcBalance] + } + } else { + balancesForTokens = await self.rpcService.fetchBalancesForTokens( + account: account, + networkAssets: allNetworkAssets + ) + } + return [account.cacheBalanceKey: balancesForTokens] } } return await group.reduce( @@ -159,13 +176,13 @@ class AccountsStore: ObservableObject, WalletObserverStore { } ) for account in accounts { - if let updatedBalancesForAccount = balancesForAccounts[account.address] { + if let updatedBalancesForAccount = balancesForAccounts[account.cacheBalanceKey] { // if balance fetch failed that we already have cached, don't overwrite existing - if var existing = self.tokenBalancesCache[account.address] { + if var existing = self.tokenBalancesCache[account.cacheBalanceKey] { existing.merge(with: updatedBalancesForAccount) - self.tokenBalancesCache[account.address] = existing + self.tokenBalancesCache[account.cacheBalanceKey] = existing } else { - self.tokenBalancesCache[account.address] = updatedBalancesForAccount + self.tokenBalancesCache[account.cacheBalanceKey] = updatedBalancesForAccount } } } @@ -223,7 +240,7 @@ class AccountsStore: ObservableObject, WalletObserverStore { for account: BraveWallet.AccountInfo, tokens: [BraveWallet.BlockchainToken] ) -> [BraveWallet.BlockchainToken] { - guard let tokenBalancesForAccount = tokenBalancesCache[account.address] else { + guard let tokenBalancesForAccount = tokenBalancesCache[account.cacheBalanceKey] else { return [] } var tokensFiatForAccount: [(token: BraveWallet.BlockchainToken, fiat: Double)] = [] @@ -247,7 +264,7 @@ class AccountsStore: ObservableObject, WalletObserverStore { for account: BraveWallet.AccountInfo, tokens: [BraveWallet.BlockchainToken] ) -> Double { - guard let accountBalanceCache = tokenBalancesCache[account.address] else { return 0 } + guard let accountBalanceCache = tokenBalancesCache[account.cacheBalanceKey] else { return 0 } return accountBalanceCache.keys.reduce(0.0) { partialResult, tokenId in guard let tokenBalanceForAccount = tokenBalanceForAccount(tokenId: tokenId, account: account) else { @@ -271,7 +288,7 @@ class AccountsStore: ObservableObject, WalletObserverStore { tokenId: String, account: BraveWallet.AccountInfo ) -> Double? { - tokenBalancesCache[account.address]?[tokenId] + tokenBalancesCache[account.cacheBalanceKey]?[tokenId] } } diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AssetDetailStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AssetDetailStore.swift index 2d8f2f69316..67b1a1cbb8b 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AssetDetailStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AssetDetailStore.swift @@ -83,6 +83,7 @@ class AssetDetailStore: ObservableObject, WalletObserverStore { private let solTxManagerProxy: BraveWalletSolanaTxManagerProxy private let ipfsApi: IpfsAPI private let swapService: BraveWalletSwapService + private let bitcoinWalletService: BraveWalletBitcoinWalletService private let assetManager: WalletUserAssetManagerType /// A list of tokens that are supported with the current selected network for all supported /// on-ramp providers. @@ -138,6 +139,7 @@ class AssetDetailStore: ObservableObject, WalletObserverStore { solTxManagerProxy: BraveWalletSolanaTxManagerProxy, ipfsApi: IpfsAPI, swapService: BraveWalletSwapService, + bitcoinWalletService: BraveWalletBitcoinWalletService, userAssetManager: WalletUserAssetManagerType, assetDetailType: AssetDetailType ) { @@ -150,6 +152,7 @@ class AssetDetailStore: ObservableObject, WalletObserverStore { self.solTxManagerProxy = solTxManagerProxy self.ipfsApi = ipfsApi self.swapService = swapService + self.bitcoinWalletService = bitcoinWalletService self.assetManager = userAssetManager self.assetDetailType = assetDetailType @@ -456,19 +459,26 @@ class AssetDetailStore: ObservableObject, WalletObserverStore { @MainActor group -> [AccountBalance] in for accountAssetViewModel in accountAssetViewModels { group.addTask { @MainActor in - let balance = await self.rpcService.balance( - for: token, - in: accountAssetViewModel.account, - network: network - ) - return [AccountBalance(accountAssetViewModel.account, balance)] + var tokenBalance: Double? + if accountAssetViewModel.account.coin == .btc { + tokenBalance = await self.bitcoinWalletService.fetchBTCBalance( + accountId: accountAssetViewModel.account.accountId + ) + } else { + tokenBalance = await self.rpcService.balance( + for: token, + in: accountAssetViewModel.account, + network: network + ) + } + return [AccountBalance(accountAssetViewModel.account, tokenBalance)] } } return await group.reduce([AccountBalance](), { $0 + $1 }) } for tokenBalance in tokenBalances { if let index = accountAssetViewModels.firstIndex(where: { - $0.account.address == tokenBalance.account.address + $0.account.cacheBalanceKey == tokenBalance.account.cacheBalanceKey }) { accountAssetViewModels[index].decimalBalance = tokenBalance.balance ?? 0.0 accountAssetViewModels[index].balance = String(format: "%.4f", tokenBalance.balance ?? 0.0) diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/CryptoStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/CryptoStore.swift index 6469fcb0431..c757ae6aad6 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/CryptoStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/CryptoStore.swift @@ -165,7 +165,8 @@ public class CryptoStore: ObservableObject, WalletObserverStore { keyringService: keyringService, rpcService: rpcService, walletService: walletService, - txService: txService + txService: txService, + bitcoinWalletService: bitcoinWalletService ) self.origin = origin @@ -184,6 +185,7 @@ public class CryptoStore: ObservableObject, WalletObserverStore { assetRatioService: assetRatioService, blockchainRegistry: blockchainRegistry, ipfsApi: ipfsApi, + bitcoinWalletService: bitcoinWalletService, userAssetManager: userAssetManager ) self.nftStore = .init( @@ -213,6 +215,7 @@ public class CryptoStore: ObservableObject, WalletObserverStore { rpcService: rpcService, walletService: walletService, assetRatioService: assetRatioService, + bitcoinWalletService: bitcoinWalletService, userAssetManager: userAssetManager ) self.marketStore = .init( @@ -504,6 +507,7 @@ public class CryptoStore: ObservableObject, WalletObserverStore { solTxManagerProxy: solTxManagerProxy, ipfsApi: ipfsApi, swapService: swapService, + bitcoinWalletService: bitcoinWalletService, userAssetManager: userAssetManager, assetDetailType: assetDetailType ) @@ -541,6 +545,7 @@ public class CryptoStore: ObservableObject, WalletObserverStore { blockchainRegistry: blockchainRegistry, solTxManagerProxy: solTxManagerProxy, ipfsApi: ipfsApi, + bitcoinWalletService: bitcoinWalletService, userAssetManager: userAssetManager ) accountActivityStore = store diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/NFTStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/NFTStore.swift index 03c1eeef3e1..83d6c177119 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/NFTStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/NFTStore.swift @@ -66,7 +66,9 @@ public class NFTStore: ObservableObject, WalletObserverStore { return Filters( accounts: allAccounts.map { account in .init( - isSelected: !nonSelectedAccountAddresses.contains(where: { $0 == account.address }), + isSelected: !nonSelectedAccountAddresses.contains(where: { + $0 == account.filterAccountKey + }), model: account ) }, diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/PortfolioStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/PortfolioStore.swift index 6bab7848da0..5c67cc3d027 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/PortfolioStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/PortfolioStore.swift @@ -67,7 +67,7 @@ public struct AssetGroupViewModel: WalletAssetGroupViewModel, Identifiable, Equa let balance: Double switch groupType { case .account(let account): - balance = asset.balanceForAccounts[account.address] ?? 0 + balance = asset.balanceForAccounts[account.cacheBalanceKey] ?? 0 case .none, .network: balance = asset.totalBalance } @@ -109,7 +109,7 @@ public struct AssetViewModel: Identifiable, Equatable { let network: BraveWallet.NetworkInfo let price: String let history: [BraveWallet.AssetTimePrice] - /// Balance for each account for this asset. The key is the account address. + /// Balance for each account for this asset. The key is the account.cacheBalanceKey. let balanceForAccounts: [String: Double] /// The total balance for all accounts for this asset. var totalBalance: Double { @@ -125,7 +125,7 @@ public struct AssetViewModel: Identifiable, Equatable { let balance: Double switch groupType { case .account(let account): - balance = balanceForAccounts[account.address] ?? 0 + balance = balanceForAccounts[account.cacheBalanceKey] ?? 0 case .none, .network: balance = totalBalance } @@ -137,7 +137,7 @@ public struct AssetViewModel: Identifiable, Equatable { let balance: Double switch groupType { case .account(let account): - balance = balanceForAccounts[account.address] ?? 0 + balance = balanceForAccounts[account.cacheBalanceKey] ?? 0 case .none, .network: balance = totalBalance } @@ -287,7 +287,9 @@ public class PortfolioStore: ObservableObject, WalletObserverStore { return Filters( accounts: allAccounts.map { account in .init( - isSelected: !nonSelectedAccountAddresses.contains(where: { $0 == account.address }), + isSelected: !nonSelectedAccountAddresses.contains( + where: { $0 == account.filterAccountKey } + ), model: account ) }, @@ -317,7 +319,7 @@ public class PortfolioStore: ObservableObject, WalletObserverStore { /// Cancellable for the last running `update()` Task. private var updateTask: Task<(), Never>? - /// Cache of token balances for each account. [token.id: [account.address: balance]] + /// Cache of token balances for each account. [token.id: [account.cacheBalanceKey: balance]] private var tokenBalancesCache: [String: [String: Double]] = [:] /// Cache of prices for each token. The key is the token's `assetRatioId`. private var pricesCache: [String: String] = [:] @@ -330,6 +332,7 @@ public class PortfolioStore: ObservableObject, WalletObserverStore { private let assetRatioService: BraveWalletAssetRatioService private let blockchainRegistry: BraveWalletBlockchainRegistry private let ipfsApi: IpfsAPI + private let bitcoinWalletService: BraveWalletBitcoinWalletService private let assetManager: WalletUserAssetManagerType private var rpcServiceObserver: JsonRpcServiceObserver? private var keyringServiceObserver: KeyringServiceObserver? @@ -346,6 +349,7 @@ public class PortfolioStore: ObservableObject, WalletObserverStore { assetRatioService: BraveWalletAssetRatioService, blockchainRegistry: BraveWalletBlockchainRegistry, ipfsApi: IpfsAPI, + bitcoinWalletService: BraveWalletBitcoinWalletService, userAssetManager: WalletUserAssetManagerType ) { self.keyringService = keyringService @@ -354,6 +358,7 @@ public class PortfolioStore: ObservableObject, WalletObserverStore { self.assetRatioService = assetRatioService self.blockchainRegistry = blockchainRegistry self.ipfsApi = ipfsApi + self.bitcoinWalletService = bitcoinWalletService self.assetManager = userAssetManager // cache balance update observer @@ -503,24 +508,39 @@ public class PortfolioStore: ObservableObject, WalletObserverStore { // 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` + // a mock `rpcService` and `bitcoinWalletService` let fetchedTokenBalances = await withTaskGroup( of: [String: [String: Double]].self, - body: { @MainActor [tokenBalancesCache, rpcService, assetManager] group in + body: { + @MainActor + [tokenBalancesCache, rpcService, bitcoinWalletService, assetManager] + group in group.addTask { @MainActor in let token = tokenNetworkAccounts.token var tokenBalances = tokenBalancesCache[token.id] ?? [:] // fetch balance for this token for each account for account in tokenNetworkAccounts.accounts { - let balanceForToken = await rpcService.balance( - for: token, - in: account, - network: tokenNetworkAccounts.network - ) - tokenBalances.merge(with: [account.address: balanceForToken ?? 0]) + var balanceForToken: Double? + let tokenNetwork = tokenNetworkAccounts.network + if account.coin == .btc, + tokenNetwork.supportedKeyrings.contains( + account.keyringId.rawValue as NSNumber + ) + { + balanceForToken = await bitcoinWalletService.fetchBTCBalance( + accountId: account.accountId + ) + } else { + balanceForToken = await rpcService.balance( + for: token, + in: account, + network: tokenNetworkAccounts.network + ) + } + tokenBalances.merge(with: [account.cacheBalanceKey: balanceForToken ?? 0]) assetManager.updateBalance( for: token, - account: account.address, + account: account.cacheBalanceKey, balance: "\(balanceForToken ?? 0)", completion: nil ) @@ -705,7 +725,7 @@ public class PortfolioStore: ObservableObject, WalletObserverStore { .filter { key, value in // if we previously fetched balance for an account it will remain in cache. // filter out to avoid including in total balance - selectedAccounts.contains(where: { $0.address == key }) + selectedAccounts.contains(where: { $0.cacheBalanceKey == key }) } ) } @@ -741,7 +761,7 @@ public class PortfolioStore: ObservableObject, WalletObserverStore { .filter { key, value in // if we previously fetched balance for an account it will remain in cache. // filter out to avoid including in total balance - selectedAccounts.contains(where: { $0.address == key }) + selectedAccounts.contains(where: { $0.cacheBalanceKey == key }) } ) } @@ -775,7 +795,7 @@ public class PortfolioStore: ObservableObject, WalletObserverStore { balanceForAccounts: tokenBalancesCache[token.id, default: [:]] .filter { key, value in // only provide grouped account balance - key == account.address + key == account.cacheBalanceKey } ) } @@ -783,7 +803,7 @@ public class PortfolioStore: ObservableObject, WalletObserverStore { .optionallyFilter( shouldFilter: filters.isHidingSmallBalances, isIncluded: { asset in - let balanceForAccount = asset.balanceForAccounts[account.address] ?? 0 + let balanceForAccount = asset.balanceForAccounts[account.cacheBalanceKey] ?? 0 let value = (Double(asset.price) ?? 0) * balanceForAccount return value >= 0.05 } diff --git a/ios/brave-ios/Sources/BraveWallet/Extensions/BitcoinWalletServiceExtensions.swift b/ios/brave-ios/Sources/BraveWallet/Extensions/BitcoinWalletServiceExtensions.swift index ad2d0d905a2..dc6ecb3d767 100644 --- a/ios/brave-ios/Sources/BraveWallet/Extensions/BitcoinWalletServiceExtensions.swift +++ b/ios/brave-ios/Sources/BraveWallet/Extensions/BitcoinWalletServiceExtensions.swift @@ -36,4 +36,25 @@ extension BraveWalletBitcoinWalletService { } ) } + + /// - Parameters: + /// - accountId: A list of `BraveWallet.AccountId` + /// + /// - Returns: The BTC balance of the given `BraveWallet.AccountId` in `Double`; Will return a nil if there is an issue fetching balance. + func fetchBTCBalance(accountId: BraveWallet.AccountId) async -> Double? { + guard let btcBalance = await self.balance(accountId: accountId).0 + else { return nil } + + let availableSatoshiString = String(btcBalance.availableBalance) + let formatter = WeiFormatter(decimalFormatStyle: .decimals(precision: 8)) + if let valueString = formatter.decimalString( + for: availableSatoshiString, + radix: .decimal, + decimals: 8 + ) { + return Double(valueString) + } else { + return nil + } + } } diff --git a/ios/brave-ios/Sources/BraveWallet/Extensions/BraveWalletExtensions.swift b/ios/brave-ios/Sources/BraveWallet/Extensions/BraveWalletExtensions.swift index adeba0ac84a..c0bf1c5146a 100644 --- a/ios/brave-ios/Sources/BraveWallet/Extensions/BraveWalletExtensions.swift +++ b/ios/brave-ios/Sources/BraveWallet/Extensions/BraveWalletExtensions.swift @@ -201,6 +201,16 @@ extension BraveWallet.AccountInfo { } return parentOrder } + + // The account unique key that we use to store for token balance in CD + var cacheBalanceKey: String { + coin == .btc ? accountId.uniqueKey : address + } + + // The account unique key that we use to store for user filter preferences + var filterAccountKey: String { + coin == .btc ? accountId.uniqueKey : address + } } extension BraveWallet.CoinType { diff --git a/ios/brave-ios/Sources/BraveWallet/Preview Content/MockKeyringService.swift b/ios/brave-ios/Sources/BraveWallet/Preview Content/MockKeyringService.swift index a5c43fe2b06..d9792b1a8a8 100644 --- a/ios/brave-ios/Sources/BraveWallet/Preview Content/MockKeyringService.swift +++ b/ios/brave-ios/Sources/BraveWallet/Preview Content/MockKeyringService.swift @@ -531,6 +531,8 @@ extension BraveWallet.AllAccountsInfo { .mockSolAccount, .mockFilAccount, .mockFilTestnetAccount, + .mockBtcAccount, + .mockBtcTestnetAccount, ], selectedAccount: .mockEthAccount, ethDappSelectedAccount: .mockEthAccount, diff --git a/ios/brave-ios/Sources/BraveWallet/Preview Content/MockStores.swift b/ios/brave-ios/Sources/BraveWallet/Preview Content/MockStores.swift index f8761cac159..d3fd1b48e45 100644 --- a/ios/brave-ios/Sources/BraveWallet/Preview Content/MockStores.swift +++ b/ios/brave-ios/Sources/BraveWallet/Preview Content/MockStores.swift @@ -153,6 +153,7 @@ extension AssetDetailStore { solTxManagerProxy: BraveWallet.TestSolanaTxManagerProxy.previewProxy, ipfsApi: TestIpfsAPI(), swapService: MockSwapService(), + bitcoinWalletService: BraveWallet.TestBitcoinWalletService.previewBitcoinWalletService, userAssetManager: TestableWalletUserAssetManager(), assetDetailType: .blockchainToken(.previewToken) ) @@ -204,6 +205,7 @@ extension AccountActivityStore { blockchainRegistry: MockBlockchainRegistry(), solTxManagerProxy: BraveWallet.TestSolanaTxManagerProxy.previewProxy, ipfsApi: TestIpfsAPI(), + bitcoinWalletService: BraveWallet.TestBitcoinWalletService.previewBitcoinWalletService, userAssetManager: TestableWalletUserAssetManager() ) } @@ -270,6 +272,7 @@ extension AccountsStore { rpcService: MockJsonRpcService(), walletService: MockBraveWalletService(), assetRatioService: MockAssetRatioService(), + bitcoinWalletService: BraveWallet.TestBitcoinWalletService.previewBitcoinWalletService, userAssetManager: TestableWalletUserAssetManager() ) } diff --git a/ios/brave-ios/Sources/BraveWallet/WalletUserAssetManager.swift b/ios/brave-ios/Sources/BraveWallet/WalletUserAssetManager.swift index 8851444e4b2..5319e1fe6e0 100644 --- a/ios/brave-ios/Sources/BraveWallet/WalletUserAssetManager.swift +++ b/ios/brave-ios/Sources/BraveWallet/WalletUserAssetManager.swift @@ -81,6 +81,7 @@ public class WalletUserAssetManager: WalletUserAssetManagerType, WalletObserverS private let rpcService: BraveWalletJsonRpcService private let walletService: BraveWalletBraveWalletService private let txService: BraveWalletTxService + private let bitcoinWalletService: BraveWalletBitcoinWalletService private var keyringServiceObserver: KeyringServiceObserver? private var txServiceObserver: TxServiceObserver? @@ -95,12 +96,14 @@ public class WalletUserAssetManager: WalletUserAssetManagerType, WalletObserverS keyringService: BraveWalletKeyringService, rpcService: BraveWalletJsonRpcService, walletService: BraveWalletBraveWalletService, - txService: BraveWalletTxService + txService: BraveWalletTxService, + bitcoinWalletService: BraveWalletBitcoinWalletService ) { self.keyringService = keyringService self.rpcService = rpcService self.walletService = walletService self.txService = txService + self.bitcoinWalletService = bitcoinWalletService setupObservers() @@ -386,30 +389,47 @@ public class WalletUserAssetManager: WalletUserAssetManagerType, WalletObserverS ) await withTaskGroup( of: Void.self, - body: { @MainActor [rpcService] group in + body: { @MainActor [rpcService, bitcoinWalletService] group in for account in accounts { guard !Task.isCancelled else { return } group.addTask { @MainActor in - let allTokenBalanceForAccount = await rpcService.fetchBalancesForTokens( - account: account, - networkAssets: allUserAssets - ) - for tokenBalanceForAccount in allTokenBalanceForAccount { - guard !Task.isCancelled else { return } - let networkAssets = allUserAssets.first { oneNetworkAssets in - oneNetworkAssets.tokens.contains { asset in - asset.id == tokenBalanceForAccount.key - } + if account.coin == .btc { + let networkAssets = allUserAssets.first { + $0.network.supportedKeyrings.contains(account.keyringId.rawValue as NSNumber) } - if let token = networkAssets?.tokens.first(where: { - $0.id == tokenBalanceForAccount.key - }) { + if let btc = networkAssets?.tokens.first, + let btcBalance = await bitcoinWalletService.fetchBTCBalance( + accountId: account.accountId + ) + { WalletUserAssetBalance.updateBalance( - for: token, - balance: String(tokenBalanceForAccount.value), - account: account.address + for: btc, + balance: String(btcBalance), + account: account.cacheBalanceKey ) } + } else { + let allTokenBalanceForAccount = await rpcService.fetchBalancesForTokens( + account: account, + networkAssets: allUserAssets + ) + for tokenBalanceForAccount in allTokenBalanceForAccount { + guard !Task.isCancelled else { return } + let networkAssets = allUserAssets.first { oneNetworkAssets in + oneNetworkAssets.tokens.contains { asset in + asset.id == tokenBalanceForAccount.key + } + } + if let token = networkAssets?.tokens.first(where: { + $0.id == tokenBalanceForAccount.key + }) { + WalletUserAssetBalance.updateBalance( + for: token, + balance: String(tokenBalanceForAccount.value), + account: account.cacheBalanceKey + ) + } + } } } } @@ -454,26 +474,33 @@ public class WalletUserAssetManager: WalletUserAssetManagerType, WalletObserverS completion: (() -> Void)? = nil ) { Task { @MainActor in - let accounts = await keyringService.allAccounts().accounts.filter { $0.coin == asset.coin } let network = await rpcService.allNetworksForSupportedCoins().first { $0.chainId.lowercased() == asset.chainId.lowercased() } - guard let network = network else { return } + let accounts = await keyringService.allAccounts().accounts.filter { $0.coin == asset.coin } + await withTaskGroup( of: Void.self, - body: { @MainActor [rpcService] group in + body: { @MainActor [rpcService, bitcoinWalletService] group in for account in accounts { // for each account group.addTask { @MainActor in - let assetBalance = await rpcService.balance( - for: asset, - in: account, - network: network - ) + var tokenBalance: Double? + if account.coin == .btc { + tokenBalance = await bitcoinWalletService.fetchBTCBalance( + accountId: account.accountId + ) + } else { + tokenBalance = await rpcService.balance( + for: asset, + in: account, + network: network + ) + } WalletUserAssetBalance.updateBalance( for: asset, - balance: String(assetBalance ?? 0), - account: account.address + balance: String(tokenBalance ?? 0), + account: account.cacheBalanceKey ) } } diff --git a/ios/brave-ios/Tests/BraveWalletTests/AccountActivityStoreTests.swift b/ios/brave-ios/Tests/BraveWalletTests/AccountActivityStoreTests.swift index b3792dd7d24..6e8b548823f 100644 --- a/ios/brave-ios/Tests/BraveWalletTests/AccountActivityStoreTests.swift +++ b/ios/brave-ios/Tests/BraveWalletTests/AccountActivityStoreTests.swift @@ -57,7 +57,8 @@ class AccountActivityStoreTests: XCTestCase { BraveWallet.TestBraveWalletService, BraveWallet.TestBlockchainRegistry, BraveWallet.TestAssetRatioService, BraveWallet.TestSwapService, BraveWallet.TestTxService, BraveWallet.TestSolanaTxManagerProxy, - IpfsAPI + IpfsAPI, + BraveWallet.TestBitcoinWalletService ) { let keyringService = BraveWallet.TestKeyringService() keyringService._addObserver = { _ in } @@ -165,9 +166,14 @@ class AccountActivityStoreTests: XCTestCase { let ipfsApi = TestIpfsAPI() + let bitcoinWalletService = BraveWallet.TestBitcoinWalletService() + bitcoinWalletService._balance = { + $1(.init(totalBalance: 1000, availableBalance: 1000, pendingBalance: 0, balances: [:]), nil) + } + return ( keyringService, rpcService, walletService, blockchainRegistry, assetRatioService, - swapService, txService, solTxManagerProxy, ipfsApi + swapService, txService, solTxManagerProxy, ipfsApi, bitcoinWalletService ) } @@ -208,7 +214,7 @@ class AccountActivityStoreTests: XCTestCase { let ( keyringService, rpcService, walletService, blockchainRegistry, assetRatioService, - swapService, txService, solTxManagerProxy, ipfsApi + swapService, txService, solTxManagerProxy, ipfsApi, bitcoinWalletService ) = setupServices( mockEthBalanceWei: mockEthBalanceWei, mockERC20BalanceWei: mockERC20BalanceWei, @@ -254,6 +260,7 @@ class AccountActivityStoreTests: XCTestCase { blockchainRegistry: blockchainRegistry, solTxManagerProxy: solTxManagerProxy, ipfsApi: ipfsApi, + bitcoinWalletService: bitcoinWalletService, userAssetManager: mockAssetManager ) @@ -397,7 +404,7 @@ class AccountActivityStoreTests: XCTestCase { let ( keyringService, rpcService, walletService, blockchainRegistry, assetRatioService, - swapService, txService, solTxManagerProxy, ipfsApi + swapService, txService, solTxManagerProxy, ipfsApi, bitcoinWalletService ) = setupServices( mockLamportBalance: mockLamportBalance, mockSplTokenBalances: mockSplTokenBalances, @@ -442,6 +449,7 @@ class AccountActivityStoreTests: XCTestCase { blockchainRegistry: blockchainRegistry, solTxManagerProxy: solTxManagerProxy, ipfsApi: ipfsApi, + bitcoinWalletService: bitcoinWalletService, userAssetManager: mockAssetManager ) @@ -600,7 +608,7 @@ class AccountActivityStoreTests: XCTestCase { let ( keyringService, rpcService, walletService, blockchainRegistry, assetRatioService, - swapService, txService, solTxManagerProxy, ipfsApi + swapService, txService, solTxManagerProxy, ipfsApi, bitcoinWalletService ) = setupServices( mockFilBalance: mockFilDecimalBalanceInWei, mockFilTestnetBalance: mockFilTestnetDecimalBalanceInWei, @@ -643,6 +651,7 @@ class AccountActivityStoreTests: XCTestCase { blockchainRegistry: blockchainRegistry, solTxManagerProxy: solTxManagerProxy, ipfsApi: ipfsApi, + bitcoinWalletService: bitcoinWalletService, userAssetManager: mockAssetManager ) diff --git a/ios/brave-ios/Tests/BraveWalletTests/AccountsStoreTests.swift b/ios/brave-ios/Tests/BraveWalletTests/AccountsStoreTests.swift index 5ca69406b4e..b2f19c7ecfc 100644 --- a/ios/brave-ios/Tests/BraveWalletTests/AccountsStoreTests.swift +++ b/ios/brave-ios/Tests/BraveWalletTests/AccountsStoreTests.swift @@ -82,8 +82,6 @@ import XCTest BraveWallet.NetworkInfo.mockFilecoinTestnet.nativeToken ] - // TODO: Update balance test once bitcoin balance fetching is integrated - // https://github.com/brave/brave-browser/issues/36966 let mockBTCBalanceAccount1: Double = 0 let mockBTCTestnetBalanceAccount1: Double = 0 let mockBTCPrice: String = "65726.00" @@ -180,8 +178,7 @@ import XCTest ].filter { $0.coin == coin } ) } - // TODO: Update balance test once bitcoin balance fetching is integrated - // https://github.com/brave/brave-browser/issues/36966 + rpcService._balance = { accountAddress, coin, chainId, completion in if coin == .eth, chainId == BraveWallet.MainnetChainId, @@ -284,11 +281,39 @@ import XCTest } } + let btcMainnetBalance: UInt64 = 1000 + let btcTestnetBalance: UInt64 = 10000 + let bitcoinWalletService = BraveWallet.TestBitcoinWalletService() + bitcoinWalletService._balance = { accountId, completion in + if accountId.uniqueKey == self.btcAccount1.accountId.uniqueKey { + completion( + .init( + totalBalance: btcMainnetBalance, + availableBalance: btcMainnetBalance, + pendingBalance: 0, + balances: [:] + ), + nil + ) + } else { + completion( + .init( + totalBalance: btcTestnetBalance, + availableBalance: btcTestnetBalance, + pendingBalance: 0, + balances: [:] + ), + nil + ) + } + } + let store = AccountsStore( keyringService: keyringService, rpcService: rpcService, walletService: walletService, assetRatioService: assetRatioService, + bitcoinWalletService: bitcoinWalletService, userAssetManager: userAssetManager ) @@ -330,13 +355,13 @@ import XCTest XCTAssertEqual(accountDetails[safe: 4]?.totalBalanceFiat, "$4.00") XCTAssertEqual(accountDetails[safe: 5]?.account, self.btcAccount1) - XCTAssertEqual(accountDetails[safe: 5]?.tokensWithBalance, []) // BTC 0 value - XCTAssertEqual(accountDetails[safe: 5]?.totalBalanceFiat, "$0.00") + XCTAssertEqual(accountDetails[safe: 5]?.tokensWithBalance, self.btcMainnetTokens) + XCTAssertEqual(accountDetails[safe: 5]?.totalBalanceFiat, "$0.66") if bitcoinTestnetEnabled { XCTAssertEqual(accountDetails[safe: 6]?.account, self.btcTestnetAccount) - XCTAssertEqual(accountDetails[safe: 6]?.tokensWithBalance, []) // BTC 0 value - XCTAssertEqual(accountDetails[safe: 6]?.totalBalanceFiat, "$0.00") + XCTAssertEqual(accountDetails[safe: 6]?.tokensWithBalance, self.btcTestnetTokens) + XCTAssertEqual(accountDetails[safe: 6]?.totalBalanceFiat, "$6.57") } }.store(in: &cancellables) diff --git a/ios/brave-ios/Tests/BraveWalletTests/AssetDetailStoreTests.swift b/ios/brave-ios/Tests/BraveWalletTests/AssetDetailStoreTests.swift index 3ec4d85abab..68c0a9c1a68 100644 --- a/ios/brave-ios/Tests/BraveWalletTests/AssetDetailStoreTests.swift +++ b/ios/brave-ios/Tests/BraveWalletTests/AssetDetailStoreTests.swift @@ -12,7 +12,6 @@ import XCTest class AssetDetailStoreTests: XCTestCase { private var cancellables: Set = .init() - // TODO: Add a test case for fetching BTC balance in AssetDetails once issue https://github.com/brave/brave-browser/issues/36966 is integrated func testUpdateWithBlockchainToken() { let currencyFormatter = NumberFormatter().then { $0.numberStyle = .currency } let formatter = WeiFormatter(decimalFormatStyle: .decimals(precision: 18)) @@ -95,6 +94,26 @@ class AssetDetailStoreTests: XCTestCase { $1(true) } + let mockBtcBalance: Double = 0.001 + let btcBalanceSatoshi = + formatter.weiString( + from: mockBtcBalance, + radix: .decimal, + decimals: Int(BraveWallet.BlockchainToken.mockBTCToken.decimals) + ) ?? "" + let bitcoinWalletService = BraveWallet.TestBitcoinWalletService() + bitcoinWalletService._balance = { + $1( + .init( + totalBalance: UInt64(btcBalanceSatoshi) ?? 0, + availableBalance: UInt64(btcBalanceSatoshi) ?? 0, + pendingBalance: 0, + balances: [:] + ), + nil + ) + } + // setup store let store = AssetDetailStore( assetRatioService: assetRatioService, @@ -106,6 +125,7 @@ class AssetDetailStoreTests: XCTestCase { solTxManagerProxy: solTxManagerProxy, ipfsApi: TestIpfsAPI(), swapService: swapService, + bitcoinWalletService: bitcoinWalletService, userAssetManager: mockAssetManager, assetDetailType: .blockchainToken(.previewToken) ) @@ -234,6 +254,222 @@ class AssetDetailStoreTests: XCTestCase { wait(for: [assetDetailException], timeout: 1) } + func testUpdateWithBlockchainTokenBitcoin() { + let currencyFormatter = NumberFormatter().then { $0.numberStyle = .currency } + let formatter = WeiFormatter(decimalFormatStyle: .decimals(precision: 8)) + + let assetRatioService = BraveWallet.TestAssetRatioService() + let mockBtcPrice: Double = 63503 + assetRatioService._price = { _, _, _, completion in + completion( + true, + [ + .init( + fromAsset: BraveWallet.BlockchainToken.mockBTCToken.tokenId, + toAsset: "usd", + price: "\(mockBtcPrice)", + assetTimeframeChange: "-1.72" + ) + ] + ) + } + assetRatioService._priceHistory = { _, _, _, completion in + completion(true, [.init(date: Date(), price: "62391.63")]) + } + + let keyringService = BraveWallet.TestKeyringService() + keyringService._allAccounts = { completion in + completion(.mock) + } + keyringService._addObserver = { _ in } + + let rpcService = BraveWallet.TestJsonRpcService() + rpcService._allNetworks = { + $1([.mockBitcoinMainnet]) + } + rpcService._network = { + $2(.mockBitcoinMainnet) + } + + let walletService = BraveWallet.TestBraveWalletService() + walletService._defaultBaseCurrency = { + $0("usd") + } + walletService._addObserver = { _ in } + + let mockAssetManager = TestableWalletUserAssetManager() + mockAssetManager._getAllUserAssetsInNetworkAssets = { _, _ in + [NetworkAssets(network: .mockBitcoinMainnet, tokens: [.mockBTCToken], sortOrder: 0)] + } + + let txService = BraveWallet.TestTxService() + txService._allTransactionInfo = { + $3([]) + } + txService._addObserver = { _ in } + + let blockchainRegistry = BraveWallet.TestBlockchainRegistry() + blockchainRegistry._buyTokens = { + $2([.mockBTCToken]) + } + blockchainRegistry._allTokens = { + $2([.mockBTCToken]) + } + + let solTxManagerProxy = BraveWallet.TestSolanaTxManagerProxy() + + let swapService = BraveWallet.TestSwapService() + swapService._isSwapSupported = { + $1(true) + } + + let mockBtcBalance: Double = 0.0001 + let btcBalanceSatoshi = + formatter.weiString( + from: mockBtcBalance, + radix: .decimal, + decimals: Int(BraveWallet.BlockchainToken.mockBTCToken.decimals) + ) ?? "" + let formattedBthBalance = + currencyFormatter.string( + from: NSNumber(value: mockBtcBalance * mockBtcPrice) + ) ?? "" + let bitcoinWalletService = BraveWallet.TestBitcoinWalletService() + bitcoinWalletService._balance = { + $1( + .init( + totalBalance: UInt64(btcBalanceSatoshi) ?? 0, + availableBalance: UInt64(btcBalanceSatoshi) ?? 0, + pendingBalance: 0, + balances: [:] + ), + nil + ) + } + + // setup store + let store = AssetDetailStore( + assetRatioService: assetRatioService, + keyringService: keyringService, + rpcService: rpcService, + walletService: walletService, + txService: txService, + blockchainRegistry: blockchainRegistry, + solTxManagerProxy: solTxManagerProxy, + ipfsApi: TestIpfsAPI(), + swapService: swapService, + bitcoinWalletService: bitcoinWalletService, + userAssetManager: mockAssetManager, + assetDetailType: .blockchainToken(.mockBTCToken) + ) + + let assetDetailException = expectation(description: "update-blockchainToken") + assetDetailException.expectedFulfillmentCount = 12 + store.$network + .dropFirst() + .sink { network in + defer { assetDetailException.fulfill() } + XCTAssertEqual(network, .mockBitcoinMainnet) + } + .store(in: &cancellables) + store.$isBuySupported + .dropFirst() + .sink { + defer { assetDetailException.fulfill() } + XCTAssertTrue($0) + } + .store(in: &cancellables) + store.$isSendSupported + .dropFirst() + .sink { + defer { assetDetailException.fulfill() } + XCTAssertTrue($0) + } + .store(in: &cancellables) + store.$isSwapSupported + .dropFirst() + .sink { + defer { assetDetailException.fulfill() } + XCTAssertTrue($0) + } + .store(in: &cancellables) + store.$priceHistory + .dropFirst() + .sink { priceHistory in + defer { assetDetailException.fulfill() } + XCTAssertEqual(priceHistory.count, 1) + XCTAssertEqual(priceHistory[0].price, "62391.63") + } + .store(in: &cancellables) + store.$price + .dropFirst() + .sink { + defer { assetDetailException.fulfill() } + XCTAssertEqual($0, "$63,503.00") + } + .store(in: &cancellables) + store.$priceIsDown + .dropFirst() + .sink { + defer { assetDetailException.fulfill() } + XCTAssertTrue($0) + } + .store(in: &cancellables) + store.$priceDelta + .dropFirst() + .sink { + defer { assetDetailException.fulfill() } + XCTAssertEqual($0, "-1.72%") + } + .store(in: &cancellables) + store.$nonZeroBalanceAccounts + .dropFirst() + .sink { accounts in + defer { assetDetailException.fulfill() } + XCTAssertEqual(accounts.count, 1) + XCTAssertEqual(accounts[0].account, .mockBtcAccount) + XCTAssertEqual(accounts[0].balance, String(format: "%.4f", mockBtcBalance)) + XCTAssertEqual(accounts[0].fiatBalance, formattedBthBalance) + } + .store(in: &cancellables) + store.$isLoadingPrice + .dropFirst() + .collect(2) + .sink { values in + defer { assetDetailException.fulfill() } + guard let value = values.last + else { + XCTFail("Unexpected isLoadingPrice") + return + } + XCTAssertFalse(value) + } + .store(in: &cancellables) + store.$isInitialState + .dropFirst() + .sink { + defer { assetDetailException.fulfill() } + XCTAssertFalse($0) + } + .store(in: &cancellables) + store.$isLoadingChart + .dropFirst() + .collect(2) + .sink { values in + defer { assetDetailException.fulfill() } + guard let value = values.last + else { + XCTFail("Unexpected isLoadingChart") + return + } + XCTAssertFalse(value) + } + .store(in: &cancellables) + + store.update() + wait(for: [assetDetailException], timeout: 1) + } + func testUpdateWithCoinMarket() { let formatter = WeiFormatter(decimalFormatStyle: .decimals(precision: 18)) @@ -304,6 +540,26 @@ class AssetDetailStoreTests: XCTestCase { let solTxManagerProxy = BraveWallet.TestSolanaTxManagerProxy() let swapService = BraveWallet.TestSwapService() + let mockBtcBalance: Double = 0.001 + let btcBalanceSatoshi = + formatter.weiString( + from: mockBtcBalance, + radix: .decimal, + decimals: Int(BraveWallet.BlockchainToken.mockBTCToken.decimals) + ) ?? "" + let bitcoinWalletService = BraveWallet.TestBitcoinWalletService() + bitcoinWalletService._balance = { + $1( + .init( + totalBalance: UInt64(btcBalanceSatoshi) ?? 0, + availableBalance: UInt64(btcBalanceSatoshi) ?? 0, + pendingBalance: 0, + balances: [:] + ), + nil + ) + } + // setup store var store = AssetDetailStore( assetRatioService: assetRatioService, @@ -315,6 +571,7 @@ class AssetDetailStoreTests: XCTestCase { solTxManagerProxy: solTxManagerProxy, ipfsApi: TestIpfsAPI(), swapService: swapService, + bitcoinWalletService: bitcoinWalletService, userAssetManager: mockAssetManager, assetDetailType: .coinMarket(.mockCoinMarketBitcoin) ) @@ -426,6 +683,7 @@ class AssetDetailStoreTests: XCTestCase { solTxManagerProxy: solTxManagerProxy, ipfsApi: TestIpfsAPI(), swapService: swapService, + bitcoinWalletService: bitcoinWalletService, userAssetManager: mockAssetManager, assetDetailType: .coinMarket(.mockCoinMarketEth) ) diff --git a/ios/brave-ios/Tests/BraveWalletTests/PortfolioStoreTests.swift b/ios/brave-ios/Tests/BraveWalletTests/PortfolioStoreTests.swift index 79a194a4f20..839f891757f 100644 --- a/ios/brave-ios/Tests/BraveWalletTests/PortfolioStoreTests.swift +++ b/ios/brave-ios/Tests/BraveWalletTests/PortfolioStoreTests.swift @@ -128,9 +128,7 @@ import XCTest // FIL Asset, balance on filecoin testnet let mockFILBalanceTestnet: Double = 100 // FIL value on testnet = $400.00 - // TODO: Update balance test once bitcoin balance fetching is integrated - // https://github.com/brave/brave-browser/issues/36966 - let mockBTCBalanceAccount1: Double = 0 + let mockBTCBalanceAccount1: Double = 0.0000001 let mockBTCPrice: String = "65726.00" lazy var mockBTCAssetPrice: BraveWallet.AssetPrice = .init( fromAsset: "btc", @@ -142,7 +140,7 @@ import XCTest .init(date: Date(timeIntervalSinceNow: -1000), price: "65326.00.06"), .init(date: Date(), price: mockBTCPrice), ] - let mockBTCBalanceTestnet: Double = 0 + let mockBTCBalanceTestnet: Double = 0.00001 var totalBalance: String { let totalEthBalanceValue: Double = @@ -153,10 +151,11 @@ import XCTest let totalSolBalanceValue: Double = (Double(mockSOLAssetPrice.price) ?? 0) * mockSOLBalance let totalFilBalanceValue: Double = (Double(mockFILAssetPrice.price) ?? 0) * mockFILBalanceAccount1 - // TODO: Add bitcoin balance once bitcoin balance fetching is integrated - // https://github.com/brave/brave-browser/issues/36966 + let totalBtcBalanceValue: Double = + (Double(mockBTCAssetPrice.price) ?? 0) * mockBTCBalanceAccount1 let totalBalanceValue = - totalEthBalanceValue + totalSolBalanceValue + totalUSDCBalanceValue + totalFilBalanceValue + totalEthBalanceValue + totalSolBalanceValue + totalUSDCBalanceValue + + totalFilBalanceValue + totalBtcBalanceValue return currencyFormatter.string(from: NSNumber(value: totalBalanceValue)) ?? "" } @@ -224,7 +223,7 @@ import XCTest let mockBtcUserAssets: [BraveWallet.BlockchainToken] = [ btcMainnet.nativeToken.copy(asVisibleAsset: true) ] - let mockBtcBalanceInWei = + let mockBtcBalanceInSantoshi = formatter.weiString( from: mockBTCBalanceAccount1, radix: .decimal, @@ -234,7 +233,7 @@ import XCTest let mockBtcTestnetUserAssets: [BraveWallet.BlockchainToken] = [ btcTestnet.nativeToken.copy(asVisibleAsset: true) ] - let mockBtcTestnetBalanceInWei = + let mockBtcTestnetBalanceInSantoshi = formatter.weiString( from: mockFILBalanceTestnet, radix: .decimal, @@ -397,6 +396,54 @@ import XCTest ), ].filter { networkAsset in networks.contains(where: { $0 == networkAsset.network }) } } + + let btcBalanceInSatoshi = + formatter.weiString( + from: mockBTCBalanceAccount1, + radix: .decimal, + decimals: Int(BraveWallet.BlockchainToken.mockBTCToken.decimals) + ) ?? "" + let btcTestnetBalanceInSatoshi = + formatter.weiString( + from: mockBTCBalanceTestnet, + radix: .decimal, + decimals: Int(BraveWallet.BlockchainToken.mockBTCToken.decimals) + ) ?? "" + let bitcoinWalletService = BraveWallet.TestBitcoinWalletService() + bitcoinWalletService._balance = { accountId, completion in + if accountId.uniqueKey == self.btcAccount1.accountId.uniqueKey { + completion( + .init( + totalBalance: UInt64(btcBalanceInSatoshi) ?? 0, + availableBalance: UInt64(btcBalanceInSatoshi) ?? 0, + pendingBalance: 0, + balances: [:] + ), + nil + ) + } else if accountId.uniqueKey == self.btcAccount2.accountId.uniqueKey { + completion( + .init( + totalBalance: 0, + availableBalance: 0, + pendingBalance: 0, + balances: [:] + ), + nil + ) + } else { + completion( + .init( + totalBalance: UInt64(btcTestnetBalanceInSatoshi) ?? 0, + availableBalance: UInt64(btcTestnetBalanceInSatoshi) ?? 0, + pendingBalance: 0, + balances: [:] + ), + nil + ) + } + } + return PortfolioStore( keyringService: keyringService, rpcService: rpcService, @@ -404,6 +451,7 @@ import XCTest assetRatioService: assetRatioService, blockchainRegistry: BraveWallet.TestBlockchainRegistry(), ipfsApi: TestIpfsAPI(), + bitcoinWalletService: bitcoinWalletService, userAssetManager: mockAssetManager ) } @@ -506,8 +554,7 @@ import XCTest group.assets[safe: 3]?.quantity, String(format: "%.04f", self.mockUSDCBalanceAccount1 + self.mockUSDCBalanceAccount2) ) - - // BTC (value $0.00) + // BTC (value $0.0065726) on mainnet XCTAssertEqual( group.assets[safe: 4]?.token.symbol, BraveWallet.BlockchainToken.mockBTCToken.symbol @@ -518,8 +565,8 @@ import XCTest ) XCTAssertEqual( group.assets[safe: 4]?.history, - [] - ) // 0 balance no fetching history + self.mockBTCPriceHistory + ) XCTAssertEqual( group.assets[safe: 4]?.quantity, String(format: "%.04f", self.mockBTCBalanceAccount1) @@ -552,7 +599,7 @@ import XCTest XCTAssertEqual( balanceDifference, .init( - priceDifference: "+$53.69", + priceDifference: "+$53.70", //"+$53.69", percentageChange: "+1.55%", isBalanceUp: true ) @@ -605,7 +652,7 @@ import XCTest isHidingUnownedNFTs: store.filters.isHidingUnownedNFTs, isShowingNFTNetworkLogo: store.filters.isShowingNFTNetworkLogo, accounts: store.filters.accounts, - networks: [ethNetwork, goerliNetwork, solNetwork, filMainnet, filTestnet].map { + networks: [ethNetwork, goerliNetwork, solNetwork, filMainnet, filTestnet, btcMainnet].map { // de-select all networks with balance .init(isSelected: $0.chainId == goerliNetwork.chainId, model: $0) } @@ -641,39 +688,38 @@ import XCTest // USDC on Ethereum mainnet, SOL on Solana mainnet, ETH on Ethereum mainnet, FIL on Filecoin mainnet, FIL on Filecoin testnet, BTC on Bitcoin mainnet. No BTC on Bitcoin testnet since Bitcoin tesnet is disabled by default let assetGroupNumber = bitcoinTestnetEnabled ? 8 : 7 XCTAssertEqual(group.assets.count, assetGroupNumber) - // BTC mainnet (value = $0) + // ETH Goerli (value = $0) XCTAssertEqual( group.assets[safe: 0]?.token.symbol, - BraveWallet.BlockchainToken.mockBTCToken.symbol + BraveWallet.BlockchainToken.previewToken.symbol ) XCTAssertEqual(group.assets[safe: 0]?.quantity, String(format: "%.04f", 0)) - var offset = 0 - if bitcoinTestnetEnabled { - offset += 1 - // BTC testnet (value = $0) - XCTAssertEqual( - group.assets[safe: 1]?.token.symbol, - BraveWallet.BlockchainToken.mockBTCToken.symbol - ) - XCTAssertEqual(group.assets[safe: 1]?.quantity, String(format: "%.04f", 0)) - } - // ETH Goerli (value = $0) - offset += 1 + // BTC mainnet (value = $0.0065726) XCTAssertEqual( - group.assets[safe: offset]?.token.symbol, - BraveWallet.BlockchainToken.previewToken.symbol + group.assets[safe: 1]?.token.symbol, + BraveWallet.BlockchainToken.mockBTCToken.symbol ) - XCTAssertEqual(group.assets[safe: offset]?.quantity, String(format: "%.04f", 0)) + XCTAssertEqual(group.assets[safe: 1]?.quantity, String(format: "%.04f", 0)) // USDC (value = $0.04) - offset += 1 XCTAssertEqual( - group.assets[safe: offset]?.token.symbol, + group.assets[safe: 2]?.token.symbol, BraveWallet.BlockchainToken.mockUSDCToken.symbol ) XCTAssertEqual( - group.assets[safe: offset]?.quantity, + group.assets[safe: 2]?.quantity, String(format: "%.04f", self.mockUSDCBalanceAccount1 + self.mockUSDCBalanceAccount2) ) + var offset = 2 + if bitcoinTestnetEnabled { + offset += 1 + // BTC testnet (value = $0.65726) + XCTAssertEqual( + group.assets[safe: offset]?.token.symbol, + BraveWallet.BlockchainToken.mockBTCToken.symbol + ) + XCTAssertEqual(group.assets[safe: offset]?.quantity, String(format: "%.04f", 0)) + } + // FIL (value = $4.00) on filecoin mainnet offset += 1 XCTAssertEqual( @@ -809,7 +855,7 @@ import XCTest String(format: "%.04f", self.mockFILBalanceAccount1) ) // USDC (value = $0.04), hidden - // BTC (value = 0), hidden + // BTC (value = $0.006), hidden XCTAssertNil(group.assets[safe: 4]) }.store(in: &cancellables) isLocked = false @@ -843,7 +889,7 @@ import XCTest // test without bitcoin testnet enabled Preferences.Wallet.isBitcoinTestnetEnabled.value = bitcoinTestnetEnabled let store = setupStore() - let accountsExpectation = expectation(description: "update-accounts") + let accountsExpectation = expectation(description: "update-accounts-bitcoin-testnet") store.$assetGroups .dropFirst() .collect(2) @@ -898,35 +944,45 @@ import XCTest group.assets[safe: 3]?.quantity, String(format: "%.04f", self.mockFILBalanceAccount1) ) + var offset: Int = 4 + if bitcoinTestnetEnabled { + // BTC testnet (value = $0.65726) + XCTAssertEqual( + group.assets[safe: offset]?.token.symbol, + self.btcMainnet.nativeToken.symbol + ) + XCTAssertEqual( + group.assets[safe: offset]?.quantity, + String(format: "%.04f", self.mockBTCBalanceTestnet) + ) + offset += 1 + } // USDC (value = $0.03, ethAccount2 hidden!) XCTAssertEqual( - group.assets[safe: 4]?.token.symbol, + group.assets[safe: offset]?.token.symbol, BraveWallet.BlockchainToken.mockUSDCToken.symbol ) XCTAssertEqual( - group.assets[safe: 4]?.quantity, + group.assets[safe: offset]?.quantity, String(format: "%.04f", self.mockUSDCBalanceAccount1) ) // verify account 2 hidden - // BTC mainnet (value = $0) + offset += 1 + // BTC mainnet (value = $0.0006) XCTAssertEqual( - group.assets[safe: 5]?.token.symbol, + group.assets[safe: offset]?.token.symbol, self.btcMainnet.nativeToken.symbol ) - var goerliOffset: Int = 6 - if bitcoinTestnetEnabled { - // BTC testnet (value = $0) - XCTAssertEqual( - group.assets[safe: 6]?.token.symbol, - self.btcMainnet.nativeToken.symbol - ) - goerliOffset = 7 - } + XCTAssertEqual( + group.assets[safe: offset]?.quantity, + String(format: "%.04f", self.mockBTCBalanceAccount1) + ) + offset += 1 // ETH Goerli (value = $0) XCTAssertEqual( - group.assets[safe: goerliOffset]?.token.symbol, + group.assets[safe: offset]?.token.symbol, self.goerliNetwork.nativeToken.symbol ) - XCTAssertEqual(group.assets[safe: goerliOffset]?.quantity, String(format: "%.04f", 0)) + XCTAssertEqual(group.assets[safe: offset]?.quantity, String(format: "%.04f", 0)) }.store(in: &cancellables) isLocked = false store.saveFilters( @@ -1083,23 +1139,36 @@ import XCTest let assetGroupNumber = bitcoinTestnetEnabled ? 10 : 9 XCTAssertEqual(lastUpdatedAssetGroups.count, assetGroupNumber) - var filAccount2Offset = bitcoinTestnetEnabled ? 9 : 8 + var btcTestnetIndex = 0 + var ethAccount2Index = 4 + var btcAccount1Index = 5 + var solAccount2Index = 6 + var btcAccount2Index = 7 + var filAccount2Index = 8 + if bitcoinTestnetEnabled { + btcTestnetIndex = 4 + ethAccount2Index = 5 + btcAccount1Index = 6 + solAccount2Index = 7 + btcAccount2Index = 8 + filAccount2Index = 9 + } guard let ethAccount1Group = lastUpdatedAssetGroups[safe: 0], let solAccount1Group = lastUpdatedAssetGroups[safe: 1], let filTestnetAccountGroup = lastUpdatedAssetGroups[safe: 2], let filAccount1Group = lastUpdatedAssetGroups[safe: 3], - let ethAccount2Group = lastUpdatedAssetGroups[safe: 4], - let solAccount2Group = lastUpdatedAssetGroups[safe: 5], - let btcAccount1Group = lastUpdatedAssetGroups[safe: 6], - let btcAccount2Group = lastUpdatedAssetGroups[safe: 7], - let filAccount2Group = lastUpdatedAssetGroups[safe: filAccount2Offset] + let ethAccount2Group = lastUpdatedAssetGroups[safe: ethAccount2Index], + let btcAccount1Group = lastUpdatedAssetGroups[safe: btcAccount1Index], + let solAccount2Group = lastUpdatedAssetGroups[safe: solAccount2Index], + let btcAccount2Group = lastUpdatedAssetGroups[safe: btcAccount2Index], + let filAccount2Group = lastUpdatedAssetGroups[safe: filAccount2Index] else { XCTFail("Unexpected test result") return } if bitcoinTestnetEnabled { - guard let btcTestnetAccountGroup = lastUpdatedAssetGroups[safe: 8] + guard let btcTestnetAccountGroup = lastUpdatedAssetGroups[safe: btcTestnetIndex] else { XCTFail("Unexpected test result") return @@ -1205,6 +1274,15 @@ import XCTest ) XCTAssertEqual(ethAccount2Group.assets[safe: 2]?.quantity, String(format: "%.04f", 0)) + XCTAssertEqual(btcAccount1Group.assets[safe: 0]?.quantity, String(format: "%.04f", 0)) + XCTAssertEqual(btcAccount1Group.groupType, .account(self.btcAccount1)) + XCTAssertEqual(btcAccount1Group.assets.count, 1) + // BTC (value = $0.0065726) + XCTAssertEqual( + btcAccount1Group.assets[safe: 0]?.token.symbol, + BraveWallet.BlockchainToken.mockBTCToken.symbol + ) + XCTAssertEqual(solAccount2Group.groupType, .account(self.solAccount2)) XCTAssertEqual(solAccount2Group.assets.count, 1) // SOL (value = $0) @@ -1214,15 +1292,6 @@ import XCTest ) XCTAssertEqual(solAccount2Group.assets[safe: 0]?.quantity, String(format: "%.04f", 0)) - XCTAssertEqual(btcAccount1Group.assets[safe: 0]?.quantity, String(format: "%.04f", 0)) - XCTAssertEqual(btcAccount1Group.groupType, .account(self.btcAccount1)) - XCTAssertEqual(btcAccount1Group.assets.count, 1) - // BTC (value = $0) - XCTAssertEqual( - btcAccount1Group.assets[safe: 0]?.token.symbol, - BraveWallet.BlockchainToken.mockBTCToken.symbol - ) - XCTAssertEqual(btcAccount2Group.assets[safe: 0]?.quantity, String(format: "%.04f", 0)) XCTAssertEqual(btcAccount2Group.groupType, .account(self.btcAccount2)) XCTAssertEqual(btcAccount2Group.assets.count, 1) @@ -1283,8 +1352,10 @@ import XCTest XCTFail("Unexpected test result") return } + // grouping by .account; 1 for each of the 2 accounts selected accounts - XCTAssertEqual(lastUpdatedAssetGroups.count, 4) + let groupNumber = bitcoinTestnetEnabled ? 5 : 4 + XCTAssertEqual(lastUpdatedAssetGroups.count, groupNumber) guard let ethAccount1Group = lastUpdatedAssetGroups[safe: 0], let solAccountGroup = lastUpdatedAssetGroups[safe: 1], let filTestnetAccountGroup = lastUpdatedAssetGroups[safe: 2], @@ -1344,13 +1415,29 @@ import XCTest String(format: "%.04f", self.mockFILBalanceAccount1) ) - // ethAccount2 hidden as it's de-selected, solAccount2 hidden for small balance, filAccount2 hidden for small balance, btcAccount1/btcAccount2/btcTestAccount hidden for small balance - XCTAssertNil(lastUpdatedAssetGroups[safe: 4]) - XCTAssertNil(lastUpdatedAssetGroups[safe: 5]) - XCTAssertNil(lastUpdatedAssetGroups[safe: 6]) - XCTAssertNil(lastUpdatedAssetGroups[safe: 7]) - XCTAssertNil(lastUpdatedAssetGroups[safe: 8]) - XCTAssertNil(lastUpdatedAssetGroups[safe: 9]) + if bitcoinTestnetEnabled { + guard let btcTestnetAccountGroup = lastUpdatedAssetGroups[safe: 4] + else { + XCTFail("Unexpected test result") + return + } + + XCTAssertEqual(btcTestnetAccountGroup.groupType, .account(self.btcTestnetAccount)) + XCTAssertEqual(btcTestnetAccountGroup.assets.count, 1) + // BTC on testnet (value = $0.65726) + XCTAssertEqual( + btcTestnetAccountGroup.assets[safe: 0]?.token.symbol, + BraveWallet.BlockchainToken.mockBTCToken.symbol + ) + XCTAssertEqual( + btcTestnetAccountGroup.assets[safe: 0]?.quantity, + String(format: "%.04f", self.mockBTCBalanceTestnet) + ) + } + + // ethAccount2 hidden as it's de-selected, solAccount2 hidden for small balance, filAccount2 hidden for small balance, btcAccount1/btcAccount2 hidden for small balance + let lastIndex = bitcoinTestnetEnabled ? 5 : 4 + XCTAssertNil(lastUpdatedAssetGroups[safe: lastIndex]) } .store(in: &cancellables) store.saveFilters( @@ -1402,23 +1489,19 @@ import XCTest XCTFail("Unexpected test result") return } + // grouping by .network; 1 for each of the 2 networks (Bitcoin testnet can be disabled) // network groups order should be the same as the order of all networks in `Filters` let assetGroupNumber = bitcoinTestnetEnabled ? 7 : 6 XCTAssertEqual(lastUpdatedAssetGroups.count, assetGroupNumber) - guard let ethMainnetGroup = lastUpdatedAssetGroups[safe: 0], - let solMainnetGroup = lastUpdatedAssetGroups[safe: 1], - let filTestnetGroup = lastUpdatedAssetGroups[safe: 2], - let filMainnetGroup = lastUpdatedAssetGroups[safe: 3], - let btcMainnetGroup = lastUpdatedAssetGroups[safe: 4], - let ethGoerliGroup = lastUpdatedAssetGroups[safe: 5] - else { - XCTFail("Unexpected test result") - return - } + var btcMainnetIndex = 4 + var goerliIndex = 5 if bitcoinTestnetEnabled { - guard let btcTestnetGroup = lastUpdatedAssetGroups[safe: 6] + btcMainnetIndex = 5 + goerliIndex = 6 + + guard let btcTestnetGroup = lastUpdatedAssetGroups[safe: 4] else { XCTFail("Unexpected test result") return @@ -1433,6 +1516,17 @@ import XCTest XCTAssertEqual(btcTestnetGroup.assets[safe: 0]?.quantity, String(format: "%.04f", 0)) } + guard let ethMainnetGroup = lastUpdatedAssetGroups[safe: 0], + let solMainnetGroup = lastUpdatedAssetGroups[safe: 1], + let filTestnetGroup = lastUpdatedAssetGroups[safe: 2], + let filMainnetGroup = lastUpdatedAssetGroups[safe: 3], + let btcMainnetGroup = lastUpdatedAssetGroups[safe: btcMainnetIndex], + let ethGoerliGroup = lastUpdatedAssetGroups[safe: goerliIndex] + else { + XCTFail("Unexpected test result") + return + } + XCTAssertEqual(ethMainnetGroup.groupType, .network(.mockMainnet)) XCTAssertEqual(ethMainnetGroup.assets.count, 2) // ETH Mainnet, USDC // ETH (value ~= $2741.7510399999996) @@ -1490,6 +1584,15 @@ import XCTest String(format: "%.04f", self.mockFILBalanceAccount1) ) + XCTAssertEqual(btcMainnetGroup.groupType, .network(self.btcMainnet)) + XCTAssertEqual(btcMainnetGroup.assets.count, 1) + // BTC mainnet (value = $0.0065726) + XCTAssertEqual( + btcMainnetGroup.assets[safe: 0]?.token.symbol, + BraveWallet.BlockchainToken.mockBTCToken.symbol + ) + XCTAssertEqual(btcMainnetGroup.assets[safe: 0]?.quantity, String(format: "%.04f", 0)) + XCTAssertEqual(ethGoerliGroup.groupType, .network(.mockGoerli)) XCTAssertEqual(ethGoerliGroup.assets.count, 1) // ETH Goerli // ETH Goerli (value = $0) @@ -1499,15 +1602,6 @@ import XCTest ) XCTAssertEqual(ethGoerliGroup.assets[safe: 0]?.quantity, String(format: "%.04f", 0)) - XCTAssertEqual(btcMainnetGroup.groupType, .network(self.btcMainnet)) - XCTAssertEqual(btcMainnetGroup.assets.count, 1) - // BTC mainnet (value = $0) - XCTAssertEqual( - btcMainnetGroup.assets[safe: 0]?.token.symbol, - BraveWallet.BlockchainToken.mockBTCToken.symbol - ) - XCTAssertEqual(btcMainnetGroup.assets[safe: 0]?.quantity, String(format: "%.04f", 0)) - // Verify NFTs not used in Portfolio #7945 let noAssetsAreNFTs = lastUpdatedAssetGroups.flatMap(\.assets).allSatisfy({ !($0.token.isNft || $0.token.isErc721) @@ -1551,7 +1645,8 @@ import XCTest return } // grouping by .network; 1 group for Solana network, 1 group for Filecoin mainnet, 1 group for Filecoin testnet - XCTAssertEqual(lastUpdatedAssetGroups.count, 3) + let groupNumber = bitcoinTestnetEnabled ? 4 : 3 + XCTAssertEqual(lastUpdatedAssetGroups.count, groupNumber) guard let solMainnetGroup = lastUpdatedAssetGroups[safe: 0], let filTestnetGroup = lastUpdatedAssetGroups[safe: 1], let filMainnetGroup = lastUpdatedAssetGroups[safe: 2] @@ -1559,6 +1654,26 @@ import XCTest XCTFail("Unexpected test result") return } + if bitcoinTestnetEnabled { + guard let btcTestnetGroup = lastUpdatedAssetGroups[safe: 3] + else { + XCTFail("Unexpected test result") + return + } + + XCTAssertEqual(btcTestnetGroup.groupType, .network(.mockBitcoinTestnet)) + XCTAssertEqual(btcTestnetGroup.assets.count, 1) // BTC + // BTC (value = $0.65726) + XCTAssertEqual( + btcTestnetGroup.assets[safe: 0]?.token.symbol, + BraveWallet.BlockchainToken.mockBTCToken.symbol + ) + XCTAssertEqual( + btcTestnetGroup.assets[safe: 0]?.quantity, + String(format: "%.04f", self.mockBTCBalanceTestnet) + ) + } + XCTAssertEqual(solMainnetGroup.groupType, .network(.mockSolana)) XCTAssertEqual(solMainnetGroup.assets.count, 1) // SOL // SOL (value = $775.3) @@ -1595,12 +1710,10 @@ import XCTest String(format: "%.04f", self.mockFILBalanceAccount1) ) // eth mainnet group hidden as network de-selected - XCTAssertNil(lastUpdatedAssetGroups[safe: 3]) // goerli network group hidden for small balance - XCTAssertNil(lastUpdatedAssetGroups[safe: 4]) // Bitcoin network group hidden for small balance - XCTAssertNil(lastUpdatedAssetGroups[safe: 5]) - XCTAssertNil(lastUpdatedAssetGroups[safe: 6]) + let lastIndex = bitcoinTestnetEnabled ? 4 : 3 + XCTAssertNil(lastUpdatedAssetGroups[safe: lastIndex]) } .store(in: &cancellables) store.saveFilters( diff --git a/ios/brave-ios/Tests/BraveWalletTests/WalletArrayExtensionTests.swift b/ios/brave-ios/Tests/BraveWalletTests/WalletArrayExtensionTests.swift index df55bdd256b..2937fb75b44 100644 --- a/ios/brave-ios/Tests/BraveWalletTests/WalletArrayExtensionTests.swift +++ b/ios/brave-ios/Tests/BraveWalletTests/WalletArrayExtensionTests.swift @@ -56,67 +56,67 @@ class WalletArrayExtensionTests: XCTestCase { let testAccount2: BraveWallet.AccountInfo = .mockBtcTestnetAccount // ETH value $1000 - var viewModel1: AssetViewModel = .init( + let viewModel1: AssetViewModel = .init( groupType: .none, token: .previewToken, network: .mockMainnet, price: "1000", history: [], - balanceForAccounts: [account1.address: 1] + balanceForAccounts: [account1.cacheBalanceKey: 1] ) // USDC value $500 - var viewModel2: AssetViewModel = .init( + let viewModel2: AssetViewModel = .init( groupType: .none, token: .mockUSDCToken, network: .mockMainnet, price: "500", history: [], - balanceForAccounts: [account1.address: 1] + balanceForAccounts: [account1.cacheBalanceKey: 1] ) // SOL value $500 - var viewModel3: AssetViewModel = .init( + let viewModel3: AssetViewModel = .init( groupType: .none, token: .mockSolToken, network: .mockSolana, price: "500", history: [], - balanceForAccounts: [account2.address: 1] + balanceForAccounts: [account2.cacheBalanceKey: 1] ) // FIL value $100 on mainnet - var viewModel4: AssetViewModel = .init( + let viewModel4: AssetViewModel = .init( groupType: .none, token: .mockFilToken, network: .mockFilecoinMainnet, price: "100", history: [], - balanceForAccounts: [account3.address: 1] + balanceForAccounts: [account3.cacheBalanceKey: 1] ) // BTC value $20000 on mainnet - var viewModel5: AssetViewModel = .init( + let viewModel5: AssetViewModel = .init( groupType: .none, token: .mockBTCToken, network: .mockBitcoinMainnet, price: "20000", history: [], - balanceForAccounts: [account4.address: 1] + balanceForAccounts: [account4.cacheBalanceKey: 1] ) // FIL value $100 on testnet - var viewModel6: AssetViewModel = .init( + let viewModel6: AssetViewModel = .init( groupType: .none, token: .mockFilToken, network: .mockFilecoinTestnet, price: "50", history: [], - balanceForAccounts: [testAccount1.address: 2] + balanceForAccounts: [testAccount1.cacheBalanceKey: 2] ) // BTC value $40000 on testnet - var viewModel7: AssetViewModel = .init( + let viewModel7: AssetViewModel = .init( groupType: .none, token: .mockBTCToken, network: .mockBitcoinTestnet, price: "20000", history: [], - balanceForAccounts: [testAccount2.address: 2] + balanceForAccounts: [testAccount2.cacheBalanceKey: 2] ) let array: [AssetViewModel] = [ viewModel1, viewModel2, @@ -153,7 +153,7 @@ class WalletArrayExtensionTests: XCTestCase { network: .mockMainnet, price: "1000", history: [], - balanceForAccounts: [account1.address: 1] + balanceForAccounts: [account1.cacheBalanceKey: 1] ) // USDC value $500 var viewModel2: AssetViewModel = .init( @@ -162,7 +162,7 @@ class WalletArrayExtensionTests: XCTestCase { network: .mockMainnet, price: "500", history: [], - balanceForAccounts: [account1.address: 1] + balanceForAccounts: [account1.cacheBalanceKey: 1] ) // SOL value $100 var viewModel3: AssetViewModel = .init( @@ -171,7 +171,7 @@ class WalletArrayExtensionTests: XCTestCase { network: .mockSolana, price: "100", history: [], - balanceForAccounts: [account2.address: 1] + balanceForAccounts: [account2.cacheBalanceKey: 1] ) // FIL value $100 on mainnet var viewModel4: AssetViewModel = .init( @@ -180,7 +180,7 @@ class WalletArrayExtensionTests: XCTestCase { network: .mockFilecoinMainnet, price: "100", history: [], - balanceForAccounts: [account3.address: 1] + balanceForAccounts: [account3.cacheBalanceKey: 1] ) // BTC value $20000 on mainnet var viewModel5: AssetViewModel = .init( @@ -189,7 +189,7 @@ class WalletArrayExtensionTests: XCTestCase { network: .mockBitcoinMainnet, price: "20000", history: [], - balanceForAccounts: [account4.address: 1] + balanceForAccounts: [account4.cacheBalanceKey: 1] ) // FIL value $100 on testnet var viewModel6: AssetViewModel = .init( @@ -198,7 +198,7 @@ class WalletArrayExtensionTests: XCTestCase { network: .mockFilecoinTestnet, price: "50", history: [], - balanceForAccounts: [testAccount1.address: 2] + balanceForAccounts: [testAccount1.cacheBalanceKey: 2] ) // BTC value $100 on testnet var viewModel7: AssetViewModel = .init( @@ -207,7 +207,7 @@ class WalletArrayExtensionTests: XCTestCase { network: .mockBitcoinTestnet, price: "20000", history: [], - balanceForAccounts: [testAccount2.address: 0.005] + balanceForAccounts: [testAccount2.cacheBalanceKey: 0.005] ) var group1: AssetGroupViewModel = .init( groupType: .account(account1),