Refactoring: Extract non datastore related code from TokenDatastore #3104

pull/3105/head
Vladyslav shepitko 3 years ago
parent 566acaab95
commit 52b700cf73
  1. 4
      AlphaWallet.xcodeproj/project.pbxproj
  2. 2
      AlphaWallet/Browser/Coordinators/QRCodeResolutionCoordinator.swift
  3. 2
      AlphaWallet/Core/Coordinators/CustomUrlSchemeCoordinator.swift
  4. 101
      AlphaWallet/Core/Coordinators/WalletBalance/PrivateBalanceFetcher.swift
  5. 359
      AlphaWallet/Core/Types/TokenProviderType.swift
  6. 9
      AlphaWallet/EtherClient/TrustClient/Models/RawTransaction.swift
  7. 7
      AlphaWallet/Market/Coordinators/UniversalLinkCoordinator.swift
  8. 7
      AlphaWallet/Tokens/Coordinators/SingleChainTokenCoordinator.swift
  9. 31
      AlphaWallet/Tokens/Logic/ContractDataDetector.swift
  10. 323
      AlphaWallet/Tokens/Types/TokensDataStore.swift
  11. 5
      AlphaWallet/Transactions/Coordinators/SingleChainTransactionEtherscanDataCoordinator.swift
  12. 2
      AlphaWallet/Transfer/Coordinators/SendCoordinator.swift

@ -877,6 +877,7 @@
87F4D41E26C26C3A00EFB9BC /* SortTokensParam.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87F4D41D26C26C3A00EFB9BC /* SortTokensParam.swift */; }; 87F4D41E26C26C3A00EFB9BC /* SortTokensParam.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87F4D41D26C26C3A00EFB9BC /* SortTokensParam.swift */; };
87F4D42026C26C6E00EFB9BC /* EmptyFilteringResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87F4D41F26C26C6E00EFB9BC /* EmptyFilteringResultView.swift */; }; 87F4D42026C26C6E00EFB9BC /* EmptyFilteringResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87F4D41F26C26C6E00EFB9BC /* EmptyFilteringResultView.swift */; };
87F9972824E155280092D262 /* SeedPhraseBackupIntroductionViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87F9972724E155280092D262 /* SeedPhraseBackupIntroductionViewControllerTests.swift */; }; 87F9972824E155280092D262 /* SeedPhraseBackupIntroductionViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87F9972724E155280092D262 /* SeedPhraseBackupIntroductionViewControllerTests.swift */; };
87FB0A8226D8D307000EC15F /* TokenProviderType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87FB0A8126D8D307000EC15F /* TokenProviderType.swift */; };
87FBAE0124A1EE67005EF293 /* AddressOrEnsNameLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87FBAE0024A1EE67005EF293 /* AddressOrEnsNameLabel.swift */; }; 87FBAE0124A1EE67005EF293 /* AddressOrEnsNameLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87FBAE0024A1EE67005EF293 /* AddressOrEnsNameLabel.swift */; };
87FF2E422567F0A3002350EB /* BlockieGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87FF2E412567F0A3002350EB /* BlockieGenerator.swift */; }; 87FF2E422567F0A3002350EB /* BlockieGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87FF2E412567F0A3002350EB /* BlockieGenerator.swift */; };
AA26C61F20412A1E00318B9B /* TokensCardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA26C61D20412A1D00318B9B /* TokensCardViewController.swift */; }; AA26C61F20412A1E00318B9B /* TokensCardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA26C61D20412A1D00318B9B /* TokensCardViewController.swift */; };
@ -1836,6 +1837,7 @@
87F4D41D26C26C3A00EFB9BC /* SortTokensParam.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SortTokensParam.swift; sourceTree = "<group>"; }; 87F4D41D26C26C3A00EFB9BC /* SortTokensParam.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SortTokensParam.swift; sourceTree = "<group>"; };
87F4D41F26C26C6E00EFB9BC /* EmptyFilteringResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyFilteringResultView.swift; sourceTree = "<group>"; }; 87F4D41F26C26C6E00EFB9BC /* EmptyFilteringResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyFilteringResultView.swift; sourceTree = "<group>"; };
87F9972724E155280092D262 /* SeedPhraseBackupIntroductionViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedPhraseBackupIntroductionViewControllerTests.swift; sourceTree = "<group>"; }; 87F9972724E155280092D262 /* SeedPhraseBackupIntroductionViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedPhraseBackupIntroductionViewControllerTests.swift; sourceTree = "<group>"; };
87FB0A8126D8D307000EC15F /* TokenProviderType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenProviderType.swift; sourceTree = "<group>"; };
87FBAE0024A1EE67005EF293 /* AddressOrEnsNameLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressOrEnsNameLabel.swift; sourceTree = "<group>"; }; 87FBAE0024A1EE67005EF293 /* AddressOrEnsNameLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressOrEnsNameLabel.swift; sourceTree = "<group>"; };
87FF2E412567F0A3002350EB /* BlockieGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockieGenerator.swift; sourceTree = "<group>"; }; 87FF2E412567F0A3002350EB /* BlockieGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockieGenerator.swift; sourceTree = "<group>"; };
AA26C61D20412A1D00318B9B /* TokensCardViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokensCardViewController.swift; sourceTree = "<group>"; }; AA26C61D20412A1D00318B9B /* TokensCardViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokensCardViewController.swift; sourceTree = "<group>"; };
@ -2984,6 +2986,7 @@
87D457F626677CD300BA1442 /* NativecryptoBalanceViewModel.swift */, 87D457F626677CD300BA1442 /* NativecryptoBalanceViewModel.swift */,
87C65F542661289400919819 /* Atomic.swift */, 87C65F542661289400919819 /* Atomic.swift */,
87F4D41D26C26C3A00EFB9BC /* SortTokensParam.swift */, 87F4D41D26C26C3A00EFB9BC /* SortTokensParam.swift */,
87FB0A8126D8D307000EC15F /* TokenProviderType.swift */,
); );
path = Types; path = Types;
sourceTree = "<group>"; sourceTree = "<group>";
@ -5694,6 +5697,7 @@
5E7C7BDE0060F5C350AFD34B /* ActivityViewController.swift in Sources */, 5E7C7BDE0060F5C350AFD34B /* ActivityViewController.swift in Sources */,
5E7C76194F5934264E5BABC8 /* ActivityViewModel.swift in Sources */, 5E7C76194F5934264E5BABC8 /* ActivityViewModel.swift in Sources */,
87F4D41A26C26C0700EFB9BC /* DropDownView.swift in Sources */, 87F4D41A26C26C0700EFB9BC /* DropDownView.swift in Sources */,
87FB0A8226D8D307000EC15F /* TokenProviderType.swift in Sources */,
5E7C72337A4230E78009B7E5 /* TransactionDetailsViewModel.swift in Sources */, 5E7C72337A4230E78009B7E5 /* TransactionDetailsViewModel.swift in Sources */,
5E7C72EF748338DDBABB53F2 /* GetBlockTimestampCoordinator.swift in Sources */, 5E7C72EF748338DDBABB53F2 /* GetBlockTimestampCoordinator.swift in Sources */,
5E7C7416B5A023776E04A21D /* Features.swift in Sources */, 5E7C7416B5A023776E04A21D /* Features.swift in Sources */,

