Merge pull request #3261 from vladyslav-iosdev/#3241

Add Arbitrum One RPCServer #3241
pull/3296/head
Hwee-Boon Yar 3 years ago committed by GitHub
commit a08a9d150a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      AlphaWallet.xcodeproj/project.pbxproj
  2. 21
      AlphaWallet/Assets.xcassets/arbitrum.imageset/Contents.json
  3. BIN
      AlphaWallet/Assets.xcassets/arbitrum.imageset/icons-tokens-arbitrum.pdf
  4. 10
      AlphaWallet/Core/BuyToken/Ramp/Ramp.swift
  5. 27
      AlphaWallet/Core/CoinTicker/CoinTickersFetcher.swift
  6. 23
      AlphaWallet/Core/Coordinators/WalletBalance/PrivateBalanceFetcher.swift
  7. 17
      AlphaWallet/Core/Coordinators/WalletBalance/WalletBalanceCoordinator.swift
  8. 4
      AlphaWallet/Core/Coordinators/WalletBalance/WalletBalanceFetcher.swift
  9. 2
      AlphaWallet/Core/DomainResolver.swift
  10. 44
      AlphaWallet/Core/SwapToken/ArbitrumBridge.swift
  11. 4
      AlphaWallet/Core/SwapToken/HoneySwap/HoneySwap.swift
  12. 14
      AlphaWallet/Core/SwapToken/Oneinch/Oneinch.swift
  13. 7
      AlphaWallet/Core/SwapToken/QuickSwap/QuickSwap.swift
  14. 5
      AlphaWallet/Core/SwapToken/SwapTokenService.swift
  15. 5
      AlphaWallet/Core/SwapToken/Uniswap/Uniswap.swift
  16. 2
      AlphaWallet/Core/SwapToken/Uniswap/UniswapERC20Token.swift
  17. 41
      AlphaWallet/Core/SwapToken/xDaiBridge.swift
  18. 8
      AlphaWallet/Core/Types/NativecryptoBalanceViewModel.swift
  19. 16
      AlphaWallet/Core/Types/TokenProviderType.swift
  20. 4
      AlphaWallet/EtherClient/OpenSea.swift
  21. 4
      AlphaWallet/EtherClient/TrustClient/AlphaWalletService.swift
  22. 16
      AlphaWallet/EtherClient/TrustClient/Models/ArrayResponse.swift
  23. 2
      AlphaWallet/Gas/Models/GasNowGasPriceEstimator.swift
  24. 4
      AlphaWallet/InCoordinator.swift
  25. 2
      AlphaWallet/Localization/en.lproj/Localizable.strings
  26. 2
      AlphaWallet/Localization/es.lproj/Localizable.strings
  27. 2
      AlphaWallet/Localization/ja.lproj/Localizable.strings
  28. 2
      AlphaWallet/Localization/ko.lproj/Localizable.strings
  29. 2
      AlphaWallet/Localization/zh-Hans.lproj/Localizable.strings
  30. 4
      AlphaWallet/Market/Coordinators/UniversalLinkCoordinator.swift
  31. 1
      AlphaWallet/Settings/Coordinators/ServersCoordinator.swift
  32. 10
      AlphaWallet/Settings/Types/ConfigExplorer.swift
  33. 5
      AlphaWallet/Settings/Types/Constants.swift
  34. 51
      AlphaWallet/Settings/Types/RPCServers.swift
  35. 1
      AlphaWallet/TokenScriptClient/Coordinators/EventSourceCoordinator.swift
  36. 3
      AlphaWallet/TokenScriptClient/Coordinators/EventSourceCoordinatorForActivities.swift
  37. 1
      AlphaWallet/Tokens/Coordinators/ENSReverseLookupCoordinator.swift
  38. 2
      AlphaWallet/Tokens/Coordinators/GetERC20BalanceCoordinator.swift
  39. 4
      AlphaWallet/Tokens/Coordinators/SingleChainTokenCoordinator.swift
  40. 1
      AlphaWallet/Tokens/Types/TokenCollection.swift
  41. 18
      AlphaWallet/Tokens/Types/TokenInstanceAction.swift
  42. 2
      AlphaWallet/Tokens/ViewControllers/Collectibles/ViewControllers/Erc1155TokenInstanceViewController.swift
  43. 2
      AlphaWallet/Tokens/ViewControllers/TokenInstanceViewController.swift
  44. 18
      AlphaWallet/Tokens/ViewControllers/TokenViewController.swift
  45. 2
      AlphaWallet/Tokens/ViewControllers/TokensCardViewController.swift
  46. 4
      AlphaWallet/Tokens/ViewModels/FungibleTokenViewCellViewModel.swift
  47. 21
      AlphaWallet/Tokens/ViewModels/TokenViewControllerViewModel.swift
  48. 43
      AlphaWallet/Transactions/Coordinators/SingleChainTransactionEtherscanDataCoordinator.swift
  49. 2
      AlphaWallet/Transactions/Coordinators/TokensCardCoordinator.swift
  50. 14
      AlphaWallet/Transfer/Controllers/TransactionConfigurator.swift
  51. 2
      AlphaWallet/Transfer/Coordinators/SendTransactionCoordinator.swift

@ -758,6 +758,8 @@
873F8063246E8E3E00EEE5EF /* SelectCurrencyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 873F8062246E8E3E00EEE5EF /* SelectCurrencyButton.swift */; }; 873F8063246E8E3E00EEE5EF /* SelectCurrencyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 873F8062246E8E3E00EEE5EF /* SelectCurrencyButton.swift */; };
874015BF270DBB4800B3515F /* MimeType+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 874015BE270DBB4800B3515F /* MimeType+Extension.swift */; }; 874015BF270DBB4800B3515F /* MimeType+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 874015BE270DBB4800B3515F /* MimeType+Extension.swift */; };
8743CB50255059780039E469 /* DomainResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8743CB4F255059780039E469 /* DomainResolver.swift */; }; 8743CB50255059780039E469 /* DomainResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8743CB4F255059780039E469 /* DomainResolver.swift */; };
874527D7270B3A25008DB272 /* ArbitrumBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 874527D6270B3A25008DB272 /* ArbitrumBridge.swift */; };
874527D9270B3A45008DB272 /* xDaiBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 874527D8270B3A45008DB272 /* xDaiBridge.swift */; };
874AF0832603405F00D613A5 /* LoadingIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 874AF0822603405F00D613A5 /* LoadingIndicatorView.swift */; }; 874AF0832603405F00D613A5 /* LoadingIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 874AF0822603405F00D613A5 /* LoadingIndicatorView.swift */; };
874BD2BB2669F65800E62E02 /* PopularTokensCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 874BD2BA2669F65800E62E02 /* PopularTokensCollection.swift */; }; 874BD2BB2669F65800E62E02 /* PopularTokensCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 874BD2BA2669F65800E62E02 /* PopularTokensCollection.swift */; };
874C6D8125E3FF2300AD8380 /* ConfirmationHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 874C6D8025E3FF2300AD8380 /* ConfirmationHeaderView.swift */; }; 874C6D8125E3FF2300AD8380 /* ConfirmationHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 874C6D8025E3FF2300AD8380 /* ConfirmationHeaderView.swift */; };
@ -1762,6 +1764,8 @@
873F8062246E8E3E00EEE5EF /* SelectCurrencyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectCurrencyButton.swift; sourceTree = "<group>"; }; 873F8062246E8E3E00EEE5EF /* SelectCurrencyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectCurrencyButton.swift; sourceTree = "<group>"; };
874015BE270DBB4800B3515F /* MimeType+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MimeType+Extension.swift"; sourceTree = "<group>"; }; 874015BE270DBB4800B3515F /* MimeType+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MimeType+Extension.swift"; sourceTree = "<group>"; };
8743CB4F255059780039E469 /* DomainResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainResolver.swift; sourceTree = "<group>"; }; 8743CB4F255059780039E469 /* DomainResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainResolver.swift; sourceTree = "<group>"; };
874527D6270B3A25008DB272 /* ArbitrumBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArbitrumBridge.swift; sourceTree = "<group>"; };
874527D8270B3A45008DB272 /* xDaiBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = xDaiBridge.swift; sourceTree = "<group>"; };
874AF0822603405F00D613A5 /* LoadingIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingIndicatorView.swift; sourceTree = "<group>"; }; 874AF0822603405F00D613A5 /* LoadingIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingIndicatorView.swift; sourceTree = "<group>"; };
874BD2BA2669F65800E62E02 /* PopularTokensCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopularTokensCollection.swift; sourceTree = "<group>"; }; 874BD2BA2669F65800E62E02 /* PopularTokensCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopularTokensCollection.swift; sourceTree = "<group>"; };
874C6D8025E3FF2300AD8380 /* ConfirmationHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationHeaderView.swift; sourceTree = "<group>"; }; 874C6D8025E3FF2300AD8380 /* ConfirmationHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationHeaderView.swift; sourceTree = "<group>"; };
@ -4508,6 +4512,8 @@
8731E6DD2565269400A6A7DA /* Oneinch */, 8731E6DD2565269400A6A7DA /* Oneinch */,
8731E6DA2565269400A6A7DA /* Uniswap */, 8731E6DA2565269400A6A7DA /* Uniswap */,
87B65269256FBF36000EF927 /* SwapTokenService.swift */, 87B65269256FBF36000EF927 /* SwapTokenService.swift */,
874527D6270B3A25008DB272 /* ArbitrumBridge.swift */,
874527D8270B3A45008DB272 /* xDaiBridge.swift */,
); );
path = SwapToken; path = SwapToken;
sourceTree = "<group>"; sourceTree = "<group>";
@ -5280,6 +5286,7 @@
2963B6B91F9A7EEA003063C1 /* CoinTicker.swift in Sources */, 2963B6B91F9A7EEA003063C1 /* CoinTicker.swift in Sources */,
29F1C85820036926003780D8 /* AppTracker.swift in Sources */, 29F1C85820036926003780D8 /* AppTracker.swift in Sources */,
293E62711FA2F63500CB0A66 /* InitialWalletCreationCoordinator.swift in Sources */, 293E62711FA2F63500CB0A66 /* InitialWalletCreationCoordinator.swift in Sources */,
874527D9270B3A45008DB272 /* xDaiBridge.swift in Sources */,
291D73C61F7F500D00A8AB56 /* TransactionItemState.swift in Sources */, 291D73C61F7F500D00A8AB56 /* TransactionItemState.swift in Sources */,
29BE3FD21F707DC300F6BFC2 /* TransactionDataCoordinator.swift in Sources */, 29BE3FD21F707DC300F6BFC2 /* TransactionDataCoordinator.swift in Sources */,
29F1C85120032688003780D8 /* Address.swift in Sources */, 29F1C85120032688003780D8 /* Address.swift in Sources */,
@ -5347,6 +5354,7 @@
77872D302026DC570032D687 /* SplashViewController.swift in Sources */, 77872D302026DC570032D687 /* SplashViewController.swift in Sources */,
87BC89B826B82288005482F4 /* UIBarButtonItem.swift in Sources */, 87BC89B826B82288005482F4 /* UIBarButtonItem.swift in Sources */,
29C80D4D1FB5202C0037B1E0 /* BalanceBaseViewModel.swift in Sources */, 29C80D4D1FB5202C0037B1E0 /* BalanceBaseViewModel.swift in Sources */,
874527D7270B3A25008DB272 /* ArbitrumBridge.swift in Sources */,
87BB63E2265E759700FF702A /* PrivateBalanceFetcher.swift in Sources */, 87BB63E2265E759700FF702A /* PrivateBalanceFetcher.swift in Sources */,
29E14FD11F7F457D00185568 /* TransactionsStorage.swift in Sources */, 29E14FD11F7F457D00185568 /* TransactionsStorage.swift in Sources */,
87ED8F97248540F90005C69B /* AdvancedSettingsViewController.swift in Sources */, 87ED8F97248540F90005C69B /* AdvancedSettingsViewController.swift in Sources */,

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "icons-tokens-arbitrum.pdf",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

