diff --git a/ios/brave-ios/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+Wallet.swift b/ios/brave-ios/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+Wallet.swift index d0b6efc54b9f..2def06060db8 100644 --- a/ios/brave-ios/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+Wallet.swift +++ b/ios/brave-ios/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+Wallet.swift @@ -34,6 +34,9 @@ extension WalletStore { let walletP3A, let bitcoinWalletService = BraveWallet.BitcoinWalletServiceFactory.get( privateMode: privateMode + ), + let zcashWalletService = BraveWallet.ZCashWalletServiceFactory.get( + privateMode: privateMode ) else { Logger.module.error("Failed to load wallet. One or more services were unavailable") @@ -51,7 +54,8 @@ extension WalletStore { solTxManagerProxy: solTxManagerProxy, ipfsApi: ipfsApi, walletP3A: walletP3A, - bitcoinWalletService: bitcoinWalletService + bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService ) } } @@ -75,6 +79,9 @@ extension CryptoStore { let walletP3A, let bitcoinWalletService = BraveWallet.BitcoinWalletServiceFactory.get( privateMode: privateMode + ), + let zcashWalletService = BraveWallet.ZCashWalletServiceFactory.get( + privateMode: privateMode ) else { Logger.module.error("Failed to load wallet. One or more services were unavailable") @@ -92,7 +99,8 @@ extension CryptoStore { solTxManagerProxy: solTxManagerProxy, ipfsApi: ipfsApi, walletP3A: walletP3A, - bitcoinWalletService: bitcoinWalletService + bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService ) } } diff --git a/ios/brave-ios/Sources/Brave/Frontend/Settings/Debug/Brave Wallet/BraveWalletDebugMenu.swift b/ios/brave-ios/Sources/Brave/Frontend/Settings/Debug/Brave Wallet/BraveWalletDebugMenu.swift index 9fdec48bffab..98fecd30a6eb 100644 --- a/ios/brave-ios/Sources/Brave/Frontend/Settings/Debug/Brave Wallet/BraveWalletDebugMenu.swift +++ b/ios/brave-ios/Sources/Brave/Frontend/Settings/Debug/Brave Wallet/BraveWalletDebugMenu.swift @@ -11,12 +11,15 @@ import SwiftUI struct BraveWalletDebugMenu: View { @ObservedObject var enableBitcoinTestnet = Preferences.Wallet.isBitcoinTestnetEnabled + @ObservedObject var enableZcashTestnet = Preferences.Wallet.isZcashTestnetEnabled var body: some View { Form { Section { Toggle("Enable Bitcoin Testnet", isOn: $enableBitcoinTestnet.value) .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Toggle("Enable Zcash Testnet", isOn: $enableZcashTestnet.value) + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) } } .listBackgroundColor(Color(UIColor.braveGroupedBackground)) diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/aurora.imageset/Contents.json b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/aurora.imageset/Contents.json index 870a55c5b37f..98f42c0e28fb 100644 --- a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/aurora.imageset/Contents.json +++ b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/aurora.imageset/Contents.json @@ -17,5 +17,8 @@ "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true } } diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/avax.imageset/Contents.json b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/avax.imageset/Contents.json index a942152564db..31043411d43e 100644 --- a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/avax.imageset/Contents.json +++ b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/avax.imageset/Contents.json @@ -5,7 +5,7 @@ "scale" : "1x" }, { - "filename" : "avax.png", + "filename" : "avax-asset-icon.svg", "idiom" : "universal", "scale" : "2x" }, @@ -17,5 +17,8 @@ "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true } } diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/avax.imageset/avax-asset-icon.svg b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/avax.imageset/avax-asset-icon.svg new file mode 100644 index 000000000000..55473a0f2400 --- /dev/null +++ b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/avax.imageset/avax-asset-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/avax.imageset/avax.png b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/avax.imageset/avax.png deleted file mode 100644 index 03c8042c846a..000000000000 Binary files a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/avax.imageset/avax.png and /dev/null differ diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/base.imageset/Contents.json b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/base.imageset/Contents.json new file mode 100644 index 000000000000..6b8c0324d070 --- /dev/null +++ b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/base.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "base.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/base.imageset/base.png b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/base.imageset/base.png new file mode 100644 index 000000000000..c32239835975 Binary files /dev/null and b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/base.imageset/base.png differ diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/bitcoin-asset-icon.imageset/Contents.json b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/bitcoin-asset-icon.imageset/Contents.json index 173aeba05870..5e9900fac7bd 100644 --- a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/bitcoin-asset-icon.imageset/Contents.json +++ b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/bitcoin-asset-icon.imageset/Contents.json @@ -5,7 +5,7 @@ "scale" : "1x" }, { - "filename" : "btc-asset-icon 1@2x.png", + "filename" : "btc-asset-icon.svg", "idiom" : "universal", "scale" : "2x" }, @@ -17,5 +17,8 @@ "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true } } diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/bitcoin-asset-icon.imageset/btc-asset-icon 1@2x.png b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/bitcoin-asset-icon.imageset/btc-asset-icon 1@2x.png deleted file mode 100644 index fda5d8e3a7c0..000000000000 Binary files a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/bitcoin-asset-icon.imageset/btc-asset-icon 1@2x.png and /dev/null differ diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/bitcoin-asset-icon.imageset/btc-asset-icon.svg b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/bitcoin-asset-icon.imageset/btc-asset-icon.svg new file mode 100644 index 000000000000..4ae91a1834b2 --- /dev/null +++ b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/bitcoin-asset-icon.imageset/btc-asset-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/eth-asset-icon.imageset/Contents.json b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/eth-asset-icon.imageset/Contents.json index c8a51ba192a0..0bea4a15cab4 100644 --- a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/eth-asset-icon.imageset/Contents.json +++ b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/eth-asset-icon.imageset/Contents.json @@ -5,7 +5,7 @@ "scale" : "1x" }, { - "filename" : "eth-asset-icon.png", + "filename" : "eth-asset-icon.svg", "idiom" : "universal", "scale" : "2x" }, @@ -17,5 +17,8 @@ "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true } } diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/eth-asset-icon.imageset/eth-asset-icon.png b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/eth-asset-icon.imageset/eth-asset-icon.png deleted file mode 100644 index 7ab864036091..000000000000 Binary files a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/eth-asset-icon.imageset/eth-asset-icon.png and /dev/null differ diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/eth-asset-icon.imageset/eth-asset-icon.svg b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/eth-asset-icon.imageset/eth-asset-icon.svg new file mode 100644 index 000000000000..b8c76f2112db --- /dev/null +++ b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/eth-asset-icon.imageset/eth-asset-icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/filecoin-asset-icon.imageset/Contents.json b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/filecoin-asset-icon.imageset/Contents.json index 4768d92b50ad..1ab49f65fe21 100644 --- a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/filecoin-asset-icon.imageset/Contents.json +++ b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/filecoin-asset-icon.imageset/Contents.json @@ -5,7 +5,7 @@ "scale" : "1x" }, { - "filename" : "filecoin-asset-icon.png", + "filename" : "filecoin-asset-icon.svg", "idiom" : "universal", "scale" : "2x" }, @@ -17,5 +17,8 @@ "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true } } diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/filecoin-asset-icon.imageset/filecoin-asset-icon.png b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/filecoin-asset-icon.imageset/filecoin-asset-icon.png deleted file mode 100644 index 777de7aabdd3..000000000000 Binary files a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/filecoin-asset-icon.imageset/filecoin-asset-icon.png and /dev/null differ diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/filecoin-asset-icon.imageset/filecoin-asset-icon.svg b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/filecoin-asset-icon.imageset/filecoin-asset-icon.svg new file mode 100644 index 000000000000..e29a55aa7af4 --- /dev/null +++ b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/filecoin-asset-icon.imageset/filecoin-asset-icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/matic.imageset/Contents.json b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/matic.imageset/Contents.json index 314f5d470414..0cdf14e533cd 100644 --- a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/matic.imageset/Contents.json +++ b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/matic.imageset/Contents.json @@ -5,7 +5,7 @@ "scale" : "1x" }, { - "filename" : "matic-asset-icon.png", + "filename" : "matic-asset-icon.svg", "idiom" : "universal", "scale" : "2x" }, @@ -17,5 +17,8 @@ "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true } } diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/matic.imageset/matic-asset-icon.png b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/matic.imageset/matic-asset-icon.png deleted file mode 100644 index 091813890052..000000000000 Binary files a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/matic.imageset/matic-asset-icon.png and /dev/null differ diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/matic.imageset/matic-asset-icon.svg b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/matic.imageset/matic-asset-icon.svg new file mode 100644 index 000000000000..184722d7d149 --- /dev/null +++ b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/matic.imageset/matic-asset-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/neon.imageset/Contents.json b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/neon.imageset/Contents.json new file mode 100644 index 000000000000..91d03d182d13 --- /dev/null +++ b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/neon.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "neon-color.svg", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/neon.imageset/neon-color.svg b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/neon.imageset/neon-color.svg new file mode 100644 index 000000000000..557c935f06ee --- /dev/null +++ b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/neon.imageset/neon-color.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/sol-asset-icon.imageset/Contents.json b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/sol-asset-icon.imageset/Contents.json index 5c0be45039cd..bd37b8ee5da4 100644 --- a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/sol-asset-icon.imageset/Contents.json +++ b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/sol-asset-icon.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "sol-asset-icon.pdf", + "filename" : "sol-asset-icon.svg", "idiom" : "universal" } ], diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/sol-asset-icon.imageset/sol-asset-icon.pdf b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/sol-asset-icon.imageset/sol-asset-icon.pdf deleted file mode 100644 index 5b7ff457d70e..000000000000 Binary files a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/sol-asset-icon.imageset/sol-asset-icon.pdf and /dev/null differ diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/sol-asset-icon.imageset/sol-asset-icon.svg b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/sol-asset-icon.imageset/sol-asset-icon.svg new file mode 100644 index 000000000000..79e92671eefa --- /dev/null +++ b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/sol-asset-icon.imageset/sol-asset-icon.svg @@ -0,0 +1 @@ +solana-logo-freelogovectors.net_ \ No newline at end of file diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/zcash-asset-icon.imageset/Contents.json b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/zcash-asset-icon.imageset/Contents.json new file mode 100644 index 000000000000..05d25e9a167a --- /dev/null +++ b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/zcash-asset-icon.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "zec-asset-icon.svg", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/zcash-asset-icon.imageset/zec-asset-icon.svg b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/zcash-asset-icon.imageset/zec-asset-icon.svg new file mode 100644 index 000000000000..82e5943f0d3d --- /dev/null +++ b/ios/brave-ios/Sources/BraveWallet/Assets.xcassets/Brave Wallet/Asset Icons/zcash-asset-icon.imageset/zec-asset-icon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Accounts/AccountsView.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Accounts/AccountsView.swift index 91e2fe41f543..636404ae16ee 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Accounts/AccountsView.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Accounts/AccountsView.swift @@ -270,7 +270,7 @@ private struct AccountCardView: View { Label(Strings.Wallet.editButtonTitle, braveSystemImage: "leo.edit.pencil") } Divider() - if account.coin != .btc { + if account.supportsAccountExport { Button { action(.exportAccount) } label: { diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Accounts/Activity/AccountActivityView.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Accounts/Activity/AccountActivityView.swift index a9f2a69d50ce..6094f7d1081b 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Accounts/Activity/AccountActivityView.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Accounts/Activity/AccountActivityView.swift @@ -41,7 +41,7 @@ struct AccountActivityView: View { } label: { Label(Strings.Wallet.editButtonTitle, braveSystemImage: "leo.edit.pencil") } - if store.account.coin != .btc { + if store.account.supportsAccountExport { Button { isPresentingExportAccount = true } label: { @@ -190,7 +190,7 @@ struct AccountActivityView: View { ) } ) - if store.account.coin != .fil && store.account.coin != .btc { + if store.account.supportsNFT { Divider() NavigationLink( destination: { diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Accounts/Add/AddAccountView.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Accounts/Add/AddAccountView.swift index 10425d031169..82ab305426db 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Accounts/Add/AddAccountView.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Accounts/Add/AddAccountView.swift @@ -176,7 +176,9 @@ struct AddAccountView: View { if isJSONImported { originPasswordSection } - privateKeySection + if selectedCoin != .zec && preSelectedCoin != .zec { + privateKeySection + } } .listStyle(.insetGrouped) .scrollContentBackground(.hidden) @@ -277,7 +279,7 @@ struct AddAccountView: View { } } .onChange(of: selectedCoin) { coin in - if coin == .fil || coin == .btc { + if coin == .fil || coin == .btc || coin == .zec { accountNetwork = selectedCoinNetworks.first(where: { $0.coin == coin }) ?? .init() } } @@ -427,6 +429,10 @@ struct AddAccountView: View { && Preferences.Wallet.isBitcoinTestnetEnabled.value { return true + } else if (selectedCoin == .zec || preSelectedCoin == .zec) + && Preferences.Wallet.isZcashTestnetEnabled.value + { + return true } return false } diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Accounts/Details/AccountDetailsView.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Accounts/Details/AccountDetailsView.swift index 3123c9c1d9a5..cf31f3ad265a 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Accounts/Details/AccountDetailsView.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Accounts/Details/AccountDetailsView.swift @@ -79,7 +79,7 @@ struct AccountDetailsView: View { ) } ) - if account.coin != .btc { + if account.supportsAccountExport { Section { NavigationLink( destination: AccountPrivateKeyView(keyringStore: keyringStore, account: account) diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/SelectAccountTokenView.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/SelectAccountTokenView.swift index 3db681999879..16b8cf6c9b2a 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/SelectAccountTokenView.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/SelectAccountTokenView.swift @@ -140,8 +140,7 @@ struct SelectAccountTokenView: View { WalletListHeaderView { CopyAddressHeader( displayText: accountSection.account.accountNameDisplay, - account: accountSection.account, - btcAccountInfo: accountSection.bitcoinAccountInfo + address: accountSection.copyAddress ) } } diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountActivityStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountActivityStore.swift index 92db3f22b0d1..0c5e17ef1e2e 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountActivityStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountActivityStore.swift @@ -45,6 +45,7 @@ class AccountActivityStore: ObservableObject, WalletObserverStore { private let solTxManagerProxy: BraveWalletSolanaTxManagerProxy private let ipfsApi: IpfsAPI private let bitcoinWalletService: BraveWalletBitcoinWalletService + private let zcashWalletService: BraveWalletZCashWalletService 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. @@ -78,6 +79,7 @@ class AccountActivityStore: ObservableObject, WalletObserverStore { solTxManagerProxy: BraveWalletSolanaTxManagerProxy, ipfsApi: IpfsAPI, bitcoinWalletService: BraveWalletBitcoinWalletService, + zcashWalletService: BraveWalletZCashWalletService, userAssetManager: WalletUserAssetManagerType ) { self.account = account @@ -92,6 +94,7 @@ class AccountActivityStore: ObservableObject, WalletObserverStore { self.solTxManagerProxy = solTxManagerProxy self.ipfsApi = ipfsApi self.bitcoinWalletService = bitcoinWalletService + self.zcashWalletService = zcashWalletService self.assetManager = userAssetManager self._isSwapSupported = .init( wrappedValue: account.coin == .eth || account.coin == .sol @@ -262,10 +265,24 @@ class AccountActivityStore: ObservableObject, WalletObserverStore { $0[tokenId] = Double($1.balance) ?? 0 } } else { - tokenBalances = await self.rpcService.fetchBalancesForTokens( - account: account, - networkAssets: allUserNetworkAssets - ) + if account.coin == .zec { + let zecNetworkAsset = allUserNetworkAssets.first { + $0.network.supportedKeyrings.contains(account.keyringId.rawValue as NSNumber) + } + if let zecToken = zecNetworkAsset?.tokens.first { + let zecBalance = + await self.zcashWalletService.fetchZECTransparentBalances( + networkId: zecToken.chainId, + accountId: account.accountId + ) ?? 0 + tokenBalances = [zecToken.id: zecBalance] + } + } else { + tokenBalances = await self.rpcService.fetchBalancesForTokens( + account: account, + networkAssets: allUserNetworkAssets + ) + } } } tokenBalanceCache.merge(with: tokenBalances) diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountsStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountsStore.swift index 6f00c3b45e94..a8ad94f59bff 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountsStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AccountsStore.swift @@ -44,6 +44,7 @@ class AccountsStore: ObservableObject, WalletObserverStore { private let walletService: BraveWalletBraveWalletService private let assetRatioService: BraveWalletAssetRatioService private let bitcoinWalletService: BraveWalletBitcoinWalletService + private let zcashWalletService: BraveWalletZCashWalletService private let userAssetManager: WalletUserAssetManagerType private var keyringServiceObserver: KeyringServiceObserver? @@ -59,6 +60,7 @@ class AccountsStore: ObservableObject, WalletObserverStore { walletService: BraveWalletBraveWalletService, assetRatioService: BraveWalletAssetRatioService, bitcoinWalletService: BraveWalletBitcoinWalletService, + zcashWalletService: BraveWalletZCashWalletService, userAssetManager: WalletUserAssetManagerType ) { self.keyringService = keyringService @@ -66,6 +68,7 @@ class AccountsStore: ObservableObject, WalletObserverStore { self.walletService = walletService self.assetRatioService = assetRatioService self.bitcoinWalletService = bitcoinWalletService + self.zcashWalletService = zcashWalletService self.userAssetManager = userAssetManager self.setupObservers() @@ -176,7 +179,34 @@ class AccountsStore: ObservableObject, WalletObserverStore { } } } - // Update non-BTC account balance + // Update Zcash account balance + if accounts.contains(where: { $0.coin == .zec }) { + await withTaskGroup( + of: [String: [String: Double]].self + ) { [zcashWalletService] group in + for account in accounts where account.coin == .zec { + group.addTask { + if let zecToken = allNetworkAssets.first(where: { + $0.network.supportedKeyrings.contains( + account.keyringId.rawValue as NSNumber + ) + })?.tokens.first { + let zecBalance = + await zcashWalletService.fetchZECTransparentBalances( + networkId: zecToken.chainId, + accountId: account.accountId + ) ?? 0 + return [account.id: [zecToken.id: zecBalance]] + } + return [:] + } + } + for await accountBTCBalances in group { + tokenBalancesCache.merge(with: accountBTCBalances) + } + } + } + // Update non-BTC/ZEC account balance let balancesForAccounts = await withTaskGroup( of: TokenBalanceCache.self, body: { group in @@ -203,10 +233,25 @@ class AccountsStore: ObservableObject, WalletObserverStore { // a mock `rpcService` and `bitcoinWalletService` group.addTask { var balancesForTokens: [String: Double] = [:] - balancesForTokens = await self.rpcService.fetchBalancesForTokens( - account: account, - networkAssets: allNetworkAssets - ) + if account.coin == .zec { + if let zecToken = allNetworkAssets.first(where: { + $0.network.supportedKeyrings.contains( + account.keyringId.rawValue as NSNumber + ) + })?.tokens.first { + let zecBalance = + await self.zcashWalletService.fetchZECTransparentBalances( + networkId: zecToken.chainId, + accountId: account.accountId + ) ?? 0 + balancesForTokens = [zecToken.id: zecBalance] + } + } else { + balancesForTokens = await self.rpcService.fetchBalancesForTokens( + account: account, + networkAssets: allNetworkAssets + ) + } return [account.id: balancesForTokens] } } diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AssetDetailStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AssetDetailStore.swift index e6126c452d08..d509e96a7df4 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AssetDetailStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/AssetDetailStore.swift @@ -82,6 +82,7 @@ class AssetDetailStore: ObservableObject, WalletObserverStore { private let ipfsApi: IpfsAPI private let swapService: BraveWalletSwapService private let bitcoinWalletService: BraveWalletBitcoinWalletService + private let zcashWalletService: BraveWalletZCashWalletService 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 { ipfsApi: IpfsAPI, swapService: BraveWalletSwapService, bitcoinWalletService: BraveWalletBitcoinWalletService, + zcashWalletService: BraveWalletZCashWalletService, userAssetManager: WalletUserAssetManagerType, assetDetailType: AssetDetailType ) { @@ -151,6 +153,7 @@ class AssetDetailStore: ObservableObject, WalletObserverStore { self.ipfsApi = ipfsApi self.swapService = swapService self.bitcoinWalletService = bitcoinWalletService + self.zcashWalletService = zcashWalletService self.assetManager = userAssetManager self.assetDetailType = assetDetailType @@ -471,11 +474,18 @@ class AssetDetailStore: ObservableObject, WalletObserverStore { )?.first { tokenBalance = Double(assetBalancePerAccount.balance) } else { - tokenBalance = await self.rpcService.balance( - for: token, - in: accountAssetViewModel.account, - network: network - ) + if accountAssetViewModel.account.coin == .zec { + tokenBalance = await self.zcashWalletService.fetchZECTransparentBalances( + networkId: network.chainId, + accountId: accountAssetViewModel.account.accountId + ) + } else { + tokenBalance = await self.rpcService.balance( + for: token, + in: accountAssetViewModel.account, + network: network + ) + } } } return [AccountBalance(accountAssetViewModel.account, tokenBalance)] diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/BuyTokenStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/BuyTokenStore.swift index bd9acb77f386..df6eaf9ffdfa 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/BuyTokenStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/BuyTokenStore.swift @@ -62,6 +62,7 @@ public class BuyTokenStore: ObservableObject, WalletObserverStore { private let walletService: BraveWalletBraveWalletService private let assetRatioService: BraveWalletAssetRatioService private let bitcoinWalletService: BraveWalletBitcoinWalletService + private let zcashWalletService: BraveWalletZCashWalletService private var selectedNetwork: BraveWallet.NetworkInfo = .init() private(set) var orderedSupportedBuyOptions: OrderedSet = [] private var prefilledToken: BraveWallet.BlockchainToken? @@ -93,6 +94,7 @@ public class BuyTokenStore: ObservableObject, WalletObserverStore { walletService: BraveWalletBraveWalletService, assetRatioService: BraveWalletAssetRatioService, bitcoinWalletService: BraveWalletBitcoinWalletService, + zcashWalletService: BraveWalletZCashWalletService, prefilledToken: BraveWallet.BlockchainToken? ) { self.blockchainRegistry = blockchainRegistry @@ -101,6 +103,7 @@ public class BuyTokenStore: ObservableObject, WalletObserverStore { self.walletService = walletService self.assetRatioService = assetRatioService self.bitcoinWalletService = bitcoinWalletService + self.zcashWalletService = zcashWalletService self.prefilledToken = prefilledToken self.buyTokens = WalletConstants.supportedOnRampProviders.reduce( into: [BraveWallet.OnRampProvider: [BraveWallet.BlockchainToken]]() @@ -198,6 +201,11 @@ public class BuyTokenStore: ObservableObject, WalletObserverStore { await bitcoinWalletService.bitcoinAccountInfo(accountId: account.accountId) { accountAddress = bitcoinAccountInfo.nextChangeAddress.addressString + } else if account.coin == .zec, + let zcashAccountInfo = + await zcashWalletService.zCashAccountInfo(accountId: account.accountId) + { + accountAddress = zcashAccountInfo.nextTransparentChangeAddress.addressString } let (urlString, error) = await assetRatioService.buyUrlV1( provider: provider, diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/CryptoStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/CryptoStore.swift index 45850b43885c..119fe093ffe3 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/CryptoStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/CryptoStore.swift @@ -124,6 +124,7 @@ public class CryptoStore: ObservableObject, WalletObserverStore { private let ipfsApi: IpfsAPI private let walletP3A: BraveWalletBraveWalletP3A private let bitcoinWalletService: BraveWalletBitcoinWalletService + private let zcashWalletService: BraveWalletZCashWalletService private let userAssetManager: WalletUserAssetManager private var isUpdatingUserAssets: Bool = false private var autoDiscoveredAssets: [BraveWallet.BlockchainToken] = [] @@ -146,6 +147,7 @@ public class CryptoStore: ObservableObject, WalletObserverStore { ipfsApi: IpfsAPI, walletP3A: BraveWalletBraveWalletP3A, bitcoinWalletService: BraveWalletBitcoinWalletService, + zcashWalletService: BraveWalletZCashWalletService, origin: URLOrigin? = nil ) { self.keyringService = keyringService @@ -160,12 +162,14 @@ public class CryptoStore: ObservableObject, WalletObserverStore { self.ipfsApi = ipfsApi self.walletP3A = walletP3A self.bitcoinWalletService = bitcoinWalletService + self.zcashWalletService = zcashWalletService self.userAssetManager = WalletUserAssetManager( keyringService: keyringService, rpcService: rpcService, walletService: walletService, txService: txService, - bitcoinWalletService: bitcoinWalletService + bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService ) self.origin = origin @@ -185,6 +189,7 @@ public class CryptoStore: ObservableObject, WalletObserverStore { blockchainRegistry: blockchainRegistry, ipfsApi: ipfsApi, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, userAssetManager: userAssetManager ) self.nftStore = .init( @@ -215,6 +220,7 @@ public class CryptoStore: ObservableObject, WalletObserverStore { walletService: walletService, assetRatioService: assetRatioService, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, userAssetManager: userAssetManager ) self.marketStore = .init( @@ -414,6 +420,7 @@ public class CryptoStore: ObservableObject, WalletObserverStore { walletService: walletService, assetRatioService: assetRatioService, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: prefilledToken ) buyTokenStore = store @@ -435,6 +442,7 @@ public class CryptoStore: ObservableObject, WalletObserverStore { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: prefilledToken, ipfsApi: ipfsApi, userAssetManager: userAssetManager @@ -483,7 +491,8 @@ public class CryptoStore: ObservableObject, WalletObserverStore { prefilledToken: prefilledToken, prefilledAccount: prefilledAccount, userAssetManager: userAssetManager, - bitcoinWalletService: bitcoinWalletService + bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService ) depositTokenStore = store return store @@ -516,6 +525,7 @@ public class CryptoStore: ObservableObject, WalletObserverStore { ipfsApi: ipfsApi, swapService: swapService, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, userAssetManager: userAssetManager, assetDetailType: assetDetailType ) @@ -554,6 +564,7 @@ public class CryptoStore: ObservableObject, WalletObserverStore { solTxManagerProxy: solTxManagerProxy, ipfsApi: ipfsApi, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, userAssetManager: userAssetManager ) accountActivityStore = store diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/DepositTokenStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/DepositTokenStore.swift index 848c95e52cfc..de7f8dd48468 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/DepositTokenStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/DepositTokenStore.swift @@ -30,6 +30,7 @@ class DepositTokenStore: ObservableObject, WalletObserverStore { private var allTokens: [BraveWallet.BlockchainToken] = [] private let assetManager: WalletUserAssetManagerType private let bitcoinWalletService: BraveWalletBitcoinWalletService + private let zcashWalletService: BraveWalletZCashWalletService private var keyringServiceObserver: KeyringServiceObserver? private var walletServiceObserver: WalletServiceObserver? @@ -38,6 +39,7 @@ class DepositTokenStore: ObservableObject, WalletObserverStore { @Published var allAccounts: [BraveWallet.AccountInfo] = [] @Published var allNetworks: [BraveWallet.NetworkInfo] = [] @Published var bitcoinAccounts: [String: BraveWallet.BitcoinAccountInfo] = [:] + @Published var zcashAccounts: [String: BraveWallet.ZCashAccountInfo] = [:] var isObserving: Bool { walletServiceObserver != nil && keyringServiceObserver != nil @@ -51,7 +53,8 @@ class DepositTokenStore: ObservableObject, WalletObserverStore { prefilledToken: BraveWallet.BlockchainToken?, prefilledAccount: BraveWallet.AccountInfo?, userAssetManager: WalletUserAssetManagerType, - bitcoinWalletService: BraveWalletBitcoinWalletService + bitcoinWalletService: BraveWalletBitcoinWalletService, + zcashWalletService: BraveWalletZCashWalletService ) { self.keyringService = keyringService self.rpcService = rpcService @@ -61,6 +64,7 @@ class DepositTokenStore: ObservableObject, WalletObserverStore { self.prefilledAccount = prefilledAccount self.assetManager = userAssetManager self.bitcoinWalletService = bitcoinWalletService + self.zcashWalletService = zcashWalletService self.setupObservers() } @@ -103,15 +107,24 @@ class DepositTokenStore: ObservableObject, WalletObserverStore { self.networkFilters = allNetworks.map { .init(isSelected: true, model: $0) } - if let prefilledAccount, prefilledAccount.coin == .btc { - self.bitcoinAccounts = await bitcoinWalletService.fetchBitcoinAccountInfo(accounts: [ - prefilledAccount - ]) + if let prefilledAccount { + if prefilledAccount.coin == .btc { + self.bitcoinAccounts = await bitcoinWalletService.fetchBitcoinAccountInfo( + accounts: [prefilledAccount] + ) + } else if prefilledAccount.coin == .zec { + self.zcashAccounts = await zcashWalletService.fetchZcashAccountInfo( + accounts: [prefilledAccount] + ) + } } else { self.allAccounts = await keyringService.allAccounts().accounts self.bitcoinAccounts = await bitcoinWalletService.fetchBitcoinAccountInfo( accounts: allAccounts.filter({ $0.coin == .btc }) ) + self.zcashAccounts = await zcashWalletService.fetchZcashAccountInfo( + accounts: allAccounts.filter({ $0.coin == .zec }) + ) } self.allNetworks = await rpcService.allNetworksForSupportedCoins() self.update() diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/KeyringStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/KeyringStore.swift index f4c5d03b8138..f232d1708180 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/KeyringStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/KeyringStore.swift @@ -196,6 +196,7 @@ public class KeyringStore: ObservableObject, WalletObserverStore { setupObservers() Preferences.Wallet.isBitcoinTestnetEnabled.observe(from: self) + Preferences.Wallet.isZcashTestnetEnabled.observe(from: self) updateInfo() @@ -565,7 +566,7 @@ public class KeyringStore: ObservableObject, WalletObserverStore { network: chainId ) case .zec: - // ZCash not supported on iOS yet, including account import + // no import for Zcash return nil default: return nil @@ -721,7 +722,9 @@ public class KeyringStore: ObservableObject, WalletObserverStore { extension KeyringStore: PreferencesObserver { public func preferencesDidChange(for key: String) { - if Preferences.Wallet.isBitcoinTestnetEnabled.value == false { + if key == Preferences.Wallet.isBitcoinTestnetEnabled.key, + Preferences.Wallet.isBitcoinTestnetEnabled.value == false + { // user disabled Bitcoin Testnet Task { @MainActor in let allAccounts = await keyringService.allAccounts() @@ -735,6 +738,22 @@ extension KeyringStore: PreferencesObserver { setSelectedAccount(to: firstAvailableAccount) } } + } else if key == Preferences.Wallet.isZcashTestnetEnabled.key, + Preferences.Wallet.isZcashTestnetEnabled.value == false + { + // user disabled Zcash Testnet + Task { @MainActor in + let allAccounts = await keyringService.allAccounts() + if let currentlySelectedAccount = allAccounts.selectedAccount, + currentlySelectedAccount.keyringId == .zCashTestnet, + let firstAvailableAccount = allAccounts.accounts.first(where: { + $0.keyringId != currentlySelectedAccount.keyringId + }) + { + // we need to switch to the first available account + setSelectedAccount(to: firstAvailableAccount) + } + } } } } diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/NetworkStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/NetworkStore.swift index 1e98fc8c6cc2..8a453371929b 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/NetworkStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/NetworkStore.swift @@ -101,6 +101,7 @@ public class NetworkStore: ObservableObject, WalletObserverStore { } } Preferences.Wallet.isBitcoinTestnetEnabled.observe(from: self) + Preferences.Wallet.isZcashTestnetEnabled.observe(from: self) } func tearDown() { @@ -500,25 +501,46 @@ extension NetworkStore: PreferencesObserver { public func preferencesDidChange(for key: String) { // only observe `Preferences.Wallet.isBitcoinTestnetEnabled` Task { @MainActor in - guard key == Preferences.Wallet.isBitcoinTestnetEnabled.key else { return } - let btcDefaultNetwork = await rpcService.network(coin: .btc, origin: nil) - let isBitcoinTestnetEnabled = Preferences.Wallet.isBitcoinTestnetEnabled.value - if !isBitcoinTestnetEnabled, btcDefaultNetwork.chainId == BraveWallet.BitcoinTestnet { - // bitcoin testnet is disabled but the btc coin type - // default network is testnet. Need to change it to btc - // mainnet, to prevent user hide the default network - if hiddenChains.contains(where: { $0.chainId == BraveWallet.BitcoinMainnet }) { - // bitcoin mainnet is currently hidden. unhide it then set it to default network - await rpcService.removeHiddenNetwork( + if key == Preferences.Wallet.isBitcoinTestnetEnabled.key { + let btcDefaultNetwork = await rpcService.network(coin: .btc, origin: nil) + let isBitcoinTestnetEnabled = Preferences.Wallet.isBitcoinTestnetEnabled.value + if !isBitcoinTestnetEnabled, btcDefaultNetwork.chainId == BraveWallet.BitcoinTestnet { + // bitcoin testnet is disabled but the btc coin type + // default network is testnet. Need to change it to btc + // mainnet, to prevent user hide the default network + if hiddenChains.contains(where: { $0.chainId == BraveWallet.BitcoinMainnet }) { + // bitcoin mainnet is currently hidden. unhide it then set it to default network + await rpcService.removeHiddenNetwork( + coin: .btc, + chainId: BraveWallet.BitcoinMainnet + ) + } + await rpcService.setNetwork( + chainId: BraveWallet.BitcoinMainnet, coin: .btc, - chainId: BraveWallet.BitcoinMainnet + origin: nil + ) + } + } else if key == Preferences.Wallet.isZcashTestnetEnabled.key { + let zcashDefaultNetwork = await rpcService.network(coin: .zec, origin: nil) + let isZcashTestnetEnabled = Preferences.Wallet.isZcashTestnetEnabled.value + if !isZcashTestnetEnabled, zcashDefaultNetwork.chainId == BraveWallet.ZCashTestnet { + // zcash testnet is disabled but the zcash coin type + // default network is testnet. Need to change it to zcash + // mainnet, to prevent user hide the default network + if hiddenChains.contains(where: { $0.chainId == BraveWallet.ZCashMainnet }) { + // zcash mainnet is currently hidden. unhide it then set it to default network + await rpcService.removeHiddenNetwork( + coin: .zec, + chainId: BraveWallet.ZCashMainnet + ) + } + await rpcService.setNetwork( + chainId: BraveWallet.ZCashMainnet, + coin: .zec, + origin: nil ) } - await rpcService.setNetwork( - chainId: BraveWallet.BitcoinMainnet, - coin: .btc, - origin: nil - ) } } } @@ -546,6 +568,11 @@ extension Array where Element == BraveWallet.NetworkInfo { { return false } + if !Preferences.Wallet.isZcashTestnetEnabled.value + && $0.chainId == BraveWallet.ZCashTestnet + { + return false + } return WalletConstants.supportedTestNetworkChainIds.contains($0.chainId) } } diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/PortfolioStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/PortfolioStore.swift index 9acb93a7a16d..d01306b15b3b 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/PortfolioStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/PortfolioStore.swift @@ -328,6 +328,7 @@ public class PortfolioStore: ObservableObject, WalletObserverStore { private let blockchainRegistry: BraveWalletBlockchainRegistry private let ipfsApi: IpfsAPI private let bitcoinWalletService: BraveWalletBitcoinWalletService + private let zcashWalletService: BraveWalletZCashWalletService private let assetManager: WalletUserAssetManagerType private var rpcServiceObserver: JsonRpcServiceObserver? private var keyringServiceObserver: KeyringServiceObserver? @@ -345,6 +346,7 @@ public class PortfolioStore: ObservableObject, WalletObserverStore { blockchainRegistry: BraveWalletBlockchainRegistry, ipfsApi: IpfsAPI, bitcoinWalletService: BraveWalletBitcoinWalletService, + zcashWalletService: BraveWalletZCashWalletService, userAssetManager: WalletUserAssetManagerType ) { self.keyringService = keyringService @@ -354,6 +356,7 @@ public class PortfolioStore: ObservableObject, WalletObserverStore { self.blockchainRegistry = blockchainRegistry self.ipfsApi = ipfsApi self.bitcoinWalletService = bitcoinWalletService + self.zcashWalletService = zcashWalletService self.assetManager = userAssetManager // cache balance update observer @@ -530,7 +533,10 @@ public class PortfolioStore: ObservableObject, WalletObserverStore { of: TokenBalanceCache.self, body: { @MainActor - [tokenBalancesCache, rpcService, bitcoinWalletService, assetManager] + [ + tokenBalancesCache, rpcService, bitcoinWalletService, + zcashWalletService, assetManager + ] group in group.addTask { @MainActor in let token = tokenNetworkAccounts.token @@ -548,6 +554,15 @@ public class PortfolioStore: ObservableObject, WalletObserverStore { accountId: account.accountId, type: .total ) + } else if account.coin == .zec, + tokenNetwork.supportedKeyrings.contains( + account.keyringId.rawValue as NSNumber + ) + { + balanceForToken = await zcashWalletService.fetchZECTransparentBalances( + networkId: tokenNetworkAccounts.network.chainId, + accountId: account.accountId + ) } else { balanceForToken = await rpcService.balance( for: token, diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/SelectAccountTokenStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/SelectAccountTokenStore.swift index 6ae43506532e..13460fa76e5e 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/SelectAccountTokenStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/SelectAccountTokenStore.swift @@ -34,7 +34,18 @@ class SelectAccountTokenStore: ObservableObject, WalletObserverStore { var id: String { account.id } let account: BraveWallet.AccountInfo let bitcoinAccountInfo: BraveWallet.BitcoinAccountInfo? + let zcashAccountInfo: BraveWallet.ZCashAccountInfo? let tokenBalances: [TokenBalance] + + var copyAddress: String { + if let bitcoinAccountInfo { + return bitcoinAccountInfo.nextReceiveAddress.addressString + } else if let zcashAccountInfo { + return zcashAccountInfo.nextTransparentChangeAddress.addressString + } else { + return account.address + } + } } /// The networks to filter the tokens/accounts by. @@ -91,6 +102,7 @@ class SelectAccountTokenStore: ObservableObject, WalletObserverStore { private let walletService: BraveWalletBraveWalletService private let assetRatioService: BraveWalletAssetRatioService private let bitcoinWalletService: BraveWalletBitcoinWalletService + private let zcashWalletService: BraveWalletZCashWalletService private let ipfsApi: IpfsAPI private let assetManager: WalletUserAssetManagerType private var walletServiceObserver: WalletServiceObserver? @@ -106,6 +118,7 @@ class SelectAccountTokenStore: ObservableObject, WalletObserverStore { walletService: BraveWalletBraveWalletService, assetRatioService: BraveWalletAssetRatioService, bitcoinWalletService: BraveWalletBitcoinWalletService, + zcashWalletService: BraveWalletZCashWalletService, ipfsApi: IpfsAPI, userAssetManager: WalletUserAssetManagerType, query: String? = nil @@ -116,6 +129,7 @@ class SelectAccountTokenStore: ObservableObject, WalletObserverStore { self.walletService = walletService self.assetRatioService = assetRatioService self.bitcoinWalletService = bitcoinWalletService + self.zcashWalletService = zcashWalletService self.ipfsApi = ipfsApi self.assetManager = userAssetManager self.query = query ?? "" @@ -163,6 +177,8 @@ class SelectAccountTokenStore: ObservableObject, WalletObserverStore { private var allAccounts: [BraveWallet.AccountInfo] = [] /// All` BitcoinAccountInfo` models for every Bitcoin account. Key is `accountId.uniqueKey` of the Account. private var bitcoinAccounts: [String: BraveWallet.BitcoinAccountInfo] = [:] + /// All` ZCashAccountInfo` models for every Zcash account. Key is `accountId.uniqueKey` of the Account. + private var zcashAccounts: [String: BraveWallet.ZCashAccountInfo] = [:] // All user visible assets, key is `Identifiable.id` of `BlockchainToken`. private var userVisibleAssets: [String: BraveWallet.BlockchainToken] = [:] // All user accounts. @@ -178,11 +194,17 @@ class SelectAccountTokenStore: ObservableObject, WalletObserverStore { .init(isSelected: true, model: $0) } let btcAccountInfos = allAccounts.filter({ $0.coin == .btc }) + let zcashAccountInfos = allAccounts.filter({ $0.coin == .zec }) if !btcAccountInfos.isEmpty { self.bitcoinAccounts = await bitcoinWalletService.fetchBitcoinAccountInfo( accounts: btcAccountInfos ) } + if !zcashAccountInfos.isEmpty { + self.zcashAccounts = await zcashWalletService.fetchZcashAccountInfo( + accounts: zcashAccountInfos + ) + } let allNetworkAssets = await assetManager.getUserAssets( networks: allNetworks, visible: true @@ -199,6 +221,7 @@ class SelectAccountTokenStore: ObservableObject, WalletObserverStore { selectedNetworks: networkFilters.filter(\.isSelected).map(\.model), allAccounts: allAccounts, bitcoinAccounts: bitcoinAccounts, + zcashAccounts: zcashAccounts, userVisibleAssets: Array(userVisibleAssets.values), balancesCache: balancesForAccountsCache, btcBalancesCache: accountsBTCBalances, @@ -226,6 +249,7 @@ class SelectAccountTokenStore: ObservableObject, WalletObserverStore { selectedNetworks: networkFilters.filter(\.isSelected).map(\.model), allAccounts: allAccounts, bitcoinAccounts: bitcoinAccounts, + zcashAccounts: zcashAccounts, userVisibleAssets: Array(userVisibleAssets.values), balancesCache: balancesForAccountsCache, btcBalancesCache: accountsBTCBalances, @@ -262,12 +286,24 @@ class SelectAccountTokenStore: ObservableObject, WalletObserverStore { 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] + if account.coin == .zec, + let zecNetworkAsset = networkAssets.first, + let zec = zecNetworkAsset.tokens.first + { + let zecBalance = + await self.zcashWalletService.fetchZECTransparentBalances( + networkId: zecNetworkAsset.network.chainId, + accountId: account.accountId + ) ?? 0 + return [account.id: [zec.id: zecBalance]] + } else { + let balancesForTokens: [String: Double] = await self.rpcService + .fetchBalancesForTokens( + account: account, + networkAssets: networkAssets + ) + return [account.id: balancesForTokens] + } } } } @@ -363,6 +399,7 @@ class SelectAccountTokenStore: ObservableObject, WalletObserverStore { selectedNetworks: [BraveWallet.NetworkInfo], allAccounts: [BraveWallet.AccountInfo], bitcoinAccounts: [String: BraveWallet.BitcoinAccountInfo], + zcashAccounts: [String: BraveWallet.ZCashAccountInfo], userVisibleAssets: [BraveWallet.BlockchainToken], balancesCache: TokenBalanceCache, btcBalancesCache: [String: [BTCBalanceType: Double]], @@ -430,6 +467,7 @@ class SelectAccountTokenStore: ObservableObject, WalletObserverStore { return AccountSection( account: account, bitcoinAccountInfo: bitcoinAccounts[account.id], + zcashAccountInfo: zcashAccounts[account.id], tokenBalances: accountTokenBalances .sorted { lhs, rhs in diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/SendTokenStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/SendTokenStore.swift index 9bf06fb1f6af..b50744c58854 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/SendTokenStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/SendTokenStore.swift @@ -159,6 +159,7 @@ public class SendTokenStore: ObservableObject, WalletObserverStore { walletService: walletService, assetRatioService: assetRatioService, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, ipfsApi: ipfsApi, userAssetManager: assetManager, query: prefilledToken?.symbol @@ -173,6 +174,7 @@ public class SendTokenStore: ObservableObject, WalletObserverStore { private let ethTxManagerProxy: BraveWalletEthTxManagerProxy private let solTxManagerProxy: BraveWalletSolanaTxManagerProxy private let bitcoinWalletService: BraveWalletBitcoinWalletService + private let zcashWalletService: BraveWalletZCashWalletService private var allTokens: [BraveWallet.BlockchainToken] = [] private var sendAddressUpdatedTimer: Timer? private var sendAmountUpdatedTimer: Timer? @@ -197,6 +199,7 @@ public class SendTokenStore: ObservableObject, WalletObserverStore { ethTxManagerProxy: BraveWalletEthTxManagerProxy, solTxManagerProxy: BraveWalletSolanaTxManagerProxy, bitcoinWalletService: BraveWalletBitcoinWalletService, + zcashWalletService: BraveWalletZCashWalletService, prefilledToken: BraveWallet.BlockchainToken?, ipfsApi: IpfsAPI, userAssetManager: WalletUserAssetManagerType @@ -210,6 +213,7 @@ public class SendTokenStore: ObservableObject, WalletObserverStore { self.ethTxManagerProxy = ethTxManagerProxy self.solTxManagerProxy = solTxManagerProxy self.bitcoinWalletService = bitcoinWalletService + self.zcashWalletService = zcashWalletService self.prefilledToken = prefilledToken self.ipfsApi = ipfsApi self.assetManager = userAssetManager @@ -399,12 +403,21 @@ public class SendTokenStore: ObservableObject, WalletObserverStore { )?.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 selectedAccount.coin == .zec { + let zecBalance = + await self.zcashWalletService.fetchZECTransparentBalances( + networkId: selectedSendToken.chainId, + accountId: selectedAccount.accountId + ) ?? 0 + balance = BDouble(zecBalance) + } else { + balance = await self.rpcService.balance( + for: selectedSendToken, + in: selectedAccount.address, + network: network, + decimalFormatStyle: .decimals(precision: Int(selectedSendToken.decimals)) + ) + } } } @@ -444,6 +457,8 @@ public class SendTokenStore: ObservableObject, WalletObserverStore { validateBitcoinSendAddress(fromAccount: selectedAccount) case .zec: break + case .ada: + break @unknown default: break } diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/WalletStore.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/WalletStore.swift index 586c167fb84a..eb4ba5204775 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/WalletStore.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/Stores/WalletStore.swift @@ -74,7 +74,8 @@ public class WalletStore { solTxManagerProxy: BraveWalletSolanaTxManagerProxy, ipfsApi: IpfsAPI, walletP3A: BraveWalletBraveWalletP3A, - bitcoinWalletService: BraveWalletBitcoinWalletService + bitcoinWalletService: BraveWalletBitcoinWalletService, + zcashWalletService: BraveWalletZCashWalletService ) { self.keyringStore = .init( keyringService: keyringService, @@ -94,7 +95,8 @@ public class WalletStore { solTxManagerProxy: solTxManagerProxy, ipfsApi: ipfsApi, walletP3A: walletP3A, - bitcoinWalletService: bitcoinWalletService + bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService ) } @@ -120,7 +122,8 @@ public class WalletStore { solTxManagerProxy: BraveWalletSolanaTxManagerProxy, ipfsApi: IpfsAPI, walletP3A: BraveWalletBraveWalletP3A, - bitcoinWalletService: BraveWalletBitcoinWalletService + bitcoinWalletService: BraveWalletBitcoinWalletService, + zcashWalletService: BraveWalletZCashWalletService ) { self.cancellable = self.keyringStore.$isWalletCreated .removeDuplicates() @@ -145,6 +148,7 @@ public class WalletStore { ipfsApi: ipfsApi, walletP3A: walletP3A, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, origin: self.origin ) if let cryptoStore = self.cryptoStore { diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/WalletActions/CopyAddressHeader.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/WalletActions/CopyAddressHeader.swift index 7fdf041fea79..3860887e114c 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/WalletActions/CopyAddressHeader.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/WalletActions/CopyAddressHeader.swift @@ -14,30 +14,21 @@ import SwiftUI struct CopyAddressHeader: View { let displayText: String - let account: BraveWallet.AccountInfo - let btcAccountInfo: BraveWallet.BitcoinAccountInfo? + let address: String init( displayText: String, - account: BraveWallet.AccountInfo, - btcAccountInfo: BraveWallet.BitcoinAccountInfo? + address: String ) { self.displayText = displayText - self.account = account - self.btcAccountInfo = btcAccountInfo + self.address = address } var body: some View { HStack { Text(displayText) Spacer() - if account.coin == .btc { - if let btcAccountInfo { - addressMenu(for: btcAccountInfo.nextReceiveAddress.addressString) - } - } else { - addressMenu(for: account.address) - } + addressMenu(for: address) } } diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/WalletActions/DepositTokenView.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/WalletActions/DepositTokenView.swift index a16e5b7958e9..95cbad948696 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/WalletActions/DepositTokenView.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/WalletActions/DepositTokenView.swift @@ -50,7 +50,8 @@ struct DepositTokenView: View { DepositDetailsView( type: .prefilledAccount( account: prefilledAccount, - bitcoinAccounts: depositTokenStore.bitcoinAccounts + bitcoinAccounts: depositTokenStore.bitcoinAccounts, + zcashAccounts: depositTokenStore.zcashAccounts ), supportedNetworks: depositTokenStore.allNetworks.supportedNetworks( keyringId: prefilledAccount.keyringId @@ -63,7 +64,8 @@ struct DepositTokenView: View { type: .prefilledToken( token: prefilledToken, availableAccounts: availableAccounts(for: prefilledToken), - bitcoinAccounts: depositTokenStore.bitcoinAccounts + bitcoinAccounts: depositTokenStore.bitcoinAccounts, + zcashAccounts: depositTokenStore.zcashAccounts ), supportedNetworks: depositTokenStore.allNetworks.supportedNetworks( keyringId: .keyringId( @@ -186,7 +188,8 @@ struct DepositTokenView: View { type: .prefilledToken( token: selectedTokenViewModel.token, availableAccounts: availableAccounts(for: selectedTokenViewModel.token), - bitcoinAccounts: depositTokenStore.bitcoinAccounts + bitcoinAccounts: depositTokenStore.bitcoinAccounts, + zcashAccounts: depositTokenStore.zcashAccounts ), supportedNetworks: depositTokenStore.allNetworks.supportedNetworks( keyringId: .keyringId( @@ -243,12 +246,14 @@ private struct DepositDetailsView: View { enum DepositType { case prefilledAccount( account: BraveWallet.AccountInfo, - bitcoinAccounts: [String: BraveWallet.BitcoinAccountInfo] + bitcoinAccounts: [String: BraveWallet.BitcoinAccountInfo], + zcashAccounts: [String: BraveWallet.ZCashAccountInfo] ) case prefilledToken( token: BraveWallet.BlockchainToken, availableAccounts: [BraveWallet.AccountInfo], - bitcoinAccounts: [String: BraveWallet.BitcoinAccountInfo] + bitcoinAccounts: [String: BraveWallet.BitcoinAccountInfo], + zcashAccounts: [String: BraveWallet.ZCashAccountInfo] ) } @@ -258,7 +263,7 @@ private struct DepositDetailsView: View { ) { self.type = type self.supportedNetworks = supportedNetworks - if case .prefilledToken(_, let availableAccounts, _) = type { + if case .prefilledToken(_, let availableAccounts, _, _) = type { self._selectedAccount = State(initialValue: availableAccounts.first) } } @@ -312,6 +317,11 @@ private struct DepositDetailsView: View { .foregroundColor(Color(.bravePrimary)) MultipleNetworkIconsView(networks: networks.filter({ $0.coin == .btc })) case .zec: + Text(Strings.Wallet.zecAccountDescription) + .fontWeight(.semibold) + .foregroundColor(Color(.bravePrimary)) + MultipleNetworkIconsView(networks: networks.filter({ $0.coin == .zec })) + case .ada: EmptyView() @unknown default: EmptyView() @@ -398,16 +408,22 @@ private struct DepositDetailsView: View { ScrollView { VStack(spacing: 24) { switch type { - case .prefilledAccount(let account, let bitcoinAccounts): + case .prefilledAccount(let account, let bitcoinAccounts, let zcashAccounts): depositHeader( coin: account.coin, networks: supportedNetworks ) - qrCodeView(buildQRCodeViewModel(account: account, bitcoinAccounts: bitcoinAccounts)) + qrCodeView( + buildQRCodeViewModel( + account: account, + bitcoinAccounts: bitcoinAccounts, + zcashAccounts: zcashAccounts + ) + ) if account.coin == .eth { ethDisclosureView } - case .prefilledToken(let token, _, let bitcoinAccounts): + case .prefilledToken(let token, _, let bitcoinAccounts, let zcashAccounts): Button { isPresentingAccountPicker = true } label: { @@ -421,7 +437,8 @@ private struct DepositDetailsView: View { qrCodeView( buildQRCodeViewModel( account: selectedAccount, - bitcoinAccounts: bitcoinAccounts + bitcoinAccounts: bitcoinAccounts, + zcashAccounts: zcashAccounts ) ) } @@ -433,7 +450,7 @@ private struct DepositDetailsView: View { .padding(.vertical, 32) .padding(.horizontal, 16) .sheet(isPresented: $isPresentingAccountPicker) { - if let selectedAccount, case .prefilledToken(_, let availableAccounts, _) = type { + if let selectedAccount, case .prefilledToken(_, let availableAccounts, _, _) = type { NavigationView { AccountSelectionRootView( navigationTitle: Strings.Wallet.selectAccountTitle, @@ -468,10 +485,16 @@ private struct DepositDetailsView: View { private func buildQRCodeViewModel( account: BraveWallet.AccountInfo, - bitcoinAccounts: [String: BraveWallet.BitcoinAccountInfo] + bitcoinAccounts: [String: BraveWallet.BitcoinAccountInfo], + zcashAccounts: [String: BraveWallet.ZCashAccountInfo] ) -> QRCodeViewModel { if let bitcoinAccount = bitcoinAccounts[account.accountId.uniqueKey] { return .init(name: account.name, address: bitcoinAccount.nextReceiveAddress.addressString) + } else if let zcashAccount = zcashAccounts[account.accountId.uniqueKey] { + return .init( + name: account.name, + address: zcashAccount.nextTransparentReceiveAddress.addressString + ) } else { return .init(name: account.name, address: account.address) } diff --git a/ios/brave-ios/Sources/BraveWallet/Crypto/WalletActions/SendTokenView.swift b/ios/brave-ios/Sources/BraveWallet/Crypto/WalletActions/SendTokenView.swift index 85832eb7661b..16744671ab59 100644 --- a/ios/brave-ios/Sources/BraveWallet/Crypto/WalletActions/SendTokenView.swift +++ b/ios/brave-ios/Sources/BraveWallet/Crypto/WalletActions/SendTokenView.swift @@ -143,13 +143,12 @@ struct SendTokenView: View { Form { Section( header: WalletListHeaderView { + // User doesn't need from BTC/ZEC account receive address in Send + // Can either use Deposit or Select Token modal CopyAddressHeader( displayText: "\(Strings.Wallet.sendCryptoFromTitle): \(keyringStore.selectedAccount.accountNameDisplay)", - account: keyringStore.selectedAccount, - // User doesn't need from BTC account receive address in Send - // Can either use Deposit or Select Token modal - btcAccountInfo: nil + address: keyringStore.selectedAccount.address ) } ) { diff --git a/ios/brave-ios/Sources/BraveWallet/Extensions/BraveWalletExtensions.swift b/ios/brave-ios/Sources/BraveWallet/Extensions/BraveWalletExtensions.swift index ee7c60a1616d..f18cacf70b5d 100644 --- a/ios/brave-ios/Sources/BraveWallet/Extensions/BraveWalletExtensions.swift +++ b/ios/brave-ios/Sources/BraveWallet/Extensions/BraveWalletExtensions.swift @@ -172,6 +172,8 @@ extension BraveWallet.AccountInfo { case .btc: return Strings.Wallet.btcAccountDescription case .zec: + return Strings.Wallet.zecAccountDescription + case .ada: return "" @unknown default: return "" @@ -185,13 +187,28 @@ extension BraveWallet.AccountInfo { /// Displays as `Account Name (truncated address)`, ex `Ethereum Account 1 (0x1234...5678)` /// or `Account Name` for Bitcoin. var accountNameDisplay: String { - if coin == .btc { + if coin == .btc || coin == .zec { return name } else { return "\(name) (\(address.truncatedAddress))" } } + var supportsAccountExport: Bool { + switch coin { + case .eth, .sol, .fil: + return true + case .btc, .zec, .ada: + return false + @unknown default: + return false + } + } + + var supportsNFT: Bool { + coin == .eth || coin == .sol + } + public func sort( with other: BraveWallet.AccountInfo, parentOrder: Bool @@ -208,6 +225,12 @@ extension BraveWallet.AccountInfo { } else if self.keyringId != .bitcoin84 && other.keyringId == .bitcoin84 { return false } + } else if self.coin == .zec && other.coin == .zec { + if self.keyringId == .zCashMainnet && other.keyringId != .zCashMainnet { + return true + } else if self.keyringId != .zCashMainnet && other.keyringId == .zCashMainnet { + return false + } } else { if self.keyringId == .solana && other.keyringId != .solana { return true @@ -232,6 +255,8 @@ extension BraveWallet.CoinType { return [.bitcoin84, .bitcoin84Testnet] case .zec: return [.zCashMainnet, .zCashTestnet] + case .ada: + return [.cardanoMainnet, .cardanoTestnet] @unknown default: return [.default] } @@ -248,6 +273,8 @@ extension BraveWallet.CoinType { case .btc: return Strings.Wallet.coinTypeBitcoin case .zec: + return Strings.Wallet.coinTypeZCash + case .ada: fallthrough @unknown default: return Strings.Wallet.coinTypeUnknown @@ -265,6 +292,8 @@ extension BraveWallet.CoinType { case .btc: return Strings.Wallet.coinTypeBitcoinDescription case .zec: + return Strings.Wallet.coinTypeZCashDescription + case .ada: fallthrough @unknown default: return Strings.Wallet.coinTypeUnknown @@ -282,6 +311,8 @@ extension BraveWallet.CoinType { case .btc: return "bitcoin-asset-icon" case .zec: + return "zcash-asset-icon" + case .ada: fallthrough @unknown default: return "" @@ -300,6 +331,8 @@ extension BraveWallet.CoinType { case .btc: return 4 case .zec: + return 5 + case .ada: fallthrough @unknown default: return 10 @@ -636,6 +669,8 @@ extension BraveWallet.KeyringId { return chainId == BraveWallet.BitcoinMainnet ? .bitcoin84 : .bitcoin84Testnet case .zec: return chainId == BraveWallet.ZCashMainnet ? .zCashMainnet : .zCashTestnet + case .ada: + return chainId == BraveWallet.CardanoMainnet ? .cardanoMainnet : .cardanoTestnet @unknown default: return .default } diff --git a/ios/brave-ios/Sources/BraveWallet/Extensions/BraveWalletSwiftUIExtensions.swift b/ios/brave-ios/Sources/BraveWallet/Extensions/BraveWalletSwiftUIExtensions.swift index 165caa8f459c..0c1983efe8d1 100644 --- a/ios/brave-ios/Sources/BraveWallet/Extensions/BraveWalletSwiftUIExtensions.swift +++ b/ios/brave-ios/Sources/BraveWallet/Extensions/BraveWalletSwiftUIExtensions.swift @@ -38,6 +38,7 @@ public enum AssetImageName: String { case solana = "sol-asset-icon" case filecoin = "filecoin-asset-icon" case bitcoin = "bitcoin-asset-icon" + case zcash = "zcash-asset-icon" case polygon = "matic" case binance = "bnb-asset-icon" case celo = "celo" @@ -45,6 +46,8 @@ public enum AssetImageName: String { case fantom = "fantom" case aurora = "aurora" case optimism = "optimism" + case base = "base" + case neon = "neon" } extension BraveWallet.NetworkInfo: Identifiable { @@ -127,6 +130,10 @@ extension BraveWallet.NetworkInfo: Identifiable { || chainId.caseInsensitiveCompare(BraveWallet.BitcoinTestnet) == .orderedSame { return AssetImageName.bitcoin.rawValue + } else if chainId.caseInsensitiveCompare(BraveWallet.ZCashMainnet) == .orderedSame + || chainId.caseInsensitiveCompare(BraveWallet.ZCashTestnet) == .orderedSame + { + return AssetImageName.zcash.rawValue } else if chainId.caseInsensitiveCompare(BraveWallet.PolygonMainnetChainId) == .orderedSame { return AssetImageName.polygon.rawValue } else if chainId.caseInsensitiveCompare(BraveWallet.BnbSmartChainMainnetChainId) @@ -143,6 +150,10 @@ extension BraveWallet.NetworkInfo: Identifiable { return AssetImageName.aurora.rawValue } else if chainId.caseInsensitiveCompare(BraveWallet.OptimismMainnetChainId) == .orderedSame { return AssetImageName.optimism.rawValue + } else if chainId.caseInsensitiveCompare(BraveWallet.BaseMainnetChainId) == .orderedSame { + return AssetImageName.base.rawValue + } else if chainId.caseInsensitiveCompare(BraveWallet.NeonEvmMainnetChainId) == .orderedSame { + return AssetImageName.neon.rawValue } else { return nil } @@ -157,6 +168,8 @@ extension BraveWallet.NetworkInfo: Identifiable { return AssetImageName.filecoin.rawValue } else if symbol.caseInsensitiveCompare("BTC") == .orderedSame { return AssetImageName.bitcoin.rawValue + } else if symbol.caseInsensitiveCompare("ZEC") == .orderedSame { + return AssetImageName.zcash.rawValue } return nil } diff --git a/ios/brave-ios/Sources/BraveWallet/Extensions/KeyringServiceExtensions.swift b/ios/brave-ios/Sources/BraveWallet/Extensions/KeyringServiceExtensions.swift index b4018a94e045..3b1d25693aae 100644 --- a/ios/brave-ios/Sources/BraveWallet/Extensions/KeyringServiceExtensions.swift +++ b/ios/brave-ios/Sources/BraveWallet/Extensions/KeyringServiceExtensions.swift @@ -21,13 +21,19 @@ extension BraveWalletKeyringService { return !allAccountsForKeyring.isEmpty } - /// Return a list of all accounts with checking if Bitcoin testnet is enabled - /// The list of account will not include Bitcoin Testnet Accounts if Bitcoin testnet is disabled. - func allAccountInfos(checkBTCTestnet: Bool = true) async -> [BraveWallet.AccountInfo] { + /// Return a list of all accounts with checking if Bitcoin/Zcash testnet is enabled + /// The list of account will not include Bitcoin/Zcash Testnet Accounts if Bitcoin/Zcash testnet is disabled. + func allAccountInfos( + checkBTCTestnet: Bool = true, + checkZcashTestnet: Bool = true + ) async -> [BraveWallet.AccountInfo] { var accounts = await self.allAccounts().accounts if checkBTCTestnet, !Preferences.Wallet.isBitcoinTestnetEnabled.value { accounts = accounts.filter({ $0.keyringId != BraveWallet.KeyringId.bitcoin84Testnet }) } + if checkZcashTestnet, !Preferences.Wallet.isZcashTestnetEnabled.value { + accounts = accounts.filter({ $0.keyringId != BraveWallet.KeyringId.zCashTestnet }) + } return accounts } } diff --git a/ios/brave-ios/Sources/BraveWallet/Extensions/RpcServiceExtensions.swift b/ios/brave-ios/Sources/BraveWallet/Extensions/RpcServiceExtensions.swift index b803fb4ea8d6..1e6dad962970 100644 --- a/ios/brave-ios/Sources/BraveWallet/Extensions/RpcServiceExtensions.swift +++ b/ios/brave-ios/Sources/BraveWallet/Extensions/RpcServiceExtensions.swift @@ -251,6 +251,7 @@ extension BraveWalletJsonRpcService { // Bitcoin balance should be fetched using `BraveWallet.BitcoinWalletService` completion(nil) case .zec: + // Zcash balance should be fetched using `BraveWallet.ZCashWalletService` completion(nil) @unknown default: completion(nil) @@ -392,6 +393,13 @@ extension BraveWalletJsonRpcService { } else { return Preferences.Wallet.isBitcoinTestnetEnabled.value } + } else if network.chainId == BraveWallet.ZCashTestnet { + if respectHiddenNetworksPreference { + return Preferences.Wallet.isZcashTestnetEnabled.value + && !allHiddenChainIds.contains(network.chainId) + } else { + return Preferences.Wallet.isZcashTestnetEnabled.value + } } if respectHiddenNetworksPreference { // filter out hidden networks diff --git a/ios/brave-ios/Sources/BraveWallet/Extensions/ZCashWalletServiceExtensions.swift b/ios/brave-ios/Sources/BraveWallet/Extensions/ZCashWalletServiceExtensions.swift new file mode 100644 index 000000000000..08809ffb01d4 --- /dev/null +++ b/ios/brave-ios/Sources/BraveWallet/Extensions/ZCashWalletServiceExtensions.swift @@ -0,0 +1,56 @@ +// Copyright 2025 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +import BraveCore + +extension BraveWalletZCashWalletService { + /// - Parameters: + /// - accounts: A list of `BraveWallet.AccountInfo` + /// + /// - Returns: A dictionary of `BraveWallet.AccountInfo.uniqueKey` as key and associated `BraveWallet.ZCashAccountInfo` + func fetchZcashAccountInfo( + accounts: [BraveWallet.AccountInfo] + ) async -> [String: BraveWallet.ZCashAccountInfo] { + await withTaskGroup( + of: [String: BraveWallet.ZCashAccountInfo].self, + body: { group in + for account in accounts { + group.addTask { + guard account.coin == .zec, account.address.isEmpty else { return [:] } + if let zcashAccount = await self.zCashAccountInfo(accountId: account.accountId) { + return [account.accountId.uniqueKey: zcashAccount] + } + return [:] + } + } + return await group.reduce( + into: [String: BraveWallet.ZCashAccountInfo](), + { partialResult, new in + partialResult.merge(with: new) + } + ) + } + ) + } + + /// Fetch all bitcoin balance types (total, available, pending) for a given account. + /// - Parameters: + /// - accountId: The `BraveWallet.AccountId` for the account + /// - Returns: The BTC balances of the given `BraveWallet.AccountId` in `Double`; Will return a nil if there is an issue fetching balance. + func fetchZECTransparentBalances( + networkId: String, + accountId: BraveWallet.AccountId + ) async -> Double? { + let (zecBalance, _) = await self.balance(networkId: networkId, accountId: accountId) + + guard + let balance = zecBalance?.transparentBalance + else { + return nil + } + + return Double(balance) / 100_000_000 + } +} diff --git a/ios/brave-ios/Sources/BraveWallet/Preview Content/MockContent.swift b/ios/brave-ios/Sources/BraveWallet/Preview Content/MockContent.swift index 65e4aa2c1884..a0ca1b4610a3 100644 --- a/ios/brave-ios/Sources/BraveWallet/Preview Content/MockContent.swift +++ b/ios/brave-ios/Sources/BraveWallet/Preview Content/MockContent.swift @@ -199,6 +199,27 @@ extension BraveWallet.BlockchainToken { coin: .btc, isShielded: false ) + + static let mockZECToken: BraveWallet.BlockchainToken = .init( + contractAddress: "", + name: "Zcash", + logo: "", + isCompressed: false, + isErc20: false, + isErc721: false, + isErc1155: false, + splTokenProgram: .unsupported, + isNft: false, + isSpam: false, + symbol: "ZEC", + decimals: 8, + visible: false, + tokenId: "", + coingeckoId: "", + chainId: BraveWallet.ZCashMainnet, + coin: .zec, + isShielded: false + ) } extension BraveWallet.AccountInfo { diff --git a/ios/brave-ios/Sources/BraveWallet/Preview Content/MockJsonRpcService.swift b/ios/brave-ios/Sources/BraveWallet/Preview Content/MockJsonRpcService.swift index 4bb46a10566f..22d6145ac296 100644 --- a/ios/brave-ios/Sources/BraveWallet/Preview Content/MockJsonRpcService.swift +++ b/ios/brave-ios/Sources/BraveWallet/Preview Content/MockJsonRpcService.swift @@ -17,6 +17,7 @@ class MockJsonRpcService: BraveWallet.TestJsonRpcService { .mockSolana, .mockSolanaTestnet, .mockFilecoinMainnet, .mockFilecoinTestnet, .mockBitcoinMainnet, .mockBitcoinTestnet, + .mockZcashMainnet, .mockZcashTestnet, ] var selectedNetworkForCoin: [BraveWallet.CoinType: BraveWallet.NetworkInfo] = [ @@ -24,6 +25,7 @@ class MockJsonRpcService: BraveWallet.TestJsonRpcService { .sol: .mockSolana, .fil: .mockFilecoinMainnet, .btc: .mockBitcoinMainnet, + .zec: .mockZcashMainnet, ] var allCustomNetworks: [BraveWallet.NetworkInfo] = [] @@ -257,6 +259,32 @@ extension BraveWallet.NetworkInfo { coin: .btc, supportedKeyrings: [BraveWallet.KeyringId.bitcoin84Testnet.rawValue].map(NSNumber.init(value:)) ) + static let mockZcashMainnet: BraveWallet.NetworkInfo = .init( + chainId: BraveWallet.ZCashMainnet, + chainName: "Zcash Mainnet", + blockExplorerUrls: ["https://zcashblockexplorer.com/"], + iconUrls: [], + activeRpcEndpointIndex: 0, + rpcEndpoints: [URL(string: "https://mainnet.lightwalletd.com/")!], + symbol: "ZEC", + symbolName: "Zcash", + decimals: 8, + coin: .zec, + supportedKeyrings: [BraveWallet.KeyringId.zCashMainnet.rawValue].map(NSNumber.init(value:)) + ) + static let mockZcashTestnet: BraveWallet.NetworkInfo = .init( + chainId: BraveWallet.ZCashTestnet, + chainName: "Zcash Testnet", + blockExplorerUrls: ["https://testnet.zcashblockexplorer.com/"], + iconUrls: [], + activeRpcEndpointIndex: 0, + rpcEndpoints: [URL(string: "https://testnet.lightwalletd.com")!], + symbol: "ZEC", + symbolName: "Zcash", + decimals: 8, + coin: .zec, + supportedKeyrings: [BraveWallet.KeyringId.zCashTestnet.rawValue].map(NSNumber.init(value:)) + ) } #endif diff --git a/ios/brave-ios/Sources/BraveWallet/Preview Content/MockKeyringService.swift b/ios/brave-ios/Sources/BraveWallet/Preview Content/MockKeyringService.swift index c6d2a9b9e3e8..01405c8bb5b7 100644 --- a/ios/brave-ios/Sources/BraveWallet/Preview Content/MockKeyringService.swift +++ b/ios/brave-ios/Sources/BraveWallet/Preview Content/MockKeyringService.swift @@ -526,6 +526,34 @@ extension BraveWallet.AccountInfo { name: "Bitcoin Testnet 1", hardware: nil ) + + static let mockZcashAccount: BraveWallet.AccountInfo = .init( + accountId: .init( + coin: .zec, + keyringId: BraveWallet.KeyringId.zCashMainnet, + kind: .derived, + address: "", + accountIndex: 0, + uniqueKey: "6_0_0_0" + ), + address: "", + name: "Zcash Account 1", + hardware: nil + ) + + static let mockZcashTestnetAccount: BraveWallet.AccountInfo = .init( + accountId: .init( + coin: .zec, + keyringId: BraveWallet.KeyringId.zCashTestnet, + kind: .derived, + address: "", + accountIndex: 0, + uniqueKey: "7_0_0_0" + ), + address: "", + name: "Zcash Testnet 1", + hardware: nil + ) } extension BraveWallet.AllAccountsInfo { diff --git a/ios/brave-ios/Sources/BraveWallet/Preview Content/MockStores.swift b/ios/brave-ios/Sources/BraveWallet/Preview Content/MockStores.swift index 3e6fdd60a1ec..4fbc66effc86 100644 --- a/ios/brave-ios/Sources/BraveWallet/Preview Content/MockStores.swift +++ b/ios/brave-ios/Sources/BraveWallet/Preview Content/MockStores.swift @@ -24,7 +24,8 @@ extension WalletStore { solTxManagerProxy: BraveWallet.TestSolanaTxManagerProxy.previewProxy, ipfsApi: TestIpfsAPI(), walletP3A: TestBraveWalletP3A(), - bitcoinWalletService: BraveWallet.TestBitcoinWalletService.previewBitcoinWalletService + bitcoinWalletService: BraveWallet.TestBitcoinWalletService(), + zcashWalletService: BraveWallet.TestZCashWalletService() ) } } @@ -43,7 +44,8 @@ extension CryptoStore { solTxManagerProxy: BraveWallet.TestSolanaTxManagerProxy.previewProxy, ipfsApi: TestIpfsAPI(), walletP3A: TestBraveWalletP3A(), - bitcoinWalletService: BraveWallet.TestBitcoinWalletService.previewBitcoinWalletService + bitcoinWalletService: BraveWallet.TestBitcoinWalletService.previewBitcoinWalletService, + zcashWalletService: BraveWallet.TestZCashWalletService.previewZCashWalletService ) } } @@ -118,6 +120,7 @@ extension BuyTokenStore { walletService: BraveWallet.TestBraveWalletService.previewWalletService, assetRatioService: BraveWallet.TestAssetRatioService.previewAssetRatioService, bitcoinWalletService: BraveWallet.TestBitcoinWalletService.previewBitcoinWalletService, + zcashWalletService: BraveWallet.TestZCashWalletService.previewZCashWalletService, prefilledToken: .previewToken ) } @@ -135,6 +138,7 @@ extension SendTokenStore { ethTxManagerProxy: MockEthTxManagerProxy(), solTxManagerProxy: BraveWallet.TestSolanaTxManagerProxy.previewProxy, bitcoinWalletService: BraveWallet.TestBitcoinWalletService.previewBitcoinWalletService, + zcashWalletService: BraveWallet.TestZCashWalletService.previewZCashWalletService, prefilledToken: .previewToken, ipfsApi: TestIpfsAPI(), userAssetManager: TestableWalletUserAssetManager() @@ -155,6 +159,7 @@ extension AssetDetailStore { ipfsApi: TestIpfsAPI(), swapService: MockSwapService(), bitcoinWalletService: BraveWallet.TestBitcoinWalletService.previewBitcoinWalletService, + zcashWalletService: BraveWallet.TestZCashWalletService.previewZCashWalletService, userAssetManager: TestableWalletUserAssetManager(), assetDetailType: .blockchainToken(.previewToken) ) @@ -207,6 +212,7 @@ extension AccountActivityStore { solTxManagerProxy: BraveWallet.TestSolanaTxManagerProxy.previewProxy, ipfsApi: TestIpfsAPI(), bitcoinWalletService: BraveWallet.TestBitcoinWalletService.previewBitcoinWalletService, + zcashWalletService: BraveWallet.TestZCashWalletService.previewZCashWalletService, userAssetManager: TestableWalletUserAssetManager() ) } @@ -289,6 +295,7 @@ extension AccountsStore { walletService: MockBraveWalletService(), assetRatioService: MockAssetRatioService(), bitcoinWalletService: BraveWallet.TestBitcoinWalletService.previewBitcoinWalletService, + zcashWalletService: BraveWallet.TestZCashWalletService.previewZCashWalletService, userAssetManager: TestableWalletUserAssetManager() ) } @@ -304,7 +311,8 @@ extension DepositTokenStore { prefilledToken: nil, prefilledAccount: nil, userAssetManager: TestableWalletUserAssetManager(), - bitcoinWalletService: BraveWallet.TestBitcoinWalletService.previewBitcoinWalletService + bitcoinWalletService: BraveWallet.TestBitcoinWalletService.previewBitcoinWalletService, + zcashWalletService: BraveWallet.TestZCashWalletService.previewZCashWalletService ) } } @@ -366,4 +374,10 @@ extension BraveWallet.TestBitcoinWalletService { } } +extension BraveWallet.TestZCashWalletService { + static var previewZCashWalletService: BraveWallet.TestZCashWalletService { + return BraveWallet.TestZCashWalletService() + } +} + #endif diff --git a/ios/brave-ios/Sources/BraveWallet/Settings/NetworkListView.swift b/ios/brave-ios/Sources/BraveWallet/Settings/NetworkListView.swift index a3461f3e2863..1e01f520d20c 100644 --- a/ios/brave-ios/Sources/BraveWallet/Settings/NetworkListView.swift +++ b/ios/brave-ios/Sources/BraveWallet/Settings/NetworkListView.swift @@ -37,6 +37,8 @@ struct NetworkListView: View { networkStore.allChains.filter { if $0.chainId == BraveWallet.BitcoinTestnet { return Preferences.Wallet.isBitcoinTestnetEnabled.value + } else if $0.chainId == BraveWallet.ZCashTestnet { + return Preferences.Wallet.isZcashTestnetEnabled.value } return true } diff --git a/ios/brave-ios/Sources/BraveWallet/WalletConstants.swift b/ios/brave-ios/Sources/BraveWallet/WalletConstants.swift index 9d8602753c08..0646978128fa 100644 --- a/ios/brave-ios/Sources/BraveWallet/WalletConstants.swift +++ b/ios/brave-ios/Sources/BraveWallet/WalletConstants.swift @@ -75,6 +75,7 @@ public struct WalletConstants { BraveWallet.FilecoinTestnet, BraveWallet.FilecoinEthereumTestnetChainId, BraveWallet.BitcoinTestnet, + BraveWallet.ZCashTestnet, ] /// Primary network chain ids @@ -83,6 +84,7 @@ public struct WalletConstants { BraveWallet.MainnetChainId, BraveWallet.FilecoinMainnet, BraveWallet.BitcoinMainnet, + BraveWallet.ZCashMainnet, ] public enum SupportedCoinTypesMode { @@ -100,28 +102,32 @@ public struct WalletConstants { public static func supportedCoinTypes( _ mode: SupportedCoinTypesMode = .general ) -> OrderedSet { + var result = OrderedSet() switch mode { case .general: #if DEBUG - // Only enable .btc for unit tests. + // Only enable .btc and .zec for unit tests. // Local Debug build need to // 1. Remove this check // 2. Enable bitcoin feature via build argument if isUnitTesting { - return [.eth, .sol, .fil, .btc] + return [.eth, .sol, .fil, .btc, .zec] } #endif - // Any non-debug build will check bitcoin feature flag from core + // Any non-debug build will check bitcoin & zcash feature flag from core // TF public build can use BraveCore Switches in Browser Settings, // Debug section in order to enable Bitcoin. + result = [.eth, .sol, .fil] if FeatureList.kBraveWalletBitcoinFeature.enabled { - return [.eth, .sol, .fil, .btc] - } else { - return [.eth, .sol, .fil] + result.append(.btc) + } + if FeatureList.kBraveWalletZCashFeature.enabled { + result.append(.zec) } case .dapps: return [.eth, .sol] } + return result } /// All of currently supported `OnRampProvider`s. diff --git a/ios/brave-ios/Sources/BraveWallet/WalletPreferences.swift b/ios/brave-ios/Sources/BraveWallet/WalletPreferences.swift index e499d6ae765a..6eb8ad1f4270 100644 --- a/ios/brave-ios/Sources/BraveWallet/WalletPreferences.swift +++ b/ios/brave-ios/Sources/BraveWallet/WalletPreferences.swift @@ -140,6 +140,12 @@ extension Preferences { default: false ) + /// Used for Debug section for anyone wants to test with ZCash Testnet network or account + public static let isZcashTestnetEnabled = Option( + key: "wallet.is-zcash-testnet-enabled", + default: false + ) + /// Used to track whether to migrate `account.address` to `account.accountId.uniqueKey` (`account.id`) static let migrateCacheKeyCompleted = Option( key: "wallet.migrate-cache-key-completed", diff --git a/ios/brave-ios/Sources/BraveWallet/WalletStrings.swift b/ios/brave-ios/Sources/BraveWallet/WalletStrings.swift index 759f0474c43a..82edd2d51fa0 100644 --- a/ios/brave-ios/Sources/BraveWallet/WalletStrings.swift +++ b/ios/brave-ios/Sources/BraveWallet/WalletStrings.swift @@ -446,6 +446,13 @@ extension Strings { value: "Bitcoin", comment: "A description of an Bitcoin account, displayed in Accounts tab." ) + public static let zecAccountDescription = NSLocalizedString( + "wallet.zecAccountDescription", + tableName: "BraveWallet", + bundle: .module, + value: "ZCash", + comment: "A description of an ZCash account, displayed in Accounts tab." + ) public static let exportButtonTitle = NSLocalizedString( "wallet.exportButtonTitle", tableName: "BraveWallet", @@ -3421,6 +3428,13 @@ extension Strings { value: "Bitcoin", comment: "One of the coin types for users to create an account to store BTC assets" ) + public static let coinTypeZCash = NSLocalizedString( + "wallet.coinTypeZCash", + tableName: "BraveWallet", + bundle: .module, + value: "ZCash", + comment: "One of the coin types for users to create an account to store ZEC assets" + ) public static let coinTypeEthereumDescription = NSLocalizedString( "wallet.coinTypeEthereumDescription", tableName: "BraveWallet", @@ -3450,6 +3464,13 @@ extension Strings { value: "Store BTC asset", comment: "A description for Bitcoin coin type." ) + public static let coinTypeZCashDescription = NSLocalizedString( + "wallet.coinTypeZCashDescription", + tableName: "BraveWallet", + bundle: .module, + value: "Store ZEC asset", + comment: "A description for ZCash coin type." + ) public static let coinTypeUnknown = NSLocalizedString( "wallet.coinTypeUnknown", tableName: "BraveWallet", diff --git a/ios/brave-ios/Sources/BraveWallet/WalletUserAssetManager.swift b/ios/brave-ios/Sources/BraveWallet/WalletUserAssetManager.swift index da4c1894a4ef..e008a51c9101 100644 --- a/ios/brave-ios/Sources/BraveWallet/WalletUserAssetManager.swift +++ b/ios/brave-ios/Sources/BraveWallet/WalletUserAssetManager.swift @@ -92,6 +92,7 @@ public class WalletUserAssetManager: WalletUserAssetManagerType, WalletObserverS private let walletService: BraveWalletBraveWalletService private let txService: BraveWalletTxService private let bitcoinWalletService: BraveWalletBitcoinWalletService + private let zcashWalletService: BraveWalletZCashWalletService private var keyringServiceObserver: KeyringServiceObserver? private var txServiceObserver: TxServiceObserver? @@ -110,13 +111,15 @@ public class WalletUserAssetManager: WalletUserAssetManagerType, WalletObserverS rpcService: BraveWalletJsonRpcService, walletService: BraveWalletBraveWalletService, txService: BraveWalletTxService, - bitcoinWalletService: BraveWalletBitcoinWalletService + bitcoinWalletService: BraveWalletBitcoinWalletService, + zcashWalletService: BraveWalletZCashWalletService ) { self.keyringService = keyringService self.rpcService = rpcService self.walletService = walletService self.txService = txService self.bitcoinWalletService = bitcoinWalletService + self.zcashWalletService = zcashWalletService setupObservers() } @@ -542,7 +545,7 @@ public class WalletUserAssetManager: WalletUserAssetManagerType, WalletObserverS ) await withTaskGroup( of: Void.self, - body: { @MainActor [rpcService, bitcoinWalletService] group in + body: { @MainActor [rpcService, bitcoinWalletService, zcashWalletService] group in for account in accounts { guard !Task.isCancelled else { return } group.addTask { @MainActor in @@ -562,6 +565,22 @@ public class WalletUserAssetManager: WalletUserAssetManagerType, WalletObserverS account: account.id ) } + } else if account.coin == .zec { + let networkAssets = allUserAssets.first { + $0.network.supportedKeyrings.contains(account.keyringId.rawValue as NSNumber) + } + if let zec = networkAssets?.tokens.first, + let zecBalance = await zcashWalletService.fetchZECTransparentBalances( + networkId: zec.chainId, + accountId: account.accountId + ) + { + await WalletUserAssetBalance.updateBalance( + for: zec, + balance: String(zecBalance), + account: account.id + ) + } } else { let allTokenBalanceForAccount = await rpcService.fetchBalancesForTokens( account: account, diff --git a/ios/brave-ios/Tests/BraveWalletTests/AccountActivityStoreTests.swift b/ios/brave-ios/Tests/BraveWalletTests/AccountActivityStoreTests.swift index c70873e50790..14186ab84732 100644 --- a/ios/brave-ios/Tests/BraveWalletTests/AccountActivityStoreTests.swift +++ b/ios/brave-ios/Tests/BraveWalletTests/AccountActivityStoreTests.swift @@ -36,6 +36,12 @@ class AccountActivityStoreTests: XCTestCase { price: "4.00", assetTimeframeChange: "-57.23" ), + .init( + fromAsset: BraveWallet.BlockchainToken.mockZECToken.assetRatioId.lowercased(), + toAsset: "usd", + price: "$31.10", + assetTimeframeChange: "0.12" + ), ] let solTestnetBalance: UInt64 = 1_000_000_000 // 1 SOL let solTestnetDecimalBalance: Double = 1 // 1 SOL @@ -48,6 +54,7 @@ class AccountActivityStoreTests: XCTestCase { mockSplTokenBalances: [String: String] = [:], // [tokenMintAddress: balance], mockFilBalance: String = "", mockFilTestnetBalance: String = "", + mockZecBalance: String = "", transactions: [BraveWallet.TransactionInfo] ) -> ( BraveWallet.TestKeyringService, BraveWallet.TestJsonRpcService, @@ -55,7 +62,8 @@ class AccountActivityStoreTests: XCTestCase { BraveWallet.TestAssetRatioService, BraveWallet.TestSwapService, BraveWallet.TestTxService, BraveWallet.TestSolanaTxManagerProxy, IpfsAPI, - BraveWallet.TestBitcoinWalletService + BraveWallet.TestBitcoinWalletService, + BraveWallet.TestZCashWalletService ) { let keyringService = BraveWallet.TestKeyringService() keyringService._addObserver = { _ in } @@ -162,9 +170,23 @@ class AccountActivityStoreTests: XCTestCase { $1(.init(totalBalance: 1000, availableBalance: 1000, pendingBalance: 0, balances: [:]), nil) } + let zcashWalletService = BraveWallet.TestZCashWalletService() + zcashWalletService._balance = { _, _, completion in + completion( + .init( + totalBalance: UInt64(mockZecBalance) ?? 0, + transparentBalance: UInt64(mockZecBalance) ?? 0, + shieldedBalance: 0, + shieldedPendingBalance: 0, + balances: [:] + ), + nil + ) + } + return ( keyringService, rpcService, walletService, blockchainRegistry, assetRatioService, - swapService, txService, solTxManagerProxy, ipfsApi, bitcoinWalletService + swapService, txService, solTxManagerProxy, ipfsApi, bitcoinWalletService, zcashWalletService ) } @@ -214,7 +236,8 @@ class AccountActivityStoreTests: XCTestCase { ) let ( keyringService, rpcService, walletService, blockchainRegistry, assetRatioService, - swapService, txService, solTxManagerProxy, ipfsApi, bitcoinWalletService + swapService, txService, solTxManagerProxy, ipfsApi, bitcoinWalletService, + zcashWalletService ) = setupServices( mockEthBalanceWei: mockEthBalanceWei, mockERC20BalanceWei: mockERC20BalanceWei, @@ -264,6 +287,7 @@ class AccountActivityStoreTests: XCTestCase { solTxManagerProxy: solTxManagerProxy, ipfsApi: ipfsApi, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, userAssetManager: mockAssetManager ) @@ -414,7 +438,7 @@ class AccountActivityStoreTests: XCTestCase { let ( keyringService, rpcService, walletService, blockchainRegistry, assetRatioService, - swapService, txService, solTxManagerProxy, ipfsApi, bitcoinWalletService + swapService, txService, solTxManagerProxy, ipfsApi, bitcoinWalletService, zcashWalletService ) = setupServices( mockNFTBalances: [mockSolanaNFTTokenIdentifier: mockSolanaNFTTokenBalance], mockLamportBalance: mockLamportBalance, @@ -464,6 +488,7 @@ class AccountActivityStoreTests: XCTestCase { solTxManagerProxy: solTxManagerProxy, ipfsApi: ipfsApi, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, userAssetManager: mockAssetManager ) @@ -636,7 +661,7 @@ class AccountActivityStoreTests: XCTestCase { let ( keyringService, rpcService, walletService, blockchainRegistry, assetRatioService, - swapService, txService, solTxManagerProxy, ipfsApi, bitcoinWalletService + swapService, txService, solTxManagerProxy, ipfsApi, bitcoinWalletService, zcashWalletService ) = setupServices( mockFilBalance: mockFilDecimalBalanceInWei, mockFilTestnetBalance: mockFilTestnetDecimalBalanceInWei, @@ -680,6 +705,7 @@ class AccountActivityStoreTests: XCTestCase { solTxManagerProxy: solTxManagerProxy, ipfsApi: ipfsApi, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, userAssetManager: mockAssetManager ) @@ -754,4 +780,85 @@ class AccountActivityStoreTests: XCTestCase { XCTAssertNil(error) } } + + func testUpdateZcashAccount() { + let account: BraveWallet.AccountInfo = .mockZcashAccount + + let formatter = WalletAmountFormatter(decimalFormatStyle: .decimals(precision: 18)) + let mockZecDecimalBalance: Double = 0.1 + let zecBalanceInSatoshi = + formatter.weiString( + from: mockZecDecimalBalance, + radix: .decimal, + decimals: Int(BraveWallet.BlockchainToken.mockZECToken.decimals) + ) ?? "" + + let ( + keyringService, rpcService, walletService, blockchainRegistry, assetRatioService, + swapService, txService, solTxManagerProxy, ipfsApi, bitcoinWalletService, zcashWalletService + ) = setupServices( + mockZecBalance: zecBalanceInSatoshi, + transactions: [] + ) + + let mockAssetManager = TestableWalletUserAssetManager() + mockAssetManager._getAllUserAssetsInNetworkAssets = { _, _ in + [ + NetworkAssets( + network: .mockZcashMainnet, + tokens: [ + BraveWallet.NetworkInfo.mockZcashMainnet.nativeToken.copy(asVisibleAsset: true) + ], + sortOrder: 0 + ) + ] + } + + let accountActivityStore = AccountActivityStore( + account: account, + isWalletPanel: false, + keyringService: keyringService, + walletService: walletService, + rpcService: rpcService, + assetRatioService: assetRatioService, + swapService: swapService, + txService: txService, + blockchainRegistry: blockchainRegistry, + solTxManagerProxy: solTxManagerProxy, + ipfsApi: ipfsApi, + bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, + userAssetManager: mockAssetManager + ) + + let userAssetsExpectation = expectation(description: "accountActivityStore-userAssets") + accountActivityStore.$userAssets + .dropFirst() + .collect(3) + .sink { userAssets in + defer { userAssetsExpectation.fulfill() } + guard let lastUpdatedAssets = userAssets.last else { + XCTFail("Unexpected test result") + return + } + XCTAssertEqual(lastUpdatedAssets.count, 1) + + XCTAssertEqual( + lastUpdatedAssets[safe: 0]?.token.symbol, + BraveWallet.NetworkInfo.mockZcashMainnet.nativeToken.symbol + ) + XCTAssertEqual(lastUpdatedAssets[safe: 0]?.totalBalance, mockZecDecimalBalance) + XCTAssertEqual( + lastUpdatedAssets[safe: 0]?.price, + self.mockAssetPrices[safe: 5]?.price ?? "" + ) + } + .store(in: &cancellables) + + accountActivityStore.update() + + waitForExpectations(timeout: 1) { error in + XCTAssertNil(error) + } + } } diff --git a/ios/brave-ios/Tests/BraveWalletTests/AccountsStoreTests.swift b/ios/brave-ios/Tests/BraveWalletTests/AccountsStoreTests.swift index d18653d0be97..455afcc161e7 100644 --- a/ios/brave-ios/Tests/BraveWalletTests/AccountsStoreTests.swift +++ b/ios/brave-ios/Tests/BraveWalletTests/AccountsStoreTests.swift @@ -15,6 +15,7 @@ import XCTest override class func tearDown() { super.tearDown() Preferences.Wallet.isBitcoinTestnetEnabled.reset() + Preferences.Wallet.isZcashTestnetEnabled.reset() } private var cancellables: Set = .init() @@ -31,6 +32,8 @@ import XCTest let filTestnetAccount: BraveWallet.AccountInfo = .mockFilTestnetAccount let btcAccount1: BraveWallet.AccountInfo = .mockBtcAccount let btcTestnetAccount: BraveWallet.AccountInfo = .mockBtcTestnetAccount + let zcashAccount1: BraveWallet.AccountInfo = .mockZcashAccount + let zcashTestnetAccount: BraveWallet.AccountInfo = .mockZcashTestnetAccount let mockETHBalanceAccount1: Double = 0.896 let mockETHPrice: String = "3059.99" @@ -91,17 +94,33 @@ import XCTest price: mockBTCPrice, assetTimeframeChange: "-57.23" ) + let mockZECBalanceAccount1: Double = 0 + let mockZECTestnetBalanceAccount1: Double = 0 + let mockZECPrice: String = "36.46" + lazy var mockZECAssetPrice: BraveWallet.AssetPrice = .init( + fromAsset: "zec", + toAsset: "usd", + price: mockZECPrice, + assetTimeframeChange: "4.32" + ) let btcMainnetTokens: [BraveWallet.BlockchainToken] = [ BraveWallet.NetworkInfo.mockBitcoinMainnet.nativeToken ] let btcTestnetTokens: [BraveWallet.BlockchainToken] = [ BraveWallet.NetworkInfo.mockBitcoinTestnet.nativeToken ] + let zcashMainnetTokens: [BraveWallet.BlockchainToken] = [ + BraveWallet.NetworkInfo.mockZcashMainnet.nativeToken + ] + let zcashTestnetTokens: [BraveWallet.BlockchainToken] = [ + BraveWallet.NetworkInfo.mockZcashTestnet.nativeToken + ] let formatter = WalletAmountFormatter(decimalFormatStyle: .decimals(precision: 18)) - func updateHelper(bitcoinTestnetEnabled: Bool) async { - Preferences.Wallet.isBitcoinTestnetEnabled.value = bitcoinTestnetEnabled + func updateHelper(bitcoinAndZcashTestnetEnabled: Bool) async { + Preferences.Wallet.isBitcoinTestnetEnabled.value = bitcoinAndZcashTestnetEnabled + Preferences.Wallet.isZcashTestnetEnabled.value = bitcoinAndZcashTestnetEnabled let ethBalanceWei = formatter.weiString( from: mockETHBalanceAccount1, @@ -145,6 +164,18 @@ import XCTest radix: .decimal, decimals: Int(BraveWallet.NetworkInfo.mockBitcoinTestnet.nativeToken.decimals) ) ?? "" + let mockZcashBalanceInWei = + formatter.weiString( + from: mockZECBalanceAccount1, + radix: .decimal, + decimals: Int(BraveWallet.NetworkInfo.mockZcashMainnet.nativeToken.decimals) + ) ?? "" + let mockZcashTestnetBalanceInWei = + formatter.weiString( + from: mockZECTestnetBalanceAccount1, + radix: .decimal, + decimals: Int(BraveWallet.NetworkInfo.mockZcashTestnet.nativeToken.decimals) + ) ?? "" let keyringService = BraveWallet.TestKeyringService() keyringService._addObserver = { _ in } @@ -156,6 +187,7 @@ import XCTest self.solAccount1, self.filAccount1, self.filTestnetAccount, self.btcAccount1, self.btcTestnetAccount, + self.zcashAccount1, self.zcashTestnetAccount, ], selectedAccount: self.ethAccount1, ethDappSelectedAccount: self.ethAccount1, @@ -189,6 +221,14 @@ import XCTest chainId == BraveWallet.BitcoinTestnet { completion(mockBtcTestnetBalanceInWei, .success, "") + } else if coin == .zec, + chainId == BraveWallet.ZCashMainnet + { + completion(mockBtcBalanceInWei, .success, "") + } else if coin == .zec, + chainId == BraveWallet.ZCashTestnet + { + completion(mockBtcTestnetBalanceInWei, .success, "") } else { completion("", .internalError, "") } @@ -223,6 +263,7 @@ import XCTest self.mockSOLAssetPrice, self.mockFILAssetPrice, self.mockBTCAssetPrice, + self.mockZECAssetPrice, ] ) } @@ -260,6 +301,16 @@ import XCTest tokens: self.btcTestnetTokens, sortOrder: 5 ), + NetworkAssets( + network: .mockZcashMainnet, + tokens: self.zcashMainnetTokens, + sortOrder: 6 + ), + NetworkAssets( + network: .mockZcashTestnet, + tokens: self.zcashTestnetTokens, + sortOrder: 7 + ), ].filter { networkAsset in networks.contains(where: { $0.chainId == networkAsset.network.chainId }) } @@ -291,6 +342,34 @@ import XCTest ) } } + let zecMainnetBalance: UInt64 = 100_000_0 + let zecTestnetBalance: UInt64 = 100_000 + let zcashWalletService = BraveWallet.TestZCashWalletService() + zcashWalletService._balance = { chainId, accountId, completion in + if chainId == BraveWallet.ZCashMainnet { + completion( + .init( + totalBalance: zecMainnetBalance, + transparentBalance: zecMainnetBalance, + shieldedBalance: 0, + shieldedPendingBalance: 0, + balances: [:] + ), + nil + ) + } else { + completion( + .init( + totalBalance: zecTestnetBalance, + transparentBalance: zecTestnetBalance, + shieldedBalance: 0, + shieldedPendingBalance: 0, + balances: [:] + ), + nil + ) + } + } let store = AccountsStore( keyringService: keyringService, @@ -298,6 +377,7 @@ import XCTest walletService: walletService, assetRatioService: assetRatioService, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, userAssetManager: userAssetManager ) @@ -311,7 +391,10 @@ import XCTest XCTFail("Expected account details models") return } - let accountNumber = bitcoinTestnetEnabled ? 7 : 6 + var accountNumber = 9 + if !bitcoinAndZcashTestnetEnabled { + accountNumber -= 2 + } XCTAssertEqual(accountDetails.count, accountNumber) XCTAssertEqual(accountDetails[safe: 0]?.account, self.ethAccount1) @@ -342,10 +425,22 @@ import XCTest XCTAssertEqual(accountDetails[safe: 5]?.tokensWithBalance, self.btcMainnetTokens) XCTAssertEqual(accountDetails[safe: 5]?.totalBalanceFiat, "$0.657") - if bitcoinTestnetEnabled { + if bitcoinAndZcashTestnetEnabled { XCTAssertEqual(accountDetails[safe: 6]?.account, self.btcTestnetAccount) XCTAssertEqual(accountDetails[safe: 6]?.tokensWithBalance, self.btcTestnetTokens) XCTAssertEqual(accountDetails[safe: 6]?.totalBalanceFiat, "$6.573") + + XCTAssertEqual(accountDetails[safe: 7]?.account, self.zcashAccount1) + XCTAssertEqual(accountDetails[safe: 7]?.tokensWithBalance, self.zcashMainnetTokens) + XCTAssertEqual(accountDetails[safe: 7]?.totalBalanceFiat, "$0.365") + + XCTAssertEqual(accountDetails[safe: 8]?.account, self.zcashTestnetAccount) + XCTAssertEqual(accountDetails[safe: 8]?.tokensWithBalance, self.zcashTestnetTokens) + XCTAssertEqual(accountDetails[safe: 8]?.totalBalanceFiat, "$0.0365") + } else { + XCTAssertEqual(accountDetails[safe: 6]?.account, self.zcashAccount1) + XCTAssertEqual(accountDetails[safe: 6]?.tokensWithBalance, self.zcashMainnetTokens) + XCTAssertEqual(accountDetails[safe: 6]?.totalBalanceFiat, "$0.365") } }.store(in: &cancellables) @@ -355,10 +450,10 @@ import XCTest } func testUpdate() async { - await updateHelper(bitcoinTestnetEnabled: false) + await updateHelper(bitcoinAndZcashTestnetEnabled: false) } func testUpdateBitcoinTestnet() async { - await updateHelper(bitcoinTestnetEnabled: true) + await updateHelper(bitcoinAndZcashTestnetEnabled: true) } } diff --git a/ios/brave-ios/Tests/BraveWalletTests/AssetDetailStoreTests.swift b/ios/brave-ios/Tests/BraveWalletTests/AssetDetailStoreTests.swift index 421c8a6c70b4..82ac3b082d97 100644 --- a/ios/brave-ios/Tests/BraveWalletTests/AssetDetailStoreTests.swift +++ b/ios/brave-ios/Tests/BraveWalletTests/AssetDetailStoreTests.swift @@ -96,6 +96,7 @@ class AssetDetailStoreTests: XCTestCase { } let bitcoinWalletService = BraveWallet.TestBitcoinWalletService() + let zcashWalletService = BraveWallet.TestZCashWalletService() // setup store let store = AssetDetailStore( @@ -109,6 +110,7 @@ class AssetDetailStoreTests: XCTestCase { ipfsApi: TestIpfsAPI(), swapService: swapService, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, userAssetManager: mockAssetManager, assetDetailType: .blockchainToken(.previewToken) ) @@ -319,6 +321,7 @@ class AssetDetailStoreTests: XCTestCase { nil ) } + let zcashWalletService = BraveWallet.TestZCashWalletService() // setup store let store = AssetDetailStore( @@ -332,6 +335,7 @@ class AssetDetailStoreTests: XCTestCase { ipfsApi: TestIpfsAPI(), swapService: swapService, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, userAssetManager: mockAssetManager, assetDetailType: .blockchainToken(.mockBTCToken) ) @@ -508,6 +512,7 @@ class AssetDetailStoreTests: XCTestCase { let swapService = BraveWallet.TestSwapService() let bitcoinWalletService = BraveWallet.TestBitcoinWalletService() + let zcashWalletService = BraveWallet.TestZCashWalletService() // setup store var store = AssetDetailStore( @@ -521,6 +526,7 @@ class AssetDetailStoreTests: XCTestCase { ipfsApi: TestIpfsAPI(), swapService: swapService, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, userAssetManager: mockAssetManager, assetDetailType: .coinMarket(.mockCoinMarketBitcoin) ) @@ -633,6 +639,7 @@ class AssetDetailStoreTests: XCTestCase { ipfsApi: TestIpfsAPI(), swapService: swapService, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, userAssetManager: mockAssetManager, assetDetailType: .coinMarket(.mockCoinMarketEth) ) diff --git a/ios/brave-ios/Tests/BraveWalletTests/BuyTokenStoreTest.swift b/ios/brave-ios/Tests/BraveWalletTests/BuyTokenStoreTest.swift index f50a0787fc92..5be40e805345 100644 --- a/ios/brave-ios/Tests/BraveWalletTests/BuyTokenStoreTest.swift +++ b/ios/brave-ios/Tests/BraveWalletTests/BuyTokenStoreTest.swift @@ -19,7 +19,8 @@ class BuyTokenStoreTests: XCTestCase { BraveWallet.TestBlockchainRegistry, BraveWallet.TestKeyringService, BraveWallet.TestJsonRpcService, BraveWallet.TestBraveWalletService, BraveWallet.TestAssetRatioService, - BraveWallet.TestBitcoinWalletService + BraveWallet.TestBitcoinWalletService, + BraveWallet.TestZCashWalletService ) { let mockTokenList: [BraveWallet.BlockchainToken] = [ .init( @@ -179,10 +180,13 @@ class BuyTokenStoreTests: XCTestCase { let bitcoinWalletService = BraveWallet.TestBitcoinWalletService() + let zcashWalletService = BraveWallet.TestZCashWalletService() + return ( blockchainRegistry, keyringService, rpcService, walletService, - assetRatioService, bitcoinWalletService + assetRatioService, bitcoinWalletService, + zcashWalletService ) } @@ -190,7 +194,8 @@ class BuyTokenStoreTests: XCTestCase { let ( blockchainRegistry, keyringService, rpcService, walletService, - assetRatioService, bitcoinWalletService + assetRatioService, bitcoinWalletService, + zcashWalletService ) = setupServices() var store = BuyTokenStore( blockchainRegistry: blockchainRegistry, @@ -199,6 +204,7 @@ class BuyTokenStoreTests: XCTestCase { walletService: walletService, assetRatioService: assetRatioService, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: nil ) XCTAssertNil(store.selectedBuyToken) @@ -210,6 +216,7 @@ class BuyTokenStoreTests: XCTestCase { walletService: walletService, assetRatioService: assetRatioService, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: .previewToken ) @@ -226,7 +233,8 @@ class BuyTokenStoreTests: XCTestCase { let ( blockchainRegistry, keyringService, rpcService, walletService, - assetRatioService, bitcoinWalletService + assetRatioService, bitcoinWalletService, + zcashWalletService ) = setupServices() rpcService._network = { coin, origin, completion in completion(selectedNetwork) @@ -246,6 +254,7 @@ class BuyTokenStoreTests: XCTestCase { walletService: walletService, assetRatioService: assetRatioService, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: .mockSolToken // not on mainnet ) await store.updateInfo() @@ -259,7 +268,8 @@ class BuyTokenStoreTests: XCTestCase { let ( blockchainRegistry, keyringService, rpcService, walletService, - assetRatioService, bitcoinWalletService + assetRatioService, bitcoinWalletService, + zcashWalletService ) = setupServices(selectedNetwork: .mockSepolia) let store = BuyTokenStore( blockchainRegistry: blockchainRegistry, @@ -268,6 +278,7 @@ class BuyTokenStoreTests: XCTestCase { walletService: walletService, assetRatioService: assetRatioService, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: nil ) @@ -288,7 +299,8 @@ class BuyTokenStoreTests: XCTestCase { let ( blockchainRegistry, keyringService, rpcService, walletService, - assetRatioService, bitcoinWalletService + assetRatioService, bitcoinWalletService, + zcashWalletService ) = setupServices(selectedNetwork: .mockMainnet) let store = BuyTokenStore( blockchainRegistry: blockchainRegistry, @@ -297,6 +309,7 @@ class BuyTokenStoreTests: XCTestCase { walletService: walletService, assetRatioService: assetRatioService, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: nil ) @@ -318,7 +331,8 @@ class BuyTokenStoreTests: XCTestCase { let ( _, keyringService, rpcService, walletService, - assetRatioService, bitcoinWalletService + assetRatioService, bitcoinWalletService, + zcashWalletService ) = setupServices() let blockchainRegistry = BraveWallet.TestBlockchainRegistry() blockchainRegistry._buyTokens = { @@ -345,6 +359,7 @@ class BuyTokenStoreTests: XCTestCase { walletService: walletService, assetRatioService: assetRatioService, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: nil ) @@ -372,7 +387,8 @@ class BuyTokenStoreTests: XCTestCase { let ( _, keyringService, rpcService, walletService, - assetRatioService, bitcoinWalletService + assetRatioService, bitcoinWalletService, + zcashWalletService ) = setupServices(selectedNetwork: selectedNetwork) let blockchainRegistry = BraveWallet.TestBlockchainRegistry() blockchainRegistry._buyTokens = { @@ -399,6 +415,7 @@ class BuyTokenStoreTests: XCTestCase { walletService: walletService, assetRatioService: assetRatioService, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: nil ) @@ -415,7 +432,8 @@ class BuyTokenStoreTests: XCTestCase { let ( blockchainRegistry, keyringService, rpcService, walletService, - assetRatioService, bitcoinWalletService + assetRatioService, bitcoinWalletService, + zcashWalletService ) = setupServices() blockchainRegistry._onRampCurrencies = { $0([.mockUSD, .mockCAD, .mockGBP, .mockEuro]) @@ -428,6 +446,7 @@ class BuyTokenStoreTests: XCTestCase { walletService: walletService, assetRatioService: assetRatioService, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: nil ) await store.updateInfo() diff --git a/ios/brave-ios/Tests/BraveWalletTests/NFTStoreTests.swift b/ios/brave-ios/Tests/BraveWalletTests/NFTStoreTests.swift index 569b3862e97b..06e645668e42 100644 --- a/ios/brave-ios/Tests/BraveWalletTests/NFTStoreTests.swift +++ b/ios/brave-ios/Tests/BraveWalletTests/NFTStoreTests.swift @@ -1076,8 +1076,8 @@ class NFTStoreTests: XCTestCase { XCTFail("Unexpected test result") return } - // Solana mainnet, Ethereum mainnet, Polygon, Filecoin mainnet, Bitcoin mainnet - XCTAssertEqual(lastUpdatedUserNFTGroups.count, 5) + // Solana mainnet, Ethereum mainnet, Polygon, Filecoin mainnet, Bitcoin mainnet, Zcash mainnet + XCTAssertEqual(lastUpdatedUserNFTGroups.count, 6) guard let solNetworkGroupVisibleNFTs = lastUpdatedUserNFTGroups[safe: 0]?.assets.filter( \.token.visible @@ -1173,8 +1173,8 @@ class NFTStoreTests: XCTestCase { XCTFail("Unexpected test result") return } - // Solana mainnet, Ethereum mainnet, Polygon, Filecoin mainnet, Bitcoin mainnet - XCTAssertEqual(lastUpdatedNFTGroups.count, 5) + // Solana mainnet, Ethereum mainnet, Polygon, Filecoin mainnet, Bitcoin mainnet, Zcash mainnet + XCTAssertEqual(lastUpdatedNFTGroups.count, 6) guard let solNetworkGroup = lastUpdatedNFTGroups[safe: 0], let ethNetworkGroup = lastUpdatedNFTGroups[safe: 1] diff --git a/ios/brave-ios/Tests/BraveWalletTests/NetworkStoreTests.swift b/ios/brave-ios/Tests/BraveWalletTests/NetworkStoreTests.swift index 1be1cb64237b..10aa0e15c3f9 100644 --- a/ios/brave-ios/Tests/BraveWalletTests/NetworkStoreTests.swift +++ b/ios/brave-ios/Tests/BraveWalletTests/NetworkStoreTests.swift @@ -175,6 +175,8 @@ import XCTest case .btc: completion(.mockBitcoinMainnet) case .zec: + completion(.mockZcashMainnet) + case .ada: fallthrough @unknown default: completion(.mockMainnet) @@ -199,6 +201,7 @@ import XCTest .mockFilecoinMainnet, .mockFilecoinTestnet, .mockBitcoinMainnet, + .mockZcashMainnet, ] let expectedCustomChains: [BraveWallet.NetworkInfo] = [ diff --git a/ios/brave-ios/Tests/BraveWalletTests/PortfolioStoreTests.swift b/ios/brave-ios/Tests/BraveWalletTests/PortfolioStoreTests.swift index 59835193e970..fbd3fc45276e 100644 --- a/ios/brave-ios/Tests/BraveWalletTests/PortfolioStoreTests.swift +++ b/ios/brave-ios/Tests/BraveWalletTests/PortfolioStoreTests.swift @@ -31,6 +31,7 @@ import XCTest Preferences.Wallet.nonSelectedAccountsFilter.reset() Preferences.Wallet.nonSelectedNetworksFilter.reset() Preferences.Wallet.isBitcoinTestnetEnabled.reset() + Preferences.Wallet.isZcashTestnetEnabled.reset() } // Accounts @@ -64,6 +65,8 @@ import XCTest $0.name = "Bitcoin Account 2" } let btcTestnetAccount: BraveWallet.AccountInfo = .mockBtcTestnetAccount + let zcashAccount1: BraveWallet.AccountInfo = .mockZcashAccount + let zcashTestnetAccount: BraveWallet.AccountInfo = .mockZcashTestnetAccount // ETH Asset, balance, price, history let mockETHBalanceAccount1: Double = 0.896 @@ -138,6 +141,18 @@ import XCTest .init(date: Date(), price: mockBTCPrice), ] let mockBTCBalanceTestnet: Double = 0.00001 + let mockZECTransparentBalanceAccount1: Double = 0.00001 + let mockZECPrice: String = "39.50" + lazy var mockZECAssetPrice: BraveWallet.AssetPrice = .init( + fromAsset: "zec", + toAsset: "usd", + price: mockZECPrice, + assetTimeframeChange: "2.93" + ) + lazy var mockZECPriceHistory: [BraveWallet.AssetTimePrice] = [ + .init(date: Date(timeIntervalSinceNow: -1000), price: "36.0"), + .init(date: Date(), price: mockZECPrice), + ] var totalBalance: String { let totalEthBalanceValue: Double = @@ -150,9 +165,11 @@ import XCTest (Double(mockFILAssetPrice.price) ?? 0) * mockFILBalanceAccount1 let totalBtcBalanceValue: Double = (Double(mockBTCAssetPrice.price) ?? 0) * mockBTCBalanceAccount1 + let totalZecBalanceValue: Double = + (Double(mockZECAssetPrice.price) ?? 0) * mockZECTransparentBalanceAccount1 let totalBalanceValue = totalEthBalanceValue + totalSolBalanceValue + totalUSDCBalanceValue - + totalFilBalanceValue + totalBtcBalanceValue + + totalFilBalanceValue + totalBtcBalanceValue + totalZecBalanceValue return currencyFormatter.formatAsFiat(totalBalanceValue) ?? "" } @@ -225,6 +242,13 @@ import XCTest BraveWallet.NetworkInfo.mockBitcoinTestnet.nativeToken.copy(asVisibleAsset: true) ] + let mockZcashUserAssets: [BraveWallet.BlockchainToken] = [ + BraveWallet.NetworkInfo.mockZcashMainnet.nativeToken.copy(asVisibleAsset: true) + ] + let mockZcashTestnetUserAssets: [BraveWallet.BlockchainToken] = [ + BraveWallet.NetworkInfo.mockZcashTestnet.nativeToken.copy(asVisibleAsset: true) + ] + // setup test services let keyringService = BraveWallet.TestKeyringService() keyringService._addObserver = { _ in } @@ -242,6 +266,7 @@ import XCTest self.filTestnetAccount, self.btcAccount1, self.btcAccount2, self.btcTestnetAccount, + self.zcashAccount1, self.zcashTestnetAccount, ], selectedAccount: self.ethAccount1, ethDappSelectedAccount: self.ethAccount1, @@ -303,7 +328,7 @@ import XCTest true, [ self.mockETHAssetPrice, self.mockUSDCAssetPrice, self.mockSOLAssetPrice, - self.mockFILAssetPrice, self.mockBTCAssetPrice, + self.mockFILAssetPrice, self.mockBTCAssetPrice, self.mockZECAssetPrice, ] ) } @@ -319,6 +344,8 @@ import XCTest completion(true, self.mockFILPriceHistory) // for both mainnet and testnet case "btc": completion(true, self.mockBTCPriceHistory) // for both mainnet and testnet + case "zec": + completion(true, self.mockZECPriceHistory) // for both mainnet and testnet default: completion(false, []) } @@ -355,51 +382,22 @@ import XCTest NetworkAssets( network: .mockBitcoinMainnet, tokens: mockBtcUserAssets.filter(\.visible), - sortOrder: 3 + sortOrder: 5 ), NetworkAssets( network: .mockBitcoinTestnet, tokens: mockBtcTestnetUserAssets.filter(\.visible), - sortOrder: 4 - ), - ].filter { networkAsset in networks.contains(where: { $0 == networkAsset.network }) } - } - mockAssetManager._getUserAssets = { networks, _ in - [ - NetworkAssets( - network: .mockMainnet, - tokens: mockEthUserAssets.filter(\.visible), - sortOrder: 0 - ), - NetworkAssets( - network: .mockSolana, - tokens: mockSolUserAssets.filter(\.visible), - sortOrder: 1 - ), - NetworkAssets( - network: .mockSepolia, - tokens: mockEthSepoliaUserAssets.filter(\.visible), - sortOrder: 2 + sortOrder: 6 ), NetworkAssets( - network: .mockFilecoinMainnet, - tokens: mockFilUserAssets.filter(\.visible), - sortOrder: 3 + network: .mockZcashMainnet, + tokens: mockZcashUserAssets.filter(\.visible), + sortOrder: 7 ), NetworkAssets( - network: .mockFilecoinTestnet, - tokens: mockFilTestnetUserAssets.filter(\.visible), - sortOrder: 4 - ), - NetworkAssets( - network: .mockBitcoinMainnet, - tokens: mockBtcUserAssets.filter(\.visible), - sortOrder: 3 - ), - NetworkAssets( - network: .mockBitcoinTestnet, - tokens: mockBtcTestnetUserAssets.filter(\.visible), - sortOrder: 4 + network: .mockZcashTestnet, + tokens: mockZcashTestnetUserAssets.filter(\.visible), + sortOrder: 8 ), ].filter { networkAsset in networks.contains(where: { $0 == networkAsset.network }) } } @@ -462,6 +460,38 @@ import XCTest ) } } + let zecTransparentBalanceInSatoshi = + formatter.weiString( + from: mockZECTransparentBalanceAccount1, + radix: .decimal, + decimals: Int(BraveWallet.BlockchainToken.mockZECToken.decimals) + ) ?? "" + let zcashWalletService = BraveWallet.TestZCashWalletService() + zcashWalletService._balance = { chainId, accountId, completion in + if chainId == BraveWallet.ZCashMainnet { + completion( + .init( + totalBalance: UInt64(zecTransparentBalanceInSatoshi) ?? 0, + transparentBalance: UInt64(zecTransparentBalanceInSatoshi) ?? 0, + shieldedBalance: 0, + shieldedPendingBalance: 0, + balances: [:] + ), + nil + ) + } else { + completion( + .init( + totalBalance: 0, + transparentBalance: 0, + shieldedBalance: 0, + shieldedPendingBalance: 0, + balances: [:] + ), + nil + ) + } + } return PortfolioStore( keyringService: keyringService, @@ -471,6 +501,7 @@ import XCTest blockchainRegistry: BraveWallet.TestBlockchainRegistry(), ipfsApi: TestIpfsAPI(), bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, userAssetManager: mockAssetManager ) } @@ -499,8 +530,8 @@ import XCTest return } - // ETH on Ethereum mainnet, SOL on Solana mainnet, USDC on Ethereum mainnet, ETH on Sepolia, FIL on Filecoin mainnet, FIL on Filecoin testnet, BTC on Bitcoin mainnet. No BTC on Bitcoin testnet - XCTAssertEqual(group.assets.count, 5) + // ETH on Ethereum mainnet, SOL on Solana mainnet, USDC on Ethereum mainnet, ETH on Sepolia, FIL on Filecoin mainnet, FIL on Filecoin testnet, BTC on Bitcoin mainnet. No BTC on Bitcoin testnet No ZEC on Zcash testnet + XCTAssertEqual(group.assets.count, 6) // ETH Mainnet (value ~= $2741.7510399999996) XCTAssertEqual( group.assets[safe: 0]?.token.symbol, @@ -559,7 +590,7 @@ import XCTest String(format: "%.04f", self.mockFILBalanceAccount1) ) XCTAssertEqual( - group.assets[safe: 0]?.btcBalances, + group.assets[safe: 2]?.btcBalances, [:] // FIL so no btcBalances ) @@ -612,11 +643,33 @@ import XCTest ], ] ) + // ZEC (value $0.00396) on mainnet + XCTAssertEqual( + group.assets[safe: 5]?.token.symbol, + BraveWallet.BlockchainToken.mockZECToken.symbol + ) + XCTAssertEqual( + group.assets[safe: 5]?.price, + self.mockZECAssetPrice.price + ) + XCTAssertEqual( + group.assets[safe: 5]?.history, + self.mockZECPriceHistory + ) + XCTAssertEqual( + group.assets[safe: 5]?.quantity, + String(format: "%.04f", self.mockZECTransparentBalanceAccount1) + ) + XCTAssertEqual( + group.assets[safe: 5]?.btcBalances, + [:] // ZEC so no btcBalances + ) // ETH Sepolia (value = 0), hidden because test networks not selected by default // FIL Testnet (value = $400.00), hidden because test networks not selected by default - // BIT Testnet (value = 0.00), hidden because Bitcoin testnet is disabled by default - XCTAssertNil(group.assets[safe: 5]) + // BTC Testnet (value = 0.00), hidden because Bitcoin testnet is disabled by default + // ZEC Testnet (value = 0.00), hidden because Zcash testnet is disabled by default + XCTAssertNil(group.assets[safe: 6]) } .store(in: &cancellables) @@ -706,8 +759,9 @@ import XCTest } /// Test `assetGroups` will be sorted to from smallest to highest fiat value when `sortOrder` filter is `valueAsc`. - func filterSortHelper(bitcoinTestnetEnabled: Bool) async { - Preferences.Wallet.isBitcoinTestnetEnabled.value = bitcoinTestnetEnabled + func filterSortHelper(bitcoinAndZcashTestnetEnabled: Bool) async { + Preferences.Wallet.isBitcoinTestnetEnabled.value = bitcoinAndZcashTestnetEnabled + Preferences.Wallet.isZcashTestnetEnabled.value = bitcoinAndZcashTestnetEnabled let store = setupStore() let sortExpectation = expectation(description: "update-sortOrder") store.$assetGroups @@ -725,79 +779,111 @@ import XCTest XCTFail("Unexpected test result") return } - // 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) + // USDC on Ethereum mainnet, SOL on Solana mainnet, ETH on Ethereum mainnet, FIL on Filecoin mainnet, FIL on Filecoin testnet, BTC on Bitcoin mainnet, ZEC on Zcash mainnet. No BTC/ZEC on Bitcoin/Zcash testnet since Bitcoin/Zcash tesnet is disabled by defaultfor group in lastUpdatedAssetGroups { + let assetsNumber = bitcoinAndZcashTestnetEnabled ? 10 : 8 + XCTAssertEqual(group.assets.count, assetsNumber) + for asset in group.assets { + print(asset.token.id) + } + var zecTestnetIndex = 0 + var btcTestnetIndex = 0 + var zecMainnetIndex = 1 + var btcMainnetIndex = 2 + var usdcMainnetIndex = 3 + var filMainnetIndex = 4 + var filTestnetIndex = 5 + var solMainnetIndex = 6 + var ethMainnetIndex = 7 + if bitcoinAndZcashTestnetEnabled { + zecTestnetIndex = 1 + zecMainnetIndex = 2 + btcMainnetIndex = 3 + usdcMainnetIndex = 4 + btcTestnetIndex = 5 + filMainnetIndex = 6 + filTestnetIndex = 7 + solMainnetIndex = 8 + ethMainnetIndex = 9 + } + // ETH Sepolia (value = $0) XCTAssertEqual( group.assets[safe: 0]?.token.symbol, BraveWallet.BlockchainToken.previewToken.symbol ) - XCTAssertEqual(group.assets[safe: 0]?.quantity, String(format: "%.04f", 0)) + if bitcoinAndZcashTestnetEnabled { + // ZEC testnet (value = $0) + XCTAssertEqual( + group.assets[safe: zecTestnetIndex]?.token.symbol, + BraveWallet.BlockchainToken.mockZECToken.symbol + ) + // BTC testnet (value = $0.65726) + XCTAssertEqual( + group.assets[safe: btcTestnetIndex]?.token.symbol, + BraveWallet.BlockchainToken.mockBTCToken.symbol + ) + XCTAssertEqual(group.assets[safe: btcTestnetIndex]?.quantity, String(format: "%.04f", 0)) + } + // ZEC mainnet (value = $0.000395) + XCTAssertEqual( + group.assets[safe: zecMainnetIndex]?.token.id, + BraveWallet.BlockchainToken.mockZECToken.id + ) + // BTC mainnet (value = $0.0065726) XCTAssertEqual( - group.assets[safe: 1]?.token.symbol, + group.assets[safe: btcMainnetIndex]?.token.symbol, BraveWallet.BlockchainToken.mockBTCToken.symbol ) - XCTAssertEqual(group.assets[safe: 1]?.quantity, String(format: "%.04f", 0)) + XCTAssertEqual(group.assets[safe: btcMainnetIndex]?.quantity, String(format: "%.04f", 0)) + // USDC (value = $0.04) XCTAssertEqual( - group.assets[safe: 2]?.token.symbol, + group.assets[safe: usdcMainnetIndex]?.token.symbol, BraveWallet.BlockchainToken.mockUSDCToken.symbol ) XCTAssertEqual( - group.assets[safe: 2]?.quantity, + group.assets[safe: usdcMainnetIndex]?.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( - group.assets[safe: offset]?.token.symbol, + group.assets[safe: filMainnetIndex]?.token.symbol, BraveWallet.BlockchainToken.mockFilToken.symbol ) XCTAssertEqual( - group.assets[safe: offset]?.quantity, + group.assets[safe: filMainnetIndex]?.quantity, String(format: "%.04f", self.mockFILBalanceAccount1) ) + // FIL (value = $400.00) on filecoin testnet - offset += 1 XCTAssertEqual( - group.assets[safe: offset]?.token.symbol, + group.assets[safe: filTestnetIndex]?.token.symbol, BraveWallet.BlockchainToken.mockFilToken.symbol ) XCTAssertEqual( - group.assets[safe: offset]?.quantity, + group.assets[safe: filTestnetIndex]?.quantity, String(format: "%.04f", self.mockFILBalanceTestnet) ) + // SOL (value = $775.3) - offset += 1 XCTAssertEqual( - group.assets[safe: offset]?.token.symbol, + group.assets[safe: solMainnetIndex]?.token.symbol, BraveWallet.BlockchainToken.mockSolToken.symbol ) XCTAssertEqual( - group.assets[safe: offset]?.quantity, + group.assets[safe: solMainnetIndex]?.quantity, String(format: "%.04f", self.mockSOLBalance) ) + // ETH Mainnet (value ~= $2741.7510399999996) - offset += 1 XCTAssertEqual( - group.assets[safe: offset]?.token.symbol, + group.assets[safe: ethMainnetIndex]?.token.symbol, BraveWallet.BlockchainToken.previewToken.symbol ) XCTAssertEqual( - group.assets[safe: offset]?.quantity, + group.assets[safe: ethMainnetIndex]?.quantity, String(format: "%.04f", self.mockETHBalanceAccount1) ) }.store(in: &cancellables) @@ -814,6 +900,7 @@ import XCTest accounts: [ ethAccount1, ethAccount2, solAccount1, solAccount2, filAccount1, filAccount2, filTestnetAccount, btcAccount1, btcAccount2, btcTestnetAccount, + zcashAccount1, zcashTestnetAccount, ].map { .init(isSelected: true, model: $0) }, @@ -827,11 +914,11 @@ import XCTest } func testFilterSort() async { - await filterSortHelper(bitcoinTestnetEnabled: false) + await filterSortHelper(bitcoinAndZcashTestnetEnabled: false) } - func testFilterSortBitcoinTestnet() async { - await filterSortHelper(bitcoinTestnetEnabled: true) + func testFilterSortBitcoinAndZcashTestnet() async { + await filterSortHelper(bitcoinAndZcashTestnetEnabled: true) } /// Test `assetGroups` will be filtered to remove small balances when `hideSmallBalances` filter is true. @@ -905,7 +992,7 @@ import XCTest isShowingNFTNetworkLogo: store.filters.isShowingNFTNetworkLogo, accounts: [ ethAccount1, ethAccount2, solAccount1, solAccount2, filAccount1, filAccount2, - filTestnetAccount, btcAccount1, btcAccount2, + filTestnetAccount, btcAccount1, btcAccount2, zcashAccount1, ].map { .init(isSelected: true, model: $0) }, @@ -919,9 +1006,10 @@ import XCTest } /// Test `assetGroups` will be filtered by accounts when `accounts` filter is has de-selected accounts. - func filterAccountsHelper(bitcoinTestnetEnabled: Bool) async { + func filterAccountsHelper(bitcoinAndZcashTestnetEnabled: Bool) async { // test without bitcoin testnet enabled - Preferences.Wallet.isBitcoinTestnetEnabled.value = bitcoinTestnetEnabled + Preferences.Wallet.isBitcoinTestnetEnabled.value = bitcoinAndZcashTestnetEnabled + Preferences.Wallet.isZcashTestnetEnabled.value = bitcoinAndZcashTestnetEnabled let store = setupStore() let accountsExpectation = expectation(description: "update-accounts-bitcoin-testnet") store.$assetGroups @@ -939,8 +1027,8 @@ import XCTest XCTFail("Unexpected test result") return } - // ETH on Ethereum mainnet, SOL on Solana mainnet, USDC on Ethereum mainnet, ETH on Sepolia, FIL on mainnet and testnet, BTC on mainnet and testnet - let assetGroupNumber: Int = bitcoinTestnetEnabled ? 8 : 7 + // ETH on Ethereum mainnet, SOL on Solana mainnet, USDC on Ethereum mainnet, ETH on Sepolia, FIL on mainnet and testnet, BTC on mainnet and testnet, ZEC on mainnet and testnet + let assetGroupNumber: Int = bitcoinAndZcashTestnetEnabled ? 10 : 8 XCTAssertEqual(group.assets.count, assetGroupNumber) // ETH Mainnet (value ~= $2741.7510399999996) XCTAssertEqual( @@ -979,7 +1067,7 @@ import XCTest String(format: "%.04f", self.mockFILBalanceAccount1) ) var offset: Int = 4 - if bitcoinTestnetEnabled { + if bitcoinAndZcashTestnetEnabled { // BTC testnet (value = $0.65726) XCTAssertEqual( group.assets[safe: offset]?.token.symbol, @@ -1011,6 +1099,13 @@ import XCTest String(format: "%.04f", self.mockBTCBalanceAccount1) ) offset += 1 + // ZEC mainnet (value = $0) for same balance native token first + XCTAssertEqual( + group.assets[safe: offset]?.token.symbol, + BraveWallet.NetworkInfo.mockZcashMainnet.nativeToken.symbol + ) + XCTAssertEqual(group.assets[safe: offset]?.quantity, String(format: "%.04f", 0)) + offset += 1 // ETH Sepolia (value = $0) XCTAssertEqual( group.assets[safe: offset]?.token.symbol, @@ -1042,11 +1137,11 @@ import XCTest } func testFilterAccounts() async { - await filterAccountsHelper(bitcoinTestnetEnabled: false) + await filterAccountsHelper(bitcoinAndZcashTestnetEnabled: false) } - func testFilterAccountsWithBitcoinTestnet() async { - await filterAccountsHelper(bitcoinTestnetEnabled: true) + func testFilterAccountsWithBitcoinAndZcashTestnet() async { + await filterAccountsHelper(bitcoinAndZcashTestnetEnabled: true) } /// Test `assetGroups` will be filtered by network when `networks` filter is has de-selected networks. @@ -1148,8 +1243,9 @@ import XCTest /// Test `assetGroups` will be grouped by account when `GroupBy` filter is assigned `.account`. /// Additionally, test de-selecting/hiding one of the available accounts. - func groupByAccountsHelper(bitcoinTestnetEnabled: Bool) async { - Preferences.Wallet.isBitcoinTestnetEnabled.value = bitcoinTestnetEnabled + func groupByAccountsHelper(bitcoinAndZcashTestnetEnabled: Bool) async { + Preferences.Wallet.isBitcoinTestnetEnabled.value = bitcoinAndZcashTestnetEnabled + Preferences.Wallet.isZcashTestnetEnabled.value = bitcoinAndZcashTestnetEnabled let store = setupStore() let assetGroupsExpectation = expectation(description: "update-assetGroups") XCTAssertTrue(store.assetGroups.isEmpty) // Initial state @@ -1163,23 +1259,27 @@ import XCTest XCTFail("Unexpected test result") return } - // grouping by .account; eth has 2 accounts, sol has 2 accounts, fil has 3 accounts, btc has 3 accounts (one tesnet) - let assetGroupNumber = bitcoinTestnetEnabled ? 10 : 9 + // grouping by .account; eth has 2 accounts, sol has 2 accounts, fil has 3 accounts, btc has 3 accounts (one tesnet) zec has 2 accounts (one testnet) + let assetGroupNumber = bitcoinAndZcashTestnetEnabled ? 12 : 10 XCTAssertEqual(lastUpdatedAssetGroups.count, assetGroupNumber) var btcTestnetIndex = 0 + var zecTestIndex = 0 var ethAccount2Index = 4 var btcAccount1Index = 5 - var solAccount2Index = 6 - var btcAccount2Index = 7 - var filAccount2Index = 8 - if bitcoinTestnetEnabled { + var zecAccountIndex = 6 + var solAccount2Index = 7 + var btcAccount2Index = 8 + var filAccount2Index = 9 + if bitcoinAndZcashTestnetEnabled { btcTestnetIndex = 4 ethAccount2Index = 5 btcAccount1Index = 6 - solAccount2Index = 7 - btcAccount2Index = 8 - filAccount2Index = 9 + zecAccountIndex = 7 + solAccount2Index = 8 + btcAccount2Index = 9 + zecTestIndex = 10 + filAccount2Index = 11 } guard let ethAccount1Group = lastUpdatedAssetGroups[safe: 0], let solAccount1Group = lastUpdatedAssetGroups[safe: 1], @@ -1187,6 +1287,7 @@ import XCTest let filAccount1Group = lastUpdatedAssetGroups[safe: 3], let ethAccount2Group = lastUpdatedAssetGroups[safe: ethAccount2Index], let btcAccount1Group = lastUpdatedAssetGroups[safe: btcAccount1Index], + let zecAccountGroup = lastUpdatedAssetGroups[safe: zecAccountIndex], let solAccount2Group = lastUpdatedAssetGroups[safe: solAccount2Index], let btcAccount2Group = lastUpdatedAssetGroups[safe: btcAccount2Index], let filAccount2Group = lastUpdatedAssetGroups[safe: filAccount2Index] @@ -1195,8 +1296,10 @@ import XCTest return } - if bitcoinTestnetEnabled { - guard let btcTestnetAccountGroup = lastUpdatedAssetGroups[safe: btcTestnetIndex] + if bitcoinAndZcashTestnetEnabled { + guard + let btcTestnetAccountGroup = lastUpdatedAssetGroups[safe: btcTestnetIndex], + let zecTestnetAccountGroup = lastUpdatedAssetGroups[safe: zecTestIndex] else { XCTFail("Unexpected test result") return @@ -1223,6 +1326,18 @@ import XCTest ] ] ) + + XCTAssertEqual(zecTestnetAccountGroup.groupType, .account(self.zcashTestnetAccount)) + XCTAssertEqual(zecTestnetAccountGroup.assets.count, 1) + // ZEC (value = $0) + XCTAssertEqual( + zecTestnetAccountGroup.assets[safe: 0]?.token.symbol, + BraveWallet.BlockchainToken.mockZECToken.symbol + ) + XCTAssertEqual( + zecTestnetAccountGroup.assets[safe: 0]?.quantity, + String(format: "%.04f", 0) + ) } XCTAssertEqual(ethAccount1Group.groupType, .account(self.ethAccount1)) @@ -1321,6 +1436,15 @@ import XCTest BraveWallet.BlockchainToken.mockBTCToken.symbol ) + XCTAssertEqual(zecAccountGroup.assets[safe: 0]?.quantity, String(format: "%.04f", 0)) + XCTAssertEqual(zecAccountGroup.groupType, .account(self.zcashAccount1)) + XCTAssertEqual(zecAccountGroup.assets.count, 1) + // ZEC mainnet (value = $0.000395) + XCTAssertEqual( + zecAccountGroup.assets[safe: 0]?.token.symbol, + BraveWallet.BlockchainToken.mockZECToken.symbol + ) + XCTAssertEqual(solAccount2Group.groupType, .account(self.solAccount2)) XCTAssertEqual(solAccount2Group.assets.count, 1) // SOL (value = $0) @@ -1390,7 +1514,7 @@ import XCTest } // grouping by .account; 1 for each of the 2 accounts selected accounts - let groupNumber = bitcoinTestnetEnabled ? 5 : 4 + let groupNumber = bitcoinAndZcashTestnetEnabled ? 5 : 4 XCTAssertEqual(lastUpdatedAssetGroups.count, groupNumber) guard let ethAccount1Group = lastUpdatedAssetGroups[safe: 0], let solAccountGroup = lastUpdatedAssetGroups[safe: 1], @@ -1451,7 +1575,7 @@ import XCTest String(format: "%.04f", self.mockFILBalanceAccount1) ) - if bitcoinTestnetEnabled { + if bitcoinAndZcashTestnetEnabled { guard let btcTestnetAccountGroup = lastUpdatedAssetGroups[safe: 4] else { XCTFail("Unexpected test result") @@ -1482,7 +1606,7 @@ import XCTest } // 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 + let lastIndex = bitcoinAndZcashTestnetEnabled ? 5 : 4 XCTAssertNil(lastUpdatedAssetGroups[safe: lastIndex]) } .store(in: &cancellables) @@ -1508,17 +1632,18 @@ import XCTest } func testGroupByAccounts() async { - await groupByAccountsHelper(bitcoinTestnetEnabled: false) + await groupByAccountsHelper(bitcoinAndZcashTestnetEnabled: false) } - func testGroupByAccountsBitcoinTestnet() async { - await groupByAccountsHelper(bitcoinTestnetEnabled: true) + func testGroupByAccountsBitcoinAndZcashTestnet() async { + await groupByAccountsHelper(bitcoinAndZcashTestnetEnabled: true) } /// Test `assetGroups` will be grouped by network when `GroupBy` filter is assigned `.network`. /// Additionally, test de-selecting/hiding one of the available networks. - func groupByNetworksHelper(bitcoinTestnetEnabled: Bool) async { - Preferences.Wallet.isBitcoinTestnetEnabled.value = bitcoinTestnetEnabled + func groupByNetworksHelper(bitcoinAndZcashTestnetEnabled: Bool) async { + Preferences.Wallet.isBitcoinTestnetEnabled.value = bitcoinAndZcashTestnetEnabled + Preferences.Wallet.isZcashTestnetEnabled.value = bitcoinAndZcashTestnetEnabled let store = setupStore() let assetGroupsExpectation = expectation(description: "update-assetGroups") XCTAssertTrue(store.assetGroups.isEmpty) // Initial state @@ -1535,12 +1660,11 @@ import XCTest // 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 + let assetGroupNumber = bitcoinAndZcashTestnetEnabled ? 9 : 7 XCTAssertEqual(lastUpdatedAssetGroups.count, assetGroupNumber) - let offset = bitcoinTestnetEnabled ? 1 : 0 - if bitcoinTestnetEnabled { - + let offset = bitcoinAndZcashTestnetEnabled ? 1 : 0 + if bitcoinAndZcashTestnetEnabled { guard let btcTestnetGroup = lastUpdatedAssetGroups[safe: 4] else { XCTFail("Unexpected test result") @@ -1554,6 +1678,20 @@ import XCTest BraveWallet.BlockchainToken.mockBTCToken.symbol ) XCTAssertEqual(btcTestnetGroup.assets[safe: 0]?.quantity, String(format: "%.04f", 0)) + + guard let zcashTestnetGroup = lastUpdatedAssetGroups.last + else { + XCTFail("Unexpected test result") + return + } + XCTAssertEqual(zcashTestnetGroup.groupType, .network(.mockZcashTestnet)) + XCTAssertEqual(zcashTestnetGroup.assets.count, 1) + // Zcash testnet (value = $0) + XCTAssertEqual( + zcashTestnetGroup.assets[safe: 0]?.token.symbol, + BraveWallet.BlockchainToken.mockZECToken.symbol + ) + XCTAssertEqual(btcTestnetGroup.assets[safe: 0]?.quantity, String(format: "%.04f", 0)) } guard let ethMainnetGroup = lastUpdatedAssetGroups[safe: 0], @@ -1561,7 +1699,8 @@ import XCTest let filTestnetGroup = lastUpdatedAssetGroups[safe: 2], let filMainnetGroup = lastUpdatedAssetGroups[safe: 3], let btcMainnetGroup = lastUpdatedAssetGroups[safe: 4 + offset], - let ethSepoliaGroup = lastUpdatedAssetGroups[safe: 5 + offset] + let zcashMainnetGroup = lastUpdatedAssetGroups[safe: 5 + offset], + let ethSepoliaGroup = lastUpdatedAssetGroups[safe: 6 + offset] else { XCTFail("Unexpected test result") return @@ -1633,6 +1772,15 @@ import XCTest ) XCTAssertEqual(btcMainnetGroup.assets[safe: 0]?.quantity, String(format: "%.04f", 0)) + XCTAssertEqual(zcashMainnetGroup.groupType, .network(.mockZcashMainnet)) + XCTAssertEqual(zcashMainnetGroup.assets.count, 1) // ZEC + // ZEC mainnet (value = $0) + XCTAssertEqual( + zcashMainnetGroup.assets[safe: 0]?.token.symbol, + BraveWallet.BlockchainToken.mockZECToken.symbol + ) + XCTAssertEqual(zcashMainnetGroup.assets[safe: 0]?.quantity, String(format: "%.04f", 0)) + XCTAssertEqual(ethSepoliaGroup.groupType, .network(.mockSepolia)) XCTAssertEqual(ethSepoliaGroup.assets.count, 1) // ETH Sepolia // ETH Sepolia (value = $0) @@ -1660,6 +1808,7 @@ import XCTest accounts: [ ethAccount1, ethAccount2, solAccount1, solAccount2, filAccount1, filAccount2, filTestnetAccount, btcAccount1, btcAccount2, btcTestnetAccount, + zcashAccount1, zcashTestnetAccount, ].map { .init(isSelected: true, model: $0) }, @@ -1683,7 +1832,7 @@ import XCTest return } // grouping by .network; 1 group for Solana network, 1 group for Filecoin mainnet, 1 group for Filecoin testnet - let groupNumber = bitcoinTestnetEnabled ? 4 : 3 + let groupNumber = bitcoinAndZcashTestnetEnabled ? 4 : 3 XCTAssertEqual(lastUpdatedAssetGroups.count, groupNumber) guard let solMainnetGroup = lastUpdatedAssetGroups[safe: 0], let filTestnetGroup = lastUpdatedAssetGroups[safe: 1], @@ -1692,7 +1841,7 @@ import XCTest XCTFail("Unexpected test result") return } - if bitcoinTestnetEnabled { + if bitcoinAndZcashTestnetEnabled { guard let btcTestnetGroup = lastUpdatedAssetGroups[safe: 3] else { XCTFail("Unexpected test result") @@ -1750,7 +1899,7 @@ import XCTest // eth mainnet group hidden as network de-selected // sepolia network group hidden for small balance // Bitcoin network group hidden for small balance - let lastIndex = bitcoinTestnetEnabled ? 4 : 3 + let lastIndex = bitcoinAndZcashTestnetEnabled ? 4 : 3 XCTAssertNil(lastUpdatedAssetGroups[safe: lastIndex]) } .store(in: &cancellables) @@ -1764,6 +1913,7 @@ import XCTest accounts: [ ethAccount1, ethAccount2, solAccount1, solAccount2, filAccount1, filAccount2, filTestnetAccount, btcAccount1, btcAccount2, btcTestnetAccount, + zcashAccount1, zcashTestnetAccount, ].map { .init(isSelected: true, model: $0) }, @@ -1778,11 +1928,11 @@ import XCTest } func testGroupByNetworks() async { - await groupByNetworksHelper(bitcoinTestnetEnabled: false) + await groupByNetworksHelper(bitcoinAndZcashTestnetEnabled: false) } - func testGroupByNetworkBitcoinTestnet() async { - await groupByNetworksHelper(bitcoinTestnetEnabled: true) + func testGroupByNetworkBitcoinAndZcashTestnet() async { + await groupByNetworksHelper(bitcoinAndZcashTestnetEnabled: true) } } diff --git a/ios/brave-ios/Tests/BraveWalletTests/SelectAccountTokenStoreTests.swift b/ios/brave-ios/Tests/BraveWalletTests/SelectAccountTokenStoreTests.swift index 587e39b08b99..8ea95bbeda03 100644 --- a/ios/brave-ios/Tests/BraveWalletTests/SelectAccountTokenStoreTests.swift +++ b/ios/brave-ios/Tests/BraveWalletTests/SelectAccountTokenStoreTests.swift @@ -222,6 +222,7 @@ class SelectAccountTokenStoreTests: XCTestCase { } let bitcoinWalletService = BraveWallet.TestBitcoinWalletService() + let zcashWalletService = BraveWallet.TestZCashWalletService() let store = SelectAccountTokenStore( didSelect: { _, _ in }, @@ -230,6 +231,7 @@ class SelectAccountTokenStoreTests: XCTestCase { walletService: walletService, assetRatioService: assetRatioService, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager ) diff --git a/ios/brave-ios/Tests/BraveWalletTests/SendTokenStoreTests.swift b/ios/brave-ios/Tests/BraveWalletTests/SendTokenStoreTests.swift index 09e22fcf645a..933a2eb26c21 100644 --- a/ios/brave-ios/Tests/BraveWalletTests/SendTokenStoreTests.swift +++ b/ios/brave-ios/Tests/BraveWalletTests/SendTokenStoreTests.swift @@ -62,7 +62,7 @@ class SendTokenStoreTests: XCTestCase { BraveWallet.TestKeyringService, BraveWallet.TestJsonRpcService, BraveWallet.TestBraveWalletService, BraveWallet.TestEthTxManagerProxy, BraveWallet.TestSolanaTxManagerProxy, BraveWallet.TestBitcoinWalletService, - WalletUserAssetManagerType + BraveWallet.TestZCashWalletService, WalletUserAssetManagerType ) { let keyringService = BraveWallet.TestKeyringService() keyringService._addObserver = { _ in } @@ -132,6 +132,8 @@ class SendTokenStoreTests: XCTestCase { completion(bitcoinBalance, "") } + let zcashWalletService = BraveWallet.TestZCashWalletService() + let mockAssetManager = TestableWalletUserAssetManager() mockAssetManager._getUserAssets = { _, _ in userAssets.map { (network, tokens) in @@ -141,7 +143,7 @@ class SendTokenStoreTests: XCTestCase { return ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) } @@ -149,7 +151,7 @@ class SendTokenStoreTests: XCTestCase { func testPrefilledToken() { let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices() var store = SendTokenStore( @@ -162,6 +164,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: nil, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -178,6 +181,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: .previewToken, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -203,7 +207,7 @@ class SendTokenStoreTests: XCTestCase { var selectedNetwork: BraveWallet.NetworkInfo = .mockMainnet let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices() rpcService._network = { coin, _, completion in completion(selectedNetwork) @@ -225,6 +229,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: .mockSolToken, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -255,7 +260,7 @@ class SendTokenStoreTests: XCTestCase { let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices(balance: mockBalanceWei) let store = SendTokenStore( keyringService: keyringService, @@ -267,6 +272,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: nil, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -308,7 +314,7 @@ class SendTokenStoreTests: XCTestCase { func testMakeSendETHEIP1559Transaction() { let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices() let store = SendTokenStore( keyringService: keyringService, @@ -320,6 +326,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: .previewToken, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -339,7 +346,7 @@ class SendTokenStoreTests: XCTestCase { func testMakeSendETHTransaction() { let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices() let store = SendTokenStore( keyringService: keyringService, @@ -351,6 +358,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: .previewToken, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -370,7 +378,7 @@ class SendTokenStoreTests: XCTestCase { func testMakeSendERC20EIP1559Transaction() { let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices() let store = SendTokenStore( keyringService: keyringService, @@ -382,6 +390,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: nil, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -422,7 +431,7 @@ class SendTokenStoreTests: XCTestCase { func testMakeSendERC20Transaction() { let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices() let store = SendTokenStore( keyringService: keyringService, @@ -434,6 +443,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: .previewToken, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -453,7 +463,7 @@ class SendTokenStoreTests: XCTestCase { func testMakeSendERC721Transaction() { let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices() ethTxManagerProxy._makeErc721TransferFromData = { _, _, _, _, completion in completion(true, .init()) @@ -468,6 +478,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: .mockERC721NFTToken, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -487,7 +498,7 @@ class SendTokenStoreTests: XCTestCase { func testSendFullBalanceNoRounding() { let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, _ + bitcoinWalletService, zcashWalletService, _ ) = setupServices() let formatter = WalletAmountFormatter(decimalFormatStyle: .decimals(precision: 18)) @@ -520,6 +531,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: .previewToken, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -557,7 +569,7 @@ class SendTokenStoreTests: XCTestCase { let mockBalance: UInt64 = 47 let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices( userAssets: [.mockSolana: [.mockSolToken]], selectedCoin: .sol, @@ -576,6 +588,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: BraveWallet.NetworkInfo.mockSolana.nativeToken, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -599,7 +612,7 @@ class SendTokenStoreTests: XCTestCase { let splTokenBalance = "1000000" let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices( userAssets: [.mockSolana: [.mockSpdToken]], selectedCoin: .sol, @@ -618,6 +631,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: .mockSpdToken, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -640,7 +654,7 @@ class SendTokenStoreTests: XCTestCase { func testMakeSendFILTransaction() { let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices() let store = SendTokenStore( keyringService: keyringService, @@ -652,6 +666,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: .previewToken, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -670,7 +685,7 @@ class SendTokenStoreTests: XCTestCase { func testMakeSendBTCTransaction() { let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices() let store = SendTokenStore( keyringService: keyringService, @@ -682,6 +697,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: .mockBTCToken, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -702,7 +718,7 @@ class SendTokenStoreTests: XCTestCase { let mockBalance: UInt64 = 47 let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices( userAssets: [.mockSolana: [.mockSolToken, .mockSpdToken]], selectedCoin: .sol, @@ -724,6 +740,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: .mockSolToken, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -747,7 +764,7 @@ class SendTokenStoreTests: XCTestCase { let mockBalance: UInt64 = 47 let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices( userAssets: [.mockSolana: [.mockSolToken, .mockSpdToken]], selectedCoin: .sol, @@ -770,6 +787,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: .mockSpdToken, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -791,7 +809,7 @@ class SendTokenStoreTests: XCTestCase { func testFetchSelectedSendNFTMetadataERC721() { let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices() ethTxManagerProxy._makeErc721TransferFromData = { _, _, _, _, completion in completion(true, .init()) @@ -807,6 +825,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: nil, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -840,7 +859,7 @@ class SendTokenStoreTests: XCTestCase { func testFetchSelectedSendNFTMetadataSolNFT() { let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices() ethTxManagerProxy._makeErc721TransferFromData = { _, _, _, _, completion in completion(true, .init()) @@ -856,6 +875,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: nil, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -893,7 +913,7 @@ class SendTokenStoreTests: XCTestCase { let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices( selectedAccount: .mockSolAccount, selectedCoin: .sol, @@ -910,6 +930,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: nil, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -938,7 +959,7 @@ class SendTokenStoreTests: XCTestCase { let expectedAddress = "" let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices( selectedAccount: .mockSolAccount, selectedCoin: .sol, @@ -955,6 +976,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: nil, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -991,7 +1013,7 @@ class SendTokenStoreTests: XCTestCase { let expectedAddress = "xxxxxxxxxxyyyyyyyyyyzzzzzzzzzz0000000000" let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices( selectedAccount: .mockSolAccount, selectedCoin: .sol, @@ -1012,6 +1034,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: nil, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -1051,7 +1074,7 @@ class SendTokenStoreTests: XCTestCase { let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices( selectedCoin: .eth, ensGetEthAddr: expectedAddress @@ -1067,6 +1090,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: nil, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -1096,7 +1120,7 @@ class SendTokenStoreTests: XCTestCase { let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices( selectedCoin: .eth, ensGetEthAddr: expectedAddress @@ -1115,6 +1139,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: nil, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -1145,7 +1170,7 @@ class SendTokenStoreTests: XCTestCase { let expectedAddress = "" let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices( selectedCoin: .eth, ensGetEthAddr: expectedAddress @@ -1161,6 +1186,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: nil, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -1197,7 +1223,7 @@ class SendTokenStoreTests: XCTestCase { let expectedAddress = "0xxxxxxxxxxxyyyyyyyyyyzzzzzzzzzz0000000000" let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices( selectedCoin: .eth, ensGetEthAddr: expectedAddress @@ -1217,6 +1243,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: nil, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -1258,7 +1285,7 @@ class SendTokenStoreTests: XCTestCase { let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices( selectedCoin: .eth, unstoppableDomainsGetWalletAddr: expectedAddress @@ -1274,6 +1301,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: .previewToken, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -1316,7 +1344,7 @@ class SendTokenStoreTests: XCTestCase { let domain = "brave.eth" let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices( selectedCoin: .eth ) @@ -1334,6 +1362,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: .previewToken, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -1387,7 +1416,7 @@ class SendTokenStoreTests: XCTestCase { let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices( selectedCoin: .sol, unstoppableDomainsGetWalletAddr: expectedAddress @@ -1403,6 +1432,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: .mockSolToken, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -1448,7 +1478,7 @@ class SendTokenStoreTests: XCTestCase { let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices( selectedCoin: .eth ) @@ -1470,6 +1500,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: .previewToken, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -1564,7 +1595,7 @@ class SendTokenStoreTests: XCTestCase { @MainActor func testDidSelectSameAccountSameNetwork() async { let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices( selectedAccount: account, userAssets: [.mockSepolia: [ethSepolia, usdcSepolia]], @@ -1580,6 +1611,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: ethSepolia, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -1616,7 +1648,7 @@ class SendTokenStoreTests: XCTestCase { .copy(asVisibleAsset: true) let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices( selectedAccount: account, userAssets: [.mockSepolia: [ethSepolia]], @@ -1638,6 +1670,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: ethSepolia, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -1666,7 +1699,7 @@ class SendTokenStoreTests: XCTestCase { var selectedNetwork: BraveWallet.NetworkInfo = .mockMainnet let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices( selectedAccount: account, userAssets: [.mockMainnet: [ethMainnet], .mockSepolia: [usdcSepolia]], @@ -1700,6 +1733,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: ethMainnet, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager @@ -1742,7 +1776,7 @@ class SendTokenStoreTests: XCTestCase { var selectedAccount: BraveWallet.AccountInfo = account let ( keyringService, rpcService, walletService, ethTxManagerProxy, solTxManagerProxy, - bitcoinWalletService, mockAssetManager + bitcoinWalletService, zcashWalletService, mockAssetManager ) = setupServices( selectedAccount: selectedAccount, userAssets: [ @@ -1802,6 +1836,7 @@ class SendTokenStoreTests: XCTestCase { ethTxManagerProxy: ethTxManagerProxy, solTxManagerProxy: solTxManagerProxy, bitcoinWalletService: bitcoinWalletService, + zcashWalletService: zcashWalletService, prefilledToken: usdcSepolia, ipfsApi: TestIpfsAPI(), userAssetManager: mockAssetManager diff --git a/ios/brave-ios/Tests/BraveWalletTests/UserAssetsStoreTests.swift b/ios/brave-ios/Tests/BraveWalletTests/UserAssetsStoreTests.swift index b522529136e1..6b84165d0b36 100644 --- a/ios/brave-ios/Tests/BraveWalletTests/UserAssetsStoreTests.swift +++ b/ios/brave-ios/Tests/BraveWalletTests/UserAssetsStoreTests.swift @@ -85,7 +85,7 @@ class UserAssetsStoreTests: XCTestCase { .dropFirst() .sink { assetStores in defer { assetStoresException.fulfill() } - XCTAssertEqual(assetStores.count, 8) + XCTAssertEqual(assetStores.count, 9) XCTAssertEqual( assetStores[0].token.symbol, @@ -139,6 +139,13 @@ class UserAssetsStoreTests: XCTestCase { ) XCTAssertTrue(assetStores[7].token.visible) XCTAssertEqual(assetStores[7].network, BraveWallet.NetworkInfo.mockBitcoinMainnet) + + XCTAssertEqual( + assetStores[8].token.symbol, + BraveWallet.NetworkInfo.mockZcashMainnet.nativeToken.symbol + ) + XCTAssertTrue(assetStores[8].token.visible) + XCTAssertEqual(assetStores[8].network, BraveWallet.NetworkInfo.mockZcashMainnet) } .store(in: &cancellables)