@ -251,7 +251,7 @@ extension QRCodeResolutionCoordinator: ScanQRCodeCoordinatorDelegate {
return .value((transactionType, token)) return .value((transactionType, token))
} else { } else {
return Promise { resolver in return Promise { resolver in
ContractDataDetector(address: contract, storage: storage, assetDefinitionStore: assetDefinitionStore).fetch { result in ContractDataDetector(address: contract, account: storage.account, server: storage.server, assetDefinitionStore: assetDefinitionStore).fetch { result in
switch result { switch result {
case .name, .symbol, .balance, .decimals, .nonFungibleTokenComplete, .delegateTokenComplete, .failed: case .name, .symbol, .balance, .decimals, .nonFungibleTokenComplete, .delegateTokenComplete, .failed:
resolver.reject(CheckEIP681Error.contractInvalid) resolver.reject(CheckEIP681Error.contractInvalid)

@ -39,7 +39,7 @@ class CustomUrlSchemeCoordinator: Coordinator {
if tokensDatastore.token(forContract: contract) != nil { if tokensDatastore.token(forContract: contract) != nil {
strongSelf.openSendPayFlowFor(server: server, contract: contract, recipient: recipient, amount: amount) strongSelf.openSendPayFlowFor(server: server, contract: contract, recipient: recipient, amount: amount)
} else { } else {
ContractDataDetector(address: contract, storage: tokensDatastore, assetDefinitionStore: strongSelf.assetDefinitionStore).fetch { data in ContractDataDetector(address: contract, account: tokensDatastore.account, server: tokensDatastore.server, assetDefinitionStore: strongSelf.assetDefinitionStore).fetch { data in
switch data { switch data {
case .name, .symbol, .balance, .decimals: case .name, .symbol, .balance, .decimals:
break break

@ -33,32 +33,11 @@ class PrivateBalanceFetcher: PrivateBalanceFetcherType {
}() }()
weak var erc721TokenIdsFetcher: Erc721TokenIdsFetcher? weak var erc721TokenIdsFetcher: Erc721TokenIdsFetcher?
private lazy var getNativeCryptoCurrencyBalanceCoordinator: GetNativeCryptoCurrencyBalanceCoordinator = { private lazy var tokenProvider: TokenProviderType = {
return GetNativeCryptoCurrencyBalanceCoordinator(forServer: server, queue: backgroundQueue) return TokenProvider(account: account, server: server, queue: backgroundQueue)
}()
private lazy var getERC20BalanceCoordinator: GetERC20BalanceCoordinator = {
return GetERC20BalanceCoordinator(forServer: server, queue: backgroundQueue)
}()
private lazy var getERC875BalanceCoordinator: GetERC875BalanceCoordinator = {
return GetERC875BalanceCoordinator(forServer: server, queue: backgroundQueue)
}()
private lazy var getERC721ForTicketsBalanceCoordinator: GetERC721ForTicketsBalanceCoordinator = {
return GetERC721ForTicketsBalanceCoordinator(forServer: server, queue: backgroundQueue)
}()
private lazy var getERC721BalanceCoordinator: GetERC721BalanceCoordinator = {
return GetERC721BalanceCoordinator(forServer: server, queue: backgroundQueue)
}() }()
private let account: Wallet private let account: Wallet
private let numberOfTimesToRetryFetchContractData = 2
private var chainId: Int {
return server.chainID
}
private let openSea: OpenSea private let openSea: OpenSea
private let backgroundQueue: DispatchQueue private let backgroundQueue: DispatchQueue
@ -99,72 +78,8 @@ class PrivateBalanceFetcher: PrivateBalanceFetcherType {
} }
} }
private func getERC20Balance(for address: AlphaWallet.Address, completion: @escaping (ResultResult<BigInt, AnyError>.t) -> Void) { deinit {
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in enabledObjectsObservation.flatMap{ $0.invalidate() }
guard let strongSelf = self else { return }
strongSelf.getERC20BalanceCoordinator.getBalance(for: strongSelf.account.address, contract: address) { result in
switch result {
case .success:
completion(result)
case .failure:
if !triggerRetry() {
completion(result)
}
}
}
}
}
private func getERC875Balance(for address: AlphaWallet.Address, completion: @escaping (ResultResult<[String], AnyError>.t) -> Void) {
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in
guard let strongSelf = self else { return }
strongSelf.getERC875BalanceCoordinator.getERC875TokenBalance(for: strongSelf.account.address, contract: address) { result in
switch result {
case .success:
completion(result)
case .failure:
if !triggerRetry() {
completion(result)
}
}
}
}
}
private func getERC721ForTicketsBalance(for address: AlphaWallet.Address, completion: @escaping (ResultResult<[String], AnyError>.t) -> Void) {
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in
guard let strongSelf = self else { return }
strongSelf.getERC721ForTicketsBalanceCoordinator.getERC721ForTicketsTokenBalance(for: strongSelf.account.address, contract: address) { result in
switch result {
case .success:
completion(result)
case .failure:
if !triggerRetry() {
completion(result)
}
}
}
}
}
private func getERC721Balance(for address: AlphaWallet.Address, completion: @escaping (ResultResult<[String], AnyError>.t) -> Void) {
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in
guard let strongSelf = self else { return }
strongSelf.getERC721BalanceCoordinator.getERC721TokenBalance(for: strongSelf.account.address, contract: address) { result in
switch result {
case .success(let balance):
if balance >= Int.max {
completion(.failure(AnyError(Web3Error(description: ""))))
} else {
completion(.success([String](repeating: "0", count: Int(balance))))
}
case .failure(let error):
if !triggerRetry() {
completion(.failure(error))
}
}
}
}
} }
private func getTokensFromOpenSea() -> OpenSea.PromiseResult { private func getTokensFromOpenSea() -> OpenSea.PromiseResult {
@ -228,7 +143,7 @@ class PrivateBalanceFetcher: PrivateBalanceFetcherType {
private func refreshEthBalance(etherToken: Activity.AssignedToken, group: DispatchGroup) { private func refreshEthBalance(etherToken: Activity.AssignedToken, group: DispatchGroup) {
let tokensDatastore = self.tokensDatastore let tokensDatastore = self.tokensDatastore
group.enter() group.enter()
getNativeCryptoCurrencyBalanceCoordinator.getBalance(for: account.address) { [weak self] result in tokenProvider.getEthBalance(for: account.address) { [weak self] result in
switch result { switch result {
case .success(let balance): case .success(let balance):
tokensDatastore.update(primaryKey: etherToken.primaryKey, action: .value(balance.value)) { balanceValueHasChange in tokensDatastore.update(primaryKey: etherToken.primaryKey, action: .value(balance.value)) { balanceValueHasChange in
@ -264,7 +179,7 @@ class PrivateBalanceFetcher: PrivateBalanceFetcherType {
case .nativeCryptocurrency: case .nativeCryptocurrency:
completion(nil) completion(nil)
case .erc20: case .erc20:
getERC20Balance(for: tokenObject.contractAddress, completion: { result in tokenProvider.getERC20Balance(for: tokenObject.contractAddress, completion: { result in
switch result { switch result {
case .success(let balance): case .success(let balance):
tokensDatastore.update(primaryKey: tokenObject.primaryKey, action: .value(balance), completion: completion) tokensDatastore.update(primaryKey: tokenObject.primaryKey, action: .value(balance), completion: completion)
@ -273,7 +188,7 @@ class PrivateBalanceFetcher: PrivateBalanceFetcherType {
} }
}) })
case .erc875: case .erc875:
getERC875Balance(for: tokenObject.contractAddress, completion: { result in tokenProvider.getERC875Balance(for: tokenObject.contractAddress, completion: { result in
switch result { switch result {
case .success(let balance): case .success(let balance):
tokensDatastore.update(primaryKey: tokenObject.primaryKey, action: .nonFungibleBalance(balance), completion: completion) tokensDatastore.update(primaryKey: tokenObject.primaryKey, action: .nonFungibleBalance(balance), completion: completion)
@ -284,7 +199,7 @@ class PrivateBalanceFetcher: PrivateBalanceFetcherType {
case .erc721: case .erc721:
break break
case .erc721ForTickets: case .erc721ForTickets:
getERC721ForTicketsBalance(for: tokenObject.contractAddress, completion: { result in tokenProvider.getERC721ForTicketsBalance(for: tokenObject.contractAddress, completion: { result in
switch result { switch result {
case .success(let balance): case .success(let balance):
tokensDatastore.update(primaryKey: tokenObject.primaryKey, action: .nonFungibleBalance(balance), completion: completion) tokensDatastore.update(primaryKey: tokenObject.primaryKey, action: .nonFungibleBalance(balance), completion: completion)

@ -0,0 +1,359 @@
//
// TokenProviderType.swift
// AlphaWallet
//
// Created by Vladyslav Shepitko on 27.08.2021.
//
import PromiseKit
import Result
import BigInt
// NOTE: Think about the name, more fittable name is needed
protocol TokenProviderType: class {
func getContractName(for address: AlphaWallet.Address, completion: @escaping (ResultResult<String, AnyError>.t) -> Void)
func getContractSymbol(for address: AlphaWallet.Address, completion: @escaping (ResultResult<String, AnyError>.t) -> Void)
func getDecimals(for address: AlphaWallet.Address, completion: @escaping (ResultResult<UInt8, AnyError>.t) -> Void)
func getContractName(for address: AlphaWallet.Address) -> Promise<String>
func getContractSymbol(for address: AlphaWallet.Address) -> Promise<String>
func getDecimals(for address: AlphaWallet.Address) -> Promise<UInt8>
func getTokenType(for address: AlphaWallet.Address) -> Promise<TokenType>
func getERC20Balance(for address: AlphaWallet.Address, completion: @escaping (ResultResult<BigInt, AnyError>.t) -> Void)
func getERC875Balance(for address: AlphaWallet.Address, completion: @escaping (ResultResult<[String], AnyError>.t) -> Void)
func getERC721ForTicketsBalance(for address: AlphaWallet.Address, completion: @escaping (ResultResult<[String], AnyError>.t) -> Void)
func getERC721Balance(for address: AlphaWallet.Address, completion: @escaping (ResultResult<[String], AnyError>.t) -> Void)
func getTokenType(for address: AlphaWallet.Address, completion: @escaping (TokenType) -> Void)
func getEthBalance(for address: AlphaWallet.Address, completion: @escaping (ResultResult<Balance, AnyError>.t) -> Void)
}
class TokenProvider: TokenProviderType {
static let fetchContractDataTimeout = TimeInterval(4)
private lazy var getNameCoordinator: GetNameCoordinator = {
return GetNameCoordinator(forServer: server)
}()
private lazy var getSymbolCoordinator: GetSymbolCoordinator = {
return GetSymbolCoordinator(forServer: server)
}()
private lazy var getNativeCryptoCurrencyBalanceCoordinator: GetNativeCryptoCurrencyBalanceCoordinator = {
return GetNativeCryptoCurrencyBalanceCoordinator(forServer: server, queue: queue)
}()
private lazy var getERC20BalanceCoordinator: GetERC20BalanceCoordinator = {
return GetERC20BalanceCoordinator(forServer: server, queue: queue)
}()
private lazy var getERC875BalanceCoordinator: GetERC875BalanceCoordinator = {
return GetERC875BalanceCoordinator(forServer: server, queue: queue)
}()
private lazy var getERC721ForTicketsBalanceCoordinator: GetERC721ForTicketsBalanceCoordinator = {
return GetERC721ForTicketsBalanceCoordinator(forServer: server, queue: queue)
}()
private lazy var getIsERC875ContractCoordinator: GetIsERC875ContractCoordinator = {
return GetIsERC875ContractCoordinator(forServer: server)
}()
private lazy var getERC721BalanceCoordinator: GetERC721BalanceCoordinator = {
return GetERC721BalanceCoordinator(forServer: server, queue: queue)
}()
private lazy var getIsERC721ForTicketsContractCoordinator: GetIsERC721ForTicketsContractCoordinator = {
return GetIsERC721ForTicketsContractCoordinator(forServer: server)
}()
private lazy var getIsERC721ContractCoordinator: GetIsERC721ContractCoordinator = {
return GetIsERC721ContractCoordinator(forServer: server)
}()
private lazy var getDecimalsCoordinator: GetDecimalsCoordinator = {
return GetDecimalsCoordinator(forServer: server)
}()
private let account: Wallet
private let numberOfTimesToRetryFetchContractData = 2
private let server: RPCServer
private let queue: DispatchQueue?
init(account: Wallet, server: RPCServer, queue: DispatchQueue? = .none) {
self.account = account
self.queue = queue
self.server = server
}
func getContractName(for address: AlphaWallet.Address,
completion: @escaping (ResultResult<String, AnyError>.t) -> Void) {
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in
guard let strongSelf = self else { return }
strongSelf.getNameCoordinator.getName(for: address) { (result) in
switch result {
case .success:
completion(result)
case .failure:
if !triggerRetry() {
completion(result)
}
}
}
}
}
func getEthBalance(for address: AlphaWallet.Address, completion: @escaping (ResultResult<Balance, AnyError>.t) -> Void) {
getNativeCryptoCurrencyBalanceCoordinator.getBalance(for: address, completion: completion)
}
func getContractSymbol(for address: AlphaWallet.Address,
completion: @escaping (ResultResult<String, AnyError>.t) -> Void) {
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in
guard let strongSelf = self else { return }
strongSelf.getSymbolCoordinator.getSymbol(for: address) { result in
switch result {
case .success:
completion(result)
case .failure:
if !triggerRetry() {
completion(result)
}
}
}
}
}
func getDecimals(for address: AlphaWallet.Address,
completion: @escaping (ResultResult<UInt8, AnyError>.t) -> Void) {
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in
guard let strongSelf = self else { return }
strongSelf.getDecimalsCoordinator.getDecimals(for: address) { result in
switch result {
case .success:
completion(result)
case .failure:
if !triggerRetry() {
completion(result)
}
}
}
}
}
func getContractName(for address: AlphaWallet.Address) -> Promise<String> {
Promise { seal in
getContractName(for: address) { (result) in
switch result {
case .success(let name):
seal.fulfill(name)
case .failure(let error):
seal.reject(error)
}
}
}
}
func getContractSymbol(for address: AlphaWallet.Address) -> Promise<String> {
Promise { seal in
getContractSymbol(for: address) { result in
switch result {
case .success(let name):
seal.fulfill(name)
case .failure(let error):
seal.reject(error)
}
}
}
}
func getDecimals(for address: AlphaWallet.Address) -> Promise<UInt8> {
Promise { seal in
getDecimals(for: address) { result in
switch result {
case .success(let name):
seal.fulfill(name)
case .failure(let error):
seal.reject(error)
}
}
}
}
func getTokenType(for address: AlphaWallet.Address) -> Promise<TokenType> {
Promise { seal in
getTokenType(for: address) { tokenType in
seal.fulfill(tokenType)
}
}
}
func getERC20Balance(for address: AlphaWallet.Address, completion: @escaping (ResultResult<BigInt, AnyError>.t) -> Void) {
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in
guard let strongSelf = self else { return }
strongSelf.getERC20BalanceCoordinator.getBalance(for: strongSelf.account.address, contract: address) { result in
switch result {
case .success:
completion(result)
case .failure:
if !triggerRetry() {
completion(result)
}
}
}
}
}
func getERC875Balance(for address: AlphaWallet.Address,
completion: @escaping (ResultResult<[String], AnyError>.t) -> Void) {
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in
guard let strongSelf = self else { return }
strongSelf.getERC875BalanceCoordinator.getERC875TokenBalance(for: strongSelf.account.address, contract: address) { result in
switch result {
case .success:
completion(result)
case .failure:
if !triggerRetry() {
completion(result)
}
}
}
}
}
func getERC721ForTicketsBalance(for address: AlphaWallet.Address,
completion: @escaping (ResultResult<[String], AnyError>.t) -> Void) {
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in
guard let strongSelf = self else { return }
strongSelf.getERC721ForTicketsBalanceCoordinator.getERC721ForTicketsTokenBalance(for: strongSelf.account.address, contract: address) { result in
switch result {
case .success:
completion(result)
case .failure:
if !triggerRetry() {
completion(result)
}
}
}
}
}
//TODO should callers call tokenURI and so on, instead?
func getERC721Balance(for address: AlphaWallet.Address, completion: @escaping (ResultResult<[String], AnyError>.t) -> Void) {
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in
guard let strongSelf = self else { return }
strongSelf.getERC721BalanceCoordinator.getERC721TokenBalance(for: strongSelf.account.address, contract: address) { result in
switch result {
case .success(let balance):
if balance >= Int.max {
completion(.failure(AnyError(Web3Error(description: ""))))
} else {
completion(.success([String](repeating: "0", count: Int(balance))))
}
case .failure(let error):
if !triggerRetry() {
completion(.failure(error))
}
}
}
}
}
func getTokenType(for address: AlphaWallet.Address, completion: @escaping (TokenType) -> Void) {
let isErc875Promise = Promise<Bool> { seal in
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in
guard let strongSelf = self else { return }
//Function hash is "0x4f452b9a". This might cause many "execution reverted" RPC errors
//TODO rewrite flow so we reduce checks for this as it causes too many "execution reverted" RPC errors and looks scary when we look in Charles proxy. Maybe check for ERC20 (via EIP165) as well as ERC721 in parallel first, then fallback to this ERC875 check
strongSelf.getIsERC875ContractCoordinator.getIsERC875Contract(for: address) { [weak self] result in
guard self != nil else { return }
switch result {
case .success(let isERC875):
if isERC875 {
seal.fulfill(true)
} else {
seal.fulfill(false)
}
case .failure:
if !triggerRetry() {
seal.fulfill(false)
}
}
}
}
}
enum Erc721Type {
case erc721
case erc721ForTickets
case notErc721
}
let isErc721Promise = Promise<Erc721Type> { seal in
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in
guard let strongSelf = self else { return }
strongSelf.getIsERC721ContractCoordinator.getIsERC721Contract(for: address) { [weak self] result in
guard let strongSelf = self else { return }
switch result {
case .success(let isERC721):
if isERC721 {
withRetry(times: strongSelf.numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry2 in
guard let strongSelf = self else { return }
strongSelf.getIsERC721ForTicketsContractCoordinator.getIsERC721ForTicketContract(for: address) { result in
switch result {
case .success(let isERC721ForTickets):
if isERC721ForTickets {
seal.fulfill(.erc721ForTickets)
} else {
seal.fulfill(.erc721)
}
case .failure:
if !triggerRetry2() {
seal.fulfill(.erc721)
}
}
}
}
} else {
seal.fulfill(.notErc721)
}
case .failure:
if !triggerRetry() {
seal.fulfill(.notErc721)
}
}
}
}
}
firstly {
isErc721Promise
}.done { isErc721 in
switch isErc721 {
case .erc721:
completion(.erc721)
case .erc721ForTickets:
completion(.erc721ForTickets)
case .notErc721:
break
}
}.cauterize()
firstly {
isErc875Promise
}.done { isErc875 in
if isErc875 {
completion(.erc875)
} else {
//no-op
}
}.cauterize()
firstly {
when(fulfilled: isErc875Promise.asVoid(), isErc721Promise.asVoid())
}.done { _, _ in
if isErc875Promise.value == false && isErc721Promise.value == .notErc721 {
completion(.erc20)
} else {
//no-op
}
}.cauterize()
}
}

@ -103,10 +103,11 @@ extension TransactionInstance {
let result = LocalizedOperationObjectInstance(from: transaction.from, to: recipient.eip55String, contract: contract, type: operationType.rawValue, value: String(value), tokenId: "", symbol: token.symbol, name: token.name, decimals: token.decimals) let result = LocalizedOperationObjectInstance(from: transaction.from, to: recipient.eip55String, contract: contract, type: operationType.rawValue, value: String(value), tokenId: "", symbol: token.symbol, name: token.name, decimals: token.decimals)
return .value([result]) return .value([result])
} else { } else {
let getContractName = tokensStorage.getContractName(for: contract) let tokenProvider: TokenProviderType = TokenProvider(account: tokensStorage.account, server: tokensStorage.server)
let getContractSymbol = tokensStorage.getContractSymbol(for: contract) let getContractName = tokenProvider.getContractName(for: contract)
let getDecimals = tokensStorage.getDecimals(for: contract) let getContractSymbol = tokenProvider.getContractSymbol(for: contract)
let getTokenType = tokensStorage.getTokenType(for: contract) let getDecimals = tokenProvider.getDecimals(for: contract)
let getTokenType = tokenProvider.getTokenType(for: contract)
return firstly { return firstly {
when(fulfilled: getContractName, getContractSymbol, getDecimals, getTokenType) when(fulfilled: getContractName, getContractSymbol, getDecimals, getTokenType)

@ -509,6 +509,7 @@ class UniversalLinkCoordinator: Coordinator {
} }
let tokensDatastore = strongSelf.tokensDatastore let tokensDatastore = strongSelf.tokensDatastore
let tokenProvider: TokenProviderType = TokenProvider(account: tokensDatastore.account, server: tokensDatastore.server)
if let existingToken = tokensDatastore.token(forContract: contractAddress) { if let existingToken = tokensDatastore.token(forContract: contractAddress) {
let name = XMLHandler(token: existingToken, assetDefinitionStore: strongSelf.assetDefinitionStore).getLabel(fallback: existingToken.name) let name = XMLHandler(token: existingToken, assetDefinitionStore: strongSelf.assetDefinitionStore).getLabel(fallback: existingToken.name)
makeTokenHolder(name: name, symbol: existingToken.symbol) makeTokenHolder(name: name, symbol: existingToken.symbol)
@ -516,9 +517,9 @@ class UniversalLinkCoordinator: Coordinator {
let localizedTokenTypeName = R.string.localizable.tokensTitlecase() let localizedTokenTypeName = R.string.localizable.tokensTitlecase()
makeTokenHolder(name: localizedTokenTypeName, symbol: "") makeTokenHolder(name: localizedTokenTypeName, symbol: "")
let getContractName = tokensDatastore.getContractName(for: contractAddress) let getContractName = tokenProvider.getContractName(for: contractAddress)
let getContractSymbol = tokensDatastore.getContractSymbol(for: contractAddress) let getContractSymbol = tokenProvider.getContractSymbol(for: contractAddress)
let getTokenType = tokensDatastore.getTokenType(for: contractAddress) let getTokenType = tokenProvider.getTokenType(for: contractAddress)
firstly { firstly {
when(fulfilled: getContractName, getContractSymbol, getTokenType) when(fulfilled: getContractName, getContractSymbol, getTokenType)
}.done { name, symbol, type in }.done { name, symbol, type in

@ -205,8 +205,9 @@ class SingleChainTokenCoordinator: Coordinator {
completion() completion()
return return
} }
let tokenProvider: TokenProviderType = TokenProvider(account: storage.account, server: storage.server)
for each in contracts { for each in contracts {
storage.getTokenType(for: each) { tokenType in tokenProvider.getTokenType(for: each) { tokenType in
switch tokenType { switch tokenType {
case .erc875: case .erc875:
//TODO long and very similar code below. Extract function //TODO long and very similar code below. Extract function
@ -358,7 +359,7 @@ class SingleChainTokenCoordinator: Coordinator {
} }
func fetchContractData(for address: AlphaWallet.Address, completion: @escaping (ContractData) -> Void) { func fetchContractData(for address: AlphaWallet.Address, completion: @escaping (ContractData) -> Void) {
ContractDataDetector(address: address, storage: storage, assetDefinitionStore: assetDefinitionStore).fetch(completion: completion) ContractDataDetector(address: address, account: session.account, server: session.server, assetDefinitionStore: assetDefinitionStore).fetch(completion: completion)
} }
func showTokenList(for type: PaymentFlow, token: TokenObject, navigationController: UINavigationController) { func showTokenList(for type: PaymentFlow, token: TokenObject, navigationController: UINavigationController) {
@ -706,4 +707,4 @@ extension SingleChainTokenCoordinator: TransactionInProgressCoordinatorDelegate
func transactionInProgressDidDismiss(in coordinator: TransactionInProgressCoordinator) { func transactionInProgressDidDismiss(in coordinator: TransactionInProgressCoordinator) {
removeCoordinator(coordinator) removeCoordinator(coordinator)
} }
} }

@ -17,7 +17,7 @@ enum ContractData {
class ContractDataDetector { class ContractDataDetector {
private let address: AlphaWallet.Address private let address: AlphaWallet.Address
private let storage: TokensDataStore private let tokenProvider: TokenProviderType
private let assetDefinitionStore: AssetDefinitionStore private let assetDefinitionStore: AssetDefinitionStore
private let namePromise: Promise<String> private let namePromise: Promise<String>
private let symbolPromise: Promise<String> private let symbolPromise: Promise<String>
@ -27,13 +27,22 @@ class ContractDataDetector {
private var failed = false private var failed = false
private var completion: ((ContractData) -> Void)? private var completion: ((ContractData) -> Void)?
init(address: AlphaWallet.Address, storage: TokensDataStore, assetDefinitionStore: AssetDefinitionStore) { init(address: AlphaWallet.Address, account: Wallet, server: RPCServer, assetDefinitionStore: AssetDefinitionStore) {
self.address = address self.address = address
self.storage = storage self.tokenProvider = TokenProvider(account: account, server: server)
self.assetDefinitionStore = assetDefinitionStore self.assetDefinitionStore = assetDefinitionStore
namePromise = storage.getContractName(for: address) namePromise = tokenProvider.getContractName(for: address)
symbolPromise = storage.getContractSymbol(for: address) symbolPromise = tokenProvider.getContractSymbol(for: address)
tokenTypePromise = storage.getTokenType(for: address) tokenTypePromise = tokenProvider.getTokenType(for: address)
}
init(address: AlphaWallet.Address, tokenProvider: TokenProviderType, assetDefinitionStore: AssetDefinitionStore) {
self.address = address
self.tokenProvider = tokenProvider
self.assetDefinitionStore = assetDefinitionStore
namePromise = tokenProvider.getContractName(for: address)
symbolPromise = tokenProvider.getContractSymbol(for: address)
tokenTypePromise = tokenProvider.getTokenType(for: address)
} }
/// Failure to obtain contract data may be due to no-connectivity. So we should check .failed(networkReachable: Bool) /// Failure to obtain contract data may be due to no-connectivity. So we should check .failed(networkReachable: Bool)
@ -68,7 +77,7 @@ class ContractDataDetector {
}.done { tokenType in }.done { tokenType in
switch tokenType { switch tokenType {
case .erc875: case .erc875:
self.storage.getERC875Balance(for: self.address) { result in self.tokenProvider.getERC875Balance(for: self.address) { result in
switch result { switch result {
case .success(let balance): case .success(let balance):
self.nonFungibleBalanceSeal.fulfill(balance) self.nonFungibleBalanceSeal.fulfill(balance)
@ -79,7 +88,7 @@ class ContractDataDetector {
} }
} }
case .erc721: case .erc721:
self.storage.getERC721Balance(for: self.address) { result in self.tokenProvider.getERC721Balance(for: self.address) { result in
switch result { switch result {
case .success(let balance): case .success(let balance):
self.nonFungibleBalanceSeal.fulfill(balance) self.nonFungibleBalanceSeal.fulfill(balance)
@ -90,7 +99,7 @@ class ContractDataDetector {
} }
} }
case .erc721ForTickets: case .erc721ForTickets:
self.storage.getERC721ForTicketsBalance(for: self.address) { result in self.tokenProvider.getERC721ForTicketsBalance(for: self.address) { result in
switch result { switch result {
case .success(let balance): case .success(let balance):
self.nonFungibleBalanceSeal.fulfill(balance) self.nonFungibleBalanceSeal.fulfill(balance)
@ -101,7 +110,7 @@ class ContractDataDetector {
} }
} }
case .erc20: case .erc20:
self.storage.getDecimals(for: self.address) { result in self.tokenProvider.getDecimals(for: self.address) { result in
switch result { switch result {
case .success(let decimal): case .success(let decimal):
self.decimalsSeal.fulfill(decimal) self.decimalsSeal.fulfill(decimal)
@ -168,4 +177,4 @@ class ContractDataDetector {
} }
} }
} }
} }

@ -29,51 +29,10 @@ class TokensDataStore {
return SessionManager(configuration: configuration) return SessionManager(configuration: configuration)
}() }()
private lazy var getNameCoordinator: GetNameCoordinator = { private lazy var tokenProvider: TokenProviderType = {
return GetNameCoordinator(forServer: server) return TokenProvider(account: account, server: server)
}() }()
private lazy var getSymbolCoordinator: GetSymbolCoordinator = {
return GetSymbolCoordinator(forServer: server)
}()
private lazy var getNativeCryptoCurrencyBalanceCoordinator: GetNativeCryptoCurrencyBalanceCoordinator = {
return GetNativeCryptoCurrencyBalanceCoordinator(forServer: server)
}()
private lazy var getERC20BalanceCoordinator: GetERC20BalanceCoordinator = {
return GetERC20BalanceCoordinator(forServer: server)
}()
private lazy var getERC875BalanceCoordinator: GetERC875BalanceCoordinator = {
return GetERC875BalanceCoordinator(forServer: server)
}()
private lazy var getERC721ForTicketsBalanceCoordinator: GetERC721ForTicketsBalanceCoordinator = {
return GetERC721ForTicketsBalanceCoordinator(forServer: server)
}()
private lazy var getIsERC875ContractCoordinator: GetIsERC875ContractCoordinator = {
return GetIsERC875ContractCoordinator(forServer: server)
}()
private lazy var getERC721BalanceCoordinator: GetERC721BalanceCoordinator = {
return GetERC721BalanceCoordinator(forServer: server)
}()
private lazy var getIsERC721ForTicketsContractCoordinator: GetIsERC721ForTicketsContractCoordinator = {
return GetIsERC721ForTicketsContractCoordinator(forServer: server)
}()
private lazy var getIsERC721ContractCoordinator: GetIsERC721ContractCoordinator = {
return GetIsERC721ContractCoordinator(forServer: server)
}()
private lazy var getDecimalsCoordinator: GetDecimalsCoordinator = {
return GetDecimalsCoordinator(forServer: server)
}()
private let account: Wallet
private let assetDefinitionStore: AssetDefinitionStore private let assetDefinitionStore: AssetDefinitionStore
private let realm: Realm private let realm: Realm
private var pricesTimer = Timer() private var pricesTimer = Timer()
@ -91,7 +50,9 @@ class TokensDataStore {
private let openSea: OpenSea private let openSea: OpenSea
private let queue = DispatchQueue.global() private let queue = DispatchQueue.global()
let account: Wallet
let server: RPCServer let server: RPCServer
weak var delegate: TokensDataStoreDelegate? weak var delegate: TokensDataStoreDelegate?
weak var priceDelegate: TokensDataStorePriceDelegate? weak var priceDelegate: TokensDataStorePriceDelegate?
weak var erc721TokenIdsFetcher: Erc721TokenIdsFetcher? weak var erc721TokenIdsFetcher: Erc721TokenIdsFetcher?
@ -220,279 +181,11 @@ class TokensDataStore {
refreshBalance() refreshBalance()
} }
func getContractName(for address: AlphaWallet.Address,
completion: @escaping (ResultResult<String, AnyError>.t) -> Void) {
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in
guard let strongSelf = self else { return }
strongSelf.getNameCoordinator.getName(for: address) { (result) in
switch result {
case .success:
completion(result)
case .failure:
if !triggerRetry() {
completion(result)
}
}
}
}
}
func getContractSymbol(for address: AlphaWallet.Address,
completion: @escaping (ResultResult<String, AnyError>.t) -> Void) {
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in
guard let strongSelf = self else { return }
strongSelf.getSymbolCoordinator.getSymbol(for: address) { result in
switch result {
case .success:
completion(result)
case .failure:
if !triggerRetry() {
completion(result)
}
}
}
}
}
func getDecimals(for address: AlphaWallet.Address,
completion: @escaping (ResultResult<UInt8, AnyError>.t) -> Void) {
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in
guard let strongSelf = self else { return }
strongSelf.getDecimalsCoordinator.getDecimals(for: address) { result in
switch result {
case .success:
completion(result)
case .failure:
if !triggerRetry() {
completion(result)
}
}
}
}
}
func getContractName(for address: AlphaWallet.Address) -> Promise<String> {
Promise { seal in
getContractName(for: address) { (result) in
switch result {
case .success(let name):
seal.fulfill(name)
case .failure(let error):
seal.reject(error)
}
}
}
}
func getContractSymbol(for address: AlphaWallet.Address) -> Promise<String> {
Promise { seal in
getContractSymbol(for: address) { result in
switch result {
case .success(let name):
seal.fulfill(name)
case .failure(let error):
seal.reject(error)
}
}
}
}
func getDecimals(for address: AlphaWallet.Address) -> Promise<UInt8> {
Promise { seal in
getDecimals(for: address) { result in
switch result {
case .success(let name):
seal.fulfill(name)
case .failure(let error):
seal.reject(error)
}
}
}
}
func getTokenType(for address: AlphaWallet.Address) -> Promise<TokenType> {
Promise { seal in
getTokenType(for: address) { tokenType in
seal.fulfill(tokenType)
}
}
}
func getERC20Balance(for address: AlphaWallet.Address, completion: @escaping (ResultResult<BigInt, AnyError>.t) -> Void) {
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in
guard let strongSelf = self else { return }
strongSelf.getERC20BalanceCoordinator.getBalance(for: strongSelf.account.address, contract: address) { result in
switch result {
case .success:
completion(result)
case .failure:
if !triggerRetry() {
completion(result)
}
}
}
}
}
func getERC875Balance(for address: AlphaWallet.Address,
completion: @escaping (ResultResult<[String], AnyError>.t) -> Void) {
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in
guard let strongSelf = self else { return }
strongSelf.getERC875BalanceCoordinator.getERC875TokenBalance(for: strongSelf.account.address, contract: address) { result in
switch result {
case .success:
completion(result)
case .failure:
if !triggerRetry() {
completion(result)
}
}
}
}
}
func getERC721ForTicketsBalance(for address: AlphaWallet.Address,
completion: @escaping (ResultResult<[String], AnyError>.t) -> Void) {
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in
guard let strongSelf = self else { return }
strongSelf.getERC721ForTicketsBalanceCoordinator.getERC721ForTicketsTokenBalance(for: strongSelf.account.address, contract: address) { result in
switch result {
case .success:
completion(result)
case .failure:
if !triggerRetry() {
completion(result)
}
}
}
}
}
//TODO should callers call tokenURI and so on, instead?
func getERC721Balance(for address: AlphaWallet.Address, completion: @escaping (ResultResult<[String], AnyError>.t) -> Void) {
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in
guard let strongSelf = self else { return }
strongSelf.getERC721BalanceCoordinator.getERC721TokenBalance(for: strongSelf.account.address, contract: address) { result in
switch result {
case .success(let balance):
if balance >= Int.max {
completion(.failure(AnyError(Web3Error(description: ""))))
} else {
completion(.success([String](repeating: "0", count: Int(balance))))
}
case .failure(let error):
if !triggerRetry() {
completion(.failure(error))
}
}
}
}
}
private func getTokensFromOpenSea() -> OpenSea.PromiseResult { private func getTokensFromOpenSea() -> OpenSea.PromiseResult {
//TODO when we no longer create multiple instances of TokensDataStore, we don't have to use singleton for OpenSea class. This was to avoid fetching multiple times from OpenSea concurrently //TODO when we no longer create multiple instances of TokensDataStore, we don't have to use singleton for OpenSea class. This was to avoid fetching multiple times from OpenSea concurrently
return openSea.makeFetchPromise(forOwner: account.address) return openSea.makeFetchPromise(forOwner: account.address)
} }
func getTokenType(for address: AlphaWallet.Address, completion: @escaping (TokenType) -> Void) {
let isErc875Promise = Promise<Bool> { seal in
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in
guard let strongSelf = self else { return }
//Function hash is "0x4f452b9a". This might cause many "execution reverted" RPC errors
//TODO rewrite flow so we reduce checks for this as it causes too many "execution reverted" RPC errors and looks scary when we look in Charles proxy. Maybe check for ERC20 (via EIP165) as well as ERC721 in parallel first, then fallback to this ERC875 check
strongSelf.getIsERC875ContractCoordinator.getIsERC875Contract(for: address) { [weak self] result in
guard self != nil else { return }
switch result {
case .success(let isERC875):
if isERC875 {
seal.fulfill(true)
} else {
seal.fulfill(false)
}
case .failure:
if !triggerRetry() {
seal.fulfill(false)
}
}
}
}
}
enum Erc721Type {
case erc721
case erc721ForTickets
case notErc721
}
let isErc721Promise = Promise<Erc721Type> { seal in
withRetry(times: numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry in
guard let strongSelf = self else { return }
strongSelf.getIsERC721ContractCoordinator.getIsERC721Contract(for: address) { [weak self] result in
guard let strongSelf = self else { return }
switch result {
case .success(let isERC721):
if isERC721 {
withRetry(times: strongSelf.numberOfTimesToRetryFetchContractData) { [weak self] triggerRetry2 in
guard let strongSelf = self else { return }
strongSelf.getIsERC721ForTicketsContractCoordinator.getIsERC721ForTicketContract(for: address) { result in
switch result {
case .success(let isERC721ForTickets):
if isERC721ForTickets {
seal.fulfill(.erc721ForTickets)
} else {
seal.fulfill(.erc721)
}
case .failure:
if !triggerRetry2() {
seal.fulfill(.erc721)
}
}
}
}
} else {
seal.fulfill(.notErc721)
}
case .failure:
if !triggerRetry() {
seal.fulfill(.notErc721)
}
}
}
}
}
firstly {
isErc721Promise
}.done { isErc721 in
switch isErc721 {
case .erc721:
completion(.erc721)
case .erc721ForTickets:
completion(.erc721ForTickets)
case .notErc721:
break
}
}.cauterize()
firstly {
isErc875Promise
}.done { isErc875 in
if isErc875 {
completion(.erc875)
} else {
//no-op
}
}.cauterize()
firstly {
when(fulfilled: isErc875Promise.asVoid(), isErc721Promise.asVoid())
}.done { _, _ in
if isErc875Promise.value == false && isErc721Promise.value == .notErc721 {
completion(.erc20)
} else {
//no-op
}
}.cauterize()
}
func tokenThreadSafe(forContract contract: AlphaWallet.Address) -> TokenObject? { func tokenThreadSafe(forContract contract: AlphaWallet.Address) -> TokenObject? {
realm.threadSafe.objects(TokenObject.self) realm.threadSafe.objects(TokenObject.self)
.filter("contract = '\(contract.eip55String)'") .filter("contract = '\(contract.eip55String)'")
@ -540,7 +233,7 @@ class TokensDataStore {
case .nativeCryptocurrency: case .nativeCryptocurrency:
completion() completion()
case .erc20: case .erc20:
getERC20Balance(for: tokenObject.contractAddress, completion: { [weak self] result in tokenProvider.getERC20Balance(for: tokenObject.contractAddress, completion: { [weak self] result in
defer { completion() } defer { completion() }
guard let strongSelf = self else { return } guard let strongSelf = self else { return }
switch result { switch result {
@ -551,7 +244,7 @@ class TokensDataStore {
} }
}) })
case .erc875: case .erc875:
getERC875Balance(for: tokenObject.contractAddress, completion: { [weak self] result in tokenProvider.getERC875Balance(for: tokenObject.contractAddress, completion: { [weak self] result in
defer { completion() } defer { completion() }
guard let strongSelf = self else { return } guard let strongSelf = self else { return }
switch result { switch result {
@ -564,7 +257,7 @@ class TokensDataStore {
case .erc721: case .erc721:
break break
case .erc721ForTickets: case .erc721ForTickets:
getERC721ForTicketsBalance(for: tokenObject.contractAddress, completion: { [weak self] result in tokenProvider.getERC721ForTicketsBalance(for: tokenObject.contractAddress, completion: { [weak self] result in
defer { completion() } defer { completion() }
guard let strongSelf = self else { return } guard let strongSelf = self else { return }
switch result { switch result {
@ -710,7 +403,7 @@ class TokensDataStore {
} }
func refreshETHBalance() { func refreshETHBalance() {
getNativeCryptoCurrencyBalanceCoordinator.getBalance(for: account.address) { [weak self] result in tokenProvider.getEthBalance(for: account.address) { [weak self] result in
guard let strongSelf = self else { return } guard let strongSelf = self else { return }
switch result { switch result {
case .success(let balance): case .success(let balance):

@ -30,6 +30,7 @@ class SingleChainTransactionEtherscanDataCoordinator: SingleChainTransactionData
private var isFetchingLatestTransactions = false private var isFetchingLatestTransactions = false
var coordinators: [Coordinator] = [] var coordinators: [Coordinator] = []
weak var delegate: SingleChainTransactionDataCoordinatorDelegate? weak var delegate: SingleChainTransactionDataCoordinatorDelegate?
private lazy var tokenProvider: TokenProviderType = TokenProvider(account: session.account, server: session.server)
required init( required init(
session: WalletSession, session: WalletSession,
@ -181,7 +182,7 @@ class SingleChainTransactionEtherscanDataCoordinator: SingleChainTransactionData
//The fetch ERC20 transactions endpoint from Etherscan returns only ERC20 token transactions but the Blockscout version also includes ERC721 transactions too (so it's likely other types that it can detect will be returned too); thus we check the token type rather than assume that they are all ERC20 //The fetch ERC20 transactions endpoint from Etherscan returns only ERC20 token transactions but the Blockscout version also includes ERC721 transactions too (so it's likely other types that it can detect will be returned too); thus we check the token type rather than assume that they are all ERC20
let contracts = Array(Set(filteredTransactions.compactMap { $0.localizedOperations.first?.contractAddress })) let contracts = Array(Set(filteredTransactions.compactMap { $0.localizedOperations.first?.contractAddress }))
let tokenTypePromises = contracts.map { self.tokensStorage.getTokenType(for: $0) } let tokenTypePromises = contracts.map { self.tokenProvider.getTokenType(for: $0) }
when(fulfilled: tokenTypePromises).map { tokenTypes in when(fulfilled: tokenTypePromises).map { tokenTypes in
let contractsToTokenTypes = Dictionary(uniqueKeysWithValues: zip(contracts, tokenTypes)) let contractsToTokenTypes = Dictionary(uniqueKeysWithValues: zip(contracts, tokenTypes))
@ -492,4 +493,4 @@ extension SingleChainTransactionEtherscanDataCoordinator.functional {
return results return results
} }
} }
} }

@ -135,7 +135,7 @@ extension SendCoordinator: SendViewControllerDelegate {
} }
func lookup(contract: AlphaWallet.Address, in viewController: SendViewController, completion: @escaping (ContractData) -> Void) { func lookup(contract: AlphaWallet.Address, in viewController: SendViewController, completion: @escaping (ContractData) -> Void) {
ContractDataDetector(address: contract, storage: storage, assetDefinitionStore: assetDefinitionStore).fetch(completion: completion) ContractDataDetector(address: contract, account: session.account, server: session.server, assetDefinitionStore: assetDefinitionStore).fetch(completion: completion)
} }
} }

Loading…
Cancel
Save