[Refactoring] refactor Ramp and Oneinch

pull/6779/head
Krypto Pank 2 years ago
parent 7867ae9456
commit a36e724b9e
  1. 4
      AlphaWallet/Common/Services/TokenActionsService+Extensions.swift
  2. 4
      AlphaWalletTests/Coordinators/AppCoordinatorTests.swift
  3. 10
      AlphaWalletTests/modules/AlphaWalletFoundation/Oneinch/OneinchTests.swift
  4. 10
      AlphaWalletTests/modules/AlphaWalletFoundation/Ramp/RampTests.swift
  5. 27
      modules/AlphaWalletFoundation/AlphaWalletFoundation/BuyToken/Ramp/Ramp.swift
  6. 6
      modules/AlphaWalletFoundation/AlphaWalletFoundation/BuyToken/Ramp/RampNetworking.swift
  7. 26
      modules/AlphaWalletFoundation/AlphaWalletFoundation/SwapToken/Oneinch/Oneinch.swift
  8. 10
      modules/AlphaWalletFoundation/AlphaWalletFoundation/SwapToken/Oneinch/OneinchNetworking.swift

@ -14,7 +14,7 @@ extension TokenActionsService {
let traitCollection = UINavigationController().traitCollection let traitCollection = UINavigationController().traitCollection
service.register(service: BuyTokenProvider(subProviders: [ service.register(service: BuyTokenProvider(subProviders: [
Coinbase(action: R.string.localizable.aWalletTokenBuyOnCoinbaseTitle()), Coinbase(action: R.string.localizable.aWalletTokenBuyOnCoinbaseTitle()),
Ramp(action: R.string.localizable.aWalletTokenBuyOnRampTitle(), networkProvider: RampNetworkProvider(networkService: networkService)) Ramp(action: R.string.localizable.aWalletTokenBuyOnRampTitle(), networking: BaseRampNetworking(networkService: networkService))
], action: R.string.localizable.aWalletTokenBuyTitle())) ], action: R.string.localizable.aWalletTokenBuyTitle()))
let honeySwapService = HoneySwap(action: R.string.localizable.aWalletTokenErc20ExchangeHoneyswapButtonTitle()) let honeySwapService = HoneySwap(action: R.string.localizable.aWalletTokenErc20ExchangeHoneyswapButtonTitle())
@ -25,7 +25,7 @@ extension TokenActionsService {
var availableSwapProviders: [SupportedTokenActionsProvider & TokenActionProvider] = [ var availableSwapProviders: [SupportedTokenActionsProvider & TokenActionProvider] = [
honeySwapService, honeySwapService,
quickSwap, quickSwap,
Oneinch(action: R.string.localizable.aWalletTokenErc20ExchangeOn1inchButtonTitle(), networkProvider: OneinchNetworkProvider(networkService: networkService)), Oneinch(action: R.string.localizable.aWalletTokenErc20ExchangeOn1inchButtonTitle(), networking: BaseOneinchNetworking(networkService: networkService)),
//uniswap //uniswap
] ]
availableSwapProviders += Features.default.isAvailable(.isSwapEnabled) ? [SwapTokenNativeProvider(tokenSwapper: tokenSwapper)] : [] availableSwapProviders += Features.default.isAvailable(.isSwapEnabled) ? [SwapTokenNativeProvider(tokenSwapper: tokenSwapper)] : []

@ -47,7 +47,7 @@ class AppCoordinatorTests: XCTestCase {
coordinator.start() coordinator.start()
XCTAssertEqual(3, coordinator.coordinators.count) XCTAssertEqual(4, coordinator.coordinators.count)
XCTAssertTrue(coordinator.navigationController.viewControllers[0] is AccountsViewController) XCTAssertTrue(coordinator.navigationController.viewControllers[0] is AccountsViewController)
XCTAssertTrue(coordinator.navigationController.viewControllers[1] is UITabBarController) XCTAssertTrue(coordinator.navigationController.viewControllers[1] is UITabBarController)
@ -112,7 +112,7 @@ class AppCoordinatorTests: XCTestCase {
coordinator.showActiveWallet(for: .make(), animated: true) coordinator.showActiveWallet(for: .make(), animated: true)
XCTAssertEqual(5, coordinator.coordinators.count) XCTAssertEqual(6, coordinator.coordinators.count)
XCTAssertTrue(coordinator.navigationController.viewControllers[0] is AccountsViewController) XCTAssertTrue(coordinator.navigationController.viewControllers[0] is AccountsViewController)
XCTAssertTrue(coordinator.navigationController.viewControllers[1] is UITabBarController) XCTAssertTrue(coordinator.navigationController.viewControllers[1] is UITabBarController)
} }

@ -18,26 +18,26 @@ final class OneinchTests: XCTestCase {
func testOneinch() { func testOneinch() {
let reachability: ReachabilityManagerProtocol = FakeReachabilityManager(true) let reachability: ReachabilityManagerProtocol = FakeReachabilityManager(true)
let networkProvider = FakeOneinchNetworkProvider() let networking = FakeBaseOneinchNetworking()
let retries: UInt = 3 let retries: UInt = 3
let retryBehavior: RetryBehavior<RunLoop> = .randomDelayed(retries: retries, delayBeforeRetry: 1, delayUpperRangeValueFrom0To: 3) let retryBehavior: RetryBehavior<RunLoop> = .randomDelayed(retries: retries, delayBeforeRetry: 1, delayUpperRangeValueFrom0To: 3)
let oneinch = Oneinch(action: "Buy with Oneinch", networkProvider: networkProvider, reachability: reachability, retryBehavior: retryBehavior) let oneinch = Oneinch(action: "Buy with Oneinch", networking: networking, reachability: reachability, retryBehavior: retryBehavior)
infoLog("[Oneinch] Start") infoLog("[Oneinch] Start")
oneinch.start() oneinch.start()
let expectation = self.expectation(description: "Wait for failure") let expectation = self.expectation(description: "Wait for failure")
oneinch.objectWillChange.sink { [weak oneinch, networkProvider] _ in oneinch.objectWillChange.sink { [weak oneinch, networking] _ in
infoLog("[Oneinch] objectWillChange") infoLog("[Oneinch] objectWillChange")
guard let oneinch = oneinch else { return XCTFail() } guard let oneinch = oneinch else { return XCTFail() }
guard case .failure = oneinch.assets else { return XCTFail() } guard case .failure = oneinch.assets else { return XCTFail() }
expectation.fulfill() expectation.fulfill()
XCTAssertTrue(networkProvider.asyncAPICallCount == retries + 1) XCTAssertTrue(networking.asyncAPICallCount == retries + 1)
}.store(in: &cancelable) }.store(in: &cancelable)
waitForExpectations(timeout: 60) waitForExpectations(timeout: 60)
} }
final class FakeOneinchNetworkProvider: OneinchNetworkProviderType { final class FakeBaseOneinchNetworking: OneinchNetworking {
let msTimeFormatter: DateFormatter = { let msTimeFormatter: DateFormatter = {
let msTimeFormatter = DateFormatter() let msTimeFormatter = DateFormatter()
msTimeFormatter.dateFormat = "[HH:mm:ss.SSSS] " msTimeFormatter.dateFormat = "[HH:mm:ss.SSSS] "

@ -18,26 +18,26 @@ final class RampTests: XCTestCase {
func testRamp() { func testRamp() {
let reachability: ReachabilityManagerProtocol = FakeReachabilityManager(true) let reachability: ReachabilityManagerProtocol = FakeReachabilityManager(true)
let networkProvider = FakeRampNetworkProvider() let networking = FakeRampNetworkProvider()
let retries: UInt = 3 let retries: UInt = 3
let retryBehavior: RetryBehavior<RunLoop> = .randomDelayed(retries: retries, delayBeforeRetry: 1, delayUpperRangeValueFrom0To: 3) let retryBehavior: RetryBehavior<RunLoop> = .randomDelayed(retries: retries, delayBeforeRetry: 1, delayUpperRangeValueFrom0To: 3)
let ramp = Ramp(action: "Buy with Ramp", networkProvider: networkProvider, reachability: reachability, retryBehavior: retryBehavior) let ramp = Ramp(action: "Buy with Ramp", networking: networking, reachability: reachability, retryBehavior: retryBehavior)
infoLog("[Ramp] Start") infoLog("[Ramp] Start")
ramp.start() ramp.start()
let expectation = self.expectation(description: "Wait for failure") let expectation = self.expectation(description: "Wait for failure")
ramp.objectWillChange.sink { [weak ramp, networkProvider] _ in ramp.objectWillChange.sink { [weak ramp, networking] _ in
infoLog("[Ramp] objectWillChange") infoLog("[Ramp] objectWillChange")
guard let ramp = ramp else { return XCTFail() } guard let ramp = ramp else { return XCTFail() }
guard case .failure = ramp.assets else { return XCTFail() } guard case .failure = ramp.assets else { return XCTFail() }
expectation.fulfill() expectation.fulfill()
XCTAssertTrue(networkProvider.asyncAPICallCount == retries + 1) XCTAssertTrue(networking.asyncAPICallCount == retries + 1)
}.store(in: &cancelable) }.store(in: &cancelable)
waitForExpectations(timeout: 60) waitForExpectations(timeout: 60)
} }
final class FakeRampNetworkProvider: RampNetworkProviderType { final class FakeRampNetworkProvider: RampNetworking {
let msTimeFormatter: DateFormatter = { let msTimeFormatter: DateFormatter = {
let msTimeFormatter = DateFormatter() let msTimeFormatter = DateFormatter()
msTimeFormatter.dateFormat = "[HH:mm:ss.SSSS] " msTimeFormatter.dateFormat = "[HH:mm:ss.SSSS] "

@ -12,11 +12,11 @@ import AlphaWalletLogger
public final class Ramp: SupportedTokenActionsProvider, BuyTokenURLProviderType { public final class Ramp: SupportedTokenActionsProvider, BuyTokenURLProviderType {
private var objectWillChangeSubject = PassthroughSubject<Void, Never>() private var objectWillChangeSubject = PassthroughSubject<Void, Never>()
private (set) public var assets: Swift.Result<[Asset], Error> = .failure(RampError()) private (set) public var assets: Loadable<[Asset], PromiseError> = .loading
private let queue: DispatchQueue = .init(label: "org.alphawallet.swift.Ramp") private let queue: DispatchQueue = .init(label: "org.alphawallet.swift.Ramp")
private var cancelable = Set<AnyCancellable>() private var cancelable = Set<AnyCancellable>()
private let reachability: ReachabilityManagerProtocol private let reachability: ReachabilityManagerProtocol
private let networkProvider: RampNetworkProviderType private let networking: RampNetworking
private let retryBehavior: RetryBehavior<RunLoop> private let retryBehavior: RetryBehavior<RunLoop>
public var objectWillChange: AnyPublisher<Void, Never> { public var objectWillChange: AnyPublisher<Void, Never> {
@ -28,10 +28,14 @@ public final class Ramp: SupportedTokenActionsProvider, BuyTokenURLProviderType
public let analyticsName: String = "Ramp" public let analyticsName: String = "Ramp"
public let action: String public let action: String
public init(action: String, networkProvider: RampNetworkProviderType, reachability: ReachabilityManagerProtocol = ReachabilityManager(), retryBehavior: RetryBehavior<RunLoop> = Oneinch.defaultRetryBehavior) { public init(action: String,
networking: RampNetworking,
reachability: ReachabilityManagerProtocol = ReachabilityManager(),
retryBehavior: RetryBehavior<RunLoop> = Oneinch.defaultRetryBehavior) {
self.action = action self.action = action
self.reachability = reachability self.reachability = reachability
self.networkProvider = networkProvider self.networking = networking
self.retryBehavior = retryBehavior self.retryBehavior = retryBehavior
} }
@ -62,21 +66,16 @@ public final class Ramp: SupportedTokenActionsProvider, BuyTokenURLProviderType
&& (asset.address == nil ? token.contractAddress == Constants.nativeCryptoAddressInDatabase : asset.address! == token.contractAddress) && (asset.address == nil ? token.contractAddress == Constants.nativeCryptoAddressInDatabase : asset.address! == token.contractAddress)
} }
guard !token.server.isTestnet else { return nil } guard let assets = assets.value, !token.server.isTestnet else { return nil }
switch assets {
case .success(let assets):
return assets.first(where: { isAssetMatchesForToken(token: token, asset: $0) }) return assets.first(where: { isAssetMatchesForToken(token: token, asset: $0) })
case .failure:
return nil
}
} }
public func start() { public func start() {
reachability.networkBecomeReachablePublisher reachability.networkBecomeReachablePublisher
.receive(on: queue) .receive(on: queue)
.setFailureType(to: PromiseError.self) .setFailureType(to: PromiseError.self)
.flatMapLatest { [networkProvider, retryBehavior] _ -> AnyPublisher<[Asset], PromiseError> in .flatMapLatest { [networking, retryBehavior] _ -> AnyPublisher<[Asset], PromiseError> in
networkProvider.retrieveAssets() networking.retrieveAssets()
.retry(retryBehavior, scheduler: RunLoop.main) .retry(retryBehavior, scheduler: RunLoop.main)
.eraseToAnyPublisher() .eraseToAnyPublisher()
}.receive(on: queue) }.receive(on: queue)
@ -84,10 +83,10 @@ public final class Ramp: SupportedTokenActionsProvider, BuyTokenURLProviderType
objectWillChangeSubject.send(()) objectWillChangeSubject.send(())
guard case .failure(let error) = result else { return } guard case .failure(let error) = result else { return }
let request = RampNetworkProvider.RampRequest() let request = BaseRampNetworking.RampRequest()
RemoteLogger.instance.logRpcOrOtherWebError("Ramp error | \(error)", url: request.urlRequest?.url?.absoluteString ?? "") RemoteLogger.instance.logRpcOrOtherWebError("Ramp error | \(error)", url: request.urlRequest?.url?.absoluteString ?? "")
} receiveValue: { } receiveValue: {
self.assets = .success($0) self.assets = .done($0)
}.store(in: &cancelable) }.store(in: &cancelable)
} }
} }

@ -9,11 +9,11 @@ import Foundation
import AlphaWalletCore import AlphaWalletCore
import Combine import Combine
public protocol RampNetworkProviderType { public protocol RampNetworking {
func retrieveAssets() -> AnyPublisher<[Asset], PromiseError> func retrieveAssets() -> AnyPublisher<[Asset], PromiseError>
} }
public final class RampNetworkProvider: RampNetworkProviderType { public final class BaseRampNetworking: RampNetworking {
private let decoder = JSONDecoder() private let decoder = JSONDecoder()
private let networkService: NetworkService private let networkService: NetworkService
@ -31,7 +31,7 @@ public final class RampNetworkProvider: RampNetworkProviderType {
} }
} }
//NOTE: internal because we use it also for debugging //NOTE: internal because we use it also for debugging
extension RampNetworkProvider { extension BaseRampNetworking {
struct RampRequest: URLRequestConvertible { struct RampRequest: URLRequestConvertible {
func asURLRequest() throws -> URLRequest { func asURLRequest() throws -> URLRequest {

@ -10,7 +10,7 @@ import Combine
import AlphaWalletCore import AlphaWalletCore
public class Oneinch: SupportedTokenActionsProvider, SwapTokenViaUrlProvider { public class Oneinch: SupportedTokenActionsProvider, SwapTokenViaUrlProvider {
private (set) public var assets: Swift.Result<[AlphaWallet.Address: Oneinch.Asset], Error> = .failure(OneinchError()) private (set) public var assets: Loadable<[AlphaWallet.Address: Oneinch.Asset], Error> = .loading
private let queue = DispatchQueue(label: "org.alphawallet.swift.Oneinch") private let queue = DispatchQueue(label: "org.alphawallet.swift.Oneinch")
private var cancelable = Set<AnyCancellable>() private var cancelable = Set<AnyCancellable>()
private var objectWillChangeSubject = PassthroughSubject<Void, Never>() private var objectWillChangeSubject = PassthroughSubject<Void, Never>()
@ -23,7 +23,7 @@ public class Oneinch: SupportedTokenActionsProvider, SwapTokenViaUrlProvider {
private var predefinedAssets: [Oneinch.Asset] { private var predefinedAssets: [Oneinch.Asset] {
[.init(symbol: "ETH", name: "ETH", address: Constants.nativeCryptoAddressInDatabase, decimal: RPCServer.main.decimals)] [.init(symbol: "ETH", name: "ETH", address: Constants.nativeCryptoAddressInDatabase, decimal: RPCServer.main.decimals)]
} }
private let networkProvider: OneinchNetworkProviderType private let networking: OneinchNetworking
private let reachability: ReachabilityManagerProtocol private let reachability: ReachabilityManagerProtocol
private let retryBehavior: RetryBehavior<RunLoop> private let retryBehavior: RetryBehavior<RunLoop>
public static let defaultRetryBehavior: RetryBehavior<RunLoop> = .randomDelayed(retries: 3, delayBeforeRetry: 5, delayUpperRangeValueFrom0To: 15) public static let defaultRetryBehavior: RetryBehavior<RunLoop> = .randomDelayed(retries: 3, delayBeforeRetry: 5, delayUpperRangeValueFrom0To: 15)
@ -37,9 +37,13 @@ public class Oneinch: SupportedTokenActionsProvider, SwapTokenViaUrlProvider {
public let analyticsNavigation: Analytics.Navigation = .onOneinch public let analyticsNavigation: Analytics.Navigation = .onOneinch
public let analyticsName: String = "Oneinch" public let analyticsName: String = "Oneinch"
public init(action: String, networkProvider: OneinchNetworkProviderType, reachability: ReachabilityManagerProtocol = ReachabilityManager(), retryBehavior: RetryBehavior<RunLoop> = Oneinch.defaultRetryBehavior) { public init(action: String,
networking: OneinchNetworking,
reachability: ReachabilityManagerProtocol = ReachabilityManager(),
retryBehavior: RetryBehavior<RunLoop> = Oneinch.defaultRetryBehavior) {
self.action = action self.action = action
self.networkProvider = networkProvider self.networking = networking
self.reachability = reachability self.reachability = reachability
self.retryBehavior = retryBehavior self.retryBehavior = retryBehavior
} }
@ -48,8 +52,8 @@ public class Oneinch: SupportedTokenActionsProvider, SwapTokenViaUrlProvider {
reachability.networkBecomeReachablePublisher reachability.networkBecomeReachablePublisher
.receive(on: queue) .receive(on: queue)
.setFailureType(to: PromiseError.self) .setFailureType(to: PromiseError.self)
.flatMapLatest { [networkProvider, retryBehavior] _ -> AnyPublisher<[Asset], PromiseError> in .flatMapLatest { [networking, retryBehavior] _ -> AnyPublisher<[Asset], PromiseError> in
networkProvider.retrieveAssets() networking.retrieveAssets()
.retry(retryBehavior, scheduler: RunLoop.main) .retry(retryBehavior, scheduler: RunLoop.main)
.eraseToAnyPublisher() .eraseToAnyPublisher()
}.receive(on: queue) }.receive(on: queue)
@ -57,14 +61,14 @@ public class Oneinch: SupportedTokenActionsProvider, SwapTokenViaUrlProvider {
objectWillChangeSubject.send(()) objectWillChangeSubject.send(())
guard case .failure(let error) = result else { return } guard case .failure(let error) = result else { return }
let request = RampNetworkProvider.RampRequest() let request = BaseOneinchNetworking.OneInchAssetsRequest()
RemoteLogger.instance.logRpcOrOtherWebError("Oneinch error | \(error)", url: request.urlRequest?.url?.absoluteString ?? "") RemoteLogger.instance.logRpcOrOtherWebError("Oneinch error | \(error)", url: request.urlRequest?.url?.absoluteString ?? "")
} receiveValue: { assets in } receiveValue: { assets in
var newAssets: [AlphaWallet.Address: Oneinch.Asset] = [:] var newAssets: [AlphaWallet.Address: Oneinch.Asset] = [:]
for asset in self.predefinedAssets + assets { for asset in self.predefinedAssets + assets {
newAssets[asset.address] = asset newAssets[asset.address] = asset
} }
self.assets = .success(newAssets) self.assets = .done(newAssets)
}.store(in: &cancelable) }.store(in: &cancelable)
} }
@ -95,12 +99,8 @@ public class Oneinch: SupportedTokenActionsProvider, SwapTokenViaUrlProvider {
} }
private func asset(for address: AlphaWallet.Address) -> Oneinch.Asset? { private func asset(for address: AlphaWallet.Address) -> Oneinch.Asset? {
switch assets { guard let assets = assets.value else { return nil }
case .success(let assets):
return assets[address] return assets[address]
case .failure:
return nil
}
} }
private func subpath(inputAddress: AlphaWallet.Address) -> String { private func subpath(inputAddress: AlphaWallet.Address) -> String {

@ -1,5 +1,5 @@
// //
// OneinchNetworkProvider.swift // BaseOneinchNetworking.swift
// AlphaWalletFoundation // AlphaWalletFoundation
// //
// Created by Vladyslav Shepitko on 19.09.2022. // Created by Vladyslav Shepitko on 19.09.2022.
@ -9,11 +9,11 @@ import Foundation
import AlphaWalletCore import AlphaWalletCore
import Combine import Combine
public protocol OneinchNetworkProviderType { public protocol OneinchNetworking {
func retrieveAssets() -> AnyPublisher<[Oneinch.Asset], PromiseError> func retrieveAssets() -> AnyPublisher<[Oneinch.Asset], PromiseError>
} }
public final class OneinchNetworkProvider: OneinchNetworkProviderType { public final class BaseOneinchNetworking: OneinchNetworking {
private let decoder = JSONDecoder() private let decoder = JSONDecoder()
private let networkService: NetworkService private let networkService: NetworkService
@ -31,8 +31,8 @@ public final class OneinchNetworkProvider: OneinchNetworkProviderType {
} }
} }
fileprivate extension OneinchNetworkProvider { extension BaseOneinchNetworking {
private struct OneInchAssetsRequest: URLRequestConvertible { struct OneInchAssetsRequest: URLRequestConvertible {
func asURLRequest() throws -> URLRequest { func asURLRequest() throws -> URLRequest {
guard var components = URLComponents(url: Constants.OneInch.exchangeUrl, resolvingAgainstBaseURL: false) else { throw URLError(.badURL) } guard var components = URLComponents(url: Constants.OneInch.exchangeUrl, resolvingAgainstBaseURL: false) else { throw URLError(.badURL) }
components.path = "/v3.0/1/tokens" components.path = "/v3.0/1/tokens"
Loading…
Cancel
Save