@ -21,13 +21,11 @@ struct Ramp: TokenActionsProvider, BuyTokenURLProviderType {
case .xDai: case .xDai:
return URL(string: "\(Constants.buyXDaiWitRampUrl)&userAddress=\(account.address.eip55String)") return URL(string: "\(Constants.buyXDaiWitRampUrl)&userAddress=\(account.address.eip55String)")
//TODO need to check if Ramp supports these? Or is it taken care of elsehwere //TODO need to check if Ramp supports these? Or is it taken care of elsehwere
case .main, .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .custom, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .main, .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .custom, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
if let asset = asset(for: token) { return asset(for: token).flatMap {
let base = Constants.buyWitRampUrl(asset: asset.symbol) return URL(string: "\(Constants.buyWitRampUrl(asset: $0.symbol))&userAddress=\(account.address.eip55String)")
return URL(string: "\(base)&userAddress=\(account.address.eip55String)")
} }
} }
return nil
} }
func actions(token: TokenActionsServiceKey) -> [TokenInstanceAction] { func actions(token: TokenActionsServiceKey) -> [TokenInstanceAction] {
@ -40,7 +38,7 @@ struct Ramp: TokenActionsProvider, BuyTokenURLProviderType {
switch token.server { switch token.server {
case .xDai: case .xDai:
return true return true
case .main, .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .custom, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .main, .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .custom, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
return asset(for: token) != nil return asset(for: token) != nil
} }
} }

@ -32,9 +32,9 @@ struct TokenMappedToTicker: Hashable {
protocol CoinTickersFetcherType { protocol CoinTickersFetcherType {
var tickersSubscribable: Subscribable<[AddressAndRPCServer: CoinTicker]> { get } var tickersSubscribable: Subscribable<[AddressAndRPCServer: CoinTicker]> { get }
var tickers: [AddressAndRPCServer: CoinTicker] { get }
func fetchPrices(forTokens tokens: ServerDictionary<[TokenMappedToTicker]>) -> Promise<Void> func ticker(for addressAndPRCServer: AddressAndRPCServer) -> CoinTicker?
func fetchPrices(forTokens tokens: [TokenMappedToTicker]) -> Promise<Void>
func fetchChartHistories(addressToRPCServerKey: AddressAndRPCServer, force: Bool, periods: [ChartHistoryPeriod]) -> Promise<[ChartHistory]> func fetchChartHistories(addressToRPCServerKey: AddressAndRPCServer, force: Bool, periods: [ChartHistoryPeriod]) -> Promise<[ChartHistory]>
} }
@ -59,9 +59,7 @@ class CoinTickersFetcher: CoinTickersFetcherType {
var tickersSubscribable: Subscribable<[AddressAndRPCServer: CoinTicker]> { var tickersSubscribable: Subscribable<[AddressAndRPCServer: CoinTicker]> {
return cache.tickersSubscribable return cache.tickersSubscribable
} }
var tickers: [AddressAndRPCServer: CoinTicker] {
return cache.tickers
}
private static let queue: DispatchQueue = DispatchQueue(label: "com.CoinTickersFetcher.updateQueue") private static let queue: DispatchQueue = DispatchQueue(label: "com.CoinTickersFetcher.updateQueue")
private let provider: MoyaProvider<AlphaWalletService> private let provider: MoyaProvider<AlphaWalletService>
@ -74,6 +72,16 @@ class CoinTickersFetcher: CoinTickersFetcherType {
self.cache = cache self.cache = cache
} }
func ticker(for addressAndPRCServer: AddressAndRPCServer) -> CoinTicker? {
//NOTE: If it doesn't include the price for the native token, hardwire it to use Ethereum's mainnet's native token price.
if addressAndPRCServer.server == .arbitrum && addressAndPRCServer.address.sameContract(as: Constants.nativeCryptoAddressInDatabase) {
let overridenAddressAndPRCServer: AddressAndRPCServer = .init(address: Constants.nativeCryptoAddressInDatabase, server: .main)
return cache.tickers[overridenAddressAndPRCServer]
} else {
return cache.tickers[addressAndPRCServer]
}
}
//Important in implementation to not cache the returned promise (which is used to further fetch prices). We only want to cache the promise/request for fetching supported tickers //Important in implementation to not cache the returned promise (which is used to further fetch prices). We only want to cache the promise/request for fetching supported tickers
private static func fetchSupportedTickers(config: Config, provider: MoyaProvider<AlphaWalletService>, shouldRetry: Bool = true) -> Promise<[Ticker]> { private static func fetchSupportedTickers(config: Config, provider: MoyaProvider<AlphaWalletService>, shouldRetry: Bool = true) -> Promise<[Ticker]> {
if let promise = fetchSupportedTokensPromise { return promise } if let promise = fetchSupportedTokensPromise { return promise }
@ -99,7 +107,7 @@ class CoinTickersFetcher: CoinTickersFetcherType {
Self.fetchSupportedTickers(config: config, provider: provider) Self.fetchSupportedTickers(config: config, provider: provider)
} }
func fetchPrices(forTokens tokens: ServerDictionary<[TokenMappedToTicker]>) -> Promise<Void> { func fetchPrices(forTokens tokens: [TokenMappedToTicker]) -> Promise<Void> {
let cache = self.cache let cache = self.cache
return firstly { return firstly {
fetchTickers(forTokens: tokens) fetchTickers(forTokens: tokens)
@ -151,8 +159,7 @@ class CoinTickersFetcher: CoinTickersFetcherType {
} }
} }
private func fetchTickers(forTokens tokens: ServerDictionary<[TokenMappedToTicker]>) -> Promise<(tickers: [AddressAndRPCServer: CoinTicker], tickerIds: [String])> { private func fetchTickers(forTokens tokens: [TokenMappedToTicker]) -> Promise<(tickers: [AddressAndRPCServer: CoinTicker], tickerIds: [String])> {
let tokens = tokens.values.flatMap { $0 }
guard !isFetchingPrices else { return .init(error: Error.alreadyFetchingPrices) } guard !isFetchingPrices else { return .init(error: Error.alreadyFetchingPrices) }
isFetchingPrices = true isFetchingPrices = true
@ -307,7 +314,8 @@ fileprivate struct Ticker: Codable {
case .avalanche: return platform == "avalanche" case .avalanche: return platform == "avalanche"
case .polygon: return platform == "polygon-pos" case .polygon: return platform == "polygon-pos"
case .fantom: return platform == "fantom" case .fantom: return platform == "fantom"
case .poa, .kovan, .sokol, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain_testnet, .ropsten, .rinkeby, .heco, .heco_testnet, .fantom_testnet, .avalanche_testnet, .mumbai_testnet, .custom, .optimistic, .optimisticKovan, .cronosTestnet: case .arbitrum: return platform == "arbitrum-one"
case .poa, .kovan, .sokol, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain_testnet, .ropsten, .rinkeby, .heco, .heco_testnet, .fantom_testnet, .avalanche_testnet, .mumbai_testnet, .custom, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
return false return false
} }
} }
@ -320,6 +328,7 @@ fileprivate struct Ticker: Codable {
case .binance_smart_chain: return true case .binance_smart_chain: return true
case .avalanche: return true case .avalanche: return true
case .polygon: return true case .polygon: return true
case .arbitrum: return true
case .poa, .kovan, .sokol, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain_testnet, .ropsten, .rinkeby, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche_testnet, .mumbai_testnet, .custom, .optimistic, .optimisticKovan, .cronosTestnet: case .poa, .kovan, .sokol, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain_testnet, .ropsten, .rinkeby, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche_testnet, .mumbai_testnet, .custom, .optimistic, .optimisticKovan, .cronosTestnet:
return false return false
} }

@ -102,14 +102,19 @@ class PrivateBalanceFetcher: PrivateBalanceFetcherType {
seal.fulfill(tokenObjects) seal.fulfill(tokenObjects)
} }
}.then(on: queue, { tokenObjects in }.then(on: queue, { tokenObjects in
return self.refreshBalance(tokenObjects: tokenObjects, updatePolicy: .all, force: false) return self.refreshBalance(tokenObjects: tokenObjects, updatePolicy: .all, force: force)
}).recover(on: queue, { e -> Promise<Void> in
error(value: e)
throw e
}) })
} }
private func refreshBalanceForNonErc721Or1155Tokens(tokens: [Activity.AssignedToken]) -> Promise<[PrivateBalanceFetcher.TokenBatchOperation]> { private func refreshBalanceForNonErc721Or1155Tokens(tokens: [Activity.AssignedToken]) -> Promise<[PrivateBalanceFetcher.TokenBatchOperation]> {
assert(!tokens.contains { $0.isERC721Or1155AndNotForTickets }) assert(!tokens.contains { $0.isERC721Or1155AndNotForTickets })
guard !tokens.isEmpty else { return .value([]) }
let promises = tokens.map { getBalanceForNonErc721Or1155Tokens(forToken: $0) } let promises = tokens.map { getBalanceForNonErc721Or1155Tokens(forToken: $0) }
let id = UUID().uuidString
return when(resolved: promises).map { values -> [PrivateBalanceFetcher.TokenBatchOperation] in return when(resolved: promises).map { values -> [PrivateBalanceFetcher.TokenBatchOperation] in
return values.compactMap { $0.optionalValue }.compactMap { $0 } return values.compactMap { $0.optionalValue }.compactMap { $0 }
@ -143,9 +148,9 @@ class PrivateBalanceFetcher: PrivateBalanceFetcherType {
//NOTE: taking for first element means that values was updated //NOTE: taking for first element means that values was updated
return values.compactMap { $0.optionalValue }.compactMap { $0 }.first return values.compactMap { $0.optionalValue }.compactMap { $0 }.first
}) })
}.get(on: queue, { balanceValueHasChange in }.ensure({
self.isRefeshingBalance = false self.isRefeshingBalance = false
}).get(on: queue, { balanceValueHasChange in
if let value = balanceValueHasChange, value { if let value = balanceValueHasChange, value {
self.delegate?.didUpdate(in: self) self.delegate?.didUpdate(in: self)
} }
@ -171,6 +176,7 @@ class PrivateBalanceFetcher: PrivateBalanceFetcherType {
let promise1 = refreshBalanceForNonErc721Or1155Tokens(tokens: notErc721Or1155Tokens) let promise1 = refreshBalanceForNonErc721Or1155Tokens(tokens: notErc721Or1155Tokens)
let promise2 = refreshBalanceForErc721Or1155Tokens(tokens: erc721Or1155Tokens) let promise2 = refreshBalanceForErc721Or1155Tokens(tokens: erc721Or1155Tokens)
let tokensDatastore = self.tokensDatastore let tokensDatastore = self.tokensDatastore
return when(resolved: [promise1, promise2]).then(on: queue, { value -> Promise<Bool?> in return when(resolved: [promise1, promise2]).then(on: queue, { value -> Promise<Bool?> in
let resolved: [TokenBatchOperation] = value.compactMap { $0.optionalValue }.flatMap { $0 } let resolved: [TokenBatchOperation] = value.compactMap { $0.optionalValue }.flatMap { $0 }
return tokensDatastore.batchUpdateTokenPromise(resolved).recover { _ -> Guarantee<Bool?> in return tokensDatastore.batchUpdateTokenPromise(resolved).recover { _ -> Guarantee<Bool?> in
@ -182,6 +188,15 @@ class PrivateBalanceFetcher: PrivateBalanceFetcherType {
enum TokenBatchOperation { enum TokenBatchOperation {
case add(ERCToken, shouldUpdateBalance: Bool) case add(ERCToken, shouldUpdateBalance: Bool)
case update(tokenObject: Activity.AssignedToken, action: TokensDataStore.TokenUpdateAction) case update(tokenObject: Activity.AssignedToken, action: TokensDataStore.TokenUpdateAction)
var updateAction: TokensDataStore.TokenUpdateAction? {
switch self {
case .add:
return nil
case .update(_, let action):
return action
}
}
} }
private func getBalanceForNonErc721Or1155Tokens(forToken tokenObject: Activity.AssignedToken) -> Promise<TokenBatchOperation?> { private func getBalanceForNonErc721Or1155Tokens(forToken tokenObject: Activity.AssignedToken) -> Promise<TokenBatchOperation?> {
@ -213,6 +228,8 @@ class PrivateBalanceFetcher: PrivateBalanceFetcherType {
private func refreshBalanceForErc721Or1155Tokens(tokens: [Activity.AssignedToken]) -> Promise<[PrivateBalanceFetcher.TokenBatchOperation]> { private func refreshBalanceForErc721Or1155Tokens(tokens: [Activity.AssignedToken]) -> Promise<[PrivateBalanceFetcher.TokenBatchOperation]> {
assert(!tokens.contains { !$0.isERC721Or1155AndNotForTickets }) assert(!tokens.contains { !$0.isERC721Or1155AndNotForTickets })
guard !tokens.isEmpty else { return .value([]) }
return firstly { return firstly {
getTokensFromOpenSea() getTokensFromOpenSea()
}.then(on: queue, { [weak self] contractToOpenSeaNonFungibles -> Guarantee<[PrivateBalanceFetcher.TokenBatchOperation]> in }.then(on: queue, { [weak self] contractToOpenSeaNonFungibles -> Guarantee<[PrivateBalanceFetcher.TokenBatchOperation]> in

@ -117,13 +117,20 @@ class WalletBalanceCoordinator: NSObject, WalletBalanceCoordinatorType {
balanceFetchers[wallet]!.tokensDatastore(server: server) balanceFetchers[wallet]!.tokensDatastore(server: server)
} }
//NOTE: for case if we disable rpc server, we don't fetch ticker for its native crypto
private static var nativeCryptoForAllChains: [Activity.AssignedToken] {
return RPCServer.allCases.map { server in
Activity.AssignedToken.init(tokenObject: TokensDataStore.etherToken(forServer: server))
}
}
private var availableTokenObjects: Promise<ServerDictionary<[TokenMappedToTicker]>> { private var availableTokenObjects: Promise<ServerDictionary<[TokenMappedToTicker]>> {
Promise<[Activity.AssignedToken]> { seal in Promise<[Activity.AssignedToken]> { seal in
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let strongSelf = self else { return seal.reject(PMKError.cancelled) } guard let strongSelf = self else { return seal.reject(PMKError.cancelled) }
let tokenObjects = strongSelf.balanceFetchers.map { $0.value.tokenObjects }.flatMap { $0 } let tokenObjects = strongSelf.balanceFetchers.map { $0.value.tokenObjects }.flatMap { $0 }
seal.fulfill(tokenObjects) seal.fulfill(tokenObjects + Self.nativeCryptoForAllChains)
} }
}.map(on: queue, { objects -> ServerDictionary<[TokenMappedToTicker]> in }.map(on: queue, { objects -> ServerDictionary<[TokenMappedToTicker]> in
let uniqueTokenObjectsOfAllWallets = Set(objects) let uniqueTokenObjectsOfAllWallets = Set(objects)
@ -170,10 +177,12 @@ class WalletBalanceCoordinator: NSObject, WalletBalanceCoordinatorType {
firstly { firstly {
availableTokenObjects availableTokenObjects
}.then(on: queue, { values -> Promise<Void> in }.then(on: queue, { values -> Promise<Void> in
self.coinTickersFetcher.fetchPrices(forTokens: values) self.coinTickersFetcher.fetchPrices(forTokens: values.values.flatMap({ $0 }))
}).done(on: queue, { _ in }).done(on: queue, { _ in
//no-op //no-op
}).cauterize() }).catch({ e in
error(value: e)
})
} }
private func notifyWalletSummary() { private func notifyWalletSummary() {

@ -156,7 +156,7 @@ class WalletBalanceFetcher: NSObject, WalletBalanceFetcherType {
} }
private func balanceViewModel(key tokenObject: TokenObject) -> BalanceBaseViewModel? { private func balanceViewModel(key tokenObject: TokenObject) -> BalanceBaseViewModel? {
let ticker = coinTickersFetcher.tickers[tokenObject.addressAndRPCServer] let ticker = coinTickersFetcher.ticker(for: tokenObject.addressAndRPCServer)
switch tokenObject.type { switch tokenObject.type {
case .nativeCryptocurrency: case .nativeCryptocurrency:
@ -222,7 +222,7 @@ class WalletBalanceFetcher: NSObject, WalletBalanceFetcherType {
var balances = Set<Activity.AssignedToken>() var balances = Set<Activity.AssignedToken>()
for var tokenObject in tokenObjects { for var tokenObject in tokenObjects {
tokenObject.ticker = coinTickersFetcher.tickers[tokenObject.addressAndRPCServer] tokenObject.ticker = coinTickersFetcher.ticker(for: tokenObject.addressAndRPCServer)
balances.insert(tokenObject) balances.insert(tokenObject)
} }

@ -113,7 +113,7 @@ fileprivate extension RPCServer {
case .kovan: return "kovan" case .kovan: return "kovan"
case .ropsten: return "ropsten" case .ropsten: return "ropsten"
case .rinkeby: return "rinkeby" case .rinkeby: return "rinkeby"
case .poa, .sokol, .classic, .callisto, .xDai, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .custom, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .poa, .sokol, .classic, .callisto, .xDai, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .custom, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
return nil return nil
} }
} }

@ -0,0 +1,44 @@
//
// ArbitrumBridge.swift
// AlphaWallet
//
// Created by Vladyslav Shepitko on 04.10.2021.
//
import UIKit
typealias BridgeTokenURLProviderType = BuyTokenURLProviderType
final class ArbitrumBridge: TokenActionsProvider, BridgeTokenURLProviderType {
private static let supportedServer: RPCServer = .main
func isSupport(token: TokenActionsServiceKey) -> Bool {
switch token.type {
case .erc1155, .erc721, .erc721ForTickets, .erc875:
return false
case .nativeCryptocurrency, .erc20:
//NOTE: we are not pretty sure what tokens it supports, so let assume for all
return token.server == ArbitrumBridge.supportedServer
}
}
func actions(token: TokenActionsServiceKey) -> [TokenInstanceAction] {
return [.init(type: .bridge(service: self))]
}
func rpcServer(forToken token: TokenActionsServiceKey) -> RPCServer? {
return ArbitrumBridge.supportedServer
}
var action: String {
return R.string.localizable.aWalletTokenArbitrumBridgeButtonTitle()
}
var analyticsName: String {
"Arbitrum Bridge"
}
func url(token: TokenActionsServiceKey) -> URL? {
return Constants.arbitrumBridge
}
}

@ -13,7 +13,7 @@ class HoneySwap: TokenActionsProvider, SwapTokenURLProviderType {
return R.string.localizable.aWalletTokenErc20ExchangeHoneyswapButtonTitle() return R.string.localizable.aWalletTokenErc20ExchangeHoneyswapButtonTitle()
} }
//NOTE: While selection on action browser will be automatically switched to defined server `rpcServer` //NOTE: While selection on action browser will be automatically switched to defined server `rpcServer`
var rpcServer: RPCServer? { func rpcServer(forToken token: TokenActionsServiceKey) -> RPCServer? {
return .xDai return .xDai
} }
@ -97,7 +97,7 @@ class HoneySwap: TokenActionsProvider, SwapTokenURLProviderType {
switch token.server { switch token.server {
case .xDai: case .xDai:
return true return true
case .main, .kovan, .ropsten, .rinkeby, .sokol, .goerli, .artis_sigma1, .artis_tau1, .custom, .poa, .callisto, .classic, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .main, .kovan, .ropsten, .rinkeby, .sokol, .goerli, .artis_sigma1, .artis_tau1, .custom, .poa, .callisto, .classic, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
return false return false
} }
} }

@ -14,8 +14,16 @@ class Oneinch: TokenActionsProvider, SwapTokenURLProviderType {
var action: String { var action: String {
return R.string.localizable.aWalletTokenErc20ExchangeOn1inchButtonTitle() return R.string.localizable.aWalletTokenErc20ExchangeOn1inchButtonTitle()
} }
var rpcServer: RPCServer? { private var supportedServers: [RPCServer] {
.main return [.main, .binance_smart_chain, .polygon, .optimistic, .arbitrum]
}
func rpcServer(forToken token: TokenActionsServiceKey) -> RPCServer? {
if supportedServers.contains(where: { $0 == token.server }) {
return token.server
} else {
return .main
}
} }
var analyticsName: String { var analyticsName: String {
@ -55,7 +63,7 @@ class Oneinch: TokenActionsProvider, SwapTokenURLProviderType {
func isSupport(token: TokenActionsServiceKey) -> Bool { func isSupport(token: TokenActionsServiceKey) -> Bool {
switch token.server { switch token.server {
case .main: case .main, .arbitrum:
return availableTokens[token.contractAddress] != nil return availableTokens[token.contractAddress] != nil
case .kovan, .ropsten, .rinkeby, .sokol, .goerli, .artis_sigma1, .artis_tau1, .custom, .poa, .callisto, .xDai, .classic, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .kovan, .ropsten, .rinkeby, .sokol, .goerli, .artis_sigma1, .artis_tau1, .custom, .poa, .callisto, .xDai, .classic, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet:
return false return false

@ -12,8 +12,9 @@ struct QuickSwap: TokenActionsProvider, SwapTokenURLProviderType {
var action: String { var action: String {
return R.string.localizable.aWalletTokenErc20ExchangeOnQuickSwapButtonTitle() return R.string.localizable.aWalletTokenErc20ExchangeOnQuickSwapButtonTitle()
} }
var rpcServer: RPCServer? {
.polygon func rpcServer(forToken token: TokenActionsServiceKey) -> RPCServer? {
return .polygon
} }
var analyticsName: String { var analyticsName: String {
@ -101,7 +102,7 @@ struct QuickSwap: TokenActionsProvider, SwapTokenURLProviderType {
switch token.server { switch token.server {
case .polygon: case .polygon:
return true return true
case .main, .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .custom, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .xDai, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .main, .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .custom, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .xDai, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
return false return false
} }
} }

@ -12,12 +12,14 @@ struct TokenActionsServiceKey {
let server: RPCServer let server: RPCServer
var symbol: String var symbol: String
var decimals: Int var decimals: Int
let type: TokenType
init(tokenObject: TokenObject) { init(tokenObject: TokenObject) {
self.contractAddress = tokenObject.contractAddress self.contractAddress = tokenObject.contractAddress
self.server = tokenObject.server self.server = tokenObject.server
self.symbol = tokenObject.symbol self.symbol = tokenObject.symbol
self.decimals = tokenObject.decimals self.decimals = tokenObject.decimals
self.type = tokenObject.type
} }
} }
@ -28,8 +30,9 @@ protocol TokenActionsProvider {
protocol SwapTokenURLProviderType { protocol SwapTokenURLProviderType {
var action: String { get } var action: String { get }
var rpcServer: RPCServer? { get }
var analyticsName: String { get } var analyticsName: String { get }
func rpcServer(forToken token: TokenActionsServiceKey) -> RPCServer?
func url(token: TokenActionsServiceKey) -> URL? func url(token: TokenActionsServiceKey) -> URL?
} }

@ -12,8 +12,9 @@ struct Uniswap: TokenActionsProvider, SwapTokenURLProviderType {
var action: String { var action: String {
return R.string.localizable.aWalletTokenErc20ExchangeOnUniswapButtonTitle() return R.string.localizable.aWalletTokenErc20ExchangeOnUniswapButtonTitle()
} }
var rpcServer: RPCServer? {
.main func rpcServer(forToken token: TokenActionsServiceKey) -> RPCServer? {
return .main
} }
var analyticsName: String { var analyticsName: String {

@ -19,7 +19,7 @@ extension UniswapERC20Token {
switch token.server { switch token.server {
case .main: case .main:
return availableTokens.contains(where: { $0.contract.sameContract(as: token.contractAddress) }) return availableTokens.contains(where: { $0.contract.sameContract(as: token.contractAddress) })
case .kovan, .ropsten, .rinkeby, .sokol, .goerli, .artis_sigma1, .artis_tau1, .custom, .poa, .callisto, .xDai, .classic, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .kovan, .ropsten, .rinkeby, .sokol, .goerli, .artis_sigma1, .artis_tau1, .custom, .poa, .callisto, .xDai, .classic, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
return false return false
} }
} }

@ -0,0 +1,41 @@
//
// xDaiBridge.swift
// AlphaWallet
//
// Created by Vladyslav Shepitko on 04.10.2021.
//
import UIKit
final class xDaiBridge: TokenActionsProvider, BridgeTokenURLProviderType {
private static let supportedServer: RPCServer = .xDai
func isSupport(token: TokenActionsServiceKey) -> Bool {
switch token.type {
case .erc1155, .erc20, .erc721, .erc721ForTickets, .erc875:
return false
case .nativeCryptocurrency:
return token.server == xDaiBridge.supportedServer
}
}
func actions(token: TokenActionsServiceKey) -> [TokenInstanceAction] {
return [.init(type: .bridge(service: self))]
}
func rpcServer(forToken token: TokenActionsServiceKey) -> RPCServer? {
return xDaiBridge.supportedServer
}
var action: String {
return R.string.localizable.aWalletTokenXDaiBridgeButtonTitle()
}
var analyticsName: String {
"xDai Bridge"
}
func url(token: TokenActionsServiceKey) -> URL? {
return Constants.xDaiBridge
}
}

@ -44,7 +44,7 @@ struct NativecryptoBalanceViewModel: BalanceBaseViewModel {
var currencyAmountWithoutSymbol: Double? { var currencyAmountWithoutSymbol: Double? {
guard let rate = ticker?.rate else { return nil } guard let rate = ticker?.rate else { return nil }
let symbol = mapSymbolToVersionInRates(server.symbol.lowercased()) let symbol = mapSymbolToVersionInRates(server)
guard let currentRate = (rate.rates.filter { $0.code == symbol }.first), currentRate.price > 0, amount > 0 else { return nil } guard let currentRate = (rate.rates.filter { $0.code == symbol }.first), currentRate.price > 0, amount > 0 else { return nil }
return amount * currentRate.price return amount * currentRate.price
@ -62,8 +62,10 @@ struct NativecryptoBalanceViewModel: BalanceBaseViewModel {
return server.symbol return server.symbol
} }
private func mapSymbolToVersionInRates(_ symbol: String) -> String { private func mapSymbolToVersionInRates(_ server: RPCServer) -> String {
let mapping = ["xdai": "dai"] let symbol = server.symbol.lowercased()
let mapping = ["xdai": "dai", "aeth": "eth"]
return mapping[symbol] ?? symbol return mapping[symbol] ?? symbol
} }
} }

@ -408,7 +408,9 @@ class TokenProvider: TokenProviderType {
case .notErc721: case .notErc721:
break break
} }
}.cauterize() }.catch({ e in
error(value: e, pref: "isErc721Promise", address: address)
})
firstly { firstly {
isErc875Promise isErc875Promise
@ -418,7 +420,9 @@ class TokenProvider: TokenProviderType {
} else { } else {
//no-op //no-op
} }
}.cauterize() }.catch({ e in
error(value: e, pref: "isErc875Promise", address: address)
})
firstly { firstly {
isErc1155Promise isErc1155Promise
@ -428,7 +432,9 @@ class TokenProvider: TokenProviderType {
} else { } else {
//no-op //no-op
} }
}.cauterize() }.catch({ e in
error(value: e, pref: "isErc1155Promise", address: address)
})
firstly { firstly {
when(fulfilled: isErc875Promise.asVoid(), isErc721Promise.asVoid(), isErc1155Promise.asVoid()) when(fulfilled: isErc875Promise.asVoid(), isErc721Promise.asVoid(), isErc1155Promise.asVoid())
@ -438,7 +444,9 @@ class TokenProvider: TokenProviderType {
} else { } else {
//no-op //no-op
} }
}.cauterize() }.catch({ e in
error(value: e, pref: "isErc20Promise", address: address)
})
} }
} }

@ -59,7 +59,7 @@ class OpenSea {
switch server { switch server {
case .main, .rinkeby: case .main, .rinkeby:
return true return true
case .kovan, .ropsten, .poa, .sokol, .classic, .callisto, .custom, .goerli, .xDai, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .kovan, .ropsten, .poa, .sokol, .classic, .callisto, .custom, .goerli, .xDai, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
return false return false
} }
} }
@ -110,7 +110,7 @@ class OpenSea {
return Constants.openseaAPI return Constants.openseaAPI
case .rinkeby: case .rinkeby:
return Constants.openseaRinkebyAPI return Constants.openseaRinkebyAPI
case .kovan, .ropsten, .poa, .sokol, .classic, .callisto, .xDai, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .kovan, .ropsten, .poa, .sokol, .classic, .callisto, .xDai, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
return Constants.openseaAPI return Constants.openseaAPI
} }
} }

@ -57,7 +57,7 @@ extension AlphaWalletService: TargetType {
switch self { switch self {
case .getTransactions(_, let server, _, _, _, _): case .getTransactions(_, let server, _, _, _, _):
switch server { switch server {
case .main, .classic, .callisto, .kovan, .ropsten, .custom, .rinkeby, .poa, .sokol, .goerli, .xDai, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .main, .classic, .callisto, .kovan, .ropsten, .custom, .rinkeby, .poa, .sokol, .goerli, .xDai, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
return "" return ""
} }
case .register: case .register:
@ -153,7 +153,7 @@ extension AlphaWalletService: TargetType {
switch self { switch self {
case .getTransactions(_, let server, _, _, _, _): case .getTransactions(_, let server, _, _, _, _):
switch server { switch server {
case .main, .classic, .callisto, .kovan, .ropsten, .custom, .rinkeby, .poa, .sokol, .goerli, .xDai, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .main, .classic, .callisto, .kovan, .ropsten, .custom, .rinkeby, .poa, .sokol, .goerli, .xDai, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
return [ return [
"Content-type": "application/json", "Content-type": "application/json",
"client": Bundle.main.bundleIdentifier ?? "", "client": Bundle.main.bundleIdentifier ?? "",

@ -3,5 +3,21 @@
import Foundation import Foundation
struct ArrayResponse<T: Decodable>: Decodable { struct ArrayResponse<T: Decodable>: Decodable {
private enum CodingKeys: CodingKey {
case result
}
let result: [T] let result: [T]
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
result = try container.decode([T].self, forKey: .result)
} catch let e {
if case DecodingError.typeMismatch(_, _) = e {
result = []
} else {
throw e
}
}
}
} }

@ -46,7 +46,7 @@ fileprivate extension RPCServer {
switch self { switch self {
case .main, .kovan, .ropsten, .rinkeby, .goerli, .binance_smart_chain, .heco, .polygon, .optimistic, .optimisticKovan: case .main, .kovan, .ropsten, .rinkeby, .goerli, .binance_smart_chain, .heco, .polygon, .optimistic, .optimisticKovan:
return etherscanApiRoot?.appendingQueryString("\("module=gastracker&action=gasoracle")\(apiKeyParameter)") return etherscanApiRoot?.appendingQueryString("\("module=gastracker&action=gasoracle")\(apiKeyParameter)")
case .artis_sigma1, .artis_tau1, .binance_smart_chain_testnet, .callisto, .poa, .sokol, .classic, .xDai, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .mumbai_testnet, .cronosTestnet, .custom: case .artis_sigma1, .artis_tau1, .binance_smart_chain_testnet, .callisto, .poa, .sokol, .classic, .xDai, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .mumbai_testnet, .cronosTestnet, .custom, .arbitrum:
return nil return nil
} }
} }

@ -125,6 +125,8 @@ class InCoordinator: NSObject, Coordinator {
quickSwap.theme = navigationController.traitCollection.uniswapTheme quickSwap.theme = navigationController.traitCollection.uniswapTheme
service.register(service: quickSwap) service.register(service: quickSwap)
service.register(service: ArbitrumBridge())
service.register(service: xDaiBridge())
return service return service
}() }()
@ -967,7 +969,7 @@ extension InCoordinator: TokensCoordinatorDelegate {
logTappedSwap(service: service) logTappedSwap(service: service)
guard let token = transactionType.swapServiceInputToken, let url = service.url(token: token) else { return } guard let token = transactionType.swapServiceInputToken, let url = service.url(token: token) else { return }
if let server = service.rpcServer { if let server = service.rpcServer(forToken: token) {
open(url: url, onServer: server) open(url: url, onServer: server)
} else { } else {
open(for: url) open(for: url)

@ -444,6 +444,7 @@
"blockchain.Optimistic" = "Optimistic Testnet"; "blockchain.Optimistic" = "Optimistic Testnet";
"blockchain.Optimistic.Kovan" = "Optimistic Kovan Testnet"; "blockchain.Optimistic.Kovan" = "Optimistic Kovan Testnet";
"blockchain.Cronos.Testnet" = "Cronos Testnet"; "blockchain.Cronos.Testnet" = "Cronos Testnet";
"blockchain.arbitrum" = "Arbitrum";
"photos" = "Browse"; "photos" = "Browse";
"light" = "Light"; "light" = "Light";
"qrCode.title" = "Point your camera on QR code"; "qrCode.title" = "Point your camera on QR code";
@ -531,6 +532,7 @@
"a.wallet.token.erc20ExchangeOn1inch.button.title" = "Swap"; "a.wallet.token.erc20ExchangeOn1inch.button.title" = "Swap";
"a.wallet.token.erc20ExchangeHoneyswap.button.title" = "Swap"; "a.wallet.token.erc20ExchangeHoneyswap.button.title" = "Swap";
"a.wallet.token.xDaiBridge.button.title" = "Convert to DAI"; "a.wallet.token.xDaiBridge.button.title" = "Convert to DAI";
"a.wallet.token.arbitrumBridge.button.title" = "Convert to Arbitrum";
"a.wallet.token.buy.xDai.title" = "Buy xDai"; "a.wallet.token.buy.xDai.title" = "Buy xDai";
"qrCode.myqrCode.title" = "My QR Code"; "qrCode.myqrCode.title" = "My QR Code";
"qrCode.sendToAddress.title" = "Send to this Address"; "qrCode.sendToAddress.title" = "Send to this Address";

@ -444,6 +444,7 @@
"blockchain.Optimistic" = "Optimistic Testnet"; "blockchain.Optimistic" = "Optimistic Testnet";
"blockchain.Optimistic.Kovan" = "Optimistic Kovan Testnet"; "blockchain.Optimistic.Kovan" = "Optimistic Kovan Testnet";
"blockchain.Cronos.Testnet" = "Cronos Testnet"; "blockchain.Cronos.Testnet" = "Cronos Testnet";
"blockchain.arbitrum" = "Arbitrum";
"photos" = "Explorar"; "photos" = "Explorar";
"light" = "Luz"; "light" = "Luz";
"qrCode.title" = "Apunta la cámara al código QR"; "qrCode.title" = "Apunta la cámara al código QR";
@ -531,6 +532,7 @@
"a.wallet.token.erc20ExchangeOn1inch.button.title" = "Swap"; "a.wallet.token.erc20ExchangeOn1inch.button.title" = "Swap";
"a.wallet.token.erc20ExchangeHoneyswap.button.title" = "Swap"; "a.wallet.token.erc20ExchangeHoneyswap.button.title" = "Swap";
"a.wallet.token.xDaiBridge.button.title" = "Convert to DAI"; "a.wallet.token.xDaiBridge.button.title" = "Convert to DAI";
"a.wallet.token.arbitrumBridge.button.title" = "Convert to Arbitrum";
"a.wallet.token.buy.xDai.title" = "Buy xDai"; "a.wallet.token.buy.xDai.title" = "Buy xDai";
"qrCode.myqrCode.title" = "My QR Code"; "qrCode.myqrCode.title" = "My QR Code";
"qrCode.sendToAddress.title" = "Send to this Address"; "qrCode.sendToAddress.title" = "Send to this Address";

@ -442,6 +442,7 @@
"blockchain.Optimistic" = "Optimistic Testnet"; "blockchain.Optimistic" = "Optimistic Testnet";
"blockchain.Optimistic.Kovan" = "Optimistic Kovan Testnet"; "blockchain.Optimistic.Kovan" = "Optimistic Kovan Testnet";
"blockchain.Cronos.Testnet" = "Cronos Testnet"; "blockchain.Cronos.Testnet" = "Cronos Testnet";
"blockchain.arbitrum" = "Arbitrum";
"a.claim.token.failed.notEnoughXDAI.title" = "a.claim.token.failed.notEnoughXDAI.title"; "a.claim.token.failed.notEnoughXDAI.title" = "a.claim.token.failed.notEnoughXDAI.title";
"photos" = "Browse"; "photos" = "Browse";
"light" = "Light"; "light" = "Light";
@ -531,6 +532,7 @@
"a.wallet.token.erc20ExchangeOn1inch.button.title" = "Swap"; "a.wallet.token.erc20ExchangeOn1inch.button.title" = "Swap";
"a.wallet.token.erc20ExchangeHoneyswap.button.title" = "Swap"; "a.wallet.token.erc20ExchangeHoneyswap.button.title" = "Swap";
"a.wallet.token.xDaiBridge.button.title" = "Convert to DAI"; "a.wallet.token.xDaiBridge.button.title" = "Convert to DAI";
"a.wallet.token.arbitrumBridge.button.title" = "Convert to Arbitrum";
"a.wallet.token.buy.xDai.title" = "Buy xDai"; "a.wallet.token.buy.xDai.title" = "Buy xDai";
"qrCode.myqrCode.title" = "My QR Code"; "qrCode.myqrCode.title" = "My QR Code";
"qrCode.sendToAddress.title" = "Send to this Address"; "qrCode.sendToAddress.title" = "Send to this Address";

@ -441,6 +441,7 @@
"blockchain.Optimistic" = "Optimistic Testnet"; "blockchain.Optimistic" = "Optimistic Testnet";
"blockchain.Optimistic.Kovan" = "Optimistic Kovan Testnet"; "blockchain.Optimistic.Kovan" = "Optimistic Kovan Testnet";
"blockchain.Cronos.Testnet" = "Cronos Testnet"; "blockchain.Cronos.Testnet" = "Cronos Testnet";
"blockchain.arbitrum" = "Arbitrum";
"a.claim.token.failed.notEnoughXDAI.title" = "a.claim.token.failed.notEnoughXDAI.title"; "a.claim.token.failed.notEnoughXDAI.title" = "a.claim.token.failed.notEnoughXDAI.title";
"photos" = "Browse"; "photos" = "Browse";
"light" = "Light"; "light" = "Light";
@ -531,6 +532,7 @@
"a.wallet.token.erc20ExchangeOn1inch.button.title" = "Swap"; "a.wallet.token.erc20ExchangeOn1inch.button.title" = "Swap";
"a.wallet.token.erc20ExchangeHoneyswap.button.title" = "Swap"; "a.wallet.token.erc20ExchangeHoneyswap.button.title" = "Swap";
"a.wallet.token.xDaiBridge.button.title" = "Convert to DAI"; "a.wallet.token.xDaiBridge.button.title" = "Convert to DAI";
"a.wallet.token.arbitrumBridge.button.title" = "Convert to Arbitrum";
"a.wallet.token.buy.xDai.title" = "Buy xDai"; "a.wallet.token.buy.xDai.title" = "Buy xDai";
"qrCode.myqrCode.title" = "My QR Code"; "qrCode.myqrCode.title" = "My QR Code";
"qrCode.sendToAddress.title" = "Send to this Address"; "qrCode.sendToAddress.title" = "Send to this Address";

@ -444,6 +444,7 @@
"blockchain.Optimistic" = "Optimistic Testnet"; "blockchain.Optimistic" = "Optimistic Testnet";
"blockchain.Optimistic.Kovan" = "Optimistic Kovan Testnet"; "blockchain.Optimistic.Kovan" = "Optimistic Kovan Testnet";
"blockchain.Cronos.Testnet" = "Cronos Testnet"; "blockchain.Cronos.Testnet" = "Cronos Testnet";
"blockchain.arbitrum" = "Arbitrum";
"photos" = "浏览"; "photos" = "浏览";
"light" = "闪光灯"; "light" = "闪光灯";
"qrCode.title" = "将相机对准二维码"; "qrCode.title" = "将相机对准二维码";
@ -531,6 +532,7 @@
"a.wallet.token.erc20ExchangeOn1inch.button.title" = "Swap"; "a.wallet.token.erc20ExchangeOn1inch.button.title" = "Swap";
"a.wallet.token.erc20ExchangeHoneyswap.button.title" = "Swap"; "a.wallet.token.erc20ExchangeHoneyswap.button.title" = "Swap";
"a.wallet.token.xDaiBridge.button.title" = "Convert to DAI"; "a.wallet.token.xDaiBridge.button.title" = "Convert to DAI";
"a.wallet.token.arbitrumBridge.button.title" = "Convert to Arbitrum";
"a.wallet.token.buy.xDai.title" = "Buy xDai"; "a.wallet.token.buy.xDai.title" = "Buy xDai";
"qrCode.myqrCode.title" = "My QR Code"; "qrCode.myqrCode.title" = "My QR Code";
"qrCode.sendToAddress.title" = "Send to this Address"; "qrCode.sendToAddress.title" = "Send to this Address";

@ -84,6 +84,8 @@ class UniversalLinkCoordinator: Coordinator {
case .custom(let custom): case .custom(let custom):
//TODO better defaults or handling for when properties of custom chain is not provided by user //TODO better defaults or handling for when properties of custom chain is not provided by user
return custom.symbol ?? "ETH" return custom.symbol ?? "ETH"
case .arbitrum:
return "AETH"
} }
} }
@ -454,7 +456,7 @@ class UniversalLinkCoordinator: Coordinator {
switch server { switch server {
case .xDai: case .xDai:
errorMessage = R.string.localizable.aClaimTokenFailedNotEnoughXDAITitle() errorMessage = R.string.localizable.aClaimTokenFailedNotEnoughXDAITitle()
case .classic, .main, .poa, .callisto, .kovan, .ropsten, .rinkeby, .sokol, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .classic, .main, .poa, .callisto, .kovan, .ropsten, .rinkeby, .sokol, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
errorMessage = R.string.localizable.aClaimTokenFailedNotEnoughEthTitle() errorMessage = R.string.localizable.aClaimTokenFailedNotEnoughEthTitle()
} }
if ethPrice.value == nil { if ethPrice.value == nil {

@ -37,6 +37,7 @@ class ServersCoordinator: Coordinator {
.optimistic, .optimistic,
.optimisticKovan, .optimisticKovan,
.cronosTestnet, .cronosTestnet,
.arbitrum
] + RPCServer.customServers ] + RPCServer.customServers
} }

@ -11,12 +11,12 @@ struct ConfigExplorer {
self.server = server self.server = server
} }
func transactionURL(for ID: String) -> (url: URL, name: String?)? { func transactionURL(for ID: String) -> (url: URL, name: String)? {
let result = explorer(for: server) let result = explorer(for: server)
guard let endpoint = result.url else { return .none } guard let endpoint = result.url else { return .none }
let urlString: String? = { let urlString: String? = {
switch server { switch server {
case .main, .kovan, .ropsten, .rinkeby, .sokol, .classic, .xDai, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .callisto, .poa, .cronosTestnet, .custom: case .main, .kovan, .ropsten, .rinkeby, .sokol, .classic, .xDai, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .callisto, .poa, .cronosTestnet, .custom, .arbitrum:
return endpoint + "/tx/" + ID return endpoint + "/tx/" + ID
} }
}() }()
@ -25,11 +25,11 @@ struct ConfigExplorer {
return (url: url, name: result.name) return (url: url, name: result.name)
} }
func explorerName(for server: RPCServer) -> String? { func explorerName(for server: RPCServer) -> String {
switch server { switch server {
case .main, .kovan, .ropsten, .rinkeby, .goerli: case .main, .kovan, .ropsten, .rinkeby, .goerli:
return "Etherscan" return "Etherscan"
case .classic, .poa, .custom, .callisto, .sokol, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .classic, .poa, .custom, .callisto, .sokol, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
return "\(server.name) Explorer" return "\(server.name) Explorer"
case .xDai: case .xDai:
return "Blockscout" return "Blockscout"
@ -38,7 +38,7 @@ struct ConfigExplorer {
} }
} }
private func explorer(for server: RPCServer) -> (url: String?, name: String?) { private func explorer(for server: RPCServer) -> (url: String?, name: String) {
let nameForServer = explorerName(for: server) let nameForServer = explorerName(for: server)
let url = server.etherscanWebpageRoot let url = server.etherscanWebpageRoot
return (url?.absoluteString, nameForServer) return (url?.absoluteString, nameForServer)

@ -37,6 +37,8 @@ public struct Constants {
public static let optimisticMagicLinkHost = "optimistic.aw.app" public static let optimisticMagicLinkHost = "optimistic.aw.app"
public static let optimisticTestMagicLinkHost = "optimistic-kovan.aw.app" public static let optimisticTestMagicLinkHost = "optimistic-kovan.aw.app"
public static let cronosTestMagicLinkHost = "test-cronos.aw.app" public static let cronosTestMagicLinkHost = "test-cronos.aw.app"
public static let arbitrumMagicLinkHost = "arbitrum.aw.app"
public enum Currency { public enum Currency {
static let usd = "USD" static let usd = "USD"
@ -87,6 +89,7 @@ public struct Constants {
//xDai dapps //xDai dapps
static let xDaiBridge = URL(string: "https://bridge.xdaichain.com/")! static let xDaiBridge = URL(string: "https://bridge.xdaichain.com/")!
static let arbitrumBridge = URL(string: "https://bridge.arbitrum.io/")!
static let buyXDaiWitRampUrl = "https://buy.ramp.network/?hostApiKey=\(Constants.Credentials.rampApiKey)&hostLogoUrl=https%3A%2F%2Falphawallet.com%2Fwp-content%2Fthemes%2Falphawallet%2Fimg%2Falphawallet-logo.svg&hostAppName=AlphaWallet&swapAsset=xDai" static let buyXDaiWitRampUrl = "https://buy.ramp.network/?hostApiKey=\(Constants.Credentials.rampApiKey)&hostLogoUrl=https%3A%2F%2Falphawallet.com%2Fwp-content%2Fthemes%2Falphawallet%2Fimg%2Falphawallet-logo.svg&hostAppName=AlphaWallet&swapAsset=xDai"
static func buyWitRampUrl(asset: String) -> String { static func buyWitRampUrl(asset: String) -> String {
@ -172,7 +175,7 @@ public struct Constants {
static let ensContractOnMainnet = AlphaWallet.Address.ethereumAddress(eip55String: "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85") static let ensContractOnMainnet = AlphaWallet.Address.ethereumAddress(eip55String: "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85")
static let defaultEnabledServers: [RPCServer] = [.main, .xDai, .polygon] static let defaultEnabledServers: [RPCServer] = [.arbitrum]//[.main, .xDai, .polygon]
static let defaultEnabledTestnetServers: [RPCServer] = [.ropsten] static let defaultEnabledTestnetServers: [RPCServer] = [.ropsten]
static let tokenScriptUrlSchemeForResources = "tokenscript-resource:///" static let tokenScriptUrlSchemeForResources = "tokenscript-resource:///"

@ -47,6 +47,7 @@ enum RPCServer: Hashable, CaseIterable {
case optimisticKovan case optimisticKovan
case cronosTestnet case cronosTestnet
case custom(CustomRPC) case custom(CustomRPC)
case arbitrum
enum EtherscanCompatibleType: String, Codable { enum EtherscanCompatibleType: String, Codable {
case etherscan case etherscan
@ -88,6 +89,7 @@ enum RPCServer: Hashable, CaseIterable {
case .optimistic: return 10 case .optimistic: return 10
case .optimisticKovan: return 69 case .optimisticKovan: return 69
case .cronosTestnet: return 338 case .cronosTestnet: return 338
case .arbitrum: return 42161
} }
} }
@ -119,12 +121,13 @@ enum RPCServer: Hashable, CaseIterable {
case .optimistic: return "Optimistic Ethereum" case .optimistic: return "Optimistic Ethereum"
case .optimisticKovan: return "Optimistic Kovan" case .optimisticKovan: return "Optimistic Kovan"
case .cronosTestnet: return "Cronos Testnet" case .cronosTestnet: return "Cronos Testnet"
case .arbitrum: return "Arbitrum One"
} }
} }
var isTestnet: Bool { var isTestnet: Bool {
switch self { switch self {
case .xDai, .classic, .main, .poa, .callisto, .binance_smart_chain, .artis_sigma1, .heco, .fantom, .avalanche, .polygon, .optimistic: case .xDai, .classic, .main, .poa, .callisto, .binance_smart_chain, .artis_sigma1, .heco, .fantom, .avalanche, .polygon, .optimistic, .arbitrum:
return false return false
case .kovan, .ropsten, .rinkeby, .sokol, .goerli, .artis_tau1, .binance_smart_chain_testnet, .heco_testnet, .fantom_testnet, .avalanche_testnet, .mumbai_testnet, .optimisticKovan, .cronosTestnet: case .kovan, .ropsten, .rinkeby, .sokol, .goerli, .artis_tau1, .binance_smart_chain_testnet, .heco_testnet, .fantom_testnet, .avalanche_testnet, .mumbai_testnet, .optimisticKovan, .cronosTestnet:
return true return true
@ -135,7 +138,7 @@ enum RPCServer: Hashable, CaseIterable {
var customRpc: CustomRPC? { var customRpc: CustomRPC? {
switch self { switch self {
case .xDai, .classic, .main, .poa, .callisto, .binance_smart_chain, .artis_sigma1, .heco, .fantom, .avalanche, .polygon, .optimistic, .kovan, .ropsten, .rinkeby, .sokol, .goerli, .artis_tau1, .binance_smart_chain_testnet, .heco_testnet, .fantom_testnet, .avalanche_testnet, .mumbai_testnet, .optimisticKovan, .cronosTestnet: case .xDai, .classic, .main, .poa, .callisto, .binance_smart_chain, .artis_sigma1, .heco, .fantom, .avalanche, .polygon, .optimistic, .kovan, .ropsten, .rinkeby, .sokol, .goerli, .artis_tau1, .binance_smart_chain_testnet, .heco_testnet, .fantom_testnet, .avalanche_testnet, .mumbai_testnet, .optimisticKovan, .cronosTestnet, .arbitrum:
return nil return nil
case .custom(let custom): case .custom(let custom):
return custom return custom
@ -148,7 +151,7 @@ enum RPCServer: Hashable, CaseIterable {
var etherscanURLForGeneralTransactionHistory: URL? { var etherscanURLForGeneralTransactionHistory: URL? {
switch self { switch self {
case .main, .ropsten, .rinkeby, .kovan, .poa, .classic, .goerli, .xDai, .artis_sigma1, .artis_tau1, .polygon, .binance_smart_chain, .binance_smart_chain_testnet, .sokol, .callisto, .optimistic, .optimisticKovan, .cronosTestnet, .custom: case .main, .ropsten, .rinkeby, .kovan, .poa, .classic, .goerli, .xDai, .artis_sigma1, .artis_tau1, .polygon, .binance_smart_chain, .binance_smart_chain_testnet, .sokol, .callisto, .optimistic, .optimisticKovan, .cronosTestnet, .custom, .arbitrum:
return etherscanApiRoot?.appendingQueryString("module=account&action=txlist") return etherscanApiRoot?.appendingQueryString("module=account&action=txlist")
case .heco: return nil case .heco: return nil
case .heco_testnet: return nil case .heco_testnet: return nil
@ -197,8 +200,8 @@ enum RPCServer: Hashable, CaseIterable {
case .optimisticKovan: return "https://kovan-optimistic.etherscan.io" case .optimisticKovan: return "https://kovan-optimistic.etherscan.io"
case .cronosTestnet: return "https://cronos-explorer.crypto.org" case .cronosTestnet: return "https://cronos-explorer.crypto.org"
case .custom: return nil case .custom: return nil
case .fantom_testnet, .avalanche, .avalanche_testnet: case .fantom_testnet, .avalanche, .avalanche_testnet: return nil
return nil case .arbitrum: return "https://arbiscan.io"
} }
}() }()
return urlString.flatMap { URL(string: $0) } return urlString.flatMap { URL(string: $0) }
@ -239,6 +242,7 @@ enum RPCServer: Hashable, CaseIterable {
case .optimistic: return "https://api-optimistic.etherscan.io/api" case .optimistic: return "https://api-optimistic.etherscan.io/api"
case .optimisticKovan: return "https://api-kovan-optimistic.etherscan.io/api" case .optimisticKovan: return "https://api-kovan-optimistic.etherscan.io/api"
case .cronosTestnet: return "https://cronos-explorer.crypto.org/api" case .cronosTestnet: return "https://cronos-explorer.crypto.org/api"
case .arbitrum: return "https://api.arbiscan.io/api"
} }
}() }()
return urlString.flatMap { URL(string: $0) } return urlString.flatMap { URL(string: $0) }
@ -258,7 +262,7 @@ enum RPCServer: Hashable, CaseIterable {
private var etherscanCompatibleType: EtherscanCompatibleType { private var etherscanCompatibleType: EtherscanCompatibleType {
switch self { switch self {
case .main, .ropsten, .rinkeby, .kovan, .goerli, .fantom, .heco, .heco_testnet, .optimistic, .optimisticKovan, .binance_smart_chain, .binance_smart_chain_testnet, .polygon: case .main, .ropsten, .rinkeby, .kovan, .goerli, .fantom, .heco, .heco_testnet, .optimistic, .optimisticKovan, .binance_smart_chain, .binance_smart_chain_testnet, .polygon, .arbitrum:
return .etherscan return .etherscan
case .poa, .sokol, .classic, .xDai, .artis_sigma1, .artis_tau1, .mumbai_testnet, .callisto, .cronosTestnet: case .poa, .sokol, .classic, .xDai, .artis_sigma1, .artis_tau1, .mumbai_testnet, .callisto, .cronosTestnet:
return .blockscout return .blockscout
@ -271,7 +275,7 @@ enum RPCServer: Hashable, CaseIterable {
var etherscanApiKey: String? { var etherscanApiKey: String? {
switch self { switch self {
case .main, .kovan, .ropsten, .rinkeby, .goerli, .optimistic, .optimisticKovan: case .main, .kovan, .ropsten, .rinkeby, .goerli, .optimistic, .optimisticKovan, .arbitrum:
return Constants.Credentials.etherscanKey return Constants.Credentials.etherscanKey
case .binance_smart_chain: case .binance_smart_chain:
//Key not needed for testnet (empirically) //Key not needed for testnet (empirically)
@ -288,7 +292,7 @@ enum RPCServer: Hashable, CaseIterable {
switch self { switch self {
case .optimistic, .optimisticKovan: case .optimistic, .optimisticKovan:
return AlphaWallet.Address(string: "0x4200000000000000000000000000000000000006")! return AlphaWallet.Address(string: "0x4200000000000000000000000000000000000006")!
case .main, .ropsten, .rinkeby, .kovan, .goerli, .fantom, .heco, .heco_testnet, .binance_smart_chain, .binance_smart_chain_testnet, .polygon, .poa, .sokol, .classic, .xDai, .artis_sigma1, .artis_tau1, .mumbai_testnet, .callisto, .cronosTestnet, .fantom_testnet, .avalanche, .avalanche_testnet, .custom: case .main, .ropsten, .rinkeby, .kovan, .goerli, .fantom, .heco, .heco_testnet, .binance_smart_chain, .binance_smart_chain_testnet, .polygon, .poa, .sokol, .classic, .xDai, .artis_sigma1, .artis_tau1, .mumbai_testnet, .callisto, .cronosTestnet, .fantom_testnet, .avalanche, .avalanche_testnet, .custom, .arbitrum:
return nil return nil
} }
} }
@ -296,7 +300,7 @@ enum RPCServer: Hashable, CaseIterable {
//Optimistic don't allow changing the gas price and limit //Optimistic don't allow changing the gas price and limit
var canUserChangeGas: Bool { var canUserChangeGas: Bool {
switch self { switch self {
case .main, .ropsten, .rinkeby, .kovan, .goerli, .fantom, .heco, .heco_testnet, .binance_smart_chain, .binance_smart_chain_testnet, .polygon, .poa, .sokol, .classic, .xDai, .artis_sigma1, .artis_tau1, .mumbai_testnet, .callisto, .cronosTestnet, .fantom_testnet, .avalanche, .avalanche_testnet, .custom: case .main, .ropsten, .rinkeby, .kovan, .goerli, .fantom, .heco, .heco_testnet, .binance_smart_chain, .binance_smart_chain_testnet, .polygon, .poa, .sokol, .classic, .xDai, .artis_sigma1, .artis_tau1, .mumbai_testnet, .callisto, .cronosTestnet, .fantom_testnet, .avalanche, .avalanche_testnet, .custom, .arbitrum:
return true return true
case .optimistic, .optimisticKovan: case .optimistic, .optimisticKovan:
return false return false
@ -373,14 +377,14 @@ enum RPCServer: Hashable, CaseIterable {
switch self { switch self {
case .main: case .main:
return etherscanWebpageRoot?.appendingPathComponent("token").appendingPathComponent(address.eip55String) return etherscanWebpageRoot?.appendingPathComponent("token").appendingPathComponent(address.eip55String)
case .ropsten, .rinkeby, .kovan, .xDai, .goerli, .poa, .sokol, .classic, .callisto, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .ropsten, .rinkeby, .kovan, .xDai, .goerli, .poa, .sokol, .classic, .callisto, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
return etherscanContractDetailsWebPageURL(for: address) return etherscanContractDetailsWebPageURL(for: address)
} }
} }
var priceID: AlphaWallet.Address { var priceID: AlphaWallet.Address {
switch self { switch self {
case .main, .ropsten, .rinkeby, .kovan, .sokol, .custom, .xDai, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .main, .ropsten, .rinkeby, .kovan, .sokol, .custom, .xDai, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
return AlphaWallet.Address(string: "0x000000000000000000000000000000000000003c")! return AlphaWallet.Address(string: "0x000000000000000000000000000000000000003c")!
case .poa: case .poa:
return AlphaWallet.Address(string: "0x00000000000000000000000000000000000000AC")! return AlphaWallet.Address(string: "0x00000000000000000000000000000000000000AC")!
@ -418,6 +422,7 @@ enum RPCServer: Hashable, CaseIterable {
case .optimistic: return "ETH" case .optimistic: return "ETH"
case .optimisticKovan: return "ETH" case .optimisticKovan: return "ETH"
case .cronosTestnet: return "tCRO" case .cronosTestnet: return "tCRO"
case .arbitrum: return "AETH"
} }
} }
@ -443,6 +448,7 @@ enum RPCServer: Hashable, CaseIterable {
return "tCRO" return "tCRO"
case .custom(let custom): case .custom(let custom):
return custom.nativeCryptoTokenName ?? "Ether" return custom.nativeCryptoTokenName ?? "Ether"
case .arbitrum: return "AETH"
} }
} }
@ -456,7 +462,7 @@ enum RPCServer: Hashable, CaseIterable {
case .kovan: return .Kovan case .kovan: return .Kovan
case .ropsten: return .Ropsten case .ropsten: return .Ropsten
case .rinkeby: return .Rinkeby case .rinkeby: return .Rinkeby
case .poa, .sokol, .classic, .callisto, .xDai, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .custom, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .poa, .sokol, .classic, .callisto, .xDai, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .custom, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
return .Custom(networkID: BigUInt(chainID)) return .Custom(networkID: BigUInt(chainID))
} }
} }
@ -520,6 +526,8 @@ enum RPCServer: Hashable, CaseIterable {
return Constants.optimisticTestMagicLinkHost return Constants.optimisticTestMagicLinkHost
case .cronosTestnet: case .cronosTestnet:
return Constants.cronosTestMagicLinkHost return Constants.cronosTestMagicLinkHost
case .arbitrum:
return Constants.arbitrumMagicLinkHost
} }
} }
@ -552,6 +560,7 @@ enum RPCServer: Hashable, CaseIterable {
case .optimistic: return "https://mainnet.optimism.io" case .optimistic: return "https://mainnet.optimism.io"
case .optimisticKovan: return "https://kovan.optimism.io" case .optimisticKovan: return "https://kovan.optimism.io"
case .cronosTestnet: return "https://cronos-testnet.crypto.org:8545" case .cronosTestnet: return "https://cronos-testnet.crypto.org:8545"
case .arbitrum: return "https://arbitrum-mainnet.infura.io/v3/\(Constants.Credentials.infuraKey)"
} }
}() }()
return URL(string: urlString)! return URL(string: urlString)!
@ -559,7 +568,7 @@ enum RPCServer: Hashable, CaseIterable {
var transactionInfoEndpoints: URL? { var transactionInfoEndpoints: URL? {
switch self { switch self {
case .main, .kovan, .ropsten, .rinkeby, .goerli, .classic, .poa, .xDai, .sokol, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .fantom, .polygon, .mumbai_testnet, .heco, .heco_testnet, .callisto, .optimistic, .optimisticKovan, .cronosTestnet, .custom: case .main, .kovan, .ropsten, .rinkeby, .goerli, .classic, .poa, .xDai, .sokol, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .fantom, .polygon, .mumbai_testnet, .heco, .heco_testnet, .callisto, .optimistic, .optimisticKovan, .cronosTestnet, .custom, .arbitrum:
return etherscanApiRoot return etherscanApiRoot
case .fantom_testnet: return URL(string: "https://explorer.testnet.fantom.network/tx/") case .fantom_testnet: return URL(string: "https://explorer.testnet.fantom.network/tx/")
case .avalanche: return URL(string: "https://cchain.explorer.avax.network/tx/") case .avalanche: return URL(string: "https://cchain.explorer.avax.network/tx/")
@ -573,14 +582,14 @@ enum RPCServer: Hashable, CaseIterable {
case .ropsten: return Constants.ENSRegistrarRopsten case .ropsten: return Constants.ENSRegistrarRopsten
case .rinkeby: return Constants.ENSRegistrarRinkeby case .rinkeby: return Constants.ENSRegistrarRinkeby
case .goerli: return Constants.ENSRegistrarGoerli case .goerli: return Constants.ENSRegistrarGoerli
case .xDai, .kovan, .poa, .sokol, .classic, .callisto, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .xDai, .kovan, .poa, .sokol, .classic, .callisto, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
return Constants.ENSRegistrarAddress return Constants.ENSRegistrarAddress
} }
} }
var endRecordsContract: AlphaWallet.Address { var endRecordsContract: AlphaWallet.Address {
switch self { switch self {
case .main, .xDai, .kovan, .ropsten, .rinkeby, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .main, .xDai, .kovan, .ropsten, .rinkeby, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
return Constants.ENSRecordsContractAddress return Constants.ENSRecordsContractAddress
case .poa: case .poa:
return Constants.ENSRecordsContractAddressPOA return Constants.ENSRecordsContractAddressPOA
@ -591,7 +600,7 @@ enum RPCServer: Hashable, CaseIterable {
switch self { switch self {
case .main, .xDai: case .main, .xDai:
return .normal return .normal
case .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
return .low return .low
} }
} }
@ -634,6 +643,8 @@ enum RPCServer: Hashable, CaseIterable {
return R.string.localizable.blockchainCronosTestnet() return R.string.localizable.blockchainCronosTestnet()
case .custom(let custom): case .custom(let custom):
return custom.chainName return custom.chainName
case .arbitrum:
return R.string.localizable.blockchainArbitrum()
} }
} }
@ -662,12 +673,13 @@ enum RPCServer: Hashable, CaseIterable {
case .optimistic: return .red case .optimistic: return .red
case .optimisticKovan: return .red case .optimisticKovan: return .red
case .cronosTestnet: return .red case .cronosTestnet: return .red
case .arbitrum: return .red
} }
} }
var transactionDataCoordinatorType: SingleChainTransactionDataCoordinator.Type { var transactionDataCoordinatorType: SingleChainTransactionDataCoordinator.Type {
switch self { switch self {
case .main, .classic, .callisto, .kovan, .ropsten, .custom, .rinkeby, .poa, .sokol, .goerli, .xDai, .artis_sigma1, .binance_smart_chain, .binance_smart_chain_testnet, .artis_tau1, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .main, .classic, .callisto, .kovan, .ropsten, .custom, .rinkeby, .poa, .sokol, .goerli, .xDai, .artis_sigma1, .binance_smart_chain, .binance_smart_chain_testnet, .artis_tau1, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
return SingleChainTransactionEtherscanDataCoordinator.self return SingleChainTransactionEtherscanDataCoordinator.self
} }
} }
@ -706,6 +718,8 @@ enum RPCServer: Hashable, CaseIterable {
return R.image.iconsTokensOptimistic() return R.image.iconsTokensOptimistic()
case .optimisticKovan: case .optimisticKovan:
return R.image.iconsTokensOptimisticKovan() return R.image.iconsTokensOptimisticKovan()
case .arbitrum:
return R.image.arbitrum()
} }
} }
@ -765,6 +779,7 @@ enum RPCServer: Hashable, CaseIterable {
.optimistic, .optimistic,
.optimisticKovan, .optimisticKovan,
.cronosTestnet, .cronosTestnet,
.arbitrum
] ]
} }
@ -793,7 +808,7 @@ enum RPCServer: Hashable, CaseIterable {
case .optimistic: case .optimistic:
//These not allow range more than 10000 //These not allow range more than 10000
return .blockNumber(fromBlockNumber + 9999) return .blockNumber(fromBlockNumber + 9999)
case .polygon, .mumbai_testnet, .cronosTestnet: case .polygon, .mumbai_testnet, .cronosTestnet, .arbitrum:
//These not allow range more than 100000 //These not allow range more than 100000
return .blockNumber(fromBlockNumber + 99990) return .blockNumber(fromBlockNumber + 99990)
case .main, .kovan, .ropsten, .rinkeby, .poa, .classic, .callisto, .xDai, .goerli, .artis_sigma1, .artis_tau1, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .optimisticKovan, .sokol, .custom: case .main, .kovan, .ropsten, .rinkeby, .poa, .classic, .callisto, .xDai, .goerli, .artis_sigma1, .artis_tau1, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .optimisticKovan, .sokol, .custom:

@ -126,6 +126,7 @@ extension EventSourceCoordinator.functional {
}).done(on: queue, { _ in }).done(on: queue, { _ in
seal.fulfill(()) seal.fulfill(())
}).catch(on: queue, { e in }).catch(on: queue, { e in
error(value: e, rpcServer: tokenServer, address: contractAddress)
seal.reject(e) seal.reject(e)
}) })
} }

@ -150,6 +150,7 @@ extension EventSourceCoordinatorForActivities.functional {
}).done { _ in }).done { _ in
seal.fulfill(()) seal.fulfill(())
}.catch { e in }.catch { e in
error(value: e, rpcServer: server, address: tokenContract)
seal.reject(e) seal.reject(e)
} }
} }
@ -190,4 +191,4 @@ extension EventSourceCoordinatorForActivities.functional {
guard let filterValueTypedForEventFilters = filterValue.coerceToArgumentTypeForEventFilter(parameterType) else { return nil } guard let filterValueTypedForEventFilters = filterValue.coerceToArgumentTypeForEventFilter(parameterType) else { return nil }
return (filter: [filterValueTypedForEventFilters], textEquivalent: textEquivalent) return (filter: [filterValueTypedForEventFilters], textEquivalent: textEquivalent)
} }
} }

@ -67,7 +67,6 @@ class ENSReverseLookupCoordinator {
} }
} }
}.cauterize() }.cauterize()
} }
} else { } else {
completion(.failure(AnyError(Web3Error(description: "Error extracting result from \(self.server.ensRegistrarContract).\(function.name)()")))) completion(.failure(AnyError(Web3Error(description: "Error extracting result from \(self.server.ensRegistrarContract).\(function.name)()"))))

@ -14,7 +14,7 @@ class GetERC20BalanceCoordinator: CallbackQueueProvider {
self.server = server self.server = server
self.queue = queue self.queue = queue
} }
func getBalance(for address: AlphaWallet.Address, contract: AlphaWallet.Address) -> Promise<BigInt> { func getBalance(for address: AlphaWallet.Address, contract: AlphaWallet.Address) -> Promise<BigInt> {
return Promise { seal in return Promise { seal in
getBalance(for: address, contract: contract) { result in getBalance(for: address, contract: contract) { result in

@ -180,7 +180,7 @@ class SingleChainTokenCoordinator: Coordinator {
autoDetectXDaiPartnerTokens() autoDetectXDaiPartnerTokens()
case .rinkeby: case .rinkeby:
autoDetectRinkebyPartnerTokens() autoDetectRinkebyPartnerTokens()
case .kovan, .ropsten, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .binance_smart_chain, .binance_smart_chain_testnet, .artis_tau1, .custom, .heco_testnet, .heco, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .kovan, .ropsten, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .binance_smart_chain, .binance_smart_chain_testnet, .artis_tau1, .custom, .heco_testnet, .heco, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
break break
} }
} }
@ -677,7 +677,7 @@ extension SingleChainTokenCoordinator: TokenViewControllerDelegate {
switch action.type { switch action.type {
case .tokenScript: case .tokenScript:
showTokenInstanceActionView(forAction: action, fungibleTokenObject: token, navigationController: navigationController) showTokenInstanceActionView(forAction: action, fungibleTokenObject: token, navigationController: navigationController)
case .erc20Send, .erc20Receive, .nftRedeem, .nftSell, .nonFungibleTransfer, .swap, .xDaiBridge, .buy: case .erc20Send, .erc20Receive, .nftRedeem, .nftSell, .nonFungibleTransfer, .swap, .buy, .bridge:
//Couldn't have reached here //Couldn't have reached here
break break
} }

@ -127,6 +127,7 @@ extension RPCServer {
case .optimistic: return 22 case .optimistic: return 22
case .optimisticKovan: return 23 case .optimisticKovan: return 23
case .cronosTestnet: return 24 case .cronosTestnet: return 24
case .arbitrum: return 25
} }
} }
} }

@ -13,7 +13,7 @@ struct TokenInstanceAction {
case nonFungibleTransfer case nonFungibleTransfer
case tokenScript(contract: AlphaWallet.Address, title: String, viewHtml: (html: String, style: String), attributes: [AttributeId: AssetAttribute], transactionFunction: FunctionOrigin?, selection: TokenScriptSelection?) case tokenScript(contract: AlphaWallet.Address, title: String, viewHtml: (html: String, style: String), attributes: [AttributeId: AssetAttribute], transactionFunction: FunctionOrigin?, selection: TokenScriptSelection?)
case swap(service: SwapTokenURLProviderType) case swap(service: SwapTokenURLProviderType)
case xDaiBridge case bridge(service: BridgeTokenURLProviderType)
case buy(service: BuyTokenURLProviderType) case buy(service: BuyTokenURLProviderType)
} }
@ -35,13 +35,13 @@ struct TokenInstanceAction {
return service.action return service.action
case .buy(let service): case .buy(let service):
return service.action return service.action
case .xDaiBridge: case .bridge(let service):
return R.string.localizable.aWalletTokenXDaiBridgeButtonTitle() return service.action
} }
} }
var attributes: [AttributeId: AssetAttribute] { var attributes: [AttributeId: AssetAttribute] {
switch type { switch type {
case .erc20Send, .erc20Receive, .swap, .xDaiBridge, .buy: case .erc20Send, .erc20Receive, .swap, .buy, .bridge:
return .init() return .init()
case .nftRedeem, .nftSell, .nonFungibleTransfer: case .nftRedeem, .nftSell, .nonFungibleTransfer:
return .init() return .init()
@ -71,7 +71,7 @@ struct TokenInstanceAction {
} }
var transactionFunction: FunctionOrigin? { var transactionFunction: FunctionOrigin? {
switch type { switch type {
case .erc20Send, .erc20Receive, .swap, .xDaiBridge, .buy: case .erc20Send, .erc20Receive, .swap, .buy, .bridge:
return nil return nil
case .nftRedeem, .nftSell, .nonFungibleTransfer: case .nftRedeem, .nftSell, .nonFungibleTransfer:
return nil return nil
@ -81,7 +81,7 @@ struct TokenInstanceAction {
} }
var contract: AlphaWallet.Address? { var contract: AlphaWallet.Address? {
switch type { switch type {
case .erc20Send, .erc20Receive, .swap, .xDaiBridge, .buy: case .erc20Send, .erc20Receive, .swap, .buy, .bridge:
return nil return nil
case .nftRedeem, .nftSell, .nonFungibleTransfer: case .nftRedeem, .nftSell, .nonFungibleTransfer:
return nil return nil
@ -97,7 +97,7 @@ struct TokenInstanceAction {
//TODO we can live-reload the action view screen now if we observe for changes //TODO we can live-reload the action view screen now if we observe for changes
func viewHtml(forTokenHolder tokenHolder: TokenHolder, tokenId: TokenId) -> (html: String, hash: Int) { func viewHtml(forTokenHolder tokenHolder: TokenHolder, tokenId: TokenId) -> (html: String, hash: Int) {
switch type { switch type {
case .erc20Send, .erc20Receive, .swap, .xDaiBridge, .buy: case .erc20Send, .erc20Receive, .swap, .buy, .bridge:
return (html: "", hash: 0) return (html: "", hash: 0)
case .nftRedeem: case .nftRedeem:
return (html: "", hash: 0) return (html: "", hash: 0)
@ -114,7 +114,7 @@ struct TokenInstanceAction {
func activeExcludingSelection(selectedTokenHolders: [TokenHolder], forWalletAddress walletAddress: AlphaWallet.Address, fungibleBalance: BigInt? = nil) -> TokenScriptSelection? { func activeExcludingSelection(selectedTokenHolders: [TokenHolder], forWalletAddress walletAddress: AlphaWallet.Address, fungibleBalance: BigInt? = nil) -> TokenScriptSelection? {
switch type { switch type {
case .erc20Send, .erc20Receive, .swap, .xDaiBridge, .buy: case .erc20Send, .erc20Receive, .swap, .buy, .bridge:
return nil return nil
case .nftRedeem, .nftSell, .nonFungibleTransfer: case .nftRedeem, .nftSell, .nonFungibleTransfer:
return nil return nil
@ -134,7 +134,7 @@ struct TokenInstanceAction {
func activeExcludingSelection(selectedTokenHolder tokenHolder: TokenHolder, tokenId: TokenId, forWalletAddress walletAddress: AlphaWallet.Address, fungibleBalance: BigInt? = nil) -> TokenScriptSelection? { func activeExcludingSelection(selectedTokenHolder tokenHolder: TokenHolder, tokenId: TokenId, forWalletAddress walletAddress: AlphaWallet.Address, fungibleBalance: BigInt? = nil) -> TokenScriptSelection? {
switch type { switch type {
case .erc20Send, .erc20Receive, .swap, .xDaiBridge, .buy: case .erc20Send, .erc20Receive, .swap, .buy, .bridge:
return nil return nil
case .nftRedeem, .nftSell, .nonFungibleTransfer: case .nftRedeem, .nftSell, .nonFungibleTransfer:
return nil return nil

@ -180,7 +180,7 @@ class Erc1155TokenInstanceViewController: UIViewController, TokenVerifiableStatu
let actions = viewModel.actions let actions = viewModel.actions
for (action, button) in zip(actions, buttonsBar.buttons) where button == sender { for (action, button) in zip(actions, buttonsBar.buttons) where button == sender {
switch action.type { switch action.type {
case .erc20Send, .erc20Receive, .swap, .xDaiBridge, .buy: case .erc20Send, .erc20Receive, .swap, .buy, .bridge:
//TODO when we support TokenScript views for ERC20s, we need to perform the action here //TODO when we support TokenScript views for ERC20s, we need to perform the action here
break break
case .nftRedeem: case .nftRedeem:

@ -152,7 +152,7 @@ class TokenInstanceViewController: UIViewController, TokenVerifiableStatusViewCo
let actions = viewModel.actions let actions = viewModel.actions
for (action, button) in zip(actions, buttonsBar.buttons) where button == sender { for (action, button) in zip(actions, buttonsBar.buttons) where button == sender {
switch action.type { switch action.type {
case .erc20Send, .erc20Receive, .swap, .xDaiBridge, .buy: case .erc20Send, .erc20Receive, .swap, .buy, .bridge:
//TODO when we support TokenScript views for ERC20s, we need to perform the action here //TODO when we support TokenScript views for ERC20s, we need to perform the action here
break break
case .nftRedeem: case .nftRedeem:

@ -256,20 +256,12 @@ class TokenViewController: UIViewController {
} else { } else {
delegate?.didTap(action: action, transactionType: transactionType, viewController: self) delegate?.didTap(action: action, transactionType: transactionType, viewController: self)
} }
case .xDaiBridge: case .bridge(let service):
delegate?.shouldOpen(url: Constants.xDaiBridge, shouldSwitchServer: true, forTransactionType: transactionType, inViewController: self) guard let token = transactionType.swapServiceInputToken, let url = service.url(token: token) else { return }
case .buy(let service):
var tokenObject: TokenActionsServiceKey?
switch transactionType {
case .nativeCryptocurrency(let token, _, _):
tokenObject = TokenActionsServiceKey(tokenObject: token)
case .erc20Token(let token, _, _):
tokenObject = TokenActionsServiceKey(tokenObject: token)
case .erc875Token, .erc875TokenOrder, .erc721Token, .erc721ForTicketToken, .erc1155Token, .dapp, .tokenScript, .claimPaidErc875MagicLink:
tokenObject = .none
}
guard let token = tokenObject, let url = service.url(token: token) else { return } delegate?.shouldOpen(url: url, shouldSwitchServer: true, forTransactionType: transactionType, inViewController: self)
case .buy(let service):
guard let token = transactionType.swapServiceInputToken, let url = service.url(token: token) else { return }
logStartOnRamp(name: "Ramp") logStartOnRamp(name: "Ramp")
delegate?.shouldOpen(url: url, shouldSwitchServer: false, forTransactionType: transactionType, inViewController: self) delegate?.shouldOpen(url: url, shouldSwitchServer: false, forTransactionType: transactionType, inViewController: self)

@ -216,7 +216,7 @@ class TokensCardViewController: UIViewController, TokenVerifiableStatusViewContr
private func handle(action: TokenInstanceAction) { private func handle(action: TokenInstanceAction) {
guard let tokenHolder = selectedTokenHolder else { return } guard let tokenHolder = selectedTokenHolder else { return }
switch action.type { switch action.type {
case .erc20Send, .erc20Receive, .swap, .xDaiBridge, .buy: case .erc20Send, .erc20Receive, .swap, .buy, .bridge:
break break
case .nftRedeem: case .nftRedeem:
redeem() redeem()

@ -71,7 +71,7 @@ struct FungibleTokenViewCellViewModel {
return "-" return "-"
} }
}() }()
return NSAttributedString(string: apprecation24hours, attributes: [ return NSAttributedString(string: apprecation24hours, attributes: [
.foregroundColor: valuePercentageChangeColor, .foregroundColor: valuePercentageChangeColor,
.font: Screen.TokenCard.Font.valueChangeLabel .font: Screen.TokenCard.Font.valueChangeLabel
@ -103,7 +103,7 @@ struct FungibleTokenViewCellViewModel {
.font: Screen.TokenCard.Font.valueChangeLabel .font: Screen.TokenCard.Font.valueChangeLabel
]) ])
} }
private var fiatValue: String { private var fiatValue: String {
if let fiatValue = EthCurrencyHelper(ticker: ticker).fiatValue(value: token.optionalDecimalValue) { if let fiatValue = EthCurrencyHelper(ticker: ticker).fiatValue(value: token.optionalDecimalValue) {
return NumberFormatter.usd(format: .fiatFormat).string(from: fiatValue) ?? "-" return NumberFormatter.usd(format: .fiatFormat).string(from: fiatValue) ?? "-"

@ -27,7 +27,7 @@ struct TokenViewControllerViewModel {
var actions: [TokenInstanceAction] { var actions: [TokenInstanceAction] {
guard let token = token else { return [] } guard let token = token else { return [] }
let xmlHandler = XMLHandler(token: token, assetDefinitionStore: assetDefinitionStore) let xmlHandler = XMLHandler(token: token, assetDefinitionStore: assetDefinitionStore)
var actionsFromTokenScript = xmlHandler.actions let actionsFromTokenScript = xmlHandler.actions
let key = TokenActionsServiceKey(tokenObject: token) let key = TokenActionsServiceKey(tokenObject: token)
if actionsFromTokenScript.isEmpty { if actionsFromTokenScript.isEmpty {
@ -54,8 +54,8 @@ struct TokenViewControllerViewModel {
] ]
switch token.server { switch token.server {
case .xDai: case .xDai:
return [.init(type: .erc20Send), .init(type: .xDaiBridge), .init(type: .erc20Receive)] + tokenActionsProvider.actions(token: key) return [.init(type: .erc20Send), .init(type: .erc20Receive)] + tokenActionsProvider.actions(token: key)
case .main, .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .custom, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .main, .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .custom, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
return actions + tokenActionsProvider.actions(token: key) return actions + tokenActionsProvider.actions(token: key)
} }
} }
@ -66,18 +66,9 @@ struct TokenViewControllerViewModel {
case .erc20: case .erc20:
return actionsFromTokenScript + tokenActionsProvider.actions(token: key) return actionsFromTokenScript + tokenActionsProvider.actions(token: key)
case .nativeCryptocurrency: case .nativeCryptocurrency:
let xDaiBridgeActions: [TokenInstanceAction] // TODO we should support retrieval of XML (and XMLHandler) based on address + server. For now, this is only important for native cryptocurrency. So might be ok to check like this for now
switch token.server {
case .xDai:
xDaiBridgeActions = [.init(type: .xDaiBridge)]
case .main, .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .heco, .heco_testnet, .custom, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet:
xDaiBridgeActions = []
}
//TODO we should support retrieval of XML (and XMLHandler) based on address + server. For now, this is only important for native cryptocurrency. So might be ok to check like this for now
if let server = xmlHandler.server, server.matches(server: token.server) { if let server = xmlHandler.server, server.matches(server: token.server) {
actionsFromTokenScript += tokenActionsProvider.actions(token: key) return actionsFromTokenScript + tokenActionsProvider.actions(token: key)
return xDaiBridgeActions + actionsFromTokenScript
} else { } else {
//TODO .erc20Send and .erc20Receive names aren't appropriate //TODO .erc20Send and .erc20Receive names aren't appropriate
let actions: [TokenInstanceAction] = [ let actions: [TokenInstanceAction] = [
@ -85,7 +76,7 @@ struct TokenViewControllerViewModel {
.init(type: .erc20Receive) .init(type: .erc20Receive)
] ]
return xDaiBridgeActions + actions + tokenActionsProvider.actions(token: key) return actions + tokenActionsProvider.actions(token: key)
} }
} }
} }

@ -9,6 +9,7 @@ import Moya
import PromiseKit import PromiseKit
import Result import Result
import UserNotifications import UserNotifications
import Mixpanel
class SingleChainTransactionEtherscanDataCoordinator: SingleChainTransactionDataCoordinator { class SingleChainTransactionEtherscanDataCoordinator: SingleChainTransactionDataCoordinator {
private let storage: TransactionsStorage private let storage: TransactionsStorage
@ -115,7 +116,9 @@ class SingleChainTransactionEtherscanDataCoordinator: SingleChainTransactionData
Config.setLastFetchedErc20InteractionBlockNumber(maxBlockNumber, server: server, wallet: wallet) Config.setLastFetchedErc20InteractionBlockNumber(maxBlockNumber, server: server, wallet: wallet)
} }
strongSelf.update(items: backFilledTransactions) strongSelf.update(items: backFilledTransactions)
}.cauterize() }.catch({ e in
error(value: e, function: #function, rpcServer: server, address: wallet)
})
.finally { [weak self] in .finally { [weak self] in
self?.isAutoDetectingERC20Transactions = false self?.isAutoDetectingERC20Transactions = false
} }
@ -141,7 +144,9 @@ class SingleChainTransactionEtherscanDataCoordinator: SingleChainTransactionData
Config.setLastFetchedErc721InteractionBlockNumber(maxBlockNumber, server: server, wallet: wallet) Config.setLastFetchedErc721InteractionBlockNumber(maxBlockNumber, server: server, wallet: wallet)
} }
strongSelf.update(items: backFilledTransactions) strongSelf.update(items: backFilledTransactions)
}.cauterize() }.catch({ e in
error(value: e, rpcServer: server, address: wallet)
})
.finally { [weak self] in .finally { [weak self] in
self?.isAutoDetectingErc721Transactions = false self?.isAutoDetectingErc721Transactions = false
} }
@ -295,9 +300,10 @@ class SingleChainTransactionEtherscanDataCoordinator: SingleChainTransactionData
}).cauterize() }).cauterize()
} }
private func handleError(error: Error) { private func handleError(error e: Error) {
//delegate?.didUpdate(result: .failure(TransactionError.failedToFetch)) //delegate?.didUpdate(result: .failure(TransactionError.failedToFetch))
// Avoid showing an error on failed request, instead show cached transactions. // Avoid showing an error on failed request, instead show cached transactions.
error(value: e)
} }
//TODO notify user of received tokens too //TODO notify user of received tokens too
@ -345,7 +351,7 @@ class SingleChainTransactionEtherscanDataCoordinator: SingleChainTransactionData
} }
case .classic, .xDai: case .classic, .xDai:
break break
case .kovan, .ropsten, .rinkeby, .poa, .sokol, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .kovan, .ropsten, .rinkeby, .poa, .sokol, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
break break
} }
} }
@ -370,7 +376,7 @@ class SingleChainTransactionEtherscanDataCoordinator: SingleChainTransactionData
switch session.server { switch session.server {
case .main, .xDai: case .main, .xDai:
content.body = R.string.localizable.transactionsReceivedEther(amount, session.server.symbol) content.body = R.string.localizable.transactionsReceivedEther(amount, session.server.symbol)
case .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
content.body = R.string.localizable.transactionsReceivedEther("\(amount) (\(session.server.name))", session.server.symbol) content.body = R.string.localizable.transactionsReceivedEther("\(amount) (\(session.server.name))", session.server.symbol)
} }
content.sound = .default content.sound = .default
@ -406,7 +412,9 @@ class SingleChainTransactionEtherscanDataCoordinator: SingleChainTransactionData
strongSelf.transactionsTracker.fetchingState = .failed strongSelf.transactionsTracker.fetchingState = .failed
} }
}).cauterize() }).catch({ e in
error(value: e, rpcServer: self.session.server, address: address)
})
} }
func stop() { func stop() {
@ -461,7 +469,7 @@ class SingleChainTransactionEtherscanDataCoordinator: SingleChainTransactionData
}).done(on: queue, { transactions in }).done(on: queue, { transactions in
coordinator.update(items: transactions) coordinator.update(items: transactions)
}).catch { e in }).catch { e in
coordinator.handleError(error: e) error(value: e, rpcServer: coordinator.session.server, address: self.session.account.address)
}.finally { [weak self] in }.finally { [weak self] in
guard let strongSelf = self else { return } guard let strongSelf = self else { return }
@ -492,10 +500,12 @@ extension SingleChainTransactionEtherscanDataCoordinator.functional {
} }
static func fetchTransactions(for address: AlphaWallet.Address, startBlock: Int, endBlock: Int = 999_999_999, sortOrder: AlphaWalletService.SortOrder, session: WalletSession, alphaWalletProvider: MoyaProvider<AlphaWalletService>, tokensStorage: TokensDataStore, tokenProvider: TokenProviderType, queue: DispatchQueue) -> Promise<[TransactionInstance]> { static func fetchTransactions(for address: AlphaWallet.Address, startBlock: Int, endBlock: Int = 999_999_999, sortOrder: AlphaWalletService.SortOrder, session: WalletSession, alphaWalletProvider: MoyaProvider<AlphaWalletService>, tokensStorage: TokensDataStore, tokenProvider: TokenProviderType, queue: DispatchQueue) -> Promise<[TransactionInstance]> {
firstly { let target: AlphaWalletService = .getTransactions(config: session.config, server: session.server, address: address, startBlock: startBlock, endBlock: endBlock, sortOrder: sortOrder)
alphaWalletProvider.request(.getTransactions(config: session.config, server: session.server, address: address, startBlock: startBlock, endBlock: endBlock, sortOrder: sortOrder))
}.map(on: queue) { return firstly {
try $0.map(ArrayResponse<RawTransaction>.self).result.map { alphaWalletProvider.request(target)
}.map(on: queue) { response -> [Promise<TransactionInstance?>] in
return try response.map(ArrayResponse<RawTransaction>.self).result.map {
TransactionInstance.from(transaction: $0, tokensStorage: tokensStorage, tokenProvider: tokenProvider) TransactionInstance.from(transaction: $0, tokensStorage: tokensStorage, tokenProvider: tokenProvider)
} }
}.then(on: queue) { }.then(on: queue) {
@ -526,3 +536,14 @@ extension SingleChainTransactionEtherscanDataCoordinator.functional {
} }
} }
} }
func error(value e: Error, pref: String = "", function f: String = #function, rpcServer: RPCServer? = nil, address: AlphaWallet.Address? = nil) {
var description = "\(f)"
description += pref.isEmpty ? "" : " \(pref)"
description += rpcServer.flatMap { " server: \($0)" } ?? ""
description += address.flatMap { " address: \($0.eip55String)" } ?? ""
description += " \(e)"
error(description)
}

@ -483,7 +483,7 @@ extension TokensCardCoordinator: TokensCardViewControllerDelegate {
switch action.type { switch action.type {
case .tokenScript: case .tokenScript:
showTokenInstanceActionView(forAction: action, tokenHolder: tokenHolder, viewController: viewController) showTokenInstanceActionView(forAction: action, tokenHolder: tokenHolder, viewController: viewController)
case .erc20Send, .erc20Receive, .nftRedeem, .nftSell, .nonFungibleTransfer, .swap, .xDaiBridge, .buy: case .erc20Send, .erc20Receive, .nftRedeem, .nftSell, .nonFungibleTransfer, .swap, .buy, .bridge:
//Couldn't have reached here //Couldn't have reached here
break break
} }

@ -170,7 +170,9 @@ class TransactionConfigurator {
} }
self.delegate?.gasLimitEstimateUpdated(to: gasLimit, in: self) self.delegate?.gasLimitEstimateUpdated(to: gasLimit, in: self)
}.cauterize() }.catch({ e in
error(value: e, rpcServer: self.session.server)
})
} }
private func estimateGasPrice() { private func estimateGasPrice() {
@ -196,7 +198,9 @@ class TransactionConfigurator {
} }
self.delegate?.gasPriceEstimateUpdated(to: standard, in: self) self.delegate?.gasPriceEstimateUpdated(to: standard, in: self)
}.cauterize() }.catch({ e in
error(value: e, rpcServer: self.session.server)
})
} }
private func shouldUseEstimatedGasPrice(_ estimatedGasPrice: BigInt) -> Bool { private func shouldUseEstimatedGasPrice(_ estimatedGasPrice: BigInt) -> Bool {
@ -217,7 +221,7 @@ class TransactionConfigurator {
switch server { switch server {
case .xDai: case .xDai:
return estimateGasPriceForXDai() return estimateGasPriceForXDai()
case .main, .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .main, .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
return Promise(estimateGasPriceForUseRpcNode(server: server)) return Promise(estimateGasPriceForUseRpcNode(server: server))
} }
} }
@ -295,7 +299,7 @@ class TransactionConfigurator {
// return .networkCongested // return .networkCongested
//} //}
break break
case .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .xDai, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .fantom, .fantom_testnet, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .xDai, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .fantom, .fantom_testnet, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
break break
} }
return nil return nil
@ -306,7 +310,7 @@ class TransactionConfigurator {
case .xDai: case .xDai:
//xdai transactions are always 1 gwei in gasPrice //xdai transactions are always 1 gwei in gasPrice
return GasPriceConfiguration.xDaiGasPrice return GasPriceConfiguration.xDaiGasPrice
case .main, .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .main, .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
if let gasPrice = transaction.gasPrice, gasPrice > 0 { if let gasPrice = transaction.gasPrice, gasPrice > 0 {
return min(max(gasPrice, GasPriceConfiguration.minPrice), GasPriceConfiguration.maxPrice) return min(max(gasPrice, GasPriceConfiguration.minPrice), GasPriceConfiguration.maxPrice)
} else { } else {

@ -113,7 +113,7 @@ fileprivate extension RPCServer {
} else { } else {
return rpcURL return rpcURL
} }
case .xDai, .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .main, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet: case .xDai, .kovan, .ropsten, .rinkeby, .poa, .sokol, .classic, .callisto, .goerli, .artis_sigma1, .artis_tau1, .binance_smart_chain, .binance_smart_chain_testnet, .custom, .heco, .heco_testnet, .main, .fantom, .fantom_testnet, .avalanche, .avalanche_testnet, .polygon, .mumbai_testnet, .optimistic, .optimisticKovan, .cronosTestnet, .arbitrum:
return self.rpcURL return self.rpcURL
} }
} }

Loading…
Cancel
Save