From 236ef4cdffcd34213303615f0a6c84d77c159178 Mon Sep 17 00:00:00 2001 From: Chung Tran Date: Thu, 4 Jan 2024 13:10:11 +0700 Subject: [PATCH 01/32] fix: token price --- p2p_wallet/Scenes/Main/Buy/BuyViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p2p_wallet/Scenes/Main/Buy/BuyViewModel.swift b/p2p_wallet/Scenes/Main/Buy/BuyViewModel.swift index 3b961ed64e..babae6a753 100644 --- a/p2p_wallet/Scenes/Main/Buy/BuyViewModel.swift +++ b/p2p_wallet/Scenes/Main/Buy/BuyViewModel.swift @@ -362,7 +362,7 @@ final class BuyViewModel: ObservableObject { tokens.map { TokenCellViewItem( token: $0, - amount: tokenPrices[fiat]?[token.mintAddress] ?? 0, + amount: tokenPrices[fiat]?[$0.mintAddress] ?? 0, fiat: fiat ) } From 9480fbb82844ec2fd68cf4e6c79847b45b81e341 Mon Sep 17 00:00:00 2001 From: Chung Tran Date: Thu, 4 Jan 2024 13:55:21 +0700 Subject: [PATCH 02/32] feat: turn off logics of usdc and usdt --- .../WarmupService/MigrationWarmupProcess.swift | 9 +++++++-- .../Main/Send/Input/SendInputViewModel.swift | 16 +++++++--------- .../AmountView/SendInputAmountViewModel.swift | 6 ++---- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/p2p_wallet/Common/Services/WarmupService/MigrationWarmupProcess.swift b/p2p_wallet/Common/Services/WarmupService/MigrationWarmupProcess.swift index fb8946a2ea..6bf8a9121c 100644 --- a/p2p_wallet/Common/Services/WarmupService/MigrationWarmupProcess.swift +++ b/p2p_wallet/Common/Services/WarmupService/MigrationWarmupProcess.swift @@ -3,9 +3,9 @@ import Resolver class MigrationWarmupProcess: WarmupProcess { func start() async { - // Migrate to endpoint solana.keyapp.org - let migration1Key = "APIEndpoint.migrationKey1" + let migration1Key = "APIEndpoint.migrationKey2" if !UserDefaults.standard.bool(forKey: migration1Key) { + // Migrate to endpoint solana.keyapp.org Resolver.resolve(ChangeNetworkResponder.self) .changeAPIEndpoint( to: .init( @@ -14,6 +14,11 @@ class MigrationWarmupProcess: WarmupProcess { additionalQuery: .secretConfig("KEYAPP_ORG_API_KEY") ) ) + + // Reset input type to token + Defaults.isTokenInputTypeChosen = true + + // Set at migrated UserDefaults.standard.set(true, forKey: migration1Key) } } diff --git a/p2p_wallet/Scenes/Main/Send/Input/SendInputViewModel.swift b/p2p_wallet/Scenes/Main/Send/Input/SendInputViewModel.swift index 56c26a6548..82684024f1 100644 --- a/p2p_wallet/Scenes/Main/Send/Input/SendInputViewModel.swift +++ b/p2p_wallet/Scenes/Main/Send/Input/SendInputViewModel.swift @@ -404,11 +404,13 @@ private extension SendInputViewModel { guard let self else { return } if currentWallet.price == nil { self.turnOffInputSwitch() - } else if - currentWallet.isUsdcOrUsdt, currentWallet.price?.value == 1.0 - { - self.turnOffInputSwitch() - } else { + } +// else if +// currentWallet.isUsdcOrUsdt, currentWallet.price?.value == 1.0 +// { +// self.turnOffInputSwitch() +// } + else { self.inputAmountViewModel.isSwitchAvailable = self.allowSwitchingMainAmountType } } @@ -664,8 +666,4 @@ private extension SolanaAccount { var isSendable: Bool { lamports > 0 && !isNFTToken } - - var isUsdcOrUsdt: Bool { - [TokenMetadata.usdc.mintAddress, TokenMetadata.usdt.mintAddress].contains(token.mintAddress) - } } diff --git a/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountViewModel.swift b/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountViewModel.swift index 24421c1410..0a8953515e 100644 --- a/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountViewModel.swift +++ b/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountViewModel.swift @@ -42,7 +42,7 @@ final class SendInputAmountViewModel: BaseViewModel, ObservableObject { @Published var amountText: String = "" @Published var amountTextColor: UIColor = .init(resource: .night) @Published var mainTokenText = "" - @Published var mainAmountType: EnteredAmountType = .fiat + @Published var mainAmountType: EnteredAmountType = Defaults.isTokenInputTypeChosen ? .token : .fiat @Published var isMaxButtonVisible: Bool = true @Published var secondaryAmountText = "" @@ -55,17 +55,15 @@ final class SendInputAmountViewModel: BaseViewModel, ObservableObject { @Published var countAfterDecimalPoint: Int @Published var isSwitchAvailable: Bool = true - private let fiat: Fiat + private let fiat: Fiat = Defaults.fiat private var currentText: String? private var tokenChangedEvent = CurrentValueSubject(.init(token: .nativeSolana)) // MARK: - Dependencies init(initialToken: SolanaAccount) { - fiat = Defaults.fiat token = initialToken countAfterDecimalPoint = Constants.fiatDecimals - mainAmountType = Defaults.isTokenInputTypeChosen ? .token : .fiat super.init() From 1399d9aafae6ca528ac73e96d4e9e8bea68aeb5f Mon Sep 17 00:00:00 2001 From: Chung Tran Date: Fri, 5 Jan 2024 11:17:49 +0700 Subject: [PATCH 03/32] feat: increase minimum buy amount to 40 --- p2p_wallet/Scenes/Main/Buy/BuyViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p2p_wallet/Scenes/Main/Buy/BuyViewModel.swift b/p2p_wallet/Scenes/Main/Buy/BuyViewModel.swift index babae6a753..30c7b10d33 100644 --- a/p2p_wallet/Scenes/Main/Buy/BuyViewModel.swift +++ b/p2p_wallet/Scenes/Main/Buy/BuyViewModel.swift @@ -66,7 +66,7 @@ final class BuyViewModel: ObservableObject { private var tokenPrices: [Fiat: [String: Double?]] = [:] // Defaults - private static let defaultMinAmount = Double(30) + private static let defaultMinAmount = Double(40) private static let defaultMaxAmount = Double(10000) private static let tokens: [TokenMetadata] = [.usdc, .nativeSolana] private static let fiats: [Fiat] = [.eur, .gbp, .usd] From e26cef108ff0987a5ccd0d353058b997ed40865a Mon Sep 17 00:00:00 2001 From: Chung Tran Date: Fri, 5 Jan 2024 16:55:01 +0700 Subject: [PATCH 04/32] feat: show token-2022 in token list --- Packages/KeyAppKit/Package.swift | 2 +- .../Solana/Socket/RealtimeSolanaAccountService.swift | 2 +- .../Sources/KeyAppBusiness/Solana/SolanaAccountsService.swift | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Packages/KeyAppKit/Package.swift b/Packages/KeyAppKit/Package.swift index 7316361758..781875a4c3 100644 --- a/Packages/KeyAppKit/Package.swift +++ b/Packages/KeyAppKit/Package.swift @@ -112,7 +112,7 @@ let package = Package( ), ], dependencies: [ - .package(url: "https://github.com/p2p-org/solana-swift", branch: "main"), + .package(url: "https://github.com/p2p-org/solana-swift", branch: "feature/token-2022"), .package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMajor(from: "1.6.0")), .package(url: "https://github.com/Boilertalk/Web3.swift.git", from: "0.6.0"), // .package(url: "https://github.com/trustwallet/wallet-core", branch: "master"), diff --git a/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/Socket/RealtimeSolanaAccountService.swift b/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/Socket/RealtimeSolanaAccountService.swift index b54532d86d..3d4f6df79a 100644 --- a/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/Socket/RealtimeSolanaAccountService.swift +++ b/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/Socket/RealtimeSolanaAccountService.swift @@ -265,7 +265,7 @@ final class RealtimeSolanaAccountServiceImpl: RealtimeSolanaAccountService { // Updating native account balance and get spl tokens let (balance, (resolved, _)) = try await( apiClient.getBalance(account: owner, commitment: "confirmed"), - apiClient.getAccountBalances( + apiClient.getAccountBalancesWithToken2022( for: owner, tokensRepository: tokensService, commitment: "confirmed" diff --git a/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/SolanaAccountsService.swift b/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/SolanaAccountsService.swift index 4e525ecd89..17c678cb61 100644 --- a/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/SolanaAccountsService.swift +++ b/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/SolanaAccountsService.swift @@ -261,7 +261,7 @@ class SolanaAccountAsyncValue: AsyncValue<[SolanaAccount]> { // Updating native account balance and get spl tokens let (balance, (resolved, _)) = try await( solanaAPIClient.getBalance(account: accountAddress, commitment: "confirmed"), - solanaAPIClient.getAccountBalances( + solanaAPIClient.getAccountBalancesWithToken2022( for: accountAddress, tokensRepository: tokensService, commitment: "confirmed" diff --git a/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 0464bb5191..cbb3533b9b 100644 --- a/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -311,8 +311,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/p2p-org/solana-swift", "state" : { - "branch" : "main", - "revision" : "0751755b18d162f534b3e96eb09de37c51e444a0" + "branch" : "feature/token-2022", + "revision" : "2e522178ea809141c160c66a5e335b05d01c683f" } }, { From aa1ceac53b4a653ffeba15820aa55133e1179874 Mon Sep 17 00:00:00 2001 From: Elizaveta Semenova Date: Fri, 5 Jan 2024 13:56:13 +0300 Subject: [PATCH 05/32] [ETH-665] Redirect to main screen when swap is done --- p2p_wallet/Scenes/TabBar/TabBarCoordinator.swift | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/p2p_wallet/Scenes/TabBar/TabBarCoordinator.swift b/p2p_wallet/Scenes/TabBar/TabBarCoordinator.swift index 21764c6ac3..a1d0d592be 100644 --- a/p2p_wallet/Scenes/TabBar/TabBarCoordinator.swift +++ b/p2p_wallet/Scenes/TabBar/TabBarCoordinator.swift @@ -157,20 +157,7 @@ final class TabBarCoordinator: Coordinator { /// Set up Swap scene private func setUpSwap() -> UIViewController { let nc = UINavigationController() - let swapCoordinator = JupiterSwapCoordinator( - navigationController: nc, - params: JupiterSwapParameters( - dismissAfterCompletion: false, - openKeyboardOnStart: false, - source: .actionPanel, - hideTabBar: false - ) - ) - jupiterSwapTabCoordinator = swapCoordinator - // coordinate to homeCoordinator - coordinate(to: swapCoordinator) - .sink(receiveValue: {}) - .store(in: &subscriptions) + routeToSwap(nc: nc, hidesBottomBarWhenPushed: false, source: .tapMain) return nc } From 51ac4299672b9d3261ddc7cfa539cbe7245296ae Mon Sep 17 00:00:00 2001 From: Elizaveta Semenova Date: Fri, 5 Jan 2024 14:16:31 +0300 Subject: [PATCH 06/32] [ETH-666] Remove network setting --- .../KeyApp/Events/KeyAppAnalyticsEvent.swift | 1 - .../network-icon.imageset/Contents.json | 12 --- .../network-icon.imageset/network.pdf | Bin 4975 -> 0 bytes .../Network/NetworkCoordinator.swift | 40 -------- .../Network/View/NetworkView.swift | 95 ------------------ .../Network/ViewModel/NetworkViewModel.swift | 35 ------- .../Settings/View/SettingsView.swift | 4 - .../ViewModel/SettingsViewModel.swift | 3 - .../NewSettings/SettingsCoordinator.swift | 5 - 9 files changed, 195 deletions(-) delete mode 100644 p2p_wallet/Resources/Assets.xcassets/network-icon.imageset/Contents.json delete mode 100644 p2p_wallet/Resources/Assets.xcassets/network-icon.imageset/network.pdf delete mode 100644 p2p_wallet/Scenes/Main/NewSettings/Network/NetworkCoordinator.swift delete mode 100644 p2p_wallet/Scenes/Main/NewSettings/Network/View/NetworkView.swift delete mode 100644 p2p_wallet/Scenes/Main/NewSettings/Network/ViewModel/NetworkViewModel.swift diff --git a/Packages/KeyAppKit/Sources/AnalyticsManager/KeyApp/Events/KeyAppAnalyticsEvent.swift b/Packages/KeyAppKit/Sources/AnalyticsManager/KeyApp/Events/KeyAppAnalyticsEvent.swift index db021a8ba1..d803a27ba7 100644 --- a/Packages/KeyAppKit/Sources/AnalyticsManager/KeyApp/Events/KeyAppAnalyticsEvent.swift +++ b/Packages/KeyAppKit/Sources/AnalyticsManager/KeyApp/Events/KeyAppAnalyticsEvent.swift @@ -178,7 +178,6 @@ public enum KeyAppAnalyticsEvent: AnalyticsEvent { case settingsSupportClick case settingsRecoveryClick case settingsPinClick - case settingsNetworkClick case settingsFaceidClick case settingsLogOut diff --git a/p2p_wallet/Resources/Assets.xcassets/network-icon.imageset/Contents.json b/p2p_wallet/Resources/Assets.xcassets/network-icon.imageset/Contents.json deleted file mode 100644 index 677f5b5bcf..0000000000 --- a/p2p_wallet/Resources/Assets.xcassets/network-icon.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "filename" : "network.pdf", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/p2p_wallet/Resources/Assets.xcassets/network-icon.imageset/network.pdf b/p2p_wallet/Resources/Assets.xcassets/network-icon.imageset/network.pdf deleted file mode 100644 index a8d65928c1506adcdb7fd024cc7e534d58952478..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4975 zcmeHLOK%iM5Wdf^=nE1=qPO1<30VRgtSCVcI|n3`!?GTX$v&Ljab)=Q`Dz~3z3fJW zOHQ7H-TQTSJ-({0sp&^=&t5&%GB%ACN^CxSZiIODOw4}&a@&g8yEh*`w~G!Kp4qSM z`nvlh3>IFD(D`j9lt+hiJhfOgN@{JqQm`c?MFU>HTdXlXnySq-qCFGnWi-)S5X(@MK5LUR z2~;TuCv=u7Xcv-Lz))vY)?OH>F*;-;jLb1vm4q?U$E=bQPG*}(tg+H&t!%{BAe~B4 zX;COj(nLdo3smeZ7}T=$1zk3p%`d2C`zwqcsO_mdV8e?G)hzo^6H!S&rtz3%?8Bm| zb~C!H&?CeY`q&<(83Kz791f1q7_?ADatyAekh1+H3E2Dsv7HRnVPSCDu5Q=D zl^vW%#avFd$tTW97O*JUe5mHol#)o*#sV&zPtC_>-Td~yoXL^}t19*dtuunDTNa0p zVfF4G^u@~@R5V$58C=pDhjvve6PFbpP<^q|1*>8dNjj~RiDK1IEesB4KFNdu1UhMb zb~%VFokNw<1<@#A(P-vc*N^+v}WU8*38WyS`D6z6r! zPLWoiN-Kk?5=MKUQ2A2U&SyBFV%8ZYi>7Q)BFdcMfP=M0Mod}j?a$z}3XUsnG--_9S?8w8Y)8sMEXbmvkU=+@+?R9< z15H2`j!-;GVx-c?82Hs#AF)p-I7^I26>`Wx5KiaF7lk(gcv&={$l>J>;IoG>&cbKJ z6lsI1@1Z(qs}W^{P9w@*D)8d1K$N}pcnXB-0k)i?mqS@+!FWQhh#-w|5kzQ)q6`Mn zFjTV`ghkedh_@6=8&b;P^;6UqjGTaxD)v?(_h=!7)p$(_I*MbY(pq8G#H14?2pDpj z$g(LQPm|P6YrN6|(nC2DU~P~pBhWoV7H??<)^Nsf!V(eUbMl%dl}0|$AZaj3uTeTS zh&Utt8Bs}P+Q+Mmj|vBf5N*M~lLmrd9uNc|Q{)a0D%CpF`4BAFko{%93r z>g5@w>NS2@_n*%1@Oq_+ zRlaav-goDh-Mnl2`Iox?clmn0`?DVnALsOQ?^C*baqo|2=YmS-_|3jQckpY5kM-xv z+fVcFi=IBn4`dK9-XO3i|0eF_WN;h8raB+?m>O>#&~Ob5{iiSa892M%H8W6qw!wGA zktmVCAxbW=YP4|GCCKyjdeiO1d%8m?F1?*!w1N%v zYiJ|}Ch#p?nsfp;0XS1?tO2NzGV?iagx0 z#^U+uyqhmK*ZX_#x9!IUO^I%XDoTGv@Mtsz@bMu9^a$LA^pA-O=F?L_bOW*n*=%r_ zzJVOr*@K*+27Mp0hpW(^e!I$}srhCj>OVu>c7C&Lw++$z^BKr}yxXkWC*twV)sunZ l*~Ml9eu=(5UbD-auPt!_a(CT)+x6_Ad34RAM=#&J`UjEk`1}9> diff --git a/p2p_wallet/Scenes/Main/NewSettings/Network/NetworkCoordinator.swift b/p2p_wallet/Scenes/Main/NewSettings/Network/NetworkCoordinator.swift deleted file mode 100644 index 91c954ccd4..0000000000 --- a/p2p_wallet/Scenes/Main/NewSettings/Network/NetworkCoordinator.swift +++ /dev/null @@ -1,40 +0,0 @@ -import Combine -import Foundation -import UIKit - -final class NetworkCoordinator: Coordinator { - private let navigationController: UINavigationController - - private let transition = PanelTransition() - - init(navigationController: UINavigationController) { - self.navigationController = navigationController - } - - override func start() -> AnyPublisher { - let viewModel = NetworkViewModel() - let viewController = NetworkView(viewModel: viewModel).asViewController() - transition.containerHeight = 432 - viewController.view.layer.cornerRadius = 16 - viewController.transitioningDelegate = transition - viewController.modalPresentationStyle = .custom - navigationController.present(viewController, animated: true) - - viewModel.dismiss - .sink(receiveValue: { - viewController.dismiss(animated: true) - }) - .store(in: &subscriptions) - transition.dimmClicked - .sink(receiveValue: { - viewController.dismiss(animated: true) - }) - .store(in: &subscriptions) - - let cancelSubject = PassthroughSubject() - viewController.onClose = { - cancelSubject.send() - } - return cancelSubject.prefix(1).eraseToAnyPublisher() - } -} diff --git a/p2p_wallet/Scenes/Main/NewSettings/Network/View/NetworkView.swift b/p2p_wallet/Scenes/Main/NewSettings/Network/View/NetworkView.swift deleted file mode 100644 index fd400c319e..0000000000 --- a/p2p_wallet/Scenes/Main/NewSettings/Network/View/NetworkView.swift +++ /dev/null @@ -1,95 +0,0 @@ -import SwiftUI - -struct NetworkView: View { - @ObservedObject var viewModel: NetworkViewModel - - @State private var lastClickedIndex = 0 - @State private var alertPresented = false - - var body: some View { - VStack(spacing: 32) { - VStack(spacing: 18) { - Color(.rain) - .frame(width: 31, height: 4) - .cornerRadius(2) - Text(L10n.network) - .foregroundColor(Color(.night)) - .font(uiFont: .font(of: .title3, weight: .semibold)) - } - VStack { - VStack(spacing: 0) { - ForEach(viewModel.endPoints.indices, id: \.self) { index in - cell( - index: index, - title: viewModel.endPoints[index].address, - withSeparator: index < viewModel.endPoints.count - 1, - selected: index == 0 - ) - } - } - Spacer() - Button( - action: { - viewModel.cancel() - }, - label: { - Text(L10n.done) - .foregroundColor(Color(.night)) - .font(uiFont: .font(of: .text2, weight: .semibold)) - .frame(height: 56) - .frame(maxWidth: .infinity) - .background(Color(.rain)) - .cornerRadius(12) - .padding(.horizontal, 24) - } - ) - } - } - .padding(.top, 6) - .padding(.bottom, 16) - } - - private func cell(index: Int, title: String, withSeparator: Bool, selected: Bool) -> some View { - Button( - action: { - if !selected { - lastClickedIndex = index - alertPresented.toggle() - } - }, - label: { - VStack(spacing: -1) { - HStack { - Text(title) - .foregroundColor(Color(.night)) - .font(uiFont: .font(of: .text2)) - Spacer() - if selected { - Image(systemName: "checkmark") - } - } - .padding(.vertical, 24) - .padding(.horizontal, 16) - if withSeparator { - Color(.rain) - .frame(height: 1) - .frame(maxWidth: .infinity) - .padding(.leading, 20) - } - } - } - ) - .alert(isPresented: $alertPresented) { - Alert( - title: Text(L10n.switchNetwork), - message: Text( - L10n.doYouReallyWantToSwitchTo + " \"" + viewModel.endPoints[lastClickedIndex].address + "\"" - ), - primaryButton: .cancel(Text(L10n.ok)) { - viewModel.setEndPoint(viewModel.endPoints[lastClickedIndex]) - }, - secondaryButton: .default(Text(L10n.cancel)) - ) - } - } -} diff --git a/p2p_wallet/Scenes/Main/NewSettings/Network/ViewModel/NetworkViewModel.swift b/p2p_wallet/Scenes/Main/NewSettings/Network/ViewModel/NetworkViewModel.swift deleted file mode 100644 index c15eb4453a..0000000000 --- a/p2p_wallet/Scenes/Main/NewSettings/Network/ViewModel/NetworkViewModel.swift +++ /dev/null @@ -1,35 +0,0 @@ -import AnalyticsManager -import Combine -import Foundation -import Resolver -import SolanaSwift - -final class NetworkViewModel: ObservableObject { - @Injected private var analyticsManager: AnalyticsManager - - private let dismissSubject = PassthroughSubject() - var dismiss: AnyPublisher { dismissSubject.eraseToAnyPublisher() } - - let endPoints: [APIEndPoint] - - init() { - endPoints = APIEndPoint.definedEndpoints - .sorted { - if $0 == Defaults.apiEndPoint { - return true - } else if $1 != Defaults.apiEndPoint { - return false - } - return false - } - } - - func cancel() { - dismissSubject.send() - } - - func setEndPoint(_ endPoint: APIEndPoint) { - guard Defaults.apiEndPoint != endPoint else { return } - analyticsManager.log(event: .networkChanging(networkName: endPoint.address)) - } -} diff --git a/p2p_wallet/Scenes/Main/NewSettings/Settings/View/SettingsView.swift b/p2p_wallet/Scenes/Main/NewSettings/Settings/View/SettingsView.swift index bdefac3782..56ffc54be4 100644 --- a/p2p_wallet/Scenes/Main/NewSettings/Settings/View/SettingsView.swift +++ b/p2p_wallet/Scenes/Main/NewSettings/Settings/View/SettingsView.swift @@ -98,10 +98,6 @@ struct SettingsView: View { action: { viewModel.showView(.yourPin) }, label: { cellView(image: .pinIcon, title: L10n.yourPIN) } ) - Button( - action: { viewModel.showView(.network) }, - label: { cellView(image: .networkIcon, title: L10n.network) } - ) if viewModel.biometryIsAvailable, viewModel.biometryType != .none { cellView( image: viewModel.biometryType == .face ? .faceIdIcon : .touchIdIcon, diff --git a/p2p_wallet/Scenes/Main/NewSettings/Settings/ViewModel/SettingsViewModel.swift b/p2p_wallet/Scenes/Main/NewSettings/Settings/ViewModel/SettingsViewModel.swift index 791008c68e..1a6dc2be46 100644 --- a/p2p_wallet/Scenes/Main/NewSettings/Settings/ViewModel/SettingsViewModel.swift +++ b/p2p_wallet/Scenes/Main/NewSettings/Settings/ViewModel/SettingsViewModel.swift @@ -64,8 +64,6 @@ final class SettingsViewModel: BaseViewModel, ObservableObject { switch $0 { case .yourPin: return KeyAppAnalyticsEvent.settingsPinClick - case .network: - return KeyAppAnalyticsEvent.settingsNetworkClick case .recoveryKit: return KeyAppAnalyticsEvent.settingsRecoveryClick default: @@ -196,6 +194,5 @@ extension SettingsViewModel { case reserveUsername(userAddress: String) case recoveryKit case yourPin - case network } } diff --git a/p2p_wallet/Scenes/Main/NewSettings/SettingsCoordinator.swift b/p2p_wallet/Scenes/Main/NewSettings/SettingsCoordinator.swift index 04652fe013..4be6504eec 100644 --- a/p2p_wallet/Scenes/Main/NewSettings/SettingsCoordinator.swift +++ b/p2p_wallet/Scenes/Main/NewSettings/SettingsCoordinator.swift @@ -40,11 +40,6 @@ final class SettingsCoordinator: Coordinator { navigationController.popToRootViewController(animated: true) }) .store(in: &subscriptions) - case .network: - let coordinator = NetworkCoordinator(navigationController: navigationController) - coordinate(to: coordinator) - .sink(receiveValue: {}) - .store(in: &subscriptions) } }) .store(in: &subscriptions) From b71bfec4f2a2312cb0fe109ab3652fa54d919812 Mon Sep 17 00:00:00 2001 From: Chung Tran Date: Mon, 8 Jan 2024 10:19:37 +0700 Subject: [PATCH 07/32] feat(eth-665): remove token.keyAppExtensions --- .../KeyAppBusiness/Price/PriceRule.swift | 22 +-- .../Solana/SolanaAccountsService.swift | 8 +- .../KeyAppKitCore/Blockchain/AnyToken.swift | 14 +- .../Blockchain/Ethereum/EthereumToken.swift | 6 +- .../Blockchain/Solana/SolanaToken.swift | 6 +- .../Solana/SolanaTokenExtension.swift | 128 +++++++++--------- .../AccountList/HomeAccountsViewModel.swift | 6 +- .../SupportedTokensViewModel.swift | 8 +- .../Service/ChooseSwapTokenService.swift | 6 +- 9 files changed, 102 insertions(+), 102 deletions(-) diff --git a/Packages/KeyAppKit/Sources/KeyAppBusiness/Price/PriceRule.swift b/Packages/KeyAppKit/Sources/KeyAppBusiness/Price/PriceRule.swift index ed04e631a6..b53473c0e3 100644 --- a/Packages/KeyAppKit/Sources/KeyAppBusiness/Price/PriceRule.swift +++ b/Packages/KeyAppKit/Sources/KeyAppBusiness/Price/PriceRule.swift @@ -14,9 +14,9 @@ protocol PriceRule { // Make price rate equals 1:1 when `ruleOfProcessingTokenPrice` equals `byCountOfTokensValue`. class OneToOnePriceRule: PriceRule { func adjustValue(token: SomeToken, price: TokenPrice, fiat _: String) -> PriceRuleHandler { - if token.keyAppExtension.ruleOfProcessingTokenPriceWS == .byCountOfTokensValue { - return .continue(TokenPrice(currencyCode: price.currencyCode, value: 1.0, token: token)) - } +// if token.keyAppExtension.ruleOfProcessingTokenPriceWS == .byCountOfTokensValue { +// return .continue(TokenPrice(currencyCode: price.currencyCode, value: 1.0, token: token)) +// } return .continue(price) } @@ -25,14 +25,14 @@ class OneToOnePriceRule: PriceRule { // Depegging price by measure percentage difference. class DepeggingPriceRule: PriceRule { func adjustValue(token: SomeToken, price: TokenPrice, fiat _: String) -> PriceRuleHandler { - if let allowPercentageDifferenceValue = token.keyAppExtension.percentDifferenceToShowByPriceOnWS { - let percentageDifferenceValue = 100 - (1 / price.value) * 100 - if abs(percentageDifferenceValue) <= BigDecimal(floatLiteral: allowPercentageDifferenceValue) { - return .break(TokenPrice(currencyCode: price.currencyCode, value: 1.0, token: token)) - } else { - return .break(price) - } - } +// if let allowPercentageDifferenceValue = token.keyAppExtension.percentDifferenceToShowByPriceOnWS { +// let percentageDifferenceValue = 100 - (1 / price.value) * 100 +// if abs(percentageDifferenceValue) <= BigDecimal(floatLiteral: allowPercentageDifferenceValue) { +// return .break(TokenPrice(currencyCode: price.currencyCode, value: 1.0, token: token)) +// } else { +// return .break(price) +// } +// } return .continue(price) } diff --git a/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/SolanaAccountsService.swift b/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/SolanaAccountsService.swift index 4e525ecd89..c9b7790654 100644 --- a/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/SolanaAccountsService.swift +++ b/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/SolanaAccountsService.swift @@ -122,10 +122,10 @@ public final class SolanaAccountsService: NSObject, AccountsService { .compactMap { $0 } .switchToLatest() .sink { [weak outputSubject] state in - var state = state - state.value = state.value.filter { account in - account.token.keyAppExtensions.isTokenCellVisibleOnWS - } +// var state = state +// state.value = state.value.filter { account in +// account.token.keyAppExtensions.isTokenCellVisibleOnWS +// } outputSubject?.send(state) } diff --git a/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/AnyToken.swift b/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/AnyToken.swift index 016f107225..d0cdc6fa52 100644 --- a/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/AnyToken.swift +++ b/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/AnyToken.swift @@ -46,7 +46,7 @@ public protocol AnyToken { @available(*, deprecated, message: "Legacy code") var address: String { get } - var keyAppExtension: KeyAppTokenExtension { get } +// var keyAppExtension: KeyAppTokenExtension { get } } public extension AnyToken { @@ -58,8 +58,8 @@ public extension AnyToken { symbol: symbol, name: name, decimals: decimals, - network: network, - keyAppExtension: keyAppExtension + network: network +// keyAppExtension: keyAppExtension ) } @@ -95,21 +95,21 @@ public struct SomeToken: AnyToken, Hashable, Codable { public let network: TokenNetwork - public var keyAppExtension: KeyAppTokenExtension +// public var keyAppExtension: KeyAppTokenExtension public init( tokenPrimaryKey: TokenPrimaryKey, symbol: String, name: String, decimals: UInt8, - network: TokenNetwork, - keyAppExtension: KeyAppTokenExtension + network: TokenNetwork +// keyAppExtension: KeyAppTokenExtension ) { primaryKey = tokenPrimaryKey self.symbol = symbol self.name = name self.decimals = decimals self.network = network - self.keyAppExtension = keyAppExtension +// self.keyAppExtension = keyAppExtension } } diff --git a/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/Ethereum/EthereumToken.swift b/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/Ethereum/EthereumToken.swift index 0e09d3b2e2..969f6e76fe 100644 --- a/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/Ethereum/EthereumToken.swift +++ b/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/Ethereum/EthereumToken.swift @@ -74,7 +74,7 @@ extension EthereumToken: AnyToken { } } - public var keyAppExtension: KeyAppTokenExtension { - KeyAppTokenExtension(data: [:]) - } +// public var keyAppExtension: KeyAppTokenExtension { +// KeyAppTokenExtension(data: [:]) +// } } diff --git a/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/Solana/SolanaToken.swift b/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/Solana/SolanaToken.swift index 726a1e8526..abfe36f892 100644 --- a/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/Solana/SolanaToken.swift +++ b/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/Solana/SolanaToken.swift @@ -36,7 +36,7 @@ extension SolanaToken: AnyToken { .solana } - public var keyAppExtension: KeyAppTokenExtension { - KeyAppTokenExtension(data: extensions ?? [:]) - } +// public var keyAppExtension: KeyAppTokenExtension { +// KeyAppTokenExtension(data: extensions ?? [:]) +// } } diff --git a/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/Solana/SolanaTokenExtension.swift b/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/Solana/SolanaTokenExtension.swift index d1b85ca0be..c6678a3064 100644 --- a/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/Solana/SolanaTokenExtension.swift +++ b/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/Solana/SolanaTokenExtension.swift @@ -1,64 +1,64 @@ -import Foundation -import SolanaSwift - -public extension SolanaToken { - var keyAppExtensions: KeyAppTokenExtension { - KeyAppTokenExtension(data: extensions ?? [:]) - } -} - -public struct KeyAppTokenExtension: Codable, Hashable { - public let ruleOfProcessingTokenPriceWS: RuleOfProcessingTokenPriceWS? - public let isPositionOnWS: Bool? - public let isTokenCellVisibleOnWS: Bool - public let percentDifferenceToShowByPriceOnWS: Double? - public let calculationOfFinalBalanceOnWS: Bool? - public let ruleOfFractionalPartOnWS: RuleOfFractionalPartOnWS? - public let canBeHidden: Bool - - public enum RuleOfProcessingTokenPriceWS: String, Codable, Hashable { - case byCountOfTokensValue - } - - public enum RuleOfFractionalPartOnWS: String, Codable, Hashable { - case droppingAfterHundredthPart - } - - init(data: [String: TokenExtensionValue]) { - isPositionOnWS = data["isPositionOnWS"]?.boolValue - isTokenCellVisibleOnWS = data["isTokenCellVisibleOnWS"]?.boolValue ?? true - calculationOfFinalBalanceOnWS = data["calculationOfFinalBalanceOnWS"]?.boolValue - percentDifferenceToShowByPriceOnWS = data["percentDifferenceToShowByPriceOnWS"]?.doubleValue - canBeHidden = data["canBeHidden"]?.boolValue ?? true - - if data["ruleOfProcessingTokenPriceWS"]?.stringValue == "byCountOfTokensValue" { - ruleOfProcessingTokenPriceWS = .byCountOfTokensValue - } else { - ruleOfProcessingTokenPriceWS = nil - } - - if data["ruleOfFractionalPartOnWS"]?.stringValue == "droppingAfterHundredthPart" { - ruleOfFractionalPartOnWS = .droppingAfterHundredthPart - } else { - ruleOfFractionalPartOnWS = nil - } - } - - public init( - ruleOfProcessingTokenPriceWS: KeyAppTokenExtension.RuleOfProcessingTokenPriceWS? = nil, - isPositionOnWS: Bool? = nil, - isTokenCellVisibleOnWS: Bool? = nil, - percentDifferenceToShowByPriceOnWS: Double? = nil, - calculationOfFinalBalanceOnWS: Bool? = nil, - ruleOfFractionalPartOnWS: KeyAppTokenExtension.RuleOfFractionalPartOnWS? = nil, - canBeHidden: Bool? = nil - ) { - self.ruleOfProcessingTokenPriceWS = ruleOfProcessingTokenPriceWS - self.isPositionOnWS = isPositionOnWS - self.isTokenCellVisibleOnWS = isTokenCellVisibleOnWS ?? true - self.percentDifferenceToShowByPriceOnWS = percentDifferenceToShowByPriceOnWS - self.calculationOfFinalBalanceOnWS = calculationOfFinalBalanceOnWS - self.ruleOfFractionalPartOnWS = ruleOfFractionalPartOnWS - self.canBeHidden = canBeHidden ?? true - } -} +//import Foundation +//import SolanaSwift +// +//public extension SolanaToken { +// var keyAppExtensions: KeyAppTokenExtension { +// KeyAppTokenExtension(data: extensions ?? [:]) +// } +//} +// +//public struct KeyAppTokenExtension: Codable, Hashable { +// public let ruleOfProcessingTokenPriceWS: RuleOfProcessingTokenPriceWS? +// public let isPositionOnWS: Bool? +// public let isTokenCellVisibleOnWS: Bool +// public let percentDifferenceToShowByPriceOnWS: Double? +// public let calculationOfFinalBalanceOnWS: Bool? +// public let ruleOfFractionalPartOnWS: RuleOfFractionalPartOnWS? +// public let canBeHidden: Bool +// +// public enum RuleOfProcessingTokenPriceWS: String, Codable, Hashable { +// case byCountOfTokensValue +// } +// +// public enum RuleOfFractionalPartOnWS: String, Codable, Hashable { +// case droppingAfterHundredthPart +// } +// +// init(data: [String: TokenExtensionValue]) { +// isPositionOnWS = data["isPositionOnWS"]?.boolValue +// isTokenCellVisibleOnWS = data["isTokenCellVisibleOnWS"]?.boolValue ?? true +// calculationOfFinalBalanceOnWS = data["calculationOfFinalBalanceOnWS"]?.boolValue +// percentDifferenceToShowByPriceOnWS = data["percentDifferenceToShowByPriceOnWS"]?.doubleValue +// canBeHidden = data["canBeHidden"]?.boolValue ?? true +// +// if data["ruleOfProcessingTokenPriceWS"]?.stringValue == "byCountOfTokensValue" { +// ruleOfProcessingTokenPriceWS = .byCountOfTokensValue +// } else { +// ruleOfProcessingTokenPriceWS = nil +// } +// +// if data["ruleOfFractionalPartOnWS"]?.stringValue == "droppingAfterHundredthPart" { +// ruleOfFractionalPartOnWS = .droppingAfterHundredthPart +// } else { +// ruleOfFractionalPartOnWS = nil +// } +// } +// +// public init( +// ruleOfProcessingTokenPriceWS: KeyAppTokenExtension.RuleOfProcessingTokenPriceWS? = nil, +// isPositionOnWS: Bool? = nil, +// isTokenCellVisibleOnWS: Bool? = nil, +// percentDifferenceToShowByPriceOnWS: Double? = nil, +// calculationOfFinalBalanceOnWS: Bool? = nil, +// ruleOfFractionalPartOnWS: KeyAppTokenExtension.RuleOfFractionalPartOnWS? = nil, +// canBeHidden: Bool? = nil +// ) { +// self.ruleOfProcessingTokenPriceWS = ruleOfProcessingTokenPriceWS +// self.isPositionOnWS = isPositionOnWS +// self.isTokenCellVisibleOnWS = isTokenCellVisibleOnWS ?? true +// self.percentDifferenceToShowByPriceOnWS = percentDifferenceToShowByPriceOnWS +// self.calculationOfFinalBalanceOnWS = calculationOfFinalBalanceOnWS +// self.ruleOfFractionalPartOnWS = ruleOfFractionalPartOnWS +// self.canBeHidden = canBeHidden ?? true +// } +//} diff --git a/p2p_wallet/Scenes/Main/NewHome/Subview/AccountList/HomeAccountsViewModel.swift b/p2p_wallet/Scenes/Main/NewHome/Subview/AccountList/HomeAccountsViewModel.swift index 49947cc1ca..3bc7544175 100644 --- a/p2p_wallet/Scenes/Main/NewHome/Subview/AccountList/HomeAccountsViewModel.swift +++ b/p2p_wallet/Scenes/Main/NewHome/Subview/AccountList/HomeAccountsViewModel.swift @@ -104,8 +104,8 @@ final class HomeAccountsViewModel: BaseViewModel, ObservableObject { solanaAccountsService.statePublisher .map { (state: AsyncValueState<[SolanaAccountsService.Account]>) -> String in let equityValue: CurrencyAmount = state.value - .filter { $0.token.keyAppExtensions.isPositionOnWS ?? false } - .filter { $0.token.keyAppExtensions.calculationOfFinalBalanceOnWS ?? true } +// .filter { $0.token.keyAppExtensions.isPositionOnWS ?? false } +// .filter { $0.token.keyAppExtensions.calculationOfFinalBalanceOnWS ?? true } .reduce(CurrencyAmount(usd: 0)) { $0 + $1.amountInFiat } @@ -128,7 +128,7 @@ final class HomeAccountsViewModel: BaseViewModel, ObservableObject { .map { (state: AsyncValueState<[SolanaAccountsService.Account]>) -> String in let cryptoFormatter = CryptoFormatter() guard let usdcAccount = state.value.first(where: { - $0.isUSDC && ($0.token.keyAppExtensions.isPositionOnWS ?? false) + $0.isUSDC /* && ($0.token.keyAppExtensions.isPositionOnWS ?? false) */ }) else { // Show zero balance for USDC if no USDC account exists return cryptoFormatter.string(amount: CryptoAmount(amount: 0, token: TokenMetadata.usdc)) diff --git a/p2p_wallet/Scenes/Main/SupportedTokens/SupportedTokensViewModel.swift b/p2p_wallet/Scenes/Main/SupportedTokens/SupportedTokensViewModel.swift index 5d823db83d..c7b23fc7d3 100644 --- a/p2p_wallet/Scenes/Main/SupportedTokens/SupportedTokensViewModel.swift +++ b/p2p_wallet/Scenes/Main/SupportedTokens/SupportedTokensViewModel.swift @@ -53,10 +53,10 @@ class SupportedTokensViewModel: BaseViewModel, ObservableObject { !SupportedTokensBusinnes.wellKnownTokens.map(\.symbol) .contains(token.symbol.trimmingCharacters(in: .whitespacesAndNewlines)) } - .filter { token in - // Filter by flag isTokenCellVisibleOnWS - token.keyAppExtensions.isTokenCellVisibleOnWS - } +// .filter { token in +// // Filter by flag isTokenCellVisibleOnWS +// token.keyAppExtensions.isTokenCellVisibleOnWS +// } .map { SupportedTokenItem(solana: $0) } solana = Set(SupportedTokensBusinnes.wellKnownTokens + filteredToken) diff --git a/p2p_wallet/Scenes/Main/Swap/ChooseSwapItem/Service/ChooseSwapTokenService.swift b/p2p_wallet/Scenes/Main/Swap/ChooseSwapItem/Service/ChooseSwapTokenService.swift index 36a501c7f1..60309a58bf 100644 --- a/p2p_wallet/Scenes/Main/Swap/ChooseSwapItem/Service/ChooseSwapTokenService.swift +++ b/p2p_wallet/Scenes/Main/Swap/ChooseSwapItem/Service/ChooseSwapTokenService.swift @@ -18,9 +18,9 @@ final class ChooseSwapTokenService: ChooseItemService { private var subscriptions = [AnyCancellable]() init(swapTokens: [SwapToken], fromToken: Bool) { - let swapTokens = swapTokens.filter { swapToken in - swapToken.token.keyAppExtensions.isTokenCellVisibleOnWS - } +// let swapTokens = swapTokens.filter { swapToken in +// swapToken.token.keyAppExtensions.isTokenCellVisibleOnWS +// } self.swapTokens = CurrentValueSubject(swapTokens) self.fromToken = fromToken From 7adf9e1e3e7b3d4768cb3779d6617545a05e9c42 Mon Sep 17 00:00:00 2001 From: Chung Tran Date: Mon, 8 Jan 2024 11:17:16 +0700 Subject: [PATCH 08/32] feat: change MARKETING_VERSION --- project.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.yml b/project.yml index 784b9b3a21..558b0950b0 100644 --- a/project.yml +++ b/project.yml @@ -76,7 +76,7 @@ configFiles: settings: base: - MARKETING_VERSION: 2.10.0 + MARKETING_VERSION: 2.11.0 configs: Debug: VALIDATE_PRODUCT: false From b63f871ff3855f6ad37c6c66ac057807940f1d12 Mon Sep 17 00:00:00 2001 From: Elizaveta Semenova Date: Mon, 8 Jan 2024 14:31:06 +0300 Subject: [PATCH 09/32] [ETH-679] Return showHide for solana accounts --- .../Aggregator/CryptoSolanaAccountsAggregator.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p2p_wallet/Scenes/Main/Crypto/Components/Crypto Accounts/Aggregator/CryptoSolanaAccountsAggregator.swift b/p2p_wallet/Scenes/Main/Crypto/Components/Crypto Accounts/Aggregator/CryptoSolanaAccountsAggregator.swift index eb11b8b6e1..91da9792b3 100644 --- a/p2p_wallet/Scenes/Main/Crypto/Components/Crypto Accounts/Aggregator/CryptoSolanaAccountsAggregator.swift +++ b/p2p_wallet/Scenes/Main/Crypto/Components/Crypto Accounts/Aggregator/CryptoSolanaAccountsAggregator.swift @@ -36,7 +36,7 @@ struct CryptoSolanaAccountsAggregator: DataAggregator { return RenderableSolanaAccount( account: account, - extraAction: nil, // extraAction, + extraAction: .showHide, tags: tags ) } From 2b529b969fd9754fa27d34f2b98dcf1bd598257c Mon Sep 17 00:00:00 2001 From: runner Date: Mon, 8 Jan 2024 11:32:10 +0000 Subject: [PATCH 10/32] fix(swiftformat): Apply Swiftformat changes --- .../Sources/KeyAppBusiness/Price/PriceRule.swift | 8 ++++---- .../Blockchain/Solana/SolanaTokenExtension.swift | 12 ++++++------ .../Subview/AccountList/HomeAccountsViewModel.swift | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Packages/KeyAppKit/Sources/KeyAppBusiness/Price/PriceRule.swift b/Packages/KeyAppKit/Sources/KeyAppBusiness/Price/PriceRule.swift index b53473c0e3..f99df05f61 100644 --- a/Packages/KeyAppKit/Sources/KeyAppBusiness/Price/PriceRule.swift +++ b/Packages/KeyAppKit/Sources/KeyAppBusiness/Price/PriceRule.swift @@ -13,18 +13,18 @@ protocol PriceRule { // Make price rate equals 1:1 when `ruleOfProcessingTokenPrice` equals `byCountOfTokensValue`. class OneToOnePriceRule: PriceRule { - func adjustValue(token: SomeToken, price: TokenPrice, fiat _: String) -> PriceRuleHandler { + func adjustValue(token _: SomeToken, price: TokenPrice, fiat _: String) -> PriceRuleHandler { // if token.keyAppExtension.ruleOfProcessingTokenPriceWS == .byCountOfTokensValue { // return .continue(TokenPrice(currencyCode: price.currencyCode, value: 1.0, token: token)) // } - return .continue(price) + .continue(price) } } // Depegging price by measure percentage difference. class DepeggingPriceRule: PriceRule { - func adjustValue(token: SomeToken, price: TokenPrice, fiat _: String) -> PriceRuleHandler { + func adjustValue(token _: SomeToken, price: TokenPrice, fiat _: String) -> PriceRuleHandler { // if let allowPercentageDifferenceValue = token.keyAppExtension.percentDifferenceToShowByPriceOnWS { // let percentageDifferenceValue = 100 - (1 / price.value) * 100 // if abs(percentageDifferenceValue) <= BigDecimal(floatLiteral: allowPercentageDifferenceValue) { @@ -34,6 +34,6 @@ class DepeggingPriceRule: PriceRule { // } // } - return .continue(price) + .continue(price) } } diff --git a/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/Solana/SolanaTokenExtension.swift b/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/Solana/SolanaTokenExtension.swift index c6678a3064..3464fd312f 100644 --- a/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/Solana/SolanaTokenExtension.swift +++ b/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/Solana/SolanaTokenExtension.swift @@ -1,13 +1,13 @@ -//import Foundation -//import SolanaSwift +// import Foundation +// import SolanaSwift // -//public extension SolanaToken { +// public extension SolanaToken { // var keyAppExtensions: KeyAppTokenExtension { // KeyAppTokenExtension(data: extensions ?? [:]) // } -//} +// } // -//public struct KeyAppTokenExtension: Codable, Hashable { +// public struct KeyAppTokenExtension: Codable, Hashable { // public let ruleOfProcessingTokenPriceWS: RuleOfProcessingTokenPriceWS? // public let isPositionOnWS: Bool? // public let isTokenCellVisibleOnWS: Bool @@ -61,4 +61,4 @@ // self.ruleOfFractionalPartOnWS = ruleOfFractionalPartOnWS // self.canBeHidden = canBeHidden ?? true // } -//} +// } diff --git a/p2p_wallet/Scenes/Main/NewHome/Subview/AccountList/HomeAccountsViewModel.swift b/p2p_wallet/Scenes/Main/NewHome/Subview/AccountList/HomeAccountsViewModel.swift index 3bc7544175..5703aace74 100644 --- a/p2p_wallet/Scenes/Main/NewHome/Subview/AccountList/HomeAccountsViewModel.swift +++ b/p2p_wallet/Scenes/Main/NewHome/Subview/AccountList/HomeAccountsViewModel.swift @@ -106,9 +106,9 @@ final class HomeAccountsViewModel: BaseViewModel, ObservableObject { let equityValue: CurrencyAmount = state.value // .filter { $0.token.keyAppExtensions.isPositionOnWS ?? false } // .filter { $0.token.keyAppExtensions.calculationOfFinalBalanceOnWS ?? true } - .reduce(CurrencyAmount(usd: 0)) { - $0 + $1.amountInFiat - } + .reduce(CurrencyAmount(usd: 0)) { + $0 + $1.amountInFiat + } let formatter = CurrencyFormatter( showSpacingAfterCurrencySymbol: false, From f00c935e3f31e2df38909ab0401f0a3843a0e74e Mon Sep 17 00:00:00 2001 From: Elizaveta Semenova Date: Mon, 8 Jan 2024 18:55:23 +0300 Subject: [PATCH 11/32] [ETH-674] Get exchange rates from moonpay service instead of keyapp service --- p2p_wallet/Scenes/Main/Buy/BuyViewModel.swift | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/p2p_wallet/Scenes/Main/Buy/BuyViewModel.swift b/p2p_wallet/Scenes/Main/Buy/BuyViewModel.swift index 30c7b10d33..a5b1e7599e 100644 --- a/p2p_wallet/Scenes/Main/Buy/BuyViewModel.swift +++ b/p2p_wallet/Scenes/Main/Buy/BuyViewModel.swift @@ -182,15 +182,14 @@ final class BuyViewModel: ObservableObject { Task { for fiat in BuyViewModel.fiats { - self.tokenPrices[fiat] = try Dictionary( - await pricesService.getPrices( - tokens: BuyViewModel.tokens, - fiat: fiat.rawValue - ) - .map { token, price in - (token.address, price.doubleValue) - } - ) { lhs, _ in lhs } + guard let fiatCurrency = fiat.buyFiatCurrency() else { continue } + var tokenPrice: [String: Double] = [:] + for token in BuyViewModel.tokens { + guard let cryptoCurrency = token.buyCryptoCurrency() else { continue } + let rate = try await exchangeService.getExchangeRate(from: fiatCurrency, to: cryptoCurrency) + tokenPrice[token.mintAddress] = rate.amount + } + self.tokenPrices[fiat] = tokenPrice } let banks = try await exchangeService.isBankTransferEnabled() From 9da1313bedbb78f51159b1c4431d4739f68ae1ae Mon Sep 17 00:00:00 2001 From: Chung Tran Date: Tue, 9 Jan 2024 16:22:24 +0700 Subject: [PATCH 12/32] feat: temp commit --- .../FeeRelayerSwift/Models/TokenAccount.swift | 10 +++- .../Relay/Context/RelayContext.swift | 4 -- .../Context/RelayContextManagerImpl.swift | 3 - .../Relay/Helpers/DestinationAnalysator.swift | 2 +- .../Helpers/TransitTokenAccountManager.swift | 8 ++- .../TopUpTransactionBuilderImpl.swift | 4 +- .../Socket/RealtimeSolanaAccountService.swift | 36 ++++++++++-- .../Solana/SolanaAccountsService.swift | 8 ++- .../Blockchain/Solana/SolanaAccount.swift | 57 ++++++++++++++++--- .../{BalancesCache.swift => Caches.swift} | 16 ++++++ .../Sources/OrcaSwapSwift/Models/Pool.swift | 2 + .../OrcaSwap/OrcaSwap+Extensions.swift | 37 +++++++++++- .../OrcaSwapSwift/OrcaSwap/OrcaSwap.swift | 23 +++++--- .../Sources/Send/Action/SendAction.swift | 6 +- .../SendInputBusinessLogic+ChangeToken.swift | 10 ++-- ...endInputBusinessLogic+ChangeTokenFee.swift | 3 +- .../Sources/Send/Input/SendInputState.swift | 20 +++---- .../Input/Services/SendFeeCalculator.swift | 11 ++-- .../xcshareddata/swiftpm/Package.resolved | 2 +- .../ReceiveFundsViaLinkViewModel.swift | 6 +- .../FeePrompt/SendInputFeePromptView.swift | 6 +- .../SendTransactionDetailViewModel.swift | 2 +- .../Main/Send/Input/SendInputViewModel.swift | 14 ++--- .../AmountView/SendInputAmountViewModel.swift | 4 +- .../SendInputAmountWrapperView.swift | 4 +- .../Scenes/Main/Swap/Swap/SwapViewModel.swift | 5 +- .../New/AccountDetailsViewModel.swift | 6 +- .../Send/WormholeSendInputCoordinator.swift | 2 +- .../Wormhole/Send/WormholeSendInputView.swift | 6 +- .../Send/WormholeSendInputViewModel.swift | 2 +- .../SendSubviews/SendInputTokenView.swift | 5 +- 31 files changed, 246 insertions(+), 78 deletions(-) rename Packages/KeyAppKit/Sources/OrcaSwapSwift/Models/{BalancesCache.swift => Caches.swift} (52%) diff --git a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Models/TokenAccount.swift b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Models/TokenAccount.swift index 47edc3a1b3..4b16898aff 100644 --- a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Models/TokenAccount.swift +++ b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Models/TokenAccount.swift @@ -3,9 +3,14 @@ import SolanaSwift /// A basic class that represents SPL TokenMetadata. public struct TokenAccount: Equatable, Codable { - public init(address: PublicKey, mint: PublicKey) { + public init( + address: PublicKey, + mint: PublicKey, + minimumTokenAccountBalance: UInt64 + ) { self.address = address self.mint = mint + self.minimumTokenAccountBalance = minimumTokenAccountBalance } /// A address of spl token. @@ -13,4 +18,7 @@ public struct TokenAccount: Equatable, Codable { /// A mint address for spl token. public let mint: PublicKey + + /// Mint rent for token + public let minimumTokenAccountBalance: UInt64 } diff --git a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Context/RelayContext.swift b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Context/RelayContext.swift index 79c5d9a4d3..b250faca8c 100644 --- a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Context/RelayContext.swift +++ b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Context/RelayContext.swift @@ -2,7 +2,6 @@ import Foundation import SolanaSwift public struct RelayContext: Hashable, Codable { - public let minimumTokenAccountBalance: UInt64 public let minimumRelayAccountBalance: UInt64 public let feePayerAddress: PublicKey public let lamportsPerSignature: UInt64 @@ -10,14 +9,12 @@ public struct RelayContext: Hashable, Codable { public var usageStatus: UsageStatus public init( - minimumTokenAccountBalance: UInt64, minimumRelayAccountBalance: UInt64, feePayerAddress: PublicKey, lamportsPerSignature: UInt64, relayAccountStatus: RelayAccountStatus, usageStatus: UsageStatus ) { - self.minimumTokenAccountBalance = minimumTokenAccountBalance self.minimumRelayAccountBalance = minimumRelayAccountBalance self.feePayerAddress = feePayerAddress self.lamportsPerSignature = lamportsPerSignature @@ -26,7 +23,6 @@ public struct RelayContext: Hashable, Codable { } public func hash(into hasher: inout Hasher) { - hasher.combine(minimumTokenAccountBalance) hasher.combine(minimumRelayAccountBalance) hasher.combine(feePayerAddress) hasher.combine(lamportsPerSignature) diff --git a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Context/RelayContextManagerImpl.swift b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Context/RelayContextManagerImpl.swift index 455f30445a..72f572068e 100644 --- a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Context/RelayContextManagerImpl.swift +++ b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Context/RelayContextManagerImpl.swift @@ -57,14 +57,12 @@ public class RelayContextManagerImpl: RelayContextManager { // retrieve RelayContext let ( - minimumTokenAccountBalance, minimumRelayAccountBalance, lamportsPerSignature, feePayerAddress, relayAccountStatus, usageStatus ) = try await( - solanaAPIClient.getMinimumBalanceForRentExemption(span: 165), solanaAPIClient.getMinimumBalanceForRentExemption(span: 0), solanaAPIClient.getFees(commitment: nil).feeCalculator?.lamportsPerSignature ?? 0, feeRelayerAPIClient.getFeePayerPubkey(), @@ -77,7 +75,6 @@ public class RelayContextManagerImpl: RelayContextManager { ) return try RelayContext( - minimumTokenAccountBalance: minimumTokenAccountBalance, minimumRelayAccountBalance: minimumRelayAccountBalance, feePayerAddress: PublicKey(string: feePayerAddress), lamportsPerSignature: lamportsPerSignature, diff --git a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Helpers/DestinationAnalysator.swift b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Helpers/DestinationAnalysator.swift index 6286b7b000..7367efc6ab 100644 --- a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Helpers/DestinationAnalysator.swift +++ b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Helpers/DestinationAnalysator.swift @@ -46,7 +46,7 @@ public class DestinationAnalysatorImpl: DestinationAnalysator { // Check destination address is exist. let info: BufferInfo? = try? await solanaAPIClient .getAccountInfo(account: address.base58EncodedString) - let needsCreateDestinationTokenAccount = info?.owner != TokenProgram.id.base58EncodedString + let needsCreateDestinationTokenAccount = !PublicKey.isSPLTokenOrToken2022ProgramId(info?.owner) return .splAccount(needsCreation: needsCreateDestinationTokenAccount) } diff --git a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Helpers/TransitTokenAccountManager.swift b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Helpers/TransitTokenAccountManager.swift index 32e562b6f6..c677d70d44 100644 --- a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Helpers/TransitTokenAccountManager.swift +++ b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Helpers/TransitTokenAccountManager.swift @@ -29,7 +29,8 @@ public class TransitTokenAccountManagerImpl: TransitTokenAccountManager { return TokenAccount( address: transitTokenAccountAddress, - mint: transitTokenMintPubkey + mint: transitTokenMintPubkey, + minimumTokenAccountBalance: getTransitTokenMintRentExemption(pools: pools) ?? 2_039_280 ) } @@ -39,6 +40,11 @@ public class TransitTokenAccountManagerImpl: TransitTokenAccountManager { return try PublicKey(string: orcaSwap.getMint(tokenName: interTokenName)) } + func getTransitTokenMintRentExemption(pools: PoolsPair) -> UInt64? { + guard pools.count == 2 else { return nil } + return pools[0].tokenBMinimumBalanceForRentExemption + } + public func checkIfNeedsCreateTransitTokenAccount(transitToken: TokenAccount?) async throws -> Bool? { guard let transitToken = transitToken else { return nil } diff --git a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Service/TopUpTransactionBuilder/TopUpTransactionBuilderImpl.swift b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Service/TopUpTransactionBuilder/TopUpTransactionBuilderImpl.swift index 641d17abfe..b5305609e9 100644 --- a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Service/TopUpTransactionBuilder/TopUpTransactionBuilderImpl.swift +++ b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Service/TopUpTransactionBuilder/TopUpTransactionBuilderImpl.swift @@ -102,7 +102,7 @@ class TopUpTransactionBuilderImpl: TopUpTransactionBuilder { switch swap.swapData { case let swap as DirectSwapData: - expectedFee.accountBalances += context.minimumTokenAccountBalance + expectedFee.accountBalances += sourceToken.minimumTokenAccountBalance // approve if let userTransferAuthority = userTransferAuthority { instructions.append( @@ -154,7 +154,7 @@ class TopUpTransactionBuilderImpl: TopUpTransactionBuilder { } // Destination WSOL account funding - expectedFee.accountBalances += context.minimumTokenAccountBalance + expectedFee.accountBalances += sourceToken.minimumTokenAccountBalance // top up try instructions.append( diff --git a/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/Socket/RealtimeSolanaAccountService.swift b/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/Socket/RealtimeSolanaAccountService.swift index 3d4f6df79a..25a284b25e 100644 --- a/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/Socket/RealtimeSolanaAccountService.swift +++ b/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/Socket/RealtimeSolanaAccountService.swift @@ -241,11 +241,30 @@ final class RealtimeSolanaAccountServiceImpl: RealtimeSolanaAccountService { ] ) + let splToken2022AccountChange = solanaWebSocketMethod.programSubscribe( + program: Token2022Program.id.base58EncodedString, + commitment: "confirmed", + encoding: "base64", + filters: [ + [ + "dataSize": 165, + ], + [ + "memcmp": [ + "offset": 32, + "bytes": owner, + ] as [String: Any], + ], + ] + ) + let nativeAccountChangeRequest = try JSONSerialization.data(withJSONObject: nativeAccountChange) let splAccountChangeRequest = try JSONSerialization.data(withJSONObject: splAccountChange) + let splToken2022AccountChangeRequest = try JSONSerialization.data(withJSONObject: splToken2022AccountChange) ws.send(nativeAccountChangeRequest.bytes) ws.send(splAccountChangeRequest.bytes) + ws.send(splToken2022AccountChangeRequest.bytes) return nil } catch { @@ -277,7 +296,9 @@ final class RealtimeSolanaAccountServiceImpl: RealtimeSolanaAccountService { let solanaAccount = try SolanaAccount( address: owner, lamports: balance, - token: await tokensService.nativeToken + token: await tokensService.nativeToken, + minRentExemption: nil, + tokenProgramId: nil ) let accounts = [solanaAccount] + resolved @@ -289,7 +310,9 @@ final class RealtimeSolanaAccountServiceImpl: RealtimeSolanaAccountService { return SolanaAccount( address: pubKey, lamports: accountBalance.lamports ?? 0, - token: accountBalance.token + token: accountBalance.token, + minRentExemption: accountBalance.minimumBalanceForRentExemption, + tokenProgramId: accountBalance.tokenProgramId ) } .compactMap { $0 } @@ -328,10 +351,13 @@ final class RealtimeSolanaAccountServiceImpl: RealtimeSolanaAccountService { // TODO: Add case when token info is invalid if let token { + let minRentExempt = value.account.lamports let splAccount = SolanaAccount( address: pubKey, lamports: tokenAccountData.lamports, - token: token + token: token, + minRentExemption: minRentExempt, + tokenProgramId: value.account.owner ) accountsSubject.send(splAccount) } @@ -347,7 +373,9 @@ final class RealtimeSolanaAccountServiceImpl: RealtimeSolanaAccountService { let nativeSolanaAccount = try SolanaAccount( address: owner, lamports: notification.result.value.lamports, - token: await tokensService.nativeToken + token: await tokensService.nativeToken, + minRentExemption: nil, + tokenProgramId: nil ) accountsSubject.send(nativeSolanaAccount) } diff --git a/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/SolanaAccountsService.swift b/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/SolanaAccountsService.swift index 692e2ce568..8b539be2a2 100644 --- a/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/SolanaAccountsService.swift +++ b/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/SolanaAccountsService.swift @@ -271,7 +271,9 @@ class SolanaAccountAsyncValue: AsyncValue<[SolanaAccount]> { let solanaAccount = try SolanaAccount( address: accountAddress, lamports: balance, - token: await tokensService.nativeToken + token: await tokensService.nativeToken, + minRentExemption: nil, + tokenProgramId: nil ) newAccounts = [solanaAccount] + resolved @@ -283,7 +285,9 @@ class SolanaAccountAsyncValue: AsyncValue<[SolanaAccount]> { return SolanaAccount( address: pubkey, lamports: accountBalance.lamports ?? 0, - token: accountBalance.token + token: accountBalance.token, + minRentExemption: accountBalance.minimumBalanceForRentExemption, + tokenProgramId: accountBalance.tokenProgramId ) } .compactMap { $0 } diff --git a/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/Solana/SolanaAccount.swift b/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/Solana/SolanaAccount.swift index d833d536a2..332791b7a1 100644 --- a/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/Solana/SolanaAccount.swift +++ b/Packages/KeyAppKit/Sources/KeyAppKitCore/Blockchain/Solana/SolanaAccount.swift @@ -16,19 +16,42 @@ public struct SolanaAccount: Identifiable, Equatable, Hashable { /// The fetched price at current moment of time. public var price: TokenPrice? - public init(address: String, lamports: Lamports, token: SolanaToken, price: TokenPrice? = nil) { + public var minRentExemption: UInt64? + + public var tokenProgramId: String? + + // MARK: - Intializers + + public init( + address: String, + lamports: Lamports, + token: SolanaToken, + price: TokenPrice? = nil, + minRentExemption: UInt64?, + tokenProgramId: String? + ) { self.address = address self.lamports = lamports self.token = token self.price = price + self.minRentExemption = minRentExemption + self.tokenProgramId = tokenProgramId } - @available(*, deprecated) - public init(pubkey: String? = nil, lamports: Lamports? = nil, token: TokenMetadata) { - address = pubkey ?? "" - self.lamports = lamports ?? 0 - self.token = token - price = nil + public static func classicSPLTokenAccount( + address: String, + lamports: Lamports, + token: SolanaToken, + price: TokenPrice? = nil + ) -> Self { + .init( + address: address, + lamports: lamports, + token: token, + price: price, + minRentExemption: 2_039_280, + tokenProgramId: TokenProgram.id.base58EncodedString + ) } public var cryptoAmount: CryptoAmount { @@ -47,6 +70,18 @@ public extension SolanaAccount { token.mintAddress } + var isNative: Bool { + token.isNative + } + + var symbol: String { + token.symbol + } + + var decimals: Decimals { + token.decimals + } + @available(*, deprecated, renamed: "address") var pubkey: String? { get { @@ -81,6 +116,12 @@ public extension SolanaAccount { @available(*, deprecated, message: "Legacy code") static func nativeSolana(pubkey: String?, lamport: Lamports?) -> Self { - .init(pubkey: pubkey, lamports: lamport, token: .nativeSolana) + .init( + address: pubkey ?? "", + lamports: lamport ?? 0, + token: .nativeSolana, + minRentExemption: nil, + tokenProgramId: nil + ) } } diff --git a/Packages/KeyAppKit/Sources/OrcaSwapSwift/Models/BalancesCache.swift b/Packages/KeyAppKit/Sources/OrcaSwapSwift/Models/Caches.swift similarity index 52% rename from Packages/KeyAppKit/Sources/OrcaSwapSwift/Models/BalancesCache.swift rename to Packages/KeyAppKit/Sources/OrcaSwapSwift/Models/Caches.swift index 2f8fb16108..c77206d09d 100644 --- a/Packages/KeyAppKit/Sources/OrcaSwapSwift/Models/BalancesCache.swift +++ b/Packages/KeyAppKit/Sources/OrcaSwapSwift/Models/Caches.swift @@ -16,3 +16,19 @@ actor BalancesCache { balancesCache[key] = value } } + +actor MinRentCache { + var minRentCache = [String: UInt64]() + + func getTokenABalance(pool: Pool) -> UInt64? { + pool.tokenAMinimumBalanceForRentExemption ?? minRentCache[pool.tokenAccountA] + } + + func getTokenBBalance(pool: Pool) -> UInt64? { + pool.tokenBMinimumBalanceForRentExemption ?? minRentCache[pool.tokenAccountB] + } + + func save(key: String, value: UInt64) { + minRentCache[key] = value + } +} diff --git a/Packages/KeyAppKit/Sources/OrcaSwapSwift/Models/Pool.swift b/Packages/KeyAppKit/Sources/OrcaSwapSwift/Models/Pool.swift index f55b4a17cc..99a5c41e63 100644 --- a/Packages/KeyAppKit/Sources/OrcaSwapSwift/Models/Pool.swift +++ b/Packages/KeyAppKit/Sources/OrcaSwapSwift/Models/Pool.swift @@ -33,6 +33,8 @@ public struct Pool: Codable, Equatable { // balance (lazy load) var tokenABalance: TokenAccountBalance? var tokenBBalance: TokenAccountBalance? + public var tokenAMinimumBalanceForRentExemption: UInt64? + public var tokenBMinimumBalanceForRentExemption: UInt64? var isStable: Bool? diff --git a/Packages/KeyAppKit/Sources/OrcaSwapSwift/OrcaSwap/OrcaSwap+Extensions.swift b/Packages/KeyAppKit/Sources/OrcaSwapSwift/OrcaSwap/OrcaSwap+Extensions.swift index 0f5ae55e2b..c2f6897c3b 100644 --- a/Packages/KeyAppKit/Sources/OrcaSwapSwift/OrcaSwap/OrcaSwap+Extensions.swift +++ b/Packages/KeyAppKit/Sources/OrcaSwapSwift/OrcaSwap/OrcaSwap+Extensions.swift @@ -81,10 +81,16 @@ extension OrcaSwap { (tokenABalance, tokenBBalance) = (tab, tbb) } else { try Task.checkCancellation() - (tokenABalance, tokenBBalance) = try await( - solanaClient.getTokenAccountBalance(pubkey: pool.tokenAccountA, commitment: nil), - solanaClient.getTokenAccountBalance(pubkey: pool.tokenAccountB, commitment: nil) + let pool = pool + async let tokenABalanceResult = solanaClient.getTokenAccountBalance( + pubkey: pool.tokenAccountA, + commitment: nil ) + async let tokenBBalanceResult = solanaClient.getTokenAccountBalance( + pubkey: pool.tokenAccountB, + commitment: nil + ) + (tokenABalance, tokenBBalance) = try await(tokenABalanceResult, tokenBBalanceResult) } await balancesCache.save(key: pool.tokenAccountA, value: tokenABalance) @@ -93,6 +99,31 @@ extension OrcaSwap { pool.tokenABalance = tokenABalance pool.tokenBBalance = tokenBBalance + // get minrent exemption + let (tokenAMinRent, tokenBMinRent): (UInt64, UInt64) + if let tab = await minRentCache.getTokenABalance(pool: pool), + let tbb = await minRentCache.getTokenBBalance(pool: pool) + { + (tokenAMinRent, tokenBMinRent) = (tab, tbb) + } else { + try Task.checkCancellation() + let pool = pool + async let tokenAMinRentResult: BufferInfo? = solanaClient + .getAccountInfo(account: pool.tokenAccountA) + async let tokenBMinRentResult: BufferInfo? = solanaClient + .getAccountInfo(account: pool.tokenAccountB) + (tokenAMinRent, tokenBMinRent) = try await( + tokenAMinRentResult?.lamports ?? 2_039_280, + tokenBMinRentResult?.lamports ?? 2_039_280 + ) + } + + await minRentCache.save(key: pool.tokenAccountA, value: tokenAMinRent) + await minRentCache.save(key: pool.tokenAccountB, value: tokenBMinRent) + + pool.tokenAMinimumBalanceForRentExemption = tokenAMinRent + pool.tokenBMinimumBalanceForRentExemption = tokenBMinRent + return pool } } diff --git a/Packages/KeyAppKit/Sources/OrcaSwapSwift/OrcaSwap/OrcaSwap.swift b/Packages/KeyAppKit/Sources/OrcaSwapSwift/OrcaSwap/OrcaSwap.swift index 64467a28d3..15fc4fae4e 100644 --- a/Packages/KeyAppKit/Sources/OrcaSwapSwift/OrcaSwap/OrcaSwap.swift +++ b/Packages/KeyAppKit/Sources/OrcaSwapSwift/OrcaSwap/OrcaSwap.swift @@ -12,6 +12,7 @@ public class OrcaSwap: OrcaSwapType { var info: SwapInfo? let balancesCache = BalancesCache() + let minRentCache = MinRentCache() let locker = NSLock() // MARK: - Initializer @@ -99,7 +100,7 @@ public class OrcaSwap: OrcaSwapType { guard let fromTokenName = getTokenFromMint(fromMint)?.name, let toTokenName = getTokenFromMint(toMint)?.name, let currentRoutes = try? findRoutes(fromTokenName: fromTokenName, toTokenName: toTokenName) - .first?.value + .first?.value else { return [] } // retrieve all routes @@ -270,11 +271,11 @@ public class OrcaSwap: OrcaSwapType { let decimals = bestPoolsPair![0].tokenABalance?.decimals, let inputAmount = inputAmount, let intermediaryToken = bestPoolsPair? - .getIntermediaryToken( - inputAmount: inputAmount.toLamport(decimals: decimals), - slippage: slippage - ), - let mint = getMint(tokenName: intermediaryToken.tokenName) + .getIntermediaryToken( + inputAmount: inputAmount.toLamport(decimals: decimals), + slippage: slippage + ), + let mint = getMint(tokenName: intermediaryToken.tokenName) { // when intermediary token is SOL, a deposit fee for creating WSOL is needed (will be returned after // transaction) @@ -286,7 +287,11 @@ public class OrcaSwap: OrcaSwapType { // Check if intermediary token creation is needed else { isIntermediaryTokenCreated = try await solanaClient - .checkIfAssociatedTokenAccountExists(owner: owner, mint: mint) + .checkIfAssociatedTokenAccountExists( + owner: owner, + mint: mint, + tokenProgramId: TokenProgram.id + ) } } @@ -610,6 +615,7 @@ public class OrcaSwap: OrcaSwapType { .prepareForCreatingAssociatedTokenAccount( owner: owner.publicKey, mint: destinationMint, + tokenProgramId: TokenProgram.id, feePayer: feePayer ?? owner.publicKey, closeAfterward: false ) @@ -620,7 +626,7 @@ public class OrcaSwap: OrcaSwapType { from: owner.publicKey, amount: 0, payer: feePayer ?? owner.publicKey, - minRentExemption: nil + minRentExemption: await solanaClient.getMinimumBalanceForRentExemption(span: 165) ), createDestinationTokenAccountInstructionsRequest ) @@ -629,6 +635,7 @@ public class OrcaSwap: OrcaSwapType { blockchainClient.prepareForCreatingAssociatedTokenAccount( owner: owner.publicKey, mint: intermediaryTokenMint, + tokenProgramId: TokenProgram.id, feePayer: feePayer ?? owner.publicKey, closeAfterward: true ), diff --git a/Packages/KeyAppKit/Sources/Send/Action/SendAction.swift b/Packages/KeyAppKit/Sources/Send/Action/SendAction.swift index 61e0f26561..ec6d85738e 100644 --- a/Packages/KeyAppKit/Sources/Send/Action/SendAction.swift +++ b/Packages/KeyAppKit/Sources/Send/Action/SendAction.swift @@ -230,7 +230,11 @@ public class SendActionServiceImpl: SendActionService { else { throw SendError.invalidPayingFeeWallet } - return .init(address: address, mint: mintAddress) + return .init( + address: address, + mint: mintAddress, + minimumTokenAccountBalance: feeWallet.minRentExemption ?? 2_039_280 + ) } return nil } diff --git a/Packages/KeyAppKit/Sources/Send/Input/SendInputBusinessLogic+ChangeToken.swift b/Packages/KeyAppKit/Sources/Send/Input/SendInputBusinessLogic+ChangeToken.swift index d624ca4988..6efdcf2716 100644 --- a/Packages/KeyAppKit/Sources/Send/Input/SendInputBusinessLogic+ChangeToken.swift +++ b/Packages/KeyAppKit/Sources/Send/Input/SendInputBusinessLogic+ChangeToken.swift @@ -6,7 +6,7 @@ import SolanaSwift extension SendInputBusinessLogic { static func changeToken( state: SendInputState, - token: TokenMetadata, + token: SolanaAccount, services: SendInputServices ) async -> SendInputState { guard let feeRelayerContext = state.feeRelayerContext else { @@ -81,9 +81,9 @@ extension SendInputBusinessLogic { static func autoSelectTokenFee( userWallets: [SolanaAccount], feeInSol: FeeAmount, - token: TokenMetadata, + token: SolanaAccount, services: SendInputServices - ) async -> (token: TokenMetadata, fee: FeeAmount?) { + ) async -> (token: SolanaAccount, fee: FeeAmount?) { var preferOrder = ["SOL": 2] if !preferOrder.keys.contains(token.symbol) { preferOrder[token.symbol] = 1 @@ -113,13 +113,13 @@ extension SendInputBusinessLogic { )) ?? .zero if feeInToken.total <= wallet.lamports { - return (wallet.token, feeInToken) + return (wallet, feeInToken) } } catch { continue } } - return (.nativeSolana, feeInSol) + return (.nativeSolana(pubkey: nil, lamport: nil), feeInSol) } } diff --git a/Packages/KeyAppKit/Sources/Send/Input/SendInputBusinessLogic+ChangeTokenFee.swift b/Packages/KeyAppKit/Sources/Send/Input/SendInputBusinessLogic+ChangeTokenFee.swift index 3026b0f69e..6945c75386 100644 --- a/Packages/KeyAppKit/Sources/Send/Input/SendInputBusinessLogic+ChangeTokenFee.swift +++ b/Packages/KeyAppKit/Sources/Send/Input/SendInputBusinessLogic+ChangeTokenFee.swift @@ -1,11 +1,12 @@ import FeeRelayerSwift +import KeyAppKitCore import Foundation import SolanaSwift extension SendInputBusinessLogic { static func changeFeeToken( state: SendInputState, - feeToken: TokenMetadata, + feeToken: SolanaAccount, services: SendInputServices ) async -> SendInputState { guard let feeRelayerContext = state.feeRelayerContext else { diff --git a/Packages/KeyAppKit/Sources/Send/Input/SendInputState.swift b/Packages/KeyAppKit/Sources/Send/Input/SendInputState.swift index 48c2ef5e8b..1c8d076c67 100644 --- a/Packages/KeyAppKit/Sources/Send/Input/SendInputState.swift +++ b/Packages/KeyAppKit/Sources/Send/Input/SendInputState.swift @@ -32,8 +32,8 @@ public enum SendInputAction: Equatable { case changeAmountInFiat(Double) case changeAmountInToken(Double) - case changeUserToken(TokenMetadata) - case changeFeeToken(TokenMetadata) + case changeUserToken(SolanaAccount) + case changeFeeToken(SolanaAccount) } public struct SendInputServices { @@ -97,7 +97,7 @@ public struct SendInputState: Equatable { public let recipient: Recipient public let recipientAdditionalInfo: RecipientAdditionalInfo - public let token: TokenMetadata + public let token: SolanaAccount public let userWalletEnvironments: UserWalletEnvironments public let amountInFiat: Double @@ -107,7 +107,7 @@ public struct SendInputState: Equatable { public let fee: FeeAmount /// Selected fee token - public let tokenFee: TokenMetadata + public let tokenFee: SolanaAccount /// Amount fee in Token (Converted from amount fee in SOL) public let feeInToken: FeeAmount @@ -127,12 +127,12 @@ public struct SendInputState: Equatable { status: Status, recipient: Recipient, recipientAdditionalInfo: RecipientAdditionalInfo, - token: TokenMetadata, + token: SolanaAccount, userWalletEnvironments: UserWalletEnvironments, amountInFiat: Double, amountInToken: Double, fee: FeeAmount, - tokenFee: TokenMetadata, + tokenFee: SolanaAccount, feeInToken: FeeAmount, feeRelayerContext: RelayContext?, sendViaLinkSeed: String? @@ -155,8 +155,8 @@ public struct SendInputState: Equatable { status: Status = .requiredInitialize, recipient: Recipient, recipientAdditionalInfo: RecipientAdditionalInfo = .zero, - token: TokenMetadata, - feeToken: TokenMetadata, + token: SolanaAccount, + feeToken: SolanaAccount, userWalletState: UserWalletEnvironments, feeRelayerContext: RelayContext? = nil, sendViaLinkSeed: String? @@ -181,12 +181,12 @@ public struct SendInputState: Equatable { status: Status? = nil, recipient: Recipient? = nil, recipientAdditionalInfo: RecipientAdditionalInfo? = nil, - token: TokenMetadata? = nil, + token: SolanaAccount? = nil, userWalletEnvironments: UserWalletEnvironments? = nil, amountInFiat: Double? = nil, amountInToken: Double? = nil, fee: FeeAmount? = nil, - tokenFee: TokenMetadata? = nil, + tokenFee: SolanaAccount? = nil, feeInToken: FeeAmount? = nil, feeRelayerContext: RelayContext? = nil, sendViaLinkSeed: String?? = nil diff --git a/Packages/KeyAppKit/Sources/Send/Input/Services/SendFeeCalculator.swift b/Packages/KeyAppKit/Sources/Send/Input/Services/SendFeeCalculator.swift index 76e8462c1f..8577a60778 100644 --- a/Packages/KeyAppKit/Sources/Send/Input/Services/SendFeeCalculator.swift +++ b/Packages/KeyAppKit/Sources/Send/Input/Services/SendFeeCalculator.swift @@ -1,10 +1,11 @@ import FeeRelayerSwift +import KeyAppKitCore import OrcaSwapSwift import SolanaSwift public protocol SendFeeCalculator: AnyObject { func getFees( - from token: TokenMetadata, + from token: SolanaAccount, recipient: Recipient, recipientAdditionalInfo: SendInputState.RecipientAdditionalInfo, payingTokenMint: String?, @@ -15,12 +16,14 @@ public protocol SendFeeCalculator: AnyObject { public class SendFeeCalculatorImpl: SendFeeCalculator { private let feeRelayerCalculator: RelayFeeCalculator - public init(feeRelayerCalculator: RelayFeeCalculator) { self.feeRelayerCalculator = feeRelayerCalculator } + public init(feeRelayerCalculator: RelayFeeCalculator) { + self.feeRelayerCalculator = feeRelayerCalculator + } // MARK: - Fees calculator public func getFees( - from token: TokenMetadata, + from token: SolanaAccount, recipient: Recipient, recipientAdditionalInfo: SendInputState.RecipientAdditionalInfo, payingTokenMint: String?, @@ -71,7 +74,7 @@ public class SendFeeCalculatorImpl: SendFeeCalculator { let expectedFee = FeeAmount( transaction: transactionFee, - accountBalances: isAssociatedTokenUnregister ? context.minimumTokenAccountBalance : 0 + accountBalances: isAssociatedTokenUnregister ? token.minRentExemption ?? 0 : 0 ) // TODO: - Remove later: Send as SendViaLink when link creation available diff --git a/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index cbb3533b9b..080d25c498 100644 --- a/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -312,7 +312,7 @@ "location" : "https://github.com/p2p-org/solana-swift", "state" : { "branch" : "feature/token-2022", - "revision" : "2e522178ea809141c160c66a5e335b05d01c683f" + "revision" : "8babbb7253af8cd6916f9a7861b98176e7fbb7f0" } }, { diff --git a/p2p_wallet/Scenes/Main/ReceiveFundsViaLink/ViewModel/ReceiveFundsViaLinkViewModel.swift b/p2p_wallet/Scenes/Main/ReceiveFundsViaLink/ViewModel/ReceiveFundsViaLinkViewModel.swift index 8268697c64..a04543dc37 100644 --- a/p2p_wallet/Scenes/Main/ReceiveFundsViaLink/ViewModel/ReceiveFundsViaLinkViewModel.swift +++ b/p2p_wallet/Scenes/Main/ReceiveFundsViaLink/ViewModel/ReceiveFundsViaLinkViewModel.swift @@ -95,7 +95,11 @@ final class ReceiveFundsViaLinkViewModel: BaseViewModel, ObservableObject { let transaction = ClaimSentViaLinkTransaction( claimableTokenInfo: claimableToken, token: token, - destinationWallet: SolanaAccount(pubkey: claimableToken.account, token: token), + destinationWallet: .classicSPLTokenAccount( + address: claimableToken.account, + lamports: 0, + token: token + ), tokenAmount: cryptoAmount, isFakeTransaction: isFakeSendingTransaction, fakeTransactionErrorType: fakeTransactionErrorType diff --git a/p2p_wallet/Scenes/Main/Send/Input/Details/FeePrompt/SendInputFeePromptView.swift b/p2p_wallet/Scenes/Main/Send/Input/Details/FeePrompt/SendInputFeePromptView.swift index eccbbda684..9e02fb0795 100644 --- a/p2p_wallet/Scenes/Main/Send/Input/Details/FeePrompt/SendInputFeePromptView.swift +++ b/p2p_wallet/Scenes/Main/Send/Input/Details/FeePrompt/SendInputFeePromptView.swift @@ -93,7 +93,11 @@ struct SendInputFeePromptView_Previews: PreviewProvider { static var previews: some View { SendInputFeePromptView( viewModel: SendInputFeePromptViewModel( - feeToken: .init(token: .usdc), + feeToken: .classicSPLTokenAccount( + address: "", + lamports: 0, + token: .usdc + ), feeInToken: .zero, availableFeeTokens: [] ) diff --git a/p2p_wallet/Scenes/Main/Send/Input/Details/SendTransactionDetails/SendTransactionDetailViewModel.swift b/p2p_wallet/Scenes/Main/Send/Input/Details/SendTransactionDetails/SendTransactionDetailViewModel.swift index bd730a9f07..0ae8aa4360 100644 --- a/p2p_wallet/Scenes/Main/Send/Input/Details/SendTransactionDetails/SendTransactionDetailViewModel.swift +++ b/p2p_wallet/Scenes/Main/Send/Input/Details/SendTransactionDetails/SendTransactionDetailViewModel.swift @@ -179,7 +179,7 @@ final class SendTransactionDetailViewModel: BaseViewModel, ObservableObject { ) } - private func convert(_ input: Lamports, _ token: TokenMetadata, _ price: TokenPrice?) -> (String, String?) { + private func convert(_ input: Lamports, _ token: SolanaAccount, _ price: TokenPrice?) -> (String, String?) { let amountInToken: Double = input.convertToBalance(decimals: token.decimals) let amountInFiat: Double? diff --git a/p2p_wallet/Scenes/Main/Send/Input/SendInputViewModel.swift b/p2p_wallet/Scenes/Main/Send/Input/SendInputViewModel.swift index 82684024f1..d48cf111b4 100644 --- a/p2p_wallet/Scenes/Main/Send/Input/SendInputViewModel.swift +++ b/p2p_wallet/Scenes/Main/Send/Input/SendInputViewModel.swift @@ -116,7 +116,7 @@ final class SendInputViewModel: BaseViewModel, ObservableObject { case let .solanaTokenAddress(_, token): tokenInWallet = wallets .first(where: { $0.token.mintAddress == token.mintAddress }) ?? - SolanaAccount(token: TokenMetadata.nativeSolana) + .nativeSolana(pubkey: nil, lamport: nil) default: if let preChosenWallet { tokenInWallet = preChosenWallet @@ -131,14 +131,14 @@ final class SendInputViewModel: BaseViewModel, ObservableObject { return lhs.amountInCurrentFiat > rhs.amountInCurrentFiat } } - tokenInWallet = sortedWallets.first ?? SolanaAccount(token: TokenMetadata.nativeSolana) + tokenInWallet = sortedWallets.first ?? .nativeSolana(pubkey: nil, lamport: nil) } } sourceWallet = tokenInWallet let feeTokenInWallet = wallets .first(where: { $0.token.mintAddress == TokenMetadata.usdc.mintAddress }) ?? - SolanaAccount(token: TokenMetadata.usdc) + .classicSPLTokenAccount(address: "", lamports: 0, token: .usdc) var exchangeRate = [String: TokenPrice]() var tokens = Set() @@ -156,8 +156,8 @@ final class SendInputViewModel: BaseViewModel, ObservableObject { let state = SendInputState.zero( recipient: recipient, - token: tokenInWallet.token, - feeToken: feeTokenInWallet.token, + token: tokenInWallet, + feeToken: feeTokenInWallet, userWalletState: env, sendViaLinkSeed: sendViaLinkSeed ) @@ -308,7 +308,7 @@ private extension SendInputViewModel { _ = await self.stateMachine.accept(action: .changeAmountInToken(0)) self.inputAmountViewModel.amountText = "0" } - _ = await self.stateMachine.accept(action: .changeUserToken(value.token)) + _ = await self.stateMachine.accept(action: .changeUserToken(value)) await MainActor.run { [weak self] in self?.inputAmountViewModel.token = value self?.isFeeLoading = false @@ -360,7 +360,7 @@ private extension SendInputViewModel { .sinkAsync { [weak self] newFeeToken in guard let self else { return } self.isFeeLoading = true - _ = await self.stateMachine.accept(action: .changeFeeToken(newFeeToken.token)) + _ = await self.stateMachine.accept(action: .changeFeeToken(newFeeToken)) self.isFeeLoading = false } .store(in: &subscriptions) diff --git a/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountViewModel.swift b/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountViewModel.swift index 0a8953515e..652eaf2ebb 100644 --- a/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountViewModel.swift +++ b/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountViewModel.swift @@ -57,7 +57,9 @@ final class SendInputAmountViewModel: BaseViewModel, ObservableObject { private let fiat: Fiat = Defaults.fiat private var currentText: String? - private var tokenChangedEvent = CurrentValueSubject(.init(token: .nativeSolana)) + private var tokenChangedEvent = CurrentValueSubject( + .nativeSolana(pubkey: nil, lamport: nil) + ) // MARK: - Dependencies diff --git a/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountWrapperView.swift b/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountWrapperView.swift index 0dc7f6f2e2..5617243eda 100644 --- a/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountWrapperView.swift +++ b/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountWrapperView.swift @@ -34,7 +34,9 @@ struct SendInputAmountWrapperView_Previews: PreviewProvider { ZStack { Color(.smoke) SendInputAmountWrapperView( - viewModel: SendInputAmountViewModel(initialToken: .init(token: .nativeSolana)) + viewModel: SendInputAmountViewModel( + initialToken: .nativeSolana(pubkey: nil, lamport: nil) + ) ) .padding(.horizontal, 16) } diff --git a/p2p_wallet/Scenes/Main/Swap/Swap/SwapViewModel.swift b/p2p_wallet/Scenes/Main/Swap/Swap/SwapViewModel.swift index f5a3b32dc9..3e5a2c027a 100644 --- a/p2p_wallet/Scenes/Main/Swap/Swap/SwapViewModel.swift +++ b/p2p_wallet/Scenes/Main/Swap/Swap/SwapViewModel.swift @@ -457,8 +457,9 @@ private extension SwapViewModel { } // form transaction - let destinationWallet = currentState.toToken.userWallet ?? SolanaAccount( - pubkey: nil, + let destinationWallet = currentState.toToken.userWallet ?? .classicSPLTokenAccount( + address: "", + lamports: 0, token: currentState.toToken.token ) diff --git a/p2p_wallet/Scenes/Main/WalletDetail/New/AccountDetailsViewModel.swift b/p2p_wallet/Scenes/Main/WalletDetail/New/AccountDetailsViewModel.swift index 970e425e81..db79498064 100644 --- a/p2p_wallet/Scenes/Main/WalletDetail/New/AccountDetailsViewModel.swift +++ b/p2p_wallet/Scenes/Main/WalletDetail/New/AccountDetailsViewModel.swift @@ -106,7 +106,11 @@ class AccountDetailsViewModel: BaseViewModel, ObservableObject { .send( .openSwapWithDestination( solanaAccount, - SolanaAccount(token: supportedWormholeToken) + .classicSPLTokenAccount( + address: "", + lamports: 0, + token: supportedWormholeToken + ) ) ) }, close: { [weak self] in diff --git a/p2p_wallet/Scenes/Main/Wormhole/Send/WormholeSendInputCoordinator.swift b/p2p_wallet/Scenes/Main/Wormhole/Send/WormholeSendInputCoordinator.swift index a7de3c6571..5a4974ba25 100644 --- a/p2p_wallet/Scenes/Main/Wormhole/Send/WormholeSendInputCoordinator.swift +++ b/p2p_wallet/Scenes/Main/Wormhole/Send/WormholeSendInputCoordinator.swift @@ -51,7 +51,7 @@ final class WormholeSendInputCoordinator: SmartCoordinator Date: Tue, 9 Jan 2024 16:55:38 +0700 Subject: [PATCH 13/32] feat: some fixes --- .../Helpers/SolanaAPIClient+FeeRelayer.swift | 12 ++++- ...nsactionBuilderImpl+CheckDestination.swift | 6 ++- .../SwapTransactionBuilderImpl.swift | 3 +- .../TopUpTransactionBuilderImpl.swift | 3 +- .../Sources/OrcaSwapSwift/Models/Pool.swift | 1 + .../Sources/Send/Action/SendAction.swift | 6 ++- .../SendInputBusinessLogic+Initializing.swift | 4 +- .../Input/Services/SendFeeCalculator.swift | 6 ++- ...rchServiceImpl+searchBySolanaAddress.swift | 46 +++++++++---------- .../RecipientSearchServiceImpl.swift | 6 ++- .../SendViaLink/SendViaLinkDataService.swift | 8 ++-- .../Send/SolanaAPIClient+Token2022.swift | 27 +++++++++++ 12 files changed, 90 insertions(+), 38 deletions(-) create mode 100644 Packages/KeyAppKit/Sources/Send/SolanaAPIClient+Token2022.swift diff --git a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Helpers/SolanaAPIClient+FeeRelayer.swift b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Helpers/SolanaAPIClient+FeeRelayer.swift index 25ee570bb8..76c9465a1c 100644 --- a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Helpers/SolanaAPIClient+FeeRelayer.swift +++ b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Helpers/SolanaAPIClient+FeeRelayer.swift @@ -16,7 +16,11 @@ extension SolanaAPIClient { // The account doesn't exists if account == nil { - return try PublicKey.associatedTokenAddress(walletAddress: address, tokenMintAddress: mint) + return try PublicKey.associatedTokenAddress( + walletAddress: address, + tokenMintAddress: mint, + tokenProgramId: TokenProgram.id + ) } // The account is already token account @@ -28,7 +32,11 @@ extension SolanaAPIClient { guard account?.owner != SystemProgram.id.base58EncodedString else { throw FeeRelayerError.wrongAddress } - return try PublicKey.associatedTokenAddress(walletAddress: address, tokenMintAddress: mint) + return try PublicKey.associatedTokenAddress( + walletAddress: address, + tokenMintAddress: mint, + tokenProgramId: TokenProgram.id + ) } func isAccountExists(_ address: PublicKey) async throws -> Bool { diff --git a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/RelaySwap/TransactionBuilder/SwapTransactionBuilderImpl/SwapTransactionBuilderImpl+Checks/SwapTransactionBuilderImpl+CheckDestination.swift b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/RelaySwap/TransactionBuilder/SwapTransactionBuilderImpl/SwapTransactionBuilderImpl+Checks/SwapTransactionBuilderImpl+CheckDestination.swift index 1255a36d30..ac2b9ed9d0 100644 --- a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/RelaySwap/TransactionBuilder/SwapTransactionBuilderImpl/SwapTransactionBuilderImpl+Checks/SwapTransactionBuilderImpl+CheckDestination.swift +++ b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/RelaySwap/TransactionBuilder/SwapTransactionBuilderImpl/SwapTransactionBuilderImpl+Checks/SwapTransactionBuilderImpl+CheckDestination.swift @@ -50,14 +50,16 @@ extension SwapTransactionBuilderImpl { // For other token, get associated token address let associatedAddress = try PublicKey.associatedTokenAddress( walletAddress: owner.publicKey, - tokenMintAddress: destinationMint + tokenMintAddress: destinationMint, + tokenProgramId: TokenProgram.id ) if needsCreation { let instruction = try AssociatedTokenProgram.createAssociatedTokenAccountInstruction( mint: destinationMint, owner: owner.publicKey, - payer: feePayerAddress + payer: feePayerAddress, + tokenProgramId: TokenProgram.id ) // SPECIAL CASE WHEN WE SWAP FROM SOL TO NON-CREATED SPL TOKEN, THEN WE NEEDS ADDITIONAL TRANSACTION diff --git a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/RelaySwap/TransactionBuilder/SwapTransactionBuilderImpl/SwapTransactionBuilderImpl.swift b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/RelaySwap/TransactionBuilder/SwapTransactionBuilderImpl/SwapTransactionBuilderImpl.swift index 69fe8d7bbe..7869058e74 100644 --- a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/RelaySwap/TransactionBuilder/SwapTransactionBuilderImpl/SwapTransactionBuilderImpl.swift +++ b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/RelaySwap/TransactionBuilder/SwapTransactionBuilderImpl/SwapTransactionBuilderImpl.swift @@ -43,7 +43,8 @@ public class SwapTransactionBuilderImpl: SwapTransactionBuilder { // assert userSource let associatedToken = try PublicKey.associatedTokenAddress( walletAddress: feePayerAddress, - tokenMintAddress: sourceTokenAccount.mint + tokenMintAddress: sourceTokenAccount.mint, + tokenProgramId: TokenProgram.id ) guard output.userSource != associatedToken else { throw FeeRelayerError.wrongAddress } diff --git a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Service/TopUpTransactionBuilder/TopUpTransactionBuilderImpl.swift b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Service/TopUpTransactionBuilder/TopUpTransactionBuilderImpl.swift index b5305609e9..3f9387c355 100644 --- a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Service/TopUpTransactionBuilder/TopUpTransactionBuilderImpl.swift +++ b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Service/TopUpTransactionBuilder/TopUpTransactionBuilderImpl.swift @@ -43,7 +43,8 @@ class TopUpTransactionBuilderImpl: TopUpTransactionBuilder { let feePayerAddress = context.feePayerAddress let associatedTokenAddress = try PublicKey.associatedTokenAddress( walletAddress: feePayerAddress, - tokenMintAddress: sourceTokenMintAddress + tokenMintAddress: sourceTokenMintAddress, + tokenProgramId: TokenProgram.id ) ?! FeeRelayerError.unknown let network = solanaApiClient.endpoint.network diff --git a/Packages/KeyAppKit/Sources/OrcaSwapSwift/Models/Pool.swift b/Packages/KeyAppKit/Sources/OrcaSwapSwift/Models/Pool.swift index 99a5c41e63..3a6d5960c0 100644 --- a/Packages/KeyAppKit/Sources/OrcaSwapSwift/Models/Pool.swift +++ b/Packages/KeyAppKit/Sources/OrcaSwapSwift/Models/Pool.swift @@ -270,6 +270,7 @@ extension Pool { destinationAccountInstructions = try await blockchainClient.prepareForCreatingAssociatedTokenAccount( owner: owner, mint: toMint, + tokenProgramId: TokenProgram.id, feePayer: feePayer ?? owner, closeAfterward: false ) diff --git a/Packages/KeyAppKit/Sources/Send/Action/SendAction.swift b/Packages/KeyAppKit/Sources/Send/Action/SendAction.swift index ec6d85738e..8a0cb2109c 100644 --- a/Packages/KeyAppKit/Sources/Send/Action/SendAction.swift +++ b/Packages/KeyAppKit/Sources/Send/Action/SendAction.swift @@ -61,6 +61,7 @@ public class SendActionServiceImpl: SendActionService { from: wallet, receiver: receiver, amount: amount, + minRentExemption: wallet.minRentExemption ?? 2_039_280, feeWallet: feeWallet, ignoreTopUp: ignoreTopUp, memo: memo, @@ -72,6 +73,7 @@ public class SendActionServiceImpl: SendActionService { from wallet: SolanaAccount, receiver: String, amount: Lamports, + minRentExemption: Lamports, feeWallet: SolanaAccount?, ignoreTopUp: Bool = false, memo: String?, @@ -89,6 +91,7 @@ public class SendActionServiceImpl: SendActionService { receiver: receiver, amount: amount.convertToBalance(decimals: wallet.token.decimals), payingFeeToken: payingFeeToken, + minRentExemption: minRentExemption, memo: memo ) @@ -155,7 +158,7 @@ public class SendActionServiceImpl: SendActionService { payingFeeToken: FeeRelayerSwift.TokenAccount?, recentBlockhash: String? = nil, lamportsPerSignature _: Lamports? = nil, - minRentExemption: Lamports? = nil, + minRentExemption: Lamports, memo: String? ) async throws -> (preparedTransaction: PreparedTransaction, useFeeRelayer: Bool) { let amount = amount.toLamport(decimals: wallet.token.decimals) @@ -191,6 +194,7 @@ public class SendActionServiceImpl: SendActionService { preparedTransaction = try await blockchainClient.prepareSendingSPLTokens( account: account, mintAddress: wallet.token.mintAddress, + tokenProgramId: TokenProgram.id, decimals: wallet.token.decimals, from: sender, to: receiver, diff --git a/Packages/KeyAppKit/Sources/Send/Input/SendInputBusinessLogic+Initializing.swift b/Packages/KeyAppKit/Sources/Send/Input/SendInputBusinessLogic+Initializing.swift index 3668dfde1d..1fc28963f8 100644 --- a/Packages/KeyAppKit/Sources/Send/Input/SendInputBusinessLogic+Initializing.swift +++ b/Packages/KeyAppKit/Sources/Send/Input/SendInputBusinessLogic+Initializing.swift @@ -17,7 +17,7 @@ extension SendInputBusinessLogic { recipientAdditionalInfo = try await .init( walletAccount: services.solanaAPIClient.getAccountInfo(account: state.recipient.address), splAccounts: services.solanaAPIClient - .getTokenAccountsByOwner( + .getTokenAccountsByOwnerWithToken2022( pubkey: state.recipient.address, params: .init( mint: nil, @@ -30,7 +30,7 @@ extension SendInputBusinessLogic { recipientAdditionalInfo = try await .init( walletAccount: services.solanaAPIClient.getAccountInfo(account: walletAddress.base58EncodedString), splAccounts: services.solanaAPIClient - .getTokenAccountsByOwner( + .getTokenAccountsByOwnerWithToken2022( pubkey: walletAddress.base58EncodedString, params: .init( mint: nil, diff --git a/Packages/KeyAppKit/Sources/Send/Input/Services/SendFeeCalculator.swift b/Packages/KeyAppKit/Sources/Send/Input/Services/SendFeeCalculator.swift index 8577a60778..28d65ec734 100644 --- a/Packages/KeyAppKit/Sources/Send/Input/Services/SendFeeCalculator.swift +++ b/Packages/KeyAppKit/Sources/Send/Input/Services/SendFeeCalculator.swift @@ -47,7 +47,8 @@ public class SendFeeCalculatorImpl: SendFeeCalculator { case let .solanaTokenAddress(walletAddress, _): let associatedAccount = try PublicKey.associatedTokenAddress( walletAddress: walletAddress, - tokenMintAddress: PublicKey(string: token.mintAddress) + tokenMintAddress: PublicKey(string: token.mintAddress), + tokenProgramId: PublicKey(string: token.tokenProgramId) ) isAssociatedTokenUnregister = !recipientAdditionalInfo.splAccounts @@ -55,7 +56,8 @@ public class SendFeeCalculatorImpl: SendFeeCalculator { case .solanaAddress, .username: let associatedAccount = try PublicKey.associatedTokenAddress( walletAddress: PublicKey(string: recipient.address), - tokenMintAddress: PublicKey(string: token.mintAddress) + tokenMintAddress: PublicKey(string: token.mintAddress), + tokenProgramId: PublicKey(string: token.tokenProgramId) ) isAssociatedTokenUnregister = !recipientAdditionalInfo.splAccounts diff --git a/Packages/KeyAppKit/Sources/Send/RecipientSearch/RecipientSearchServiceImpl/RecipientSearchServiceImpl+searchBySolanaAddress.swift b/Packages/KeyAppKit/Sources/Send/RecipientSearch/RecipientSearchServiceImpl/RecipientSearchServiceImpl+searchBySolanaAddress.swift index 5e76405dae..e69ce3201f 100644 --- a/Packages/KeyAppKit/Sources/Send/RecipientSearch/RecipientSearchServiceImpl/RecipientSearchServiceImpl+searchBySolanaAddress.swift +++ b/Packages/KeyAppKit/Sources/Send/RecipientSearch/RecipientSearchServiceImpl/RecipientSearchServiceImpl+searchBySolanaAddress.swift @@ -88,29 +88,29 @@ extension RecipientSearchServiceImpl { } } } else { - let splAccounts = try await solanaClient.getTokenAccountsByOwner( - pubkey: address.base58EncodedString, - params: .init( - mint: nil, - programId: TokenProgram.id.base58EncodedString - ), - configs: .init(encoding: "base64") - ) - - if splAccounts.isEmpty { - // This account doesn't exits in blockchain - return .ok([.init( - address: addressBase58, - category: .solanaAddress, - attributes: [.funds, attributes] - )]) - } else { - return .ok([.init( - address: addressBase58, - category: .solanaAddress, - attributes: [.funds, attributes] - )]) - } +// let splAccounts = try await solanaClient.getTokenAccountsByOwner( +// pubkey: address.base58EncodedString, +// params: .init( +// mint: nil, +// programId: TokenProgram.id.base58EncodedString +// ), +// configs: .init(encoding: "base64") +// ) +// +// if splAccounts.isEmpty { +// // This account doesn't exits in blockchain +// return .ok([.init( +// address: addressBase58, +// category: .solanaAddress, +// attributes: [.funds, attributes] +// )]) +// } else { + return .ok([.init( + address: addressBase58, + category: .solanaAddress, + attributes: [.funds, attributes] + )]) +// } } } catch let error as SolanaSwift.APIClientError { return handleSolanaAPIClientError(error) diff --git a/Packages/KeyAppKit/Sources/Send/RecipientSearch/RecipientSearchServiceImpl/RecipientSearchServiceImpl.swift b/Packages/KeyAppKit/Sources/Send/RecipientSearch/RecipientSearchServiceImpl/RecipientSearchServiceImpl.swift index 8f48146012..f5803e946f 100644 --- a/Packages/KeyAppKit/Sources/Send/RecipientSearch/RecipientSearchServiceImpl/RecipientSearchServiceImpl.swift +++ b/Packages/KeyAppKit/Sources/Send/RecipientSearch/RecipientSearchServiceImpl/RecipientSearchServiceImpl.swift @@ -8,7 +8,11 @@ public class RecipientSearchServiceImpl: RecipientSearchService { let solanaClient: SolanaAPIClient let swapService: SwapService - public init(nameService: NameService, solanaClient: SolanaAPIClient, swapService: SwapService) { + public init( + nameService: NameService, + solanaClient: SolanaAPIClient, + swapService: SwapService + ) { self.nameService = nameService self.solanaClient = solanaClient self.swapService = swapService diff --git a/Packages/KeyAppKit/Sources/Send/SendViaLink/SendViaLinkDataService.swift b/Packages/KeyAppKit/Sources/Send/SendViaLink/SendViaLinkDataService.swift index b176be09a6..67c79c1528 100644 --- a/Packages/KeyAppKit/Sources/Send/SendViaLink/SendViaLinkDataService.swift +++ b/Packages/KeyAppKit/Sources/Send/SendViaLink/SendViaLinkDataService.swift @@ -351,7 +351,7 @@ public final class SendViaLinkDataServiceImpl: SendViaLinkDataService { } // 2. Get token accounts by owner - let tokenAccounts = try await solanaAPIClient.getTokenAccountsByOwner( + let tokenAccounts = try await solanaAPIClient.getTokenAccountsByOwnerWithToken2022( pubkey: keypair.publicKey.base58EncodedString, params: .init( mint: nil, @@ -442,7 +442,8 @@ public final class SendViaLinkDataServiceImpl: SendViaLinkDataService { // get associated token address let splDestination = try await solanaAPIClient.findSPLTokenDestinationAddress( mintAddress: mintAddress.base58EncodedString, - destinationAddress: receiver.base58EncodedString + destinationAddress: receiver.base58EncodedString, + tokenProgramId: TokenProgram.id ) // form instruction @@ -456,7 +457,8 @@ public final class SendViaLinkDataServiceImpl: SendViaLinkDataService { .createAssociatedTokenAccountInstruction( mint: mintAddress, owner: receiver, - payer: feePayer + payer: feePayer, + tokenProgramId: TokenProgram.id ) ) accountsCreationFee += minRentExemption diff --git a/Packages/KeyAppKit/Sources/Send/SolanaAPIClient+Token2022.swift b/Packages/KeyAppKit/Sources/Send/SolanaAPIClient+Token2022.swift new file mode 100644 index 0000000000..5b1fbd0e51 --- /dev/null +++ b/Packages/KeyAppKit/Sources/Send/SolanaAPIClient+Token2022.swift @@ -0,0 +1,27 @@ +import Foundation +import SolanaSwift + +extension SolanaAPIClient { + func getTokenAccountsByOwnerWithToken2022( + pubkey: String, + params: OwnerInfoParams?, + configs: RequestConfiguration? + ) async throws -> [TokenAccount] + { // Temporarily convert all state into basic SPLTokenAccountState layout + async let classicTokenAccounts = getTokenAccountsByOwner( + pubkey: pubkey, + params: params, + configs: configs, + decodingTo: SPLTokenAccountState.self + ) + + async let token2022Accounts = getTokenAccountsByOwner( + pubkey: pubkey, + params: params, + configs: configs, + decodingTo: SPLTokenAccountState.self + ) + + return try await classicTokenAccounts + token2022Accounts + } +} From 98ced4a79f274930ff2cea951b7d1c9ac027bdb3 Mon Sep 17 00:00:00 2001 From: Chung Tran Date: Tue, 9 Jan 2024 18:06:51 +0700 Subject: [PATCH 14/32] fix: send --- Packages/KeyAppKit/Sources/Send/Action/SendAction.swift | 8 ++------ .../xcshareddata/swiftpm/Package.resolved | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Packages/KeyAppKit/Sources/Send/Action/SendAction.swift b/Packages/KeyAppKit/Sources/Send/Action/SendAction.swift index 8a0cb2109c..425c74abaf 100644 --- a/Packages/KeyAppKit/Sources/Send/Action/SendAction.swift +++ b/Packages/KeyAppKit/Sources/Send/Action/SendAction.swift @@ -61,7 +61,6 @@ public class SendActionServiceImpl: SendActionService { from: wallet, receiver: receiver, amount: amount, - minRentExemption: wallet.minRentExemption ?? 2_039_280, feeWallet: feeWallet, ignoreTopUp: ignoreTopUp, memo: memo, @@ -73,7 +72,6 @@ public class SendActionServiceImpl: SendActionService { from wallet: SolanaAccount, receiver: String, amount: Lamports, - minRentExemption: Lamports, feeWallet: SolanaAccount?, ignoreTopUp: Bool = false, memo: String?, @@ -91,7 +89,6 @@ public class SendActionServiceImpl: SendActionService { receiver: receiver, amount: amount.convertToBalance(decimals: wallet.token.decimals), payingFeeToken: payingFeeToken, - minRentExemption: minRentExemption, memo: memo ) @@ -158,7 +155,6 @@ public class SendActionServiceImpl: SendActionService { payingFeeToken: FeeRelayerSwift.TokenAccount?, recentBlockhash: String? = nil, lamportsPerSignature _: Lamports? = nil, - minRentExemption: Lamports, memo: String? ) async throws -> (preparedTransaction: PreparedTransaction, useFeeRelayer: Bool) { let amount = amount.toLamport(decimals: wallet.token.decimals) @@ -194,14 +190,14 @@ public class SendActionServiceImpl: SendActionService { preparedTransaction = try await blockchainClient.prepareSendingSPLTokens( account: account, mintAddress: wallet.token.mintAddress, - tokenProgramId: TokenProgram.id, + tokenProgramId: PublicKey(string: wallet.tokenProgramId), decimals: wallet.token.decimals, from: sender, to: receiver, amount: amount, feePayer: feePayer, transferChecked: useFeeRelayer, // create transferChecked instruction when using fee relayer - minRentExemption: minRentExemption + minRentExemption: wallet.minRentExemption ?? 2_039_280 ).preparedTransaction } diff --git a/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 080d25c498..57ad3a9382 100644 --- a/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -312,7 +312,7 @@ "location" : "https://github.com/p2p-org/solana-swift", "state" : { "branch" : "feature/token-2022", - "revision" : "8babbb7253af8cd6916f9a7861b98176e7fbb7f0" + "revision" : "80c8a71df5906444e2aefb31fd99fd35056c91e5" } }, { From 637c4694b66a41baddaaa66088a14f93a23020cc Mon Sep 17 00:00:00 2001 From: Elizaveta Semenova Date: Tue, 9 Jan 2024 15:53:27 +0300 Subject: [PATCH 15/32] [ETH-682] Return address to the main screen --- .../xcshareddata/swiftpm/Package.resolved | 8 ++-- .../Resources/en.lproj/Localizable.strings | 1 - .../Crypto Pending/CryptoPendingView.swift | 28 +++++++------ .../Crypto/Container/CryptoCoordinator.swift | 1 - .../Main/Crypto/Container/CryptoView.swift | 23 +++++++++++ .../Crypto/Container/CryptoViewModel.swift | 39 ++++++++++++++++++- 6 files changed, 77 insertions(+), 23 deletions(-) diff --git a/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 0464bb5191..f7c98deee6 100644 --- a/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -392,8 +392,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-snapshot-testing", "state" : { - "revision" : "59b663f68e69f27a87b45de48cb63264b8194605", - "version" : "1.15.1" + "revision" : "8e68404f641300bfd0e37d478683bb275926760c", + "version" : "1.15.2" } }, { @@ -401,8 +401,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-syntax.git", "state" : { - "revision" : "6ad4ea24b01559dde0773e3d091f1b9e36175036", - "version" : "509.0.2" + "revision" : "43c802fb7f96e090dde015344a94b5e85779eff1", + "version" : "509.1.0" } }, { diff --git a/p2p_wallet/Resources/en.lproj/Localizable.strings b/p2p_wallet/Resources/en.lproj/Localizable.strings index 696734e37e..14da62ca87 100644 --- a/p2p_wallet/Resources/en.lproj/Localizable.strings +++ b/p2p_wallet/Resources/en.lproj/Localizable.strings @@ -245,7 +245,6 @@ "Multi-factor authentication" = "Multi-factor authentication"; "My Ethereum address" = "My Ethereum address"; "My Solana address" = "My Solana address"; -"My crypto" = "My crypto"; "My username" = "My username"; "Name was booked" = "Name was booked"; "Network" = "Network"; diff --git a/p2p_wallet/Scenes/Main/Crypto/Components/Crypto Pending/CryptoPendingView.swift b/p2p_wallet/Scenes/Main/Crypto/Components/Crypto Pending/CryptoPendingView.swift index 61c08dbeb8..03e0b0d96b 100644 --- a/p2p_wallet/Scenes/Main/Crypto/Components/Crypto Pending/CryptoPendingView.swift +++ b/p2p_wallet/Scenes/Main/Crypto/Components/Crypto Pending/CryptoPendingView.swift @@ -4,22 +4,20 @@ import SwiftUI /// View of `CryptoPending` scene struct CryptoPendingView: View { var body: some View { - NavigationView { - content - .padding(.top, 11) - .navigationBarTitleDisplayMode(.inline) - .navigationViewStyle(StackNavigationViewStyle()) - .toolbar { - ToolbarItem(placement: .principal) { - Text("") - .skeleton( - with: true, - size: CGSize(width: 164, height: 40), - animated: .default - ) - } + content + .padding(.top, 11) + .navigationBarTitleDisplayMode(.inline) + .navigationViewStyle(StackNavigationViewStyle()) + .toolbar { + ToolbarItem(placement: .principal) { + Text("") + .skeleton( + with: true, + size: CGSize(width: 164, height: 40), + animated: .default + ) } - } + } } private var content: some View { diff --git a/p2p_wallet/Scenes/Main/Crypto/Container/CryptoCoordinator.swift b/p2p_wallet/Scenes/Main/Crypto/Container/CryptoCoordinator.swift index 0ff1d39a3f..92070aac7a 100644 --- a/p2p_wallet/Scenes/Main/Crypto/Container/CryptoCoordinator.swift +++ b/p2p_wallet/Scenes/Main/Crypto/Container/CryptoCoordinator.swift @@ -63,7 +63,6 @@ final class CryptoCoordinator: Coordinator { accountsViewModel: accountsViewModel ) let cryptoVC = UIHostingController(rootView: cryptoView) - cryptoVC.title = L10n.myCrypto navigationController.setViewControllers([cryptoVC], animated: false) // handle navigation diff --git a/p2p_wallet/Scenes/Main/Crypto/Container/CryptoView.swift b/p2p_wallet/Scenes/Main/Crypto/Container/CryptoView.swift index e631b19b25..dac148f331 100644 --- a/p2p_wallet/Scenes/Main/Crypto/Container/CryptoView.swift +++ b/p2p_wallet/Scenes/Main/Crypto/Container/CryptoView.swift @@ -46,6 +46,29 @@ struct CryptoView: View { ) } } + .if(viewModel.state != .pending, transform: { view in + view.toolbar { + ToolbarItem(placement: .principal) { + Button( + action: { + viewModel.copyToClipboard() + }, + label: { + ZStack { + Color(.snow) + .cornerRadius(80) + Text("🔗 \(viewModel.address)") + .fontWeight(.semibold) + .apply(style: .text3) + .foregroundColor(Color(.mountain)) + .padding(.horizontal, 18) + .padding(.vertical, 12) + } + } + ) + } + } + }) .onAppear { viewModel.viewAppeared() } diff --git a/p2p_wallet/Scenes/Main/Crypto/Container/CryptoViewModel.swift b/p2p_wallet/Scenes/Main/Crypto/Container/CryptoViewModel.swift index 3f040deb9f..63a0adb208 100644 --- a/p2p_wallet/Scenes/Main/Crypto/Container/CryptoViewModel.swift +++ b/p2p_wallet/Scenes/Main/Crypto/Container/CryptoViewModel.swift @@ -22,12 +22,14 @@ final class CryptoViewModel: BaseViewModel, ObservableObject { @Injected private var accountStorage: SolanaAccountStorage @Injected private var nameStorage: NameStorageType @Injected private var sellDataService: any SellDataService + @Injected private var createNameService: CreateNameService let navigation: PassthroughSubject // MARK: - Properties @Published var state = State.pending + @Published var address = "" // MARK: - Initializers @@ -52,9 +54,30 @@ final class CryptoViewModel: BaseViewModel, ObservableObject { if available(.solanaNegativeStatus) { solanaTracker.startTracking() } - + updateAddressIfNeeded() analyticsManager.log(event: .cryptoScreenOpened) } + + func copyToClipboard() { + clipboardManager + .copyToClipboard(nameStorage.getName() ?? solanaAccountsService.state.value.nativeWallet?.address ?? "") + let text: String + if nameStorage.getName() != nil { + text = L10n.usernameCopiedToClipboard + } else { + text = L10n.addressCopiedToClipboard + } + notificationsService.showToast(title: "", text: text, haptic: true) + analyticsManager.log(event: .mainScreenAddressClick) + } + + func updateAddressIfNeeded() { + if let name = nameStorage.getName(), !name.isEmpty { + address = name + } else if let address = accountStorage.account?.publicKey.base58EncodedString.shortAddress { + self.address = address + } + } } private extension CryptoViewModel { @@ -109,7 +132,7 @@ private extension CryptoViewModel { } .store(in: &subscriptions) - // state, error, log + // state, address, error, log Publishers .CombineLatest(solanaAccountsService.statePublisher, ethereumAccountsService.statePublisher) @@ -121,6 +144,9 @@ private extension CryptoViewModel { partialResult = partialResult + account.amountInFiatDouble } + // TODO: Bad place + self.updateAddressIfNeeded() + let hasAnyTokenWithPositiveBalance = solanaState.value.contains(where: { $0.lamports > 0 }) || ethereumState.value.contains(where: { $0.balance > 0 }) @@ -140,6 +166,15 @@ private extension CryptoViewModel { } } .store(in: &subscriptions) + + // update name when needed + createNameService.createNameResult + .receive(on: DispatchQueue.main) + .sink { [weak self] isSuccess in + guard isSuccess else { return } + self?.updateAddressIfNeeded() + } + .store(in: &subscriptions) } } From b88ce906cbb9f6a2b9d4a58f26070faa45b4234c Mon Sep 17 00:00:00 2001 From: Elizaveta Semenova Date: Tue, 9 Jan 2024 16:00:11 +0300 Subject: [PATCH 16/32] [ETH-682] Six symbols for prefix/suffix address --- p2p_wallet/Scenes/Main/Crypto/Container/CryptoViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p2p_wallet/Scenes/Main/Crypto/Container/CryptoViewModel.swift b/p2p_wallet/Scenes/Main/Crypto/Container/CryptoViewModel.swift index 63a0adb208..1af470d5ed 100644 --- a/p2p_wallet/Scenes/Main/Crypto/Container/CryptoViewModel.swift +++ b/p2p_wallet/Scenes/Main/Crypto/Container/CryptoViewModel.swift @@ -192,6 +192,6 @@ extension CryptoViewModel { private extension String { var shortAddress: String { - "\(prefix(4))...\(suffix(4))" + "\(prefix(6))...\(suffix(6))" } } From 6f47d6bb4b6535b467a27fc7121216b1b775ac58 Mon Sep 17 00:00:00 2001 From: Elizaveta Semenova Date: Tue, 9 Jan 2024 16:59:58 +0300 Subject: [PATCH 17/32] [ETH-677] Remove tap to switch for all tokens and remake view a bit --- .../Crypto/Container/CryptoCoordinator.swift | 3 +-- .../History/New/NewHistoryCoordinator.swift | 3 +-- .../Scenes/Main/NewHome/HomeCoordinator.swift | 3 +-- .../Sell/Pending/SellPendingCoordinator.swift | 3 +-- .../Send/Input/SendInputCoordinator.swift | 4 --- .../Main/Send/Input/SendInputView.swift | 7 +---- .../Main/Send/Input/SendInputViewModel.swift | 25 ------------------ .../AmountView/SendInputAmountViewModel.swift | 4 +-- .../Main/Send/Send\320\241oordinator.swift" | 12 +++------ .../New/AccountDetailsCoordinator.swift | 3 +-- .../SendSubviews/SendInputAmountView.swift | 26 +++++++++---------- 11 files changed, 24 insertions(+), 69 deletions(-) diff --git a/p2p_wallet/Scenes/Main/Crypto/Container/CryptoCoordinator.swift b/p2p_wallet/Scenes/Main/Crypto/Container/CryptoCoordinator.swift index 0ff1d39a3f..aa7a7c9a61 100644 --- a/p2p_wallet/Scenes/Main/Crypto/Container/CryptoCoordinator.swift +++ b/p2p_wallet/Scenes/Main/Crypto/Container/CryptoCoordinator.swift @@ -105,8 +105,7 @@ final class CryptoCoordinator: Coordinator { to: SendCoordinator( rootViewController: navigationController, preChosenWallet: nil, - hideTabBar: true, - allowSwitchingMainAmountType: true + hideTabBar: true ) ) .receive(on: RunLoop.main) diff --git a/p2p_wallet/Scenes/Main/History/New/NewHistoryCoordinator.swift b/p2p_wallet/Scenes/Main/History/New/NewHistoryCoordinator.swift index a4f7c26c7c..4ac560c4d3 100644 --- a/p2p_wallet/Scenes/Main/History/New/NewHistoryCoordinator.swift +++ b/p2p_wallet/Scenes/Main/History/New/NewHistoryCoordinator.swift @@ -154,8 +154,7 @@ class NewHistoryCoordinator: SmartCoordinator { category: .solanaAddress, attributes: [.funds] ), - preChosenAmount: transaction.baseCurrencyAmount, - allowSwitchingMainAmountType: false + preChosenAmount: transaction.baseCurrencyAmount )) .sink { _ in } .store(in: &subscriptions) diff --git a/p2p_wallet/Scenes/Main/NewHome/HomeCoordinator.swift b/p2p_wallet/Scenes/Main/NewHome/HomeCoordinator.swift index 336d7976c3..6e2088096c 100644 --- a/p2p_wallet/Scenes/Main/NewHome/HomeCoordinator.swift +++ b/p2p_wallet/Scenes/Main/NewHome/HomeCoordinator.swift @@ -128,8 +128,7 @@ final class HomeCoordinator: Coordinator { to: SendCoordinator( rootViewController: navigationController, preChosenWallet: nil, - hideTabBar: true, - allowSwitchingMainAmountType: true + hideTabBar: true ) ) .receive(on: RunLoop.main) diff --git a/p2p_wallet/Scenes/Main/Sell/Pending/SellPendingCoordinator.swift b/p2p_wallet/Scenes/Main/Sell/Pending/SellPendingCoordinator.swift index 3a27fed7cf..1a75390f4d 100644 --- a/p2p_wallet/Scenes/Main/Sell/Pending/SellPendingCoordinator.swift +++ b/p2p_wallet/Scenes/Main/Sell/Pending/SellPendingCoordinator.swift @@ -88,8 +88,7 @@ final class SellPendingCoordinator: Coordinator { ), preChosenAmount: transaction.baseCurrencyAmount, hideTabBar: true, - flow: .sell, - allowSwitchingMainAmountType: false + flow: .sell )) } .sink { [weak self] res in diff --git a/p2p_wallet/Scenes/Main/Send/Input/SendInputCoordinator.swift b/p2p_wallet/Scenes/Main/Send/Input/SendInputCoordinator.swift index 6735c50ba9..754d45cb0f 100644 --- a/p2p_wallet/Scenes/Main/Send/Input/SendInputCoordinator.swift +++ b/p2p_wallet/Scenes/Main/Send/Input/SendInputCoordinator.swift @@ -13,7 +13,6 @@ final class SendInputCoordinator: Coordinator { private var subject = PassthroughSubject() private let flow: SendFlow private let pushedWithoutRecipientSearchView: Bool - private let allowSwitchingMainAmountType: Bool private let sendViaLinkSeed: String? @@ -24,7 +23,6 @@ final class SendInputCoordinator: Coordinator { navigationController: UINavigationController, flow: SendFlow, pushedWithoutRecipientSearchView: Bool = false, - allowSwitchingMainAmountType: Bool, sendViaLinkSeed: String? = nil ) { self.recipient = recipient @@ -33,7 +31,6 @@ final class SendInputCoordinator: Coordinator { self.navigationController = navigationController self.flow = flow self.pushedWithoutRecipientSearchView = pushedWithoutRecipientSearchView - self.allowSwitchingMainAmountType = allowSwitchingMainAmountType self.sendViaLinkSeed = sendViaLinkSeed } @@ -43,7 +40,6 @@ final class SendInputCoordinator: Coordinator { preChosenWallet: preChosenWallet, preChosenAmount: preChosenAmount, flow: flow, - allowSwitchingMainAmountType: allowSwitchingMainAmountType, sendViaLinkSeed: sendViaLinkSeed ) let view = SendInputView(viewModel: viewModel) diff --git a/p2p_wallet/Scenes/Main/Send/Input/SendInputView.swift b/p2p_wallet/Scenes/Main/Send/Input/SendInputView.swift index 6776c664b1..65ba0939b5 100644 --- a/p2p_wallet/Scenes/Main/Send/Input/SendInputView.swift +++ b/p2p_wallet/Scenes/Main/Send/Input/SendInputView.swift @@ -176,12 +176,8 @@ struct SendInputView: View { animated: .default ) } - Image(.arrowUpDown) - .renderingMode(.template) - .foregroundColor(Color(.rain)) - .frame(width: 16, height: 16) } - .padding(EdgeInsets(top: 21, leading: 24, bottom: 21, trailing: 12)) + .padding(EdgeInsets(top: 21, leading: 24, bottom: 21, trailing: 24)) .background(RoundedRectangle(cornerRadius: 12).fill(Color(.snow))) .frame(height: 90) } @@ -246,7 +242,6 @@ struct SendInputView_Previews: PreviewProvider { preChosenWallet: nil, preChosenAmount: nil, flow: .send, - allowSwitchingMainAmountType: false, sendViaLinkSeed: nil ) ) diff --git a/p2p_wallet/Scenes/Main/Send/Input/SendInputViewModel.swift b/p2p_wallet/Scenes/Main/Send/Input/SendInputViewModel.swift index 82684024f1..29434b84b9 100644 --- a/p2p_wallet/Scenes/Main/Send/Input/SendInputViewModel.swift +++ b/p2p_wallet/Scenes/Main/Send/Input/SendInputViewModel.swift @@ -87,7 +87,6 @@ final class SendInputViewModel: BaseViewModel, ObservableObject { private let flow: SendFlow private var wasMaxWarningToastShown: Bool = false private let preChosenAmount: Double? - private let allowSwitchingMainAmountType: Bool // MARK: - Dependencies @@ -98,12 +97,10 @@ final class SendInputViewModel: BaseViewModel, ObservableObject { preChosenWallet: SolanaAccount?, preChosenAmount: Double?, flow: SendFlow, - allowSwitchingMainAmountType: Bool, sendViaLinkSeed: String? ) { self.flow = flow self.preChosenAmount = preChosenAmount - self.allowSwitchingMainAmountType = allowSwitchingMainAmountType let repository = Resolver.resolve(SolanaAccountsService.self) let wallets = repository.getWallets() @@ -398,32 +395,10 @@ private extension SendInputViewModel { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { self?.openKeyboard() } } .store(in: &subscriptions) - - $sourceWallet.eraseToAnyPublisher() - .sink { [weak self] currentWallet in - guard let self else { return } - if currentWallet.price == nil { - self.turnOffInputSwitch() - } -// else if -// currentWallet.isUsdcOrUsdt, currentWallet.price?.value == 1.0 -// { -// self.turnOffInputSwitch() -// } - else { - self.inputAmountViewModel.isSwitchAvailable = self.allowSwitchingMainAmountType - } - } - .store(in: &subscriptions) } } private extension SendInputViewModel { - func turnOffInputSwitch() { - inputAmountViewModel.mainAmountType = .token - inputAmountViewModel.isSwitchAvailable = false - } - func updateInputAmountView() { guard currentState.amountInToken != .zero else { inputAmountViewModel.isError = false diff --git a/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountViewModel.swift b/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountViewModel.swift index 0a8953515e..c6b50942d4 100644 --- a/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountViewModel.swift +++ b/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountViewModel.swift @@ -42,7 +42,7 @@ final class SendInputAmountViewModel: BaseViewModel, ObservableObject { @Published var amountText: String = "" @Published var amountTextColor: UIColor = .init(resource: .night) @Published var mainTokenText = "" - @Published var mainAmountType: EnteredAmountType = Defaults.isTokenInputTypeChosen ? .token : .fiat + @Published var mainAmountType: EnteredAmountType = .token // Can't switch, can't remember Defaults.isTokenInputTypeChosen ? .token : .fiat @Published var isMaxButtonVisible: Bool = true @Published var secondaryAmountText = "" @@ -53,7 +53,7 @@ final class SendInputAmountViewModel: BaseViewModel, ObservableObject { @Published var amount: Amount? @Published var isError: Bool = false @Published var countAfterDecimalPoint: Int - @Published var isSwitchAvailable: Bool = true + @Published var isSwitchAvailable = false // Switch is cancelled private let fiat: Fiat = Defaults.fiat private var currentText: String? diff --git "a/p2p_wallet/Scenes/Main/Send/Send\320\241oordinator.swift" "b/p2p_wallet/Scenes/Main/Send/Send\320\241oordinator.swift" index 703f871d3b..268fd4afc0 100644 --- "a/p2p_wallet/Scenes/Main/Send/Send\320\241oordinator.swift" +++ "b/p2p_wallet/Scenes/Main/Send/Send\320\241oordinator.swift" @@ -37,7 +37,6 @@ final class SendCoordinator: Coordinator { let preChosenWallet: SolanaAccount? let preChosenRecipient: Recipient? let preChosenAmount: Double? - let allowSwitchingMainAmountType: Bool // MARK: - Initializer @@ -47,8 +46,7 @@ final class SendCoordinator: Coordinator { preChosenRecipient: Recipient? = nil, preChosenAmount: Double? = nil, hideTabBar: Bool = false, - flow: SendFlow = .send, - allowSwitchingMainAmountType: Bool + flow: SendFlow = .send ) { self.rootViewController = rootViewController self.preChosenWallet = preChosenWallet @@ -56,7 +54,6 @@ final class SendCoordinator: Coordinator { self.preChosenAmount = preChosenAmount self.hideTabBar = hideTabBar self.flow = flow - self.allowSwitchingMainAmountType = allowSwitchingMainAmountType super.init() } @@ -91,8 +88,7 @@ final class SendCoordinator: Coordinator { preChosenAmount: preChosenAmount, navigationController: rootViewController, flow: flow, - pushedWithoutRecipientSearchView: true, - allowSwitchingMainAmountType: allowSwitchingMainAmountType + pushedWithoutRecipientSearchView: true )) .sink { [weak self] result in switch result { @@ -120,8 +116,7 @@ final class SendCoordinator: Coordinator { preChosenWallet: preChosenWallet, preChosenAmount: preChosenAmount, navigationController: rootViewController, - flow: flow, - allowSwitchingMainAmountType: allowSwitchingMainAmountType + flow: flow )) } .sink { [weak self] result in @@ -228,7 +223,6 @@ final class SendCoordinator: Coordinator { preChosenAmount: preChosenAmount, navigationController: rootViewController, flow: .sendViaLink, - allowSwitchingMainAmountType: true, sendViaLinkSeed: seed )) .sink { [weak self] result in diff --git a/p2p_wallet/Scenes/Main/WalletDetail/New/AccountDetailsCoordinator.swift b/p2p_wallet/Scenes/Main/WalletDetail/New/AccountDetailsCoordinator.swift index caac9e3731..5366859df4 100644 --- a/p2p_wallet/Scenes/Main/WalletDetail/New/AccountDetailsCoordinator.swift +++ b/p2p_wallet/Scenes/Main/WalletDetail/New/AccountDetailsCoordinator.swift @@ -274,8 +274,7 @@ class AccountDetailsCoordinator: SmartCoordinator Date: Wed, 10 Jan 2024 14:01:00 +0300 Subject: [PATCH 18/32] [ETH-700] Add non strict token icon on SwapInputView --- .../Main/Swap/ChooseSwapItem/ChooseSwapTokenCoordinator.swift | 2 +- p2p_wallet/Scenes/Main/Swap/Common/Models/SwapToken.swift | 3 +++ .../Scenes/Main/Swap/Swap/Subviews/Input/SwapInputView.swift | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/p2p_wallet/Scenes/Main/Swap/ChooseSwapItem/ChooseSwapTokenCoordinator.swift b/p2p_wallet/Scenes/Main/Swap/ChooseSwapItem/ChooseSwapTokenCoordinator.swift index 204570af0c..772f1f99aa 100644 --- a/p2p_wallet/Scenes/Main/Swap/ChooseSwapItem/ChooseSwapTokenCoordinator.swift +++ b/p2p_wallet/Scenes/Main/Swap/ChooseSwapItem/ChooseSwapTokenCoordinator.swift @@ -63,7 +63,7 @@ final class ChooseSwapTokenCoordinator: Coordinator { private func openNonStrictTokenConfirmationIfNeededOrClose( token: SwapToken? ) { - if token?.token.tags.map(\.name).contains("unknown") == true { + if token?.isNonStrict == true { nonStrictTokenAlertVC = UIBottomSheetHostingController( rootView: NonStrictTokenConfirmationView( token: token diff --git a/p2p_wallet/Scenes/Main/Swap/Common/Models/SwapToken.swift b/p2p_wallet/Scenes/Main/Swap/Common/Models/SwapToken.swift index d3af347f01..968a99fe23 100644 --- a/p2p_wallet/Scenes/Main/Swap/Common/Models/SwapToken.swift +++ b/p2p_wallet/Scenes/Main/Swap/Common/Models/SwapToken.swift @@ -6,6 +6,9 @@ struct SwapToken: Equatable { let token: TokenMetadata let userWallet: SolanaAccount? var mintAddress: String { token.mintAddress } + var isNonStrict: Bool { + token.tags.map(\.name).contains("unknown") == true + } } extension SwapToken { diff --git a/p2p_wallet/Scenes/Main/Swap/Swap/Subviews/Input/SwapInputView.swift b/p2p_wallet/Scenes/Main/Swap/Swap/Subviews/Input/SwapInputView.swift index f713eb9dba..9d187afe0c 100644 --- a/p2p_wallet/Scenes/Main/Swap/Swap/Subviews/Input/SwapInputView.swift +++ b/p2p_wallet/Scenes/Main/Swap/Swap/Subviews/Input/SwapInputView.swift @@ -68,7 +68,7 @@ private extension SwapInputView { viewModel.changeTokenPressed.send() } label: { HStack { - Text(viewModel.tokenSymbol) + Text("\(viewModel.tokenSymbol) \(viewModel.token.isNonStrict ? "⚠" : "")") .apply(style: .title1) .foregroundColor(Color(.night)) .if(viewModel.isLoading) { view in From 48a12350e1c2f4caa11debf390ad30d1b7111da7 Mon Sep 17 00:00:00 2001 From: Chung Tran Date: Thu, 11 Jan 2024 11:23:17 +0700 Subject: [PATCH 19/32] fix: return amount to token 2022 --- .../KeyAppKit/Sources/Send/Action/SendAction.swift | 12 +++++++++--- .../SendInputBusinessLogic+ChangeTokenFee.swift | 2 +- .../xcshareddata/swiftpm/Package.resolved | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Packages/KeyAppKit/Sources/Send/Action/SendAction.swift b/Packages/KeyAppKit/Sources/Send/Action/SendAction.swift index 425c74abaf..8bd46a6a3b 100644 --- a/Packages/KeyAppKit/Sources/Send/Action/SendAction.swift +++ b/Packages/KeyAppKit/Sources/Send/Action/SendAction.swift @@ -81,7 +81,10 @@ public class SendActionServiceImpl: SendActionService { let currency = wallet.token.mintAddress // get paying fee token - let payingFeeToken = try? getPayingFeeToken(feeWallet: feeWallet) + let payingFeeToken = try? getPayingFeeToken( + feeWallet: feeWallet, + minimumTokenAccountBalance: wallet.minRentExemption ?? 2_039_280 + ) // prepare sending to Solana (returning legacy transaction) var (preparedTransaction, useFeeRelayer) = try await prepareForSendingToSolanaNetworkViaRelayMethod( @@ -222,7 +225,10 @@ public class SendActionServiceImpl: SendActionService { context.usageStatus.isFreeTransactionFeeAvailable(transactionFee: expectedTransactionFee) == false } - private func getPayingFeeToken(feeWallet: SolanaAccount?) throws -> FeeRelayerSwift.TokenAccount? { + private func getPayingFeeToken( + feeWallet: SolanaAccount?, + minimumTokenAccountBalance: UInt64 + ) throws -> FeeRelayerSwift.TokenAccount? { if let feeWallet = feeWallet { let addressString = feeWallet.address guard let address = try? PublicKey(string: addressString), @@ -233,7 +239,7 @@ public class SendActionServiceImpl: SendActionService { return .init( address: address, mint: mintAddress, - minimumTokenAccountBalance: feeWallet.minRentExemption ?? 2_039_280 + minimumTokenAccountBalance: minimumTokenAccountBalance ) } return nil diff --git a/Packages/KeyAppKit/Sources/Send/Input/SendInputBusinessLogic+ChangeTokenFee.swift b/Packages/KeyAppKit/Sources/Send/Input/SendInputBusinessLogic+ChangeTokenFee.swift index 6945c75386..2d105fccf8 100644 --- a/Packages/KeyAppKit/Sources/Send/Input/SendInputBusinessLogic+ChangeTokenFee.swift +++ b/Packages/KeyAppKit/Sources/Send/Input/SendInputBusinessLogic+ChangeTokenFee.swift @@ -1,6 +1,6 @@ import FeeRelayerSwift -import KeyAppKitCore import Foundation +import KeyAppKitCore import SolanaSwift extension SendInputBusinessLogic { diff --git a/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 57ad3a9382..b92014e394 100644 --- a/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -312,7 +312,7 @@ "location" : "https://github.com/p2p-org/solana-swift", "state" : { "branch" : "feature/token-2022", - "revision" : "80c8a71df5906444e2aefb31fd99fd35056c91e5" + "revision" : "04d339da349b700917eba1cc0e9dc8c4f377f69d" } }, { From ae19d81b1686636d0a5637a869f821326b64a269 Mon Sep 17 00:00:00 2001 From: runner Date: Thu, 11 Jan 2024 04:43:59 +0000 Subject: [PATCH 20/32] fix(swiftformat): Apply Swiftformat changes --- .../Sources/OrcaSwapSwift/OrcaSwap/OrcaSwap.swift | 12 ++++++------ .../AmountView/SendInputAmountViewModel.swift | 3 ++- .../Wormhole/Send/WormholeSendInputCoordinator.swift | 6 +++++- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Packages/KeyAppKit/Sources/OrcaSwapSwift/OrcaSwap/OrcaSwap.swift b/Packages/KeyAppKit/Sources/OrcaSwapSwift/OrcaSwap/OrcaSwap.swift index 15fc4fae4e..21860c2780 100644 --- a/Packages/KeyAppKit/Sources/OrcaSwapSwift/OrcaSwap/OrcaSwap.swift +++ b/Packages/KeyAppKit/Sources/OrcaSwapSwift/OrcaSwap/OrcaSwap.swift @@ -100,7 +100,7 @@ public class OrcaSwap: OrcaSwapType { guard let fromTokenName = getTokenFromMint(fromMint)?.name, let toTokenName = getTokenFromMint(toMint)?.name, let currentRoutes = try? findRoutes(fromTokenName: fromTokenName, toTokenName: toTokenName) - .first?.value + .first?.value else { return [] } // retrieve all routes @@ -271,11 +271,11 @@ public class OrcaSwap: OrcaSwapType { let decimals = bestPoolsPair![0].tokenABalance?.decimals, let inputAmount = inputAmount, let intermediaryToken = bestPoolsPair? - .getIntermediaryToken( - inputAmount: inputAmount.toLamport(decimals: decimals), - slippage: slippage - ), - let mint = getMint(tokenName: intermediaryToken.tokenName) + .getIntermediaryToken( + inputAmount: inputAmount.toLamport(decimals: decimals), + slippage: slippage + ), + let mint = getMint(tokenName: intermediaryToken.tokenName) { // when intermediary token is SOL, a deposit fee for creating WSOL is needed (will be returned after // transaction) diff --git a/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountViewModel.swift b/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountViewModel.swift index ea983fb380..7889203c7f 100644 --- a/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountViewModel.swift +++ b/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountViewModel.swift @@ -42,7 +42,8 @@ final class SendInputAmountViewModel: BaseViewModel, ObservableObject { @Published var amountText: String = "" @Published var amountTextColor: UIColor = .init(resource: .night) @Published var mainTokenText = "" - @Published var mainAmountType: EnteredAmountType = .token // Can't switch, can't remember Defaults.isTokenInputTypeChosen ? .token : .fiat + @Published var mainAmountType: EnteredAmountType = + .token // Can't switch, can't remember Defaults.isTokenInputTypeChosen ? .token : .fiat @Published var isMaxButtonVisible: Bool = true @Published var secondaryAmountText = "" diff --git a/p2p_wallet/Scenes/Main/Wormhole/Send/WormholeSendInputCoordinator.swift b/p2p_wallet/Scenes/Main/Wormhole/Send/WormholeSendInputCoordinator.swift index 5a4974ba25..eeec0ff8fc 100644 --- a/p2p_wallet/Scenes/Main/Wormhole/Send/WormholeSendInputCoordinator.swift +++ b/p2p_wallet/Scenes/Main/Wormhole/Send/WormholeSendInputCoordinator.swift @@ -51,7 +51,11 @@ final class WormholeSendInputCoordinator: SmartCoordinator Date: Thu, 11 Jan 2024 11:48:13 +0700 Subject: [PATCH 21/32] fix: format --- .../Crypto Actions Panel/CryptoActionsPanelViewModel.swift | 6 +++--- .../Subviews/AmountView/SendInputAmountViewModel.swift | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/p2p_wallet/Scenes/Main/Crypto/Components/Crypto Actions Panel/CryptoActionsPanelViewModel.swift b/p2p_wallet/Scenes/Main/Crypto/Components/Crypto Actions Panel/CryptoActionsPanelViewModel.swift index 3fb82fa387..5d7af58f37 100644 --- a/p2p_wallet/Scenes/Main/Crypto/Components/Crypto Actions Panel/CryptoActionsPanelViewModel.swift +++ b/p2p_wallet/Scenes/Main/Crypto/Components/Crypto Actions Panel/CryptoActionsPanelViewModel.swift @@ -43,9 +43,9 @@ final class CryptoActionsPanelViewModel: BaseViewModel, ObservableObject { .map { (state: AsyncValueState<[SolanaAccountsService.Account]>) -> String in let equityValue: CurrencyAmount = state.value // .filter { !($0.token.keyAppExtensions.isPositionOnWS ?? false) } - .reduce(CurrencyAmount(usd: 0)) { - $0 + $1.amountInFiat - } + .reduce(CurrencyAmount(usd: 0)) { + $0 + $1.amountInFiat + } let formatter = CurrencyFormatter( showSpacingAfterCurrencySymbol: false, diff --git a/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountViewModel.swift b/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountViewModel.swift index c6b50942d4..43eeb1b477 100644 --- a/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountViewModel.swift +++ b/p2p_wallet/Scenes/Main/Send/Input/Subviews/AmountView/SendInputAmountViewModel.swift @@ -42,7 +42,10 @@ final class SendInputAmountViewModel: BaseViewModel, ObservableObject { @Published var amountText: String = "" @Published var amountTextColor: UIColor = .init(resource: .night) @Published var mainTokenText = "" - @Published var mainAmountType: EnteredAmountType = .token // Can't switch, can't remember Defaults.isTokenInputTypeChosen ? .token : .fiat + + // Can't switch, can't remember Defaults.isTokenInputTypeChosen ? .token : .fiat + @Published var mainAmountType: EnteredAmountType = .token + @Published var isMaxButtonVisible: Bool = true @Published var secondaryAmountText = "" From 351efbcd7f661a86c009e376cf4e8ca2d7a5a260 Mon Sep 17 00:00:00 2001 From: Chung Tran Date: Thu, 11 Jan 2024 12:08:29 +0700 Subject: [PATCH 22/32] feat: lamportsPerSignature --- Packages/KeyAppKit/Sources/Send/Action/SendAction.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Packages/KeyAppKit/Sources/Send/Action/SendAction.swift b/Packages/KeyAppKit/Sources/Send/Action/SendAction.swift index 8bd46a6a3b..693d62db04 100644 --- a/Packages/KeyAppKit/Sources/Send/Action/SendAction.swift +++ b/Packages/KeyAppKit/Sources/Send/Action/SendAction.swift @@ -200,6 +200,7 @@ public class SendActionServiceImpl: SendActionService { amount: amount, feePayer: feePayer, transferChecked: useFeeRelayer, // create transferChecked instruction when using fee relayer + lamportsPerSignature: context.lamportsPerSignature, minRentExemption: wallet.minRentExemption ?? 2_039_280 ).preparedTransaction } From 5c21b3441d05949448c627ffbb43ad8bf9afea63 Mon Sep 17 00:00:00 2001 From: Chung Tran Date: Fri, 12 Jan 2024 11:25:06 +0700 Subject: [PATCH 23/32] feat: withToken2022 --- .../Solana/Socket/RealtimeSolanaAccountService.swift | 3 ++- .../Sources/KeyAppBusiness/Solana/SolanaAccountsService.swift | 3 ++- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/Socket/RealtimeSolanaAccountService.swift b/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/Socket/RealtimeSolanaAccountService.swift index 25a284b25e..f2d84b2232 100644 --- a/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/Socket/RealtimeSolanaAccountService.swift +++ b/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/Socket/RealtimeSolanaAccountService.swift @@ -284,8 +284,9 @@ final class RealtimeSolanaAccountServiceImpl: RealtimeSolanaAccountService { // Updating native account balance and get spl tokens let (balance, (resolved, _)) = try await( apiClient.getBalance(account: owner, commitment: "confirmed"), - apiClient.getAccountBalancesWithToken2022( + apiClient.getAccountBalances( for: owner, + withToken2022: true, tokensRepository: tokensService, commitment: "confirmed" ) diff --git a/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/SolanaAccountsService.swift b/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/SolanaAccountsService.swift index 8b539be2a2..aef0bbda20 100644 --- a/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/SolanaAccountsService.swift +++ b/Packages/KeyAppKit/Sources/KeyAppBusiness/Solana/SolanaAccountsService.swift @@ -261,8 +261,9 @@ class SolanaAccountAsyncValue: AsyncValue<[SolanaAccount]> { // Updating native account balance and get spl tokens let (balance, (resolved, _)) = try await( solanaAPIClient.getBalance(account: accountAddress, commitment: "confirmed"), - solanaAPIClient.getAccountBalancesWithToken2022( + solanaAPIClient.getAccountBalances( for: accountAddress, + withToken2022: true, tokensRepository: tokensService, commitment: "confirmed" ) diff --git a/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 727c38d470..5b0a362f8b 100644 --- a/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -312,7 +312,7 @@ "location" : "https://github.com/p2p-org/solana-swift", "state" : { "branch" : "feature/token-2022", - "revision" : "04d339da349b700917eba1cc0e9dc8c4f377f69d" + "revision" : "2cbdb61ca2ec368b63a60d5c6436b0774932c2ec" } }, { From 918616ecacebf4e9f093676e717aff93bbff2d52 Mon Sep 17 00:00:00 2001 From: Elizaveta Semenova Date: Fri, 12 Jan 2024 14:15:32 +0400 Subject: [PATCH 24/32] Remove profit line --- p2p_wallet/Resources/en.lproj/Localizable.strings | 1 - p2p_wallet/Scenes/Main/Swap/Swap/SwapView.swift | 8 +------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/p2p_wallet/Resources/en.lproj/Localizable.strings b/p2p_wallet/Resources/en.lproj/Localizable.strings index 14da62ca87..9f13411d17 100644 --- a/p2p_wallet/Resources/en.lproj/Localizable.strings +++ b/p2p_wallet/Resources/en.lproj/Localizable.strings @@ -213,7 +213,6 @@ "Keep your pin safe. Hide your pin from other people." = "Keep your pin safe. Hide your pin from other people."; "Key App" = "Key App"; "Key App cannot scan QR codes without access to your camera. Please enable access under Privacy settings." = "Key App cannot scan QR codes without access to your camera. Please enable access under Privacy settings."; -"Key App doesn’t make any profit from this swap 💚" = "Key App doesn’t make any profit from this swap 💚"; "Key App one-time transfer link" = "Key App one-time transfer link"; "Key App respects your privacy - it can't access your funds or personal details. Your information stays securely stored on your device and in the blockchain" = "Key App respects your privacy - it can't access your funds or personal details. Your information stays securely stored on your device and in the blockchain."; "Key App’s" = "Key App’s"; diff --git a/p2p_wallet/Scenes/Main/Swap/Swap/SwapView.swift b/p2p_wallet/Scenes/Main/Swap/Swap/SwapView.swift index 0d21ccd550..d35a3231e4 100644 --- a/p2p_wallet/Scenes/Main/Swap/Swap/SwapView.swift +++ b/p2p_wallet/Scenes/Main/Swap/Swap/SwapView.swift @@ -75,6 +75,7 @@ private extension SwapView { Text("Route: " + (viewModel.getRouteInSymbols()?.joined(separator: " -> ") ?? "")) .apply(style: .label2) .foregroundColor(.red) + .padding(.top, 8) // Slippage (for debugging) Text("Slippage: \(Double(viewModel.stateMachine.currentState.slippageBps) / 100)%") @@ -82,13 +83,6 @@ private extension SwapView { .foregroundColor(.red) #endif - // Disclaimer - Text(L10n.keyAppDoesnTMakeAnyProfitFromThisSwap💚) - .apply(style: .label1) - .foregroundColor(Color(.mountain)) - .padding(.top, 16) - .accessibilityIdentifier("SwapView.profitInfoLabel") - // Warning message if let warningState = viewModel.warningState { SwapPriceImpactView(model: warningState) From a505a921f383ffcc031b133ecff964238ca34505 Mon Sep 17 00:00:00 2001 From: Chung Tran Date: Fri, 12 Jan 2024 18:00:07 +0700 Subject: [PATCH 25/32] feat: remove unused service --- .../Sources/Send/Wormhole/WormholeSendInputState.swift | 1 - .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- .../Main/Wormhole/Send/WormholeSendInputViewModel.swift | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Packages/KeyAppKit/Sources/Send/Wormhole/WormholeSendInputState.swift b/Packages/KeyAppKit/Sources/Send/Wormhole/WormholeSendInputState.swift index b531e0809c..782442fe7c 100644 --- a/Packages/KeyAppKit/Sources/Send/Wormhole/WormholeSendInputState.swift +++ b/Packages/KeyAppKit/Sources/Send/Wormhole/WormholeSendInputState.swift @@ -10,7 +10,6 @@ import Wormhole public enum WormholeSendInputState: Equatable { public typealias Service = ( wormhole: WormholeAPI, - relay: RelayService, relayContextManager: RelayContextManager, orcaSwap: OrcaSwapType ) diff --git a/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index f7c98deee6..75916bc683 100644 --- a/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/p2p_wallet.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -185,8 +185,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/onevcat/Kingfisher.git", "state" : { - "revision" : "add0a87ec4e31e2ca2a0b2085f0559201e4f5918", - "version" : "7.10.1" + "revision" : "3ec0ab0bca4feb56e8b33e289c9496e89059dd08", + "version" : "7.10.2" } }, { diff --git a/p2p_wallet/Scenes/Main/Wormhole/Send/WormholeSendInputViewModel.swift b/p2p_wallet/Scenes/Main/Wormhole/Send/WormholeSendInputViewModel.swift index 9a34227cfa..7f98000d44 100644 --- a/p2p_wallet/Scenes/Main/Wormhole/Send/WormholeSendInputViewModel.swift +++ b/p2p_wallet/Scenes/Main/Wormhole/Send/WormholeSendInputViewModel.swift @@ -67,7 +67,6 @@ class WormholeSendInputViewModel: BaseViewModel, ObservableObject { recipient: Recipient, userWalletManager: UserWalletManager = Resolver.resolve(), wormholeAPI: WormholeAPI = Resolver.resolve(), - relayService: RelayService = Resolver.resolve(), relayContextManager: RelayContextManager = Resolver.resolve(), orcaSwap: OrcaSwapType = Resolver.resolve(), solanaAccountsService: SolanaAccountsService = Resolver.resolve(), @@ -77,7 +76,7 @@ class WormholeSendInputViewModel: BaseViewModel, ObservableObject { self.recipient = recipient self.solanaAccountsService = solanaAccountsService - let services: WormholeSendInputState.Service = (wormholeAPI, relayService, relayContextManager, orcaSwap) + let services: WormholeSendInputState.Service = (wormholeAPI, relayContextManager, orcaSwap) // Ensure user wallet is available guard let wallet = userWalletManager.wallet else { From 73cd95f1ff9b14c509289179ba9395bbc90d4c3a Mon Sep 17 00:00:00 2001 From: Elizaveta Semenova Date: Mon, 15 Jan 2024 11:46:30 +0400 Subject: [PATCH 26/32] Fix empty name in case of lookup request data race --- p2p_wallet/Scenes/TabBar/TabBarViewModel.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/p2p_wallet/Scenes/TabBar/TabBarViewModel.swift b/p2p_wallet/Scenes/TabBar/TabBarViewModel.swift index 6a08342c62..fb3885b7b2 100644 --- a/p2p_wallet/Scenes/TabBar/TabBarViewModel.swift +++ b/p2p_wallet/Scenes/TabBar/TabBarViewModel.swift @@ -37,8 +37,9 @@ final class TabBarViewModel { // Name service Task { guard let account = accountStorage.account else { return } - let name: String = try await nameService.getName(account.publicKey.base58EncodedString, withTLD: true) ?? "" - nameStorage.save(name: name) + if let name = try await nameService.getName(account.publicKey.base58EncodedString, withTLD: true) { + nameStorage.save(name: name) + } } // Notification From 8ea3c051649782b05697545ea76d1b5191e53c51 Mon Sep 17 00:00:00 2001 From: runner Date: Mon, 15 Jan 2024 16:40:31 +0000 Subject: [PATCH 27/32] feat(lokalise): Update localization from Lokalise --- p2p_wallet/Resources/en.lproj/Localizable.strings | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/p2p_wallet/Resources/en.lproj/Localizable.strings b/p2p_wallet/Resources/en.lproj/Localizable.strings index 9f13411d17..4ee5d3c79e 100644 --- a/p2p_wallet/Resources/en.lproj/Localizable.strings +++ b/p2p_wallet/Resources/en.lproj/Localizable.strings @@ -90,6 +90,7 @@ "Confirm" = "Confirm"; "Confirm access to your account that was used to create the wallet" = "Confirm access to your account that was used to create the wallet"; "Confirm claiming the tokens " = "Confirm claiming\nthe tokens"; +"Confirm selection" = "Confirm selection"; "Confirm your new PIN code" = "Confirm your new PIN code"; "Confirm your number" = "Confirm your number"; "Confirmation Code Limit Hit" = "Confirmation Code Limit Hit"; @@ -213,6 +214,7 @@ "Keep your pin safe. Hide your pin from other people." = "Keep your pin safe. Hide your pin from other people."; "Key App" = "Key App"; "Key App cannot scan QR codes without access to your camera. Please enable access under Privacy settings." = "Key App cannot scan QR codes without access to your camera. Please enable access under Privacy settings."; +"Key App doesn’t make any profit from this swap 💚" = "Key App doesn’t make any profit from this swap 💚"; "Key App one-time transfer link" = "Key App one-time transfer link"; "Key App respects your privacy - it can't access your funds or personal details. Your information stays securely stored on your device and in the blockchain" = "Key App respects your privacy - it can't access your funds or personal details. Your information stays securely stored on your device and in the blockchain."; "Key App’s" = "Key App’s"; @@ -225,6 +227,7 @@ "Log out" = "Log out"; "Looks like you already have a wallet with" = "Looks like you already have a wallet with"; "Low slippage %@. We recommend to increase slippage manually" = "Low slippage %@. We recommend to increase slippage manually"; +"Make sure the mint address %@ is correct before confirming" = "Make sure the mint address %@ is correct before confirming"; "Make sure this is still your device" = "Make sure this is still your device"; "Make sure you understand" = "Make sure you understand"; "Make sure you understand these aspects" = "Make sure you understand these aspects"; @@ -244,6 +247,7 @@ "Multi-factor authentication" = "Multi-factor authentication"; "My Ethereum address" = "My Ethereum address"; "My Solana address" = "My Solana address"; +"My crypto" = "My crypto"; "My username" = "My username"; "Name was booked" = "Name was booked"; "Network" = "Network"; @@ -391,6 +395,7 @@ "The price is higher because of your trade size. Consider splitting your transaction into multiple swaps." = "The price is higher because of your trade size. Consider splitting your transaction into multiple swaps."; "The seed phrase doesn't match. Please try again" = "The seed phrase doesn't match. Please try again"; "The slippage could be" = "The slippage could be"; +"The token %@ is out of the strict list" = "The token %@ is out of the strict list"; "The transaction has been rejected" = "The transaction has been rejected"; "The transaction has been successfully completed" = "The transaction has been successfully completed"; "The transaction has been successfully completed 🤟" = "The transaction has been successfully completed 🤟"; @@ -568,6 +573,3 @@ "✌️ Great! Your new PIN is set." = "✌️ Great! Your new PIN is set."; "😓 name is not available" = "😓 name is not available"; "😢 PIN doesn't match. Please try again" = "😢 PIN doesn't match. Please try again"; -"Confirm selection" = "Confirm selection"; -"The token %@ is out of the strict list" = "The token %@ is out of the strict list"; -"Make sure the mint address %@ is correct before confirming" = "Make sure the mint address %@ is correct before confirming"; From 7397b4ef05459a1a507d230813b66afe3fa12580 Mon Sep 17 00:00:00 2001 From: Chung Tran Date: Tue, 16 Jan 2024 18:03:54 +0700 Subject: [PATCH 28/32] fix: retrieve token 2022 --- .../Input/SendInputBusinessLogic+Initializing.swift | 8 -------- .../Send/SendViaLink/SendViaLinkDataService.swift | 4 ---- .../Sources/Send/SolanaAPIClient+Token2022.swift | 11 ++++++++--- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/Packages/KeyAppKit/Sources/Send/Input/SendInputBusinessLogic+Initializing.swift b/Packages/KeyAppKit/Sources/Send/Input/SendInputBusinessLogic+Initializing.swift index 1fc28963f8..a0a205e175 100644 --- a/Packages/KeyAppKit/Sources/Send/Input/SendInputBusinessLogic+Initializing.swift +++ b/Packages/KeyAppKit/Sources/Send/Input/SendInputBusinessLogic+Initializing.swift @@ -19,10 +19,6 @@ extension SendInputBusinessLogic { splAccounts: services.solanaAPIClient .getTokenAccountsByOwnerWithToken2022( pubkey: state.recipient.address, - params: .init( - mint: nil, - programId: TokenProgram.id.base58EncodedString - ), configs: .init(encoding: "base64") ) ) @@ -32,10 +28,6 @@ extension SendInputBusinessLogic { splAccounts: services.solanaAPIClient .getTokenAccountsByOwnerWithToken2022( pubkey: walletAddress.base58EncodedString, - params: .init( - mint: nil, - programId: TokenProgram.id.base58EncodedString - ), configs: .init(encoding: "base64") ) ) diff --git a/Packages/KeyAppKit/Sources/Send/SendViaLink/SendViaLinkDataService.swift b/Packages/KeyAppKit/Sources/Send/SendViaLink/SendViaLinkDataService.swift index 67c79c1528..4d6ca05c48 100644 --- a/Packages/KeyAppKit/Sources/Send/SendViaLink/SendViaLinkDataService.swift +++ b/Packages/KeyAppKit/Sources/Send/SendViaLink/SendViaLinkDataService.swift @@ -353,10 +353,6 @@ public final class SendViaLinkDataServiceImpl: SendViaLinkDataService { // 2. Get token accounts by owner let tokenAccounts = try await solanaAPIClient.getTokenAccountsByOwnerWithToken2022( pubkey: keypair.publicKey.base58EncodedString, - params: .init( - mint: nil, - programId: TokenProgram.id.base58EncodedString - ), configs: .init( commitment: "confirmed", encoding: "base64" diff --git a/Packages/KeyAppKit/Sources/Send/SolanaAPIClient+Token2022.swift b/Packages/KeyAppKit/Sources/Send/SolanaAPIClient+Token2022.swift index 5b1fbd0e51..f59ea67d34 100644 --- a/Packages/KeyAppKit/Sources/Send/SolanaAPIClient+Token2022.swift +++ b/Packages/KeyAppKit/Sources/Send/SolanaAPIClient+Token2022.swift @@ -4,20 +4,25 @@ import SolanaSwift extension SolanaAPIClient { func getTokenAccountsByOwnerWithToken2022( pubkey: String, - params: OwnerInfoParams?, configs: RequestConfiguration? ) async throws -> [TokenAccount] { // Temporarily convert all state into basic SPLTokenAccountState layout async let classicTokenAccounts = getTokenAccountsByOwner( pubkey: pubkey, - params: params, + params: .init( + mint: nil, + programId: TokenProgram.id.base58EncodedString + ), configs: configs, decodingTo: SPLTokenAccountState.self ) async let token2022Accounts = getTokenAccountsByOwner( pubkey: pubkey, - params: params, + params: .init( + mint: nil, + programId: Token2022Program.id.base58EncodedString + ), configs: configs, decodingTo: SPLTokenAccountState.self ) From ce0da9d8b7efa70e180e14a890dfbdce291daea1 Mon Sep 17 00:00:00 2001 From: Chung Tran Date: Tue, 16 Jan 2024 22:41:09 +0700 Subject: [PATCH 29/32] feat: speed up relayContextManager by using async let --- .../Context/RelayContextManagerImpl.swift | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Context/RelayContextManagerImpl.swift b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Context/RelayContextManagerImpl.swift index 72f572068e..e2ab386de6 100644 --- a/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Context/RelayContextManagerImpl.swift +++ b/Packages/KeyAppKit/Sources/FeeRelayerSwift/Relay/Context/RelayContextManagerImpl.swift @@ -56,28 +56,29 @@ public class RelayContextManagerImpl: RelayContextManager { else { throw RelayContextManagerError.invalidContext } // retrieve RelayContext - let ( - minimumRelayAccountBalance, - lamportsPerSignature, - feePayerAddress, - relayAccountStatus, - usageStatus - ) = try await( - solanaAPIClient.getMinimumBalanceForRentExemption(span: 0), - solanaAPIClient.getFees(commitment: nil).feeCalculator?.lamportsPerSignature ?? 0, - feeRelayerAPIClient.getFeePayerPubkey(), - solanaAPIClient.getRelayAccountStatus( - RelayProgram.getUserRelayAddress(user: account.publicKey, network: solanaAPIClient.endpoint.network) + async let minimumRelayAccountBalance = solanaAPIClient + .getMinimumBalanceForRentExemption(span: 0) + async let lamportsPerSignature = solanaAPIClient + .getFees(commitment: nil).feeCalculator? + .lamportsPerSignature + async let feePayerAddress = feeRelayerAPIClient.getFeePayerPubkey() + async let relayAccountStatus = solanaAPIClient + .getRelayAccountStatus( + RelayProgram + .getUserRelayAddress( + user: account.publicKey, + network: solanaAPIClient.endpoint.network + ) .base58EncodedString - ), - feeRelayerAPIClient.getFreeFeeLimits(for: account.publicKey.base58EncodedString) - .asUsageStatus() - ) + ) + async let usageStatus = feeRelayerAPIClient + .getFreeFeeLimits(for: account.publicKey.base58EncodedString) + .asUsageStatus() - return try RelayContext( + return try await RelayContext( minimumRelayAccountBalance: minimumRelayAccountBalance, feePayerAddress: PublicKey(string: feePayerAddress), - lamportsPerSignature: lamportsPerSignature, + lamportsPerSignature: lamportsPerSignature ?? 5000, relayAccountStatus: relayAccountStatus, usageStatus: usageStatus ) From e0730c8f553aab656e72285351d020a39209caa8 Mon Sep 17 00:00:00 2001 From: Chung Tran Date: Wed, 17 Jan 2024 13:37:21 +0700 Subject: [PATCH 30/32] feat: imporve KeyAppNetworking --- .../xcschemes/KeyAppNetworkingTests.xcscheme | 53 ++++++ .../JSONRPC/HTTPJSONRPCClient.swift | 1 + .../HTTPClient/DefaultHTTPEndpoint.swift | 14 +- .../HTTPClient/HTTPClient.swift | 31 ++-- .../HTTPClient/HTTPResponseDecoder.swift | 15 +- .../JSONRPC/DefaultHTTPEndpoint+JSONRPC.swift | 21 +++ .../JSONRPC/JSONRPCDecoder.swift | 21 ++- .../JSONRPC/JSONRPCHTTPClient.swift | 85 +++++++++ .../JSONRPC/Models/JSONRPCError.swift | 25 +++ .../{ => Models}/JSONRPCRequestDto.swift | 8 +- .../{ => Models}/JSONRPCResponseDto.swift | 12 +- .../Mocks/MockHTTPClient.swift | 31 ---- .../HTTPClientTests.swift | 48 ++++- ...NRPCHTTPClientJSONRPCHTTPClientTests.swift | 167 ++++++++++++++++++ .../Services/Networking/HttpClient.swift | 8 +- 15 files changed, 451 insertions(+), 89 deletions(-) create mode 100644 Packages/KeyAppKit/.swiftpm/xcode/xcshareddata/xcschemes/KeyAppNetworkingTests.xcscheme create mode 100644 Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/DefaultHTTPEndpoint+JSONRPC.swift create mode 100644 Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/JSONRPCHTTPClient.swift create mode 100644 Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/Models/JSONRPCError.swift rename Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/{ => Models}/JSONRPCRequestDto.swift (75%) rename Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/{ => Models}/JSONRPCResponseDto.swift (53%) delete mode 100644 Packages/KeyAppKit/Sources/KeyAppNetworking/Mocks/MockHTTPClient.swift create mode 100644 Packages/KeyAppKit/Tests/UnitTests/KeyAppNetworkingTests/JSONRPCHTTPClientJSONRPCHTTPClientTests.swift diff --git a/Packages/KeyAppKit/.swiftpm/xcode/xcshareddata/xcschemes/KeyAppNetworkingTests.xcscheme b/Packages/KeyAppKit/.swiftpm/xcode/xcshareddata/xcschemes/KeyAppNetworkingTests.xcscheme new file mode 100644 index 0000000000..a45f2a57f3 --- /dev/null +++ b/Packages/KeyAppKit/.swiftpm/xcode/xcshareddata/xcschemes/KeyAppNetworkingTests.xcscheme @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Packages/KeyAppKit/Sources/KeyAppKitCore/JSONRPC/HTTPJSONRPCClient.swift b/Packages/KeyAppKit/Sources/KeyAppKitCore/JSONRPC/HTTPJSONRPCClient.swift index cc05a29cef..04ae461cfc 100644 --- a/Packages/KeyAppKit/Sources/KeyAppKitCore/JSONRPC/HTTPJSONRPCClient.swift +++ b/Packages/KeyAppKit/Sources/KeyAppKitCore/JSONRPC/HTTPJSONRPCClient.swift @@ -1,5 +1,6 @@ import Foundation +@available(*, deprecated, message: "Use KeyAppNetworking.JSONRPCHTTPClient instead") public class HTTPJSONRPCCLient { static let decoder: JSONDecoder = { let decoder = JSONDecoder() diff --git a/Packages/KeyAppKit/Sources/KeyAppNetworking/HTTPClient/DefaultHTTPEndpoint.swift b/Packages/KeyAppKit/Sources/KeyAppNetworking/HTTPClient/DefaultHTTPEndpoint.swift index 27c5d8eff3..1886ba2cf4 100644 --- a/Packages/KeyAppKit/Sources/KeyAppNetworking/HTTPClient/DefaultHTTPEndpoint.swift +++ b/Packages/KeyAppKit/Sources/KeyAppNetworking/HTTPClient/DefaultHTTPEndpoint.swift @@ -23,6 +23,9 @@ public struct DefaultHTTPEndpoint: HTTPEndpoint { } } +/// String represent EmptyData +public typealias EmptyData = String + /// Public extension for HTTPClient for handy use of DefaultHTTPEndpoint public extension HTTPClient { /// Send request to specific endpoint @@ -30,10 +33,15 @@ public extension HTTPClient { /// - endpoint: default endpoint to send request to /// - responseModel: result type of model /// - Returns: specific result of `responseModel` type - func request( + func request( endpoint: DefaultHTTPEndpoint, - responseModel: T.Type + responseModel: T.Type, + errorType: E.Type = EmptyData.self ) async throws -> T { - try await request(endpoint: endpoint as HTTPEndpoint, responseModel: responseModel) + try await request( + endpoint: endpoint as HTTPEndpoint, + responseModel: responseModel, + errorModel: errorType + ) } } diff --git a/Packages/KeyAppKit/Sources/KeyAppNetworking/HTTPClient/HTTPClient.swift b/Packages/KeyAppKit/Sources/KeyAppNetworking/HTTPClient/HTTPClient.swift index 7744aff35e..a228db3dfb 100644 --- a/Packages/KeyAppKit/Sources/KeyAppNetworking/HTTPClient/HTTPClient.swift +++ b/Packages/KeyAppKit/Sources/KeyAppNetworking/HTTPClient/HTTPClient.swift @@ -1,20 +1,7 @@ import Foundation -/// HttpClient to handle http network requests -public protocol IHTTPClient { - /// Send request to specific endpoint - /// - Parameters: - /// - endpoint: endpoint to send request to - /// - responseModel: result type of model - /// - Returns: specific result of `responseModel` type - func request( - endpoint: HTTPEndpoint, - responseModel: T.Type - ) async throws -> T -} - -/// Default implementation for `IHTTPClient` -public class HTTPClient { +/// Default implementation for `HTTPClient` +public struct HTTPClient { // MARK: - Properties /// URLSession to handle network request @@ -39,15 +26,16 @@ public class HTTPClient { // MARK: - IHTTPClient -extension HTTPClient: IHTTPClient { +public extension HTTPClient { /// Send request to specific endpoint /// - Parameters: /// - endpoint: endpoint to send request to /// - responseModel: result type of model /// - Returns: specific result of `responseModel` type - public func request( + func request( endpoint: HTTPEndpoint, - responseModel: T.Type + responseModel: T.Type, + errorModel: E.Type ) async throws -> T { /// URL assertion guard let url = URL(string: endpoint.urlString) else { @@ -75,6 +63,11 @@ extension HTTPClient: IHTTPClient { } // Decode response - return try decoder.decode(responseModel, data: data, httpURLResponse: response) + return try decoder.decode( + responseModel, + errorType: errorModel, + data: data, + httpURLResponse: response + ) } } diff --git a/Packages/KeyAppKit/Sources/KeyAppNetworking/HTTPClient/HTTPResponseDecoder.swift b/Packages/KeyAppKit/Sources/KeyAppNetworking/HTTPClient/HTTPResponseDecoder.swift index 4020c84a51..17c4a85f7a 100644 --- a/Packages/KeyAppKit/Sources/KeyAppNetworking/HTTPClient/HTTPResponseDecoder.swift +++ b/Packages/KeyAppKit/Sources/KeyAppNetworking/HTTPClient/HTTPResponseDecoder.swift @@ -8,7 +8,12 @@ public protocol HTTPResponseDecoder { /// - data: data to decode /// - response: httpURLResponse from network /// - Returns: object of predefined type - func decode(_ type: T.Type, data: Data, httpURLResponse response: HTTPURLResponse) throws -> T + func decode( + _ type: T.Type, + errorType: U.Type, + data: Data, + httpURLResponse response: HTTPURLResponse + ) throws -> T } /// ResponseDecoder for JSON type @@ -36,8 +41,12 @@ extension JSONResponseDecoder: HTTPResponseDecoder { /// - data: data to decode /// - response: httpURLResponse from network /// - Returns: object of predefined type - public func decode(_ type: T.Type, data: Data, httpURLResponse response: HTTPURLResponse) throws -> T - { + public func decode( + _ type: T.Type, + errorType _: U.Type, + data: Data, + httpURLResponse response: HTTPURLResponse + ) throws -> T { switch response.statusCode { case 200 ... 299: // Special case when return type is string diff --git a/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/DefaultHTTPEndpoint+JSONRPC.swift b/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/DefaultHTTPEndpoint+JSONRPC.swift new file mode 100644 index 0000000000..9bfa7fc1dd --- /dev/null +++ b/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/DefaultHTTPEndpoint+JSONRPC.swift @@ -0,0 +1,21 @@ +import Foundation + +/// Public extension for HTTPClient for handy use of DefaultHTTPEndpoint +public extension DefaultHTTPEndpoint { + init( + baseURL: String, + path: String, + method: HTTPMethod, + header: [String: String], + body: JSONRPCRequestDto

+ ) throws { + self.baseURL = baseURL + self.path = path + self.method = method + self.header = header + self.body = try String( + data: JSONEncoder().encode(body), + encoding: .utf8 + ) + } +} diff --git a/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/JSONRPCDecoder.swift b/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/JSONRPCDecoder.swift index 0b5d95b46e..0df9cead7d 100644 --- a/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/JSONRPCDecoder.swift +++ b/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/JSONRPCDecoder.swift @@ -25,7 +25,12 @@ extension JSONRPCDecoder: HTTPResponseDecoder { /// - data: data to decode /// - response: httpURLResponse from network /// - Returns: object of predefined type - public func decode(_: T.Type, data: Data, httpURLResponse response: HTTPURLResponse) throws -> T { + public func decode( + _: T.Type, + errorType: U.Type, + data: Data, + httpURLResponse response: HTTPURLResponse + ) throws -> T { // Check status code switch response.statusCode { case 200 ... 299: @@ -33,17 +38,23 @@ extension JSONRPCDecoder: HTTPResponseDecoder { do { return try jsonDecoder.decode(T.self, from: data) } catch { - throw decodeRpcError(from: data) ?? error + throw decodeRpcError( + from: data, + errorType: errorType + ) ?? error } default: - throw decodeRpcError(from: data) ?? HTTPClientError.invalidResponse(response, data) + throw decodeRpcError( + from: data, + errorType: errorType + ) ?? HTTPClientError.invalidResponse(response, data) } } // MARK: - Helpers /// Custom error return from rpc endpoint - private func decodeRpcError(from data: Data) -> JSONRPCError? { - try? jsonDecoder.decode(JSONRPCResponseErrorDto.self, from: data).error + private func decodeRpcError(from data: Data, errorType _: U.Type) -> JSONRPCError? { + try? jsonDecoder.decode(JSONRPCResponseErrorDto.self, from: data).error } } diff --git a/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/JSONRPCHTTPClient.swift b/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/JSONRPCHTTPClient.swift new file mode 100644 index 0000000000..cf212108d9 --- /dev/null +++ b/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/JSONRPCHTTPClient.swift @@ -0,0 +1,85 @@ +import Foundation + +/// Implementation for `JSONRPCHTTPClient` +public struct JSONRPCHTTPClient { + // MARK: - Properties + + /// URLSession to handle network request + private let urlSession: HTTPURLSession + + /// Decoder for response + private let decoder = JSONRPCDecoder() + + // MARK: - Initializer + + /// HttpClient's initializer + /// - Parameter urlSession: URLSession to handle network request + /// - Parameter decoder: Decoder for response + public init( + urlSession: HTTPURLSession = URLSession.shared + ) { + self.urlSession = urlSession + } + + /// Invoke request and do not expect returning data + public func invoke( + baseURL: String, + path: String, + method: HTTPMethod = .post, + header: [String: String] = [ + "Accept": "application/json", + "Content-Type": "application/json", + ], + body: JSONRPCRequestDto

, + errorDataType: E.Type = EmptyData.self + ) async throws { + let httpClient = HTTPClient( + urlSession: urlSession, + decoder: decoder + ) + + _ = try await httpClient.request( + endpoint: DefaultHTTPEndpoint( + baseURL: baseURL, + path: path, + method: method, + header: header, + body: body + ), + responseModel: JSONRPCResponseDto.self, + errorModel: errorDataType + ) + } + + /// Send request to specific JSONRPC endpoint + public func request( + baseURL: String, + path: String = "", + method: HTTPMethod = .post, + header: [String: String] = [ + "Accept": "application/json", + "Content-Type": "application/json", + ], + body: JSONRPCRequestDto

, + responseModel _: T.Type, + errorDataType: E.Type = EmptyData.self + ) async throws -> T { + let httpClient = HTTPClient( + urlSession: urlSession, + decoder: decoder + ) + + let response = try await httpClient.request( + endpoint: DefaultHTTPEndpoint( + baseURL: baseURL, + path: path, + method: method, + header: header, + body: body + ), + responseModel: JSONRPCResponseDto.self, + errorModel: errorDataType + ) + return response.result + } +} diff --git a/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/Models/JSONRPCError.swift b/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/Models/JSONRPCError.swift new file mode 100644 index 0000000000..d373589069 --- /dev/null +++ b/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/Models/JSONRPCError.swift @@ -0,0 +1,25 @@ +import Foundation + +public struct JSONRPCError: Decodable, Error, LocalizedError { + public let code: Int? + public let message: String? + public let data: U? + + public init(code: Int, message: String, data: U?) { + self.code = code + self.message = message + self.data = data + } + + public var errorDescription: String? { + "Code \(code ?? -1). Reason: \(message ?? "Unknown")" + } +} + +public extension JSONRPCError where U == String { + init(code: Int, message: String) { + self.code = code + self.message = message + data = nil + } +} diff --git a/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/JSONRPCRequestDto.swift b/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/Models/JSONRPCRequestDto.swift similarity index 75% rename from Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/JSONRPCRequestDto.swift rename to Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/Models/JSONRPCRequestDto.swift index 6f433b411f..d0ac937816 100644 --- a/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/JSONRPCRequestDto.swift +++ b/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/Models/JSONRPCRequestDto.swift @@ -4,13 +4,13 @@ public struct JSONRPCRequestDto: Encodable { let jsonrpc: String let id: String let method: String - let params: [T]? + let params: T? public init( jsonrpc: String = "2.0", id: String = UUID().uuidString, method: String, - params: [T]? = nil + params: T? = nil ) { self.jsonrpc = jsonrpc self.id = id @@ -19,9 +19,7 @@ public struct JSONRPCRequestDto: Encodable { } } -public extension JSONRPCRequestDto -where T == String { /* T == String, or what ever confirmed to Encodable to fix ambiguous type */ - /// Non-params initializer +public extension JSONRPCRequestDto where T == EmptyData { init( jsonrpc: String = "2.0", id: String = UUID().uuidString, diff --git a/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/JSONRPCResponseDto.swift b/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/Models/JSONRPCResponseDto.swift similarity index 53% rename from Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/JSONRPCResponseDto.swift rename to Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/Models/JSONRPCResponseDto.swift index 873c95f9ed..2f25f7f878 100644 --- a/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/JSONRPCResponseDto.swift +++ b/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/Models/JSONRPCResponseDto.swift @@ -1,8 +1,8 @@ import Foundation public struct JSONRPCResponseDto: Decodable { - let id: String? - let result: T + public let id: String? + public let result: T public init( id: String, @@ -13,11 +13,7 @@ public struct JSONRPCResponseDto: Decodable { } } -public struct JSONRPCResponseErrorDto: Decodable { +public struct JSONRPCResponseErrorDto: Decodable { public let id: String? - public let error: JSONRPCError? -} - -public struct JSONRPCError: Decodable, Error { - public let code: Int? + public let error: JSONRPCError } diff --git a/Packages/KeyAppKit/Sources/KeyAppNetworking/Mocks/MockHTTPClient.swift b/Packages/KeyAppKit/Sources/KeyAppNetworking/Mocks/MockHTTPClient.swift deleted file mode 100644 index a680d0c2ad..0000000000 --- a/Packages/KeyAppKit/Sources/KeyAppNetworking/Mocks/MockHTTPClient.swift +++ /dev/null @@ -1,31 +0,0 @@ -import Foundation - -final class MockHTTPClient: IHTTPClient { - // MARK: - Init - - init() {} - - // MARK: - IHTTPClient - - var invokedRequest = false - var invokedRequestCount = 0 - var invokedRequestParameters: (endpoint: HTTPEndpoint?, Void)? - var invokedRequestParametersList = [HTTPEndpoint]() - var stubbedRequestResult: Any? - - func request(endpoint: HTTPEndpoint, responseModel _: T.Type) async throws -> T { - invokedRequest = true - invokedRequestCount += 1 - invokedRequestParameters = (endpoint, ()) - invokedRequestParametersList.append(endpoint) - - if let result = stubbedRequestResult { - guard let model = result as? T else { - throw NSError(domain: "", code: 0) - } - return model - } - - throw NSError(domain: "", code: 0) - } -} diff --git a/Packages/KeyAppKit/Tests/UnitTests/KeyAppNetworkingTests/HTTPClientTests.swift b/Packages/KeyAppKit/Tests/UnitTests/KeyAppNetworkingTests/HTTPClientTests.swift index a102fc905e..651f57b48a 100644 --- a/Packages/KeyAppKit/Tests/UnitTests/KeyAppNetworkingTests/HTTPClientTests.swift +++ b/Packages/KeyAppKit/Tests/UnitTests/KeyAppNetworkingTests/HTTPClientTests.swift @@ -11,10 +11,17 @@ class HTTPClientTests: XCTestCase { "name": "John Doe" } """ - let mockURLSession = MockURLSession(responseString: mockString, statusCode: 200, error: nil) + let mockURLSession = MockURLSession( + responseString: mockString, + statusCode: 200, + error: nil + ) let mockDecoder = JSONResponseDecoder() - let httpClient = HTTPClient(urlSession: mockURLSession, decoder: mockDecoder) + let httpClient = HTTPClient( + urlSession: mockURLSession, + decoder: mockDecoder + ) let endpoint = DefaultHTTPEndpoint( baseURL: "https://example.com/api", path: "/users", @@ -24,7 +31,10 @@ class HTTPClientTests: XCTestCase { ) // Act - let userModel: UserModel = try await httpClient.request(endpoint: endpoint, responseModel: UserModel.self) + let userModel: UserModel = try await httpClient.request( + endpoint: endpoint, + responseModel: UserModel.self + ) // Assert XCTAssertEqual(userModel.id, 1) @@ -34,10 +44,17 @@ class HTTPClientTests: XCTestCase { func testRequest_SuccessfulResponse_ReturnsString() async throws { // Arrange let mockString = "OK" - let mockURLSession = MockURLSession(responseString: mockString, statusCode: 200, error: nil) + let mockURLSession = MockURLSession( + responseString: mockString, + statusCode: 200, + error: nil + ) let mockDecoder = JSONResponseDecoder() - let httpClient = HTTPClient(urlSession: mockURLSession, decoder: mockDecoder) + let httpClient = HTTPClient( + urlSession: mockURLSession, + decoder: mockDecoder + ) let endpoint = DefaultHTTPEndpoint( baseURL: "https://example.com/api", path: "/users", @@ -47,7 +64,10 @@ class HTTPClientTests: XCTestCase { ) // Act - let response = try await httpClient.request(endpoint: endpoint, responseModel: String.self) + let response = try await httpClient.request( + endpoint: endpoint, + responseModel: String.self + ) // Assert XCTAssertEqual(response, "OK") @@ -56,10 +76,17 @@ class HTTPClientTests: XCTestCase { func testRequest_InvalidResponse_ThrowsError() async throws { // Arrange let mockData = Data() - let mockURLSession = MockURLSession(responseString: nil, statusCode: 200, error: nil) + let mockURLSession = MockURLSession( + responseString: nil, + statusCode: 200, + error: nil + ) let mockDecoder = JSONResponseDecoder() - let httpClient = HTTPClient(urlSession: mockURLSession, decoder: mockDecoder) + let httpClient = HTTPClient( + urlSession: mockURLSession, + decoder: mockDecoder + ) let endpoint = DefaultHTTPEndpoint( baseURL: "https://example.com/api", path: "/users", @@ -70,7 +97,10 @@ class HTTPClientTests: XCTestCase { // Act & Assert do { - _ = try await httpClient.request(endpoint: endpoint, responseModel: UserModel.self) + _ = try await httpClient.request( + endpoint: endpoint, + responseModel: UserModel.self + ) XCTFail() } catch let HTTPClientError.invalidResponse(response, data) { XCTAssertEqual(response, nil) diff --git a/Packages/KeyAppKit/Tests/UnitTests/KeyAppNetworkingTests/JSONRPCHTTPClientJSONRPCHTTPClientTests.swift b/Packages/KeyAppKit/Tests/UnitTests/KeyAppNetworkingTests/JSONRPCHTTPClientJSONRPCHTTPClientTests.swift new file mode 100644 index 0000000000..7656542396 --- /dev/null +++ b/Packages/KeyAppKit/Tests/UnitTests/KeyAppNetworkingTests/JSONRPCHTTPClientJSONRPCHTTPClientTests.swift @@ -0,0 +1,167 @@ +import KeyAppNetworking +import XCTest + +final class JSONRPCHTTPClientTests: XCTestCase { + func testRequest_SuccessfulResponse_ReturnsDecodedModel() async throws { + // Test request https://www.jsonrpc.org/specification + let mockParams = JSONRPCRequestDto( + id: "1", + method: "subtract", + params: [42, 23] + ) + + let mockString = #"{"jsonrpc": "2.0", "result": 19, "id": "1"}"# + + let httpClient = JSONRPCHTTPClient( + urlSession: MockURLSession( + responseString: mockString, + statusCode: 200, + error: nil + ) + ) + + // Act + let response = try await httpClient.request( + baseURL: "https://example.com/api", + path: "/users", + body: mockParams, + responseModel: Int.self + ) + // Assert + XCTAssertEqual(response, 19) + } + + func testRequest_UnsuccessfulResponse_ThrowsErrorWithEmptyErrorData() async throws { + // Test when the server returns a non-successful status code + let mockParams = JSONRPCRequestDto( + id: "1", + method: "subtract", + params: [42, 23] + ) + + let mockString = #"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid params"},"id":"1"}"# + + let httpClient = JSONRPCHTTPClient( + urlSession: MockURLSession( + responseString: mockString, + statusCode: 400, // Simulate a bad request + error: nil + ) + ) + + // Act & Assert + do { + _ = try await httpClient.request( + baseURL: "https://example.com/api", + path: "/users", + body: mockParams, + responseModel: Int.self + ) + XCTFail() + } catch let error as JSONRPCError { + XCTAssertEqual(error.code, -32602) + XCTAssertEqual(error.message, "Invalid params") + } catch { + XCTFail() + } + } + + func testRequest_UnsuccessfulResponse_ThrowsErrorWithCustomErrorData_AsString() async throws { + // Test when the server returns a non-successful status code with custom error data + let mockParams = JSONRPCRequestDto( + id: "1", + method: "subtract", + params: [42, 23] + ) + + let mockString = """ + { + "jsonrpc": "2.0", + "error": { + "code": -32602, + "message": "Invalid params", + "data": "Additional details" + }, + "id": "1" + } + """ + + let httpClient = JSONRPCHTTPClient( + urlSession: MockURLSession( + responseString: mockString, + statusCode: 400, // Simulate a bad request + error: nil + ) + ) + + // Act & Assert + do { + _ = try await httpClient.request( + baseURL: "https://example.com/api", + path: "/users", + body: mockParams, + responseModel: Int.self + ) + XCTFail() + } catch let error as JSONRPCError { + XCTAssertEqual(error.code, -32602) + XCTAssertEqual(error.message, "Invalid params") + XCTAssertEqual(error.data, "Additional details") + } catch { + XCTFail() + } + } + + func testRequest_UnsuccessfulResponse_ThrowsErrorWithCustomErrorData_AsCustomType() async throws { + struct User: Decodable { + let userId: String + let name: String + } + + // Test when the server returns a non-successful status code with custom error data + let mockParams = JSONRPCRequestDto( + id: "1", + method: "subtract", + params: [42, 23] + ) + + let mockString = """ + { + "jsonrpc": "2.0", + "error": { + "code": -32602, + "message": "Invalid user", + "data": {"userId":"id","name":"ABC"} + }, + "id": "1" + } + """ + + let httpClient = JSONRPCHTTPClient( + urlSession: MockURLSession( + responseString: mockString, + statusCode: 400, // Simulate a bad request + error: nil + ) + ) + + // Act & Assert + do { + _ = try await httpClient.request( + baseURL: "https://example.com/api", + path: "/users", + body: mockParams, + responseModel: Int.self, + errorDataType: User.self + ) + XCTFail() + } catch let error as JSONRPCError { + XCTAssertEqual(error.code, -32602) + XCTAssertEqual(error.message, "Invalid user") + XCTAssertEqual(error.data?.userId, "id") + XCTAssertEqual(error.data?.name, "ABC") + } catch { + XCTFail() + } + } +} diff --git a/p2p_wallet/Common/Services/Networking/HttpClient.swift b/p2p_wallet/Common/Services/Networking/HttpClient.swift index db7810c87d..535ea473be 100644 --- a/p2p_wallet/Common/Services/Networking/HttpClient.swift +++ b/p2p_wallet/Common/Services/Networking/HttpClient.swift @@ -1,15 +1,11 @@ -// -// HttpClient.swift -// p2p_wallet -// -// Created by Ivan on 28.04.2022. -// import Foundation +@available(*, deprecated, message: "Use KeyAppNetworking.HTTPClient instead") protocol HttpClient { func sendRequest(endpoint: Endpoint, responseModel: T.Type) async throws -> T } +@available(*, deprecated, message: "Use KeyAppNetworking.HTTPClient instead") final class HttpClientImpl: HttpClient { func sendRequest(endpoint: Endpoint, responseModel: T.Type) async throws -> T { guard let url = URL(string: endpoint.baseURL + endpoint.path) else { throw ErrorModel.invalidURL } From 41b687da91231126be7fbf9df4cc250732f4e999 Mon Sep 17 00:00:00 2001 From: Chung Tran Date: Wed, 17 Jan 2024 15:20:16 +0700 Subject: [PATCH 31/32] fix: change U to concrete type --- .../KeyAppNetworking/JSONRPC/Models/JSONRPCError.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/Models/JSONRPCError.swift b/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/Models/JSONRPCError.swift index d373589069..cb8483083f 100644 --- a/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/Models/JSONRPCError.swift +++ b/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/Models/JSONRPCError.swift @@ -1,11 +1,11 @@ import Foundation -public struct JSONRPCError: Decodable, Error, LocalizedError { +public struct JSONRPCError: Decodable, Error, LocalizedError { public let code: Int? public let message: String? - public let data: U? + public let data: DataType? - public init(code: Int, message: String, data: U?) { + public init(code: Int, message: String, data: DataType?) { self.code = code self.message = message self.data = data @@ -16,7 +16,7 @@ public struct JSONRPCError: Decodable, Error, LocalizedError { } } -public extension JSONRPCError where U == String { +public extension JSONRPCError where DataType == EmptyData { init(code: Int, message: String) { self.code = code self.message = message From 43f3e9a05202989ca5deba8dfb2b0a1a7331c559 Mon Sep 17 00:00:00 2001 From: Chung Tran Date: Wed, 17 Jan 2024 15:31:14 +0700 Subject: [PATCH 32/32] feat: add default initializer --- .../HTTPClient/DefaultHTTPEndpoint.swift | 17 +++++++++++++++ .../JSONRPC/DefaultHTTPEndpoint+JSONRPC.swift | 21 ------------------- 2 files changed, 17 insertions(+), 21 deletions(-) delete mode 100644 Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/DefaultHTTPEndpoint+JSONRPC.swift diff --git a/Packages/KeyAppKit/Sources/KeyAppNetworking/HTTPClient/DefaultHTTPEndpoint.swift b/Packages/KeyAppKit/Sources/KeyAppNetworking/HTTPClient/DefaultHTTPEndpoint.swift index 1886ba2cf4..551f5c68b6 100644 --- a/Packages/KeyAppKit/Sources/KeyAppNetworking/HTTPClient/DefaultHTTPEndpoint.swift +++ b/Packages/KeyAppKit/Sources/KeyAppNetworking/HTTPClient/DefaultHTTPEndpoint.swift @@ -21,6 +21,23 @@ public struct DefaultHTTPEndpoint: HTTPEndpoint { self.header = header self.body = body } + + public init( + baseURL: String, + path: String, + method: HTTPMethod, + header: [String: String], + body: P + ) throws { + self.baseURL = baseURL + self.path = path + self.method = method + self.header = header + self.body = try String( + data: JSONEncoder().encode(body), + encoding: .utf8 + ) + } } /// String represent EmptyData diff --git a/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/DefaultHTTPEndpoint+JSONRPC.swift b/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/DefaultHTTPEndpoint+JSONRPC.swift deleted file mode 100644 index 9bfa7fc1dd..0000000000 --- a/Packages/KeyAppKit/Sources/KeyAppNetworking/JSONRPC/DefaultHTTPEndpoint+JSONRPC.swift +++ /dev/null @@ -1,21 +0,0 @@ -import Foundation - -/// Public extension for HTTPClient for handy use of DefaultHTTPEndpoint -public extension DefaultHTTPEndpoint { - init( - baseURL: String, - path: String, - method: HTTPMethod, - header: [String: String], - body: JSONRPCRequestDto

- ) throws { - self.baseURL = baseURL - self.path = path - self.method = method - self.header = header - self.body = try String( - data: JSONEncoder().encode(body), - encoding: .utf8 - ) - } -}