From 6c95c2c4b98c4cccb62c8a40b360860e0c3576a5 Mon Sep 17 00:00:00 2001 From: Anton Stavnichiy Date: Fri, 14 Jun 2024 15:34:06 +0600 Subject: [PATCH] Fix reSync logic (from background and change internet state) --- .../project.pbxproj | 8 +- .../Core/Adapters/TonAdapter.swift | 76 ++++++------ .../Core/Managers/AppManager.swift | 7 ++ .../UnstoppableWallet/Extensions/Kmm.swift | 117 ------------------ .../Ton/TonIncomingTransactionRecord.swift | 4 +- .../Ton/TonTransactionRecord.swift | 2 +- .../Send/Platforms/Ton/SendTonService.swift | 10 +- .../TransactionsViewItemFactory.swift | 2 +- 8 files changed, 56 insertions(+), 170 deletions(-) delete mode 100644 UnstoppableWallet/UnstoppableWallet/Extensions/Kmm.swift diff --git a/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj b/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj index 82f21b8d40..552d1edca1 100644 --- a/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj +++ b/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj @@ -420,7 +420,6 @@ 11B354B5E42290EE934C428E /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11B359697FC3E92D4111ED5D /* String.swift */; }; 11B354B8BD1C3C036F6DE16A /* LitecoinAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11B356861F703A5A5C6630B6 /* LitecoinAdapter.swift */; }; 11B354BC4D954CCDA2E75C68 /* AddEvmSyncSourceViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11B350B29037572DDAAF9E16 /* AddEvmSyncSourceViewModel.swift */; }; - 11B354C1218C0776499FAA5E /* Kmm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11B35D813B2B43683404CCD6 /* Kmm.swift */; }; 11B354CAD4BC4FAB3889838D /* EvmSyncSourceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11B35D9C2409FD9060974F67 /* EvmSyncSourceManager.swift */; }; 11B354CF393A2EAFDABE1C47 /* WalletTokenListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11B357D222B4819BE881E182 /* WalletTokenListViewController.swift */; }; 11B354D628AADF3AFD9123E1 /* SingleCoinPriceWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11B351F5E57874D4517F67B7 /* SingleCoinPriceWidget.swift */; }; @@ -781,7 +780,6 @@ 11B358C4D4C466ACCEF0E4C7 /* MultiSwapMainField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11B35D0D43137223A01FC2DA /* MultiSwapMainField.swift */; }; 11B358C72B4E7F70331084AA /* SendEvmViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11B35113CB935A0E54504C1C /* SendEvmViewController.swift */; }; 11B358D01760F90518DA612F /* SendHandlerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11B353B02ADF5EC5CC83FB33 /* SendHandlerFactory.swift */; }; - 11B358D0D4AE015DC9FECF29 /* Kmm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11B35D813B2B43683404CCD6 /* Kmm.swift */; }; 11B358D1687049E5DACEBC96 /* AppManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11B352884D47E0B23DCF2C2C /* AppManager.swift */; }; 11B358D35D2270FD78C6EF82 /* AutoLockPeriod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11B35E41142BD3D2FF59BAE7 /* AutoLockPeriod.swift */; }; 11B358D519ACFE88A7823C7E /* ApiProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11B3531363949F235A210921 /* ApiProvider.swift */; }; @@ -3777,7 +3775,6 @@ 11B35D6FC62F4797DEE1C419 /* StatStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatStorage.swift; sourceTree = ""; }; 11B35D747108CE6727D3103D /* HsToolKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HsToolKit.swift; sourceTree = ""; }; 11B35D805327837A9E81801C /* ManageAccountsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManageAccountsViewController.swift; sourceTree = ""; }; - 11B35D813B2B43683404CCD6 /* Kmm.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Kmm.swift; sourceTree = ""; }; 11B35D8AF9D337A98530548D /* Auditor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Auditor.swift; sourceTree = ""; }; 11B35D8B730D82D948B27210 /* ISendHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ISendHandler.swift; sourceTree = ""; }; 11B35D96B8963CDC30DC5643 /* NftViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NftViewModel.swift; sourceTree = ""; }; @@ -5169,7 +5166,6 @@ ABC9A9B35C58F6525F3B2D5C /* FullCoin.swift */, ABC9A830FE79DBF62FD63CC4 /* ThemeMode.swift */, ABC9A3DFC1E03CB2E6C12F2C /* Encodable.swift */, - 11B35D813B2B43683404CCD6 /* Kmm.swift */, D00DAE442B626C2900F48E1D /* GasPrice.swift */, ABC9A448ABC30B93088DE978 /* Binding.swift */, 11B35ED0A8819AB7EA27D368 /* StatExtensions.swift */, @@ -10501,7 +10497,6 @@ 11B35ACE7B126DCF9F7F1A19 /* SearchBar.swift in Sources */, 11B3546CB3C043D22A5F7A88 /* TransactionsViewModel.swift in Sources */, 11B35071705455BC25C214D2 /* TonAdapter.swift in Sources */, - 11B358D0D4AE015DC9FECF29 /* Kmm.swift in Sources */, 11B3535D6A72ED1D564A0F7C /* TonOutgoingTransactionRecord.swift in Sources */, 11B35C60FE9B94994FCCB0CB /* TonTransactionRecord.swift in Sources */, 11B3589CF4D819A0430DE3D9 /* TonIncomingTransactionRecord.swift in Sources */, @@ -11957,7 +11952,6 @@ 11B358781EBEFCE7CED000F0 /* SearchBar.swift in Sources */, 11B358D91C9D8102C46B97ED /* TransactionsViewModel.swift in Sources */, 11B35D88633A14FD13E91702 /* TonAdapter.swift in Sources */, - 11B354C1218C0776499FAA5E /* Kmm.swift in Sources */, 11B3589C124F6BBDDBB144F4 /* TonOutgoingTransactionRecord.swift in Sources */, D389BC4F2C0DEF1800724504 /* MarketAdvancedSearchResultsView.swift in Sources */, 11B35EB4CAA93773DF09B479 /* TonTransactionRecord.swift in Sources */, @@ -12870,7 +12864,7 @@ repositoryURL = "https://github.com/horizontalsystems/TonKit.Swift"; requirement = { kind = exactVersion; - version = 0.1.0; + version = 0.2.0; }; }; 6BF66DD82BA1A73300963242 /* XCRemoteSwiftPackageReference "ObjectMapper" */ = { diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Adapters/TonAdapter.swift b/UnstoppableWallet/UnstoppableWallet/Core/Adapters/TonAdapter.swift index 4b4d6ca179..3c0a95806e 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Adapters/TonAdapter.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Adapters/TonAdapter.swift @@ -1,3 +1,4 @@ +import BigInt import Combine import Foundation import HdWalletKit @@ -5,9 +6,8 @@ import HsToolKit import MarketKit import RxSwift import TonKit -import TweetNacl import TonSwift -import BigInt +import TweetNacl class TonAdapter { private static let coinRate: Decimal = 1_000_000_000 @@ -18,11 +18,15 @@ class TonAdapter { private let transactionSource: TransactionSource private let baseToken: Token private let reachabilityManager = App.shared.reachabilityManager + private let appManager = App.shared.appManager + private var cancellables = Set() private var adapterStarted = false private var kitStarted = false + private let logger: Logger? + private let adapterStateSubject = PublishSubject() private(set) var adapterState: AdapterState { didSet { @@ -43,12 +47,15 @@ class TonAdapter { transactionSource = wallet.transactionSource self.baseToken = baseToken +// logger = Logger(minLogLevel: .debug) + logger = App.shared.logger.scoped(with: "TonKit") + switch wallet.account.type { case .mnemonic: guard let seed = wallet.account.type.mnemonicSeed else { throw AdapterError.unsupportedAccount } - + let hdWallet = HDWallet(seed: seed, coinType: 607, xPrivKey: 0, curve: .ed25519) let privateKey = try hdWallet.privateKey(account: 0) let privateRaw = Data(privateKey.raw.bytes) @@ -61,7 +68,7 @@ class TonAdapter { network: .mainNet, walletId: wallet.account.id, apiKey: nil, - minLogLevel: .debug + logger: logger ) case let .tonAddress(address): @@ -71,7 +78,7 @@ class TonAdapter { network: .mainNet, walletId: wallet.account.id, apiKey: nil, - minLogLevel: .debug + logger: logger ) default: throw AdapterError.unsupportedAccount @@ -79,7 +86,6 @@ class TonAdapter { ownAddress = tonKit.address - adapterState = Self.adapterState(kitSyncState: tonKit.syncState) balanceData = BalanceData(available: Self.amount(kitAmount: tonKit.balance)) @@ -95,23 +101,17 @@ class TonAdapter { } .store(in: &cancellables) - reachabilityManager.$isReachable - .sink { [weak self] isReachable in - self?.handle(isReachable: isReachable) + appManager.didEnterBackgroundPublisher + .sink { [weak self] in + self?.stop() } .store(in: &cancellables) - } - - private func handle(isReachable: Bool) { - guard adapterStarted else { - return - } - if isReachable, !kitStarted { - startKit() - } else if !isReachable, kitStarted { - stopKit() - } + appManager.willEnterForegroundPublisher + .sink { [weak self] in + self?.start() + } + .store(in: &cancellables) } private func handle(tonTransactions: [TonKit.FullTransaction]) { @@ -136,7 +136,7 @@ class TonAdapter { } static func amount(kitAmount: Decimal) -> Decimal { - return kitAmount / coinRate + kitAmount / coinRate } private func transactionRecord(tonTransaction tx: TonKit.FullTransaction) -> TonTransactionRecord { @@ -159,15 +159,15 @@ class TonAdapter { ) default: - return TonTransactionRecord( - source: .init(blockchainType: .ton, meta: nil), - event: tx.event, - feeToken: baseToken - ) + return TonTransactionRecord( + source: .init(blockchainType: .ton, meta: nil), + event: tx.event, + feeToken: baseToken + ) } } - - private func tagQuery(token: MarketKit.Token?, filter: TransactionTypeFilter, address: String?) -> TransactionTagQuery { + + private func tagQuery(token _: MarketKit.Token?, filter: TransactionTypeFilter, address: String?) -> TransactionTagQuery { var type: TransactionTag.TagType? switch filter { @@ -182,11 +182,13 @@ class TonAdapter { } private func startKit() { + logger?.log(level: .debug, message: "TonAdapter, start kit.") tonKit.start() kitStarted = true } private func stopKit() { + logger?.log(level: .debug, message: "TonAdapter, stop kit.") tonKit.stop() kitStarted = false } @@ -220,7 +222,7 @@ extension TonAdapter: IAdapter { } var statusInfo: [(String, Any)] { - [] + [] // tonKit.statusInfo() } var debugInfo: String { @@ -236,7 +238,7 @@ extension TonAdapter: IBalanceAdapter { var balanceDataUpdatedObservable: Observable { balanceDataSubject.asObservable() } - + var balanceState: AdapterState { adapterState } @@ -295,8 +297,8 @@ extension TonAdapter: ITransactionsAdapter { Task { [weak self] in let address = address.flatMap { try? FriendlyAddress(string: $0) }?.address.toRaw() - - let beforeLt = (from as? TonTransactionRecord).map { $0.lt } + + let beforeLt = (from as? TonTransactionRecord).map(\.lt) var tagQueries = [TransactionTagQuery]() switch filter { case .all: () @@ -304,14 +306,14 @@ extension TonAdapter: ITransactionsAdapter { case .outgoing: tagQueries.append(.init(type: .outgoing, address: address)) default: observer(.success([])) } - + let txs = (self?.tonKit .transactions(tagQueries: tagQueries, beforeLt: beforeLt, limit: limit) .compactMap { self?.transactionRecord(tonTransaction: $0) }) ?? [] - + observer(.success(txs)) } - + return Disposables.create() } } @@ -332,11 +334,11 @@ extension TonAdapter: ISendTonAdapter { func estimateFee(recipient: String, amount: Decimal, memo: String?) async throws -> Decimal { let amount = (amount * Self.coinRate).rounded(decimal: 0) - + let kitAmount = try await tonKit.estimateFee(recipient: recipient, amount: amount, comment: memo) return Self.amount(kitAmount: kitAmount) } - + func send(recipient: String, amount: Decimal, memo: String?) async throws { let amount = (amount * Self.coinRate).rounded(decimal: 0) diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Managers/AppManager.swift b/UnstoppableWallet/UnstoppableWallet/Core/Managers/AppManager.swift index cb9e956ce5..b34740f5b4 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Managers/AppManager.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Managers/AppManager.swift @@ -26,6 +26,7 @@ class AppManager { private let didBecomeActiveSubject = PublishSubject() private let willEnterForegroundSubjectOld = PublishSubject() + private let didEnterBackgroundSubject = PassthroughSubject() private let willEnterForegroundSubject = PassthroughSubject() init(accountManager: AccountManager, walletManager: WalletManager, adapterManager: AdapterManager, lockManager: LockManager, @@ -97,6 +98,8 @@ extension AppManager { func didEnterBackground() { debugBackgroundLogger?.logEnterBackground() + didEnterBackgroundSubject.send() + lockManager.didEnterBackground() walletConnectSocketConnectionService.didEnterBackground() balanceHiddenManager.didEnterBackground() @@ -133,6 +136,10 @@ extension AppManager { } extension AppManager { + var didEnterBackgroundPublisher: AnyPublisher { + didEnterBackgroundSubject.eraseToAnyPublisher() + } + var willEnterForegroundPublisher: AnyPublisher { willEnterForegroundSubject.eraseToAnyPublisher() } diff --git a/UnstoppableWallet/UnstoppableWallet/Extensions/Kmm.swift b/UnstoppableWallet/UnstoppableWallet/Extensions/Kmm.swift deleted file mode 100644 index a8e76defc5..0000000000 --- a/UnstoppableWallet/UnstoppableWallet/Extensions/Kmm.swift +++ /dev/null @@ -1,117 +0,0 @@ -//import Combine -//import TonKitKmm -// -//typealias OnEach = (Output) -> Void -//typealias OnCompletion = (Failure?) -> Void -// -//typealias OnCollect = (@escaping OnEach, @escaping OnCompletion) -> TonKitKmm.Cancellable -// -///** -// Creates a `Publisher` that collects output from a flow wrapper function emitting values from an underlying -// instance of `Flow`. -// */ -//func collect(_ onCollect: @escaping OnCollect) -> Publishers.Flow { -// Publishers.Flow(onCollect: onCollect) -//} -// -//class SharedCancellableSubscription: Subscription { -// private var isCancelled: Bool = false -// -// var cancellable: TonKitKmm.Cancellable? { -// didSet { -// if isCancelled { -// cancellable?.cancel() -// } -// } -// } -// -// func request(_: Subscribers.Demand) { -// // Not supported -// } -// -// func cancel() { -// isCancelled = true -// cancellable?.cancel() -// } -//} -// -//extension Publishers { -// class Flow: Publisher { -// private let onCollect: OnCollect -// -// init(onCollect: @escaping OnCollect) { -// self.onCollect = onCollect -// } -// -// func receive(subscriber: S) where S: Subscriber, Failure == S.Failure, Output == S.Input { -// let subscription = SharedCancellableSubscription() -// subscriber.receive(subscription: subscription) -// -// let cancellable = onCollect({ input in _ = subscriber.receive(input) }) { failure in -// if let failure { -// subscriber.receive(completion: .failure(failure)) -// } else { -// subscriber.receive(completion: .finished) -// } -// } -// -// subscription.cancellable = cancellable -// } -// } -//} -// -//extension KotlinThrowable: Error {} -// -//enum PublisherFailures { -// /** -// The action to invoke when a failure is dropped as the result of a `Publisher` returned by -// `Publisher.completeOnFailure()`. -// */ -// static var willCompleteOnFailure: (Error, Callsite) -> Void = { error, callsite in -// // if error.isKotlinCancellation { -// // return -// // } -// -// print("[ERROR] A publisher failed and was completed due to a call to `completeOnFailure()` \(callsite): \(error)") -// } -//} -// -//struct Callsite: CustomStringConvertible { -// let file: String -// let fileID: String -// let filePath: String -// let line: Int -// let column: Int -// let function: String -// let dsoHandle: UnsafeRawPointer -// -// var description: String { -// "in \(function) at \(filePath)#\(line):\(column)" -// } -//} -// -//extension Publisher { -// /** -// Ignores errors in the upstream publisher, emitting an empty sequence instead, and otherwise republishes all received input. -// You can hook into these failures by assigning a function to `PublisherHooks.willCompleteOnFailure`. -// */ -// func completeOnFailure(file: String = #file, fileID: String = #fileID, filePath: String = #filePath, line: Int = #line, column: Int = #column, function: String = #function, dsoHandle: UnsafeRawPointer = #dsohandle) -> Publishers.Catch> { -// `catch` { error in -// let callsite = Callsite(file: file, fileID: fileID, filePath: filePath, line: line, column: column, function: function, dsoHandle: dsoHandle) -// PublisherFailures.willCompleteOnFailure(error, callsite) -// return Empty(completeImmediately: true) -// } -// } -//} -// -//extension Data { -// func toKotlinByteArray() -> KotlinByteArray { -// let swiftByteArray = [UInt8](self) -// return swiftByteArray -// .map(Int8.init(bitPattern:)) -// .enumerated() -// .reduce(into: KotlinByteArray(size: Int32(swiftByteArray.count))) { result, row in -// result.set(index: Int32(row.offset), value: row.element) -// } -// } -//} diff --git a/UnstoppableWallet/UnstoppableWallet/Models/TransactionRecords/Ton/TonIncomingTransactionRecord.swift b/UnstoppableWallet/UnstoppableWallet/Models/TransactionRecords/Ton/TonIncomingTransactionRecord.swift index fedfc11b38..a7e58ef1a7 100644 --- a/UnstoppableWallet/UnstoppableWallet/Models/TransactionRecords/Ton/TonIncomingTransactionRecord.swift +++ b/UnstoppableWallet/UnstoppableWallet/Models/TransactionRecords/Ton/TonIncomingTransactionRecord.swift @@ -1,8 +1,8 @@ +import BigInt import Foundation import MarketKit import TonKit import TonSwift -import BigInt class TonIncomingTransactionRecord: TonTransactionRecord { let transfer: Transfer? @@ -17,7 +17,7 @@ class TonIncomingTransactionRecord: TonTransactionRecord { address: transfer.recipient.address.toString(bounceable: TonAdapter.bounceableDefault), value: .coinValue(token: token, value: TonAdapter.amount(kitAmount: Decimal(transfer.amount))) ) - } + } super.init(source: source, event: event, feeToken: feeToken) } diff --git a/UnstoppableWallet/UnstoppableWallet/Models/TransactionRecords/Ton/TonTransactionRecord.swift b/UnstoppableWallet/UnstoppableWallet/Models/TransactionRecords/Ton/TonTransactionRecord.swift index 4ab9956aae..d3966e1b2c 100644 --- a/UnstoppableWallet/UnstoppableWallet/Models/TransactionRecords/Ton/TonTransactionRecord.swift +++ b/UnstoppableWallet/UnstoppableWallet/Models/TransactionRecords/Ton/TonTransactionRecord.swift @@ -11,7 +11,7 @@ class TonTransactionRecord: TransactionRecord { fee = .coinValue(token: feeToken, value: TonAdapter.amount(kitAmount: Decimal(event.fee))) lt = event.lt memo = event.actions.compactMap { ($0 as? TonTransfer)?.comment }.first - + super.init( source: source, uid: event.eventId, diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Ton/SendTonService.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Ton/SendTonService.swift index 34123045f6..a21d2ffdcc 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Ton/SendTonService.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Send/Platforms/Ton/SendTonService.swift @@ -77,7 +77,7 @@ class SendTonService { loadFee() } - + private func updateFee() { loadFee() } @@ -113,17 +113,17 @@ class SendTonService { case let .fetchError(error): throw error default: throw AppError.addressInvalid } - + let amount = amountService.amount - + guard !amount.isZero else { throw SendTransactionError.wrongAmount } - + let memo = memoService.memo return (address, amount, memo) } - + private func loadFee() { do { let data = try params() diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/Transactions/TransactionsViewItemFactory.swift b/UnstoppableWallet/UnstoppableWallet/Modules/Transactions/TransactionsViewItemFactory.swift index fe910d9c38..f317939ec6 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/Transactions/TransactionsViewItemFactory.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/Transactions/TransactionsViewItemFactory.swift @@ -432,7 +432,7 @@ class TransactionsViewItemFactory { if let currencyValue = item.currencyValue { secondaryValue = BaseTransactionsViewModel.Value(text: currencyString(from: currencyValue), type: .secondary) } - + sentToSelf = record.sentToSelf case is TonTransactionRecord: