Skip to content

Commit

Permalink
Fix showing custom tokens in receive coin list module
Browse files Browse the repository at this point in the history
  • Loading branch information
ant013 committed Sep 15, 2023
1 parent 6eaae15 commit 4b8677c
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 97 deletions.
6 changes: 6 additions & 0 deletions UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2023,6 +2023,7 @@
ABC9A3CC73251E7F83A94181 /* UniswapV3TradeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9ACF418357FF7AFC64B3F /* UniswapV3TradeService.swift */; };
ABC9A3D46AA7356763213BA6 /* FeePriceScale.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AF8E8DE67732371A00E0 /* FeePriceScale.swift */; };
ABC9A3D48A3E0E1733F70686 /* SendBinanceService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A3F41BDCD5F4146E6E06 /* SendBinanceService.swift */; };
ABC9A3EA19771B14B0502A0A /* FullCoin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A9B35C58F6525F3B2D5C /* FullCoin.swift */; };
ABC9A3FCFC46EC73A7E57EA3 /* WalletConnectPairingModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A4BA46EDEEAB6CD9B25C /* WalletConnectPairingModule.swift */; };
ABC9A3FEF48388A60B8BACB5 /* DataSourceChain.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A22311B6AA64B7D93CB4 /* DataSourceChain.swift */; };
ABC9A4045F498EE345B998D8 /* IntegerFormAmountInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AB9077A6A0ABE4909B76 /* IntegerFormAmountInputView.swift */; };
Expand Down Expand Up @@ -2276,6 +2277,7 @@
ABC9AE042D6A3D70CA64F959 /* ContactBookModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A5518367F0DDDB94D320 /* ContactBookModule.swift */; };
ABC9AE0D23A7B54521E77052 /* ECashAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A4C563432A34A634B82A /* ECashAdapter.swift */; };
ABC9AE18DE62E4DDDD44916D /* SwapInputModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AAD55B8932EE75E3C037 /* SwapInputModule.swift */; };
ABC9AE1E60CABA0101D62738 /* FullCoin.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A9B35C58F6525F3B2D5C /* FullCoin.swift */; };
ABC9AE223619E13A296BED51 /* MarketCardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A2B7FBA735A76083990C /* MarketCardCell.swift */; };
ABC9AE2C026D04B679644279 /* IntegerAmountInputCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9AA527E63E18179CB689A /* IntegerAmountInputCell.swift */; };
ABC9AE3D64AF3981A68D9913 /* SendConfirmationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABC9A3AB799024C8FC2C7DD8 /* SendConfirmationViewModel.swift */; };
Expand Down Expand Up @@ -3768,6 +3770,7 @@
ABC9A950663B76424B1761B3 /* EventHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventHandler.swift; sourceTree = "<group>"; };
ABC9A9628A708749A31EEA70 /* ProFeaturesAuthorizationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProFeaturesAuthorizationManager.swift; sourceTree = "<group>"; };
ABC9A99184EE1D5D052C52E9 /* ContactBookSettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactBookSettingsViewController.swift; sourceTree = "<group>"; };
ABC9A9B35C58F6525F3B2D5C /* FullCoin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FullCoin.swift; sourceTree = "<group>"; };
ABC9A9C09ECB9B0CCBAD8C21 /* SendEip1155ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendEip1155ViewController.swift; sourceTree = "<group>"; };
ABC9A9E0190FAD212E2E007F /* RestoreCloudPassphraseModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RestoreCloudPassphraseModule.swift; sourceTree = "<group>"; };
ABC9A9E2C039C005650491D2 /* WalletConnectAppShowModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletConnectAppShowModule.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4413,6 +4416,7 @@
2FA5D1C0A43841C61C61E3AF /* CurrencyValue.swift */,
ABC9A8CE84FA36438BE4D6B5 /* FileManager.swift */,
11B3593FBD158050C9FEF6B9 /* Misc.swift */,
ABC9A9B35C58F6525F3B2D5C /* FullCoin.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -9104,6 +9108,7 @@
11B359425D03F504ECA51B1A /* BlockchainSettingsView.swift in Sources */,
11B35FFD159D864F6D914F08 /* AppearanceView.swift in Sources */,
11B350CA618DD7BBA452FC33 /* AppearanceViewModel.swift in Sources */,
ABC9AE1E60CABA0101D62738 /* FullCoin.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -10380,6 +10385,7 @@
11B35B6E11AE440A79D53E0F /* BlockchainSettingsView.swift in Sources */,
11B35245CD0D5B0E44E413F4 /* AppearanceView.swift in Sources */,
11B35A18AA61F8C06AB1C15B /* AppearanceViewModel.swift in Sources */,
ABC9A3EA19771B14B0502A0A /* FullCoin.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
7 changes: 7 additions & 0 deletions UnstoppableWallet/UnstoppableWallet/Extensions/FullCoin.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import MarketKit

extension FullCoin: Equatable {
public static func ==(lhs: Self, rhs: Self) -> Bool {
lhs.coin == rhs.coin && lhs.tokens == rhs.tokens
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ struct ReceiveModule {

let coinProvider = CoinProvider(
marketKit: App.shared.marketKit,
accountType: account.type,
predefined: service.predefinedCoins
walletManager: App.shared.walletManager,
accountType: account.type
)

let selectCoinService = ReceiveSelectCoinService(provider: coinProvider)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Combine
import Foundation
import MarketKit
import Combine

class ReceiveService {
private let account: Account
Expand All @@ -24,8 +24,8 @@ class ReceiveService {
private func showReceive(token: Token) {
// check if wallet already exist
let wallet = walletManager
.activeWallets
.first { $0.token == token }
.activeWallets
.first { $0.token == token }

if let wallet {
showTokenSubject.send(wallet)
Expand All @@ -50,11 +50,11 @@ class ReceiveService {

// check if has existed wallets
let wallets = walletManager
.activeWallets
.filter { wallet in tokens.contains(wallet.token) }
.activeWallets
.filter { wallet in tokens.contains(wallet.token) }

switch wallets.count {
case 0: // create wallet and show deposit
case 0: // create wallet and show deposit
switch first.blockchainType {
case .bitcoin, .litecoin, .bitcoinCash:
guard let defaultToken = try? marketKit.token(query: first.blockchainType.defaultTokenQuery) else {
Expand All @@ -64,12 +64,12 @@ class ReceiveService {
let wallet = createWallet(token: defaultToken)
showTokenSubject.send(wallet)
case .zcash:
showZcashRestoreSelectSubject.send(first) // we must enable zcash wallet and ask for birthday
showZcashRestoreSelectSubject.send(first) // we must enable zcash wallet and ask for birthday
default: ()
}
case 1: // just show deposit. When unique token and it's restored
case 1: // just show deposit. When unique token and it's restored
showTokenSubject.send(wallets[0])
default: // show choose derivation, addressFormat or other (when token is unique, but many wallets)
default: // show choose derivation, addressFormat or other (when token is unique, but many wallets)
chooseTokenType(blockchainType: first.blockchainType, wallets: wallets)
}
}
Expand All @@ -86,7 +86,7 @@ class ReceiveService {
}

private func hasSettings(_ tokens: [Token]) -> Bool {
tokens.allSatisfy({ token in
tokens.allSatisfy { token in
switch token.blockchainType {
case .zcash: return true
default: ()
Expand All @@ -95,13 +95,11 @@ class ReceiveService {
case .derived, .addressType: return true
default: return false
}
})
}
}

}

extension ReceiveService {

var showTokenPublisher: AnyPublisher<Wallet, Never> {
showTokenSubject.eraseToAnyPublisher()
}
Expand Down Expand Up @@ -151,44 +149,10 @@ extension ReceiveService {
restoreSettingsService.save(settings: tokenWithSettings.settings, account: account, blockchainType: token.blockchainType)
showReceive(token: token)
}

}
extension ReceiveService {

extension ReceiveService {
func isEnabled(coin: Coin) -> Bool {
walletManager.activeWallets.contains { $0.coin == coin }
}

var predefinedCoins: [FullCoin] {
// get all restored coins
let activeWallets = walletManager.activeWallets
let walletCoins = activeWallets.map {
$0.coin
}
// get all native coins for supported blockchains
let nativeCoins = CoinProvider.nativeCoins(marketKit: marketKit)
let predefinedCoins = (walletCoins + nativeCoins).removeDuplicates()

// found all full coins
let fullCoins = try? marketKit.fullCoins(coinUids: predefinedCoins.map {
$0.uid
})

// filter not supported by current account
let predefined = fullCoins?.filter { fullCoin in
fullCoin.tokens.contains { account.type.supports(token: $0) }
}.sorted { lhsCoin, rhsCoin in
let lhsEnabled = isEnabled(coin: lhsCoin.coin)
let rhsEnabled = isEnabled(coin: rhsCoin.coin)

if lhsEnabled != rhsEnabled {
return lhsEnabled
}

return lhsCoin.coin.marketCapRank ?? Int.max < rhsCoin.coin.marketCapRank ?? Int.max
}

return predefined ?? []
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,50 +3,91 @@ import MarketKit

class CoinProvider {
private let marketKit: MarketKit.Kit
private let walletManager: WalletManager
private let accountType: AccountType

var filter: String = ""
let predefined: [FullCoin]
var custom: [FullCoin] = []
var predefined: [FullCoin] = []

init(marketKit: MarketKit.Kit, accountType: AccountType, predefined: [FullCoin]) {
init(marketKit: MarketKit.Kit, walletManager: WalletManager, accountType: AccountType) {
self.marketKit = marketKit
self.walletManager = walletManager
self.accountType = accountType
self.predefined = predefined

custom = walletManager.activeWallets
.filter { wallet in wallet.token.isCustom }
.map { FullCoin(coin: $0.coin, tokens: [$0.token]) }

predefined = predefinedCoins
}

private var nativeFullCoins: [FullCoin] {
do {
let blockchainTypes = BlockchainType.supported.sorted()
let queries = blockchainTypes.map { $0.nativeTokenQueries }.flatMap { $0 }
let coinUids = try marketKit
.tokens(queries: queries)
.map { $0.coin.uid }

return try marketKit.fullCoins(coinUids: coinUids)
} catch {
return []
}
}

private func customCoins(filter: String) -> [FullCoin] {
custom.filter { fullCoin in
fullCoin.coin.code.localizedCaseInsensitiveContains(filter) || fullCoin.coin.name.localizedCaseInsensitiveContains(filter)
}
}

}

extension CoinProvider {

func fetch() -> [FullCoin] {
func fetch(filter: String) -> [FullCoin] {
guard !filter.isEmpty else {
return predefined
}

do {
if !filter.isEmpty {
let fullCoins = try marketKit.fullCoins(filter: filter)

return fullCoins.filter { fullCoin in
fullCoin.tokens.contains { accountType.supports(token: $0) }
}
} else {
return predefined
var fullCoins = try marketKit.fullCoins(filter: filter)
fullCoins.append(contentsOf: customCoins(filter: filter))

return fullCoins.filter { fullCoin in
fullCoin.tokens.contains { accountType.supports(token: $0) }
}
} catch {
return []
}

return predefined
}

}

extension CoinProvider {

static func nativeCoins(marketKit: MarketKit.Kit) -> [Coin] {
do {
let blockchainTypes = BlockchainType.supported.sorted()
let queries = blockchainTypes.map { $0.nativeTokenQueries }.flatMap { $0 }
let nativeTokens = try marketKit.tokens(queries: queries)
return nativeTokens.map { $0.coin }
} catch {
return []
var predefinedCoins: [FullCoin] {
// get all restored coins
let activeWallets = walletManager.activeWallets
let walletCoins = activeWallets.map { $0.coin }

// found account full coins
var walletFullCoins = (try? marketKit.fullCoins(coinUids: walletCoins.map { $0.uid })) ?? []
walletFullCoins.append(contentsOf: custom)

// get all native coins for supported blockchains
let nativeFullCoins = nativeFullCoins


// filter not supported by current account
let predefined = (walletFullCoins + nativeFullCoins).removeDuplicates()
.filter { fullCoin in
fullCoin.tokens.contains { accountType.supports(token: $0) }
}

return predefined
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import MarketKit
class ReceiveSelectCoinService {
private let provider: CoinProvider

private var filter: String = "" {
didSet {
sync()
}
}

@PostPublished private(set) var coins = [FullCoin]()

init(provider: CoinProvider) {
Expand All @@ -14,47 +20,45 @@ class ReceiveSelectCoinService {
}

private func sync() {
let filter = provider.filter
let coins = provider.fetch(filter: filter)

let coins = provider.fetch()
if !filter.isEmpty {
self.coins = coins.sorted { lhsFullCoin, rhsFullCoin in
let filter = filter.lowercased()
if filter.isEmpty {
self.coins = coins
return
}

let lhsExactCode = lhsFullCoin.coin.code.lowercased() == filter
let rhsExactCode = rhsFullCoin.coin.code.lowercased() == filter
self.coins = coins.sorted { lhsFullCoin, rhsFullCoin in
let filter = filter.lowercased()

if lhsExactCode != rhsExactCode {
return lhsExactCode
}
let lhsExactCode = lhsFullCoin.coin.code.lowercased() == filter
let rhsExactCode = rhsFullCoin.coin.code.lowercased() == filter

let lhsStartsWithCode = lhsFullCoin.coin.code.lowercased().starts(with: filter)
let rhsStartsWithCode = rhsFullCoin.coin.code.lowercased().starts(with: filter)
if lhsExactCode != rhsExactCode {
return lhsExactCode
}

if lhsStartsWithCode != rhsStartsWithCode {
return lhsStartsWithCode
}
let lhsStartsWithCode = lhsFullCoin.coin.code.lowercased().starts(with: filter)
let rhsStartsWithCode = rhsFullCoin.coin.code.lowercased().starts(with: filter)

let lhsStartsWithName = lhsFullCoin.coin.name.lowercased().starts(with: filter)
let rhsStartsWithName = rhsFullCoin.coin.name.lowercased().starts(with: filter)
if lhsStartsWithCode != rhsStartsWithCode {
return lhsStartsWithCode
}

if lhsStartsWithName != rhsStartsWithName {
return lhsStartsWithName
}
let lhsStartsWithName = lhsFullCoin.coin.name.lowercased().starts(with: filter)
let rhsStartsWithName = rhsFullCoin.coin.name.lowercased().starts(with: filter)

return lhsFullCoin.coin.name.lowercased() < rhsFullCoin.coin.name.lowercased()
if lhsStartsWithName != rhsStartsWithName {
return lhsStartsWithName
}
} else {
self.coins = coins

return lhsFullCoin.coin.name.lowercased() < rhsFullCoin.coin.name.lowercased()
}
}
}

extension ReceiveSelectCoinService {
func set(filter: String) {
provider.filter = filter

sync()
self.filter = filter
}

func fullCoin(uid: String) -> FullCoin? {
Expand Down

0 comments on commit 4b8677c

Please sign in to comment.