From df51ad303deaec1e6068a0b15a890ce786115b1b Mon Sep 17 00:00:00 2001 From: rommex Date: Wed, 20 Mar 2024 12:14:00 +0200 Subject: [PATCH 01/80] initted --- .../project.pbxproj | 8 ++ .../CryptoSenderProtocol.swift | 77 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift diff --git a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj index 6aabb2c6c..bb901927e 100644 --- a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj +++ b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj @@ -29,6 +29,9 @@ 299713CD28F6931E00743003 /* Function.swift in Sources */ = {isa = PBXBuildFile; fileRef = 299713CC28F6931E00743003 /* Function.swift */; }; 299713D228F6933F00743003 /* ABIEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 299713D128F6933F00743003 /* ABIEncoder.swift */; }; 299713D728F693F600743003 /* EIP712TypedData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 299713D628F693F600743003 /* EIP712TypedData.swift */; }; + 29AA6AA42BAAE9F500D24FB5 /* CryptoSenderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA32BAAE9F500D24FB5 /* CryptoSenderProtocol.swift */; }; + 29AA6AA52BAAE9F500D24FB5 /* CryptoSenderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA32BAAE9F500D24FB5 /* CryptoSenderProtocol.swift */; }; + 29AA6AA62BAAE9F500D24FB5 /* CryptoSenderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA32BAAE9F500D24FB5 /* CryptoSenderProtocol.swift */; }; 29AEA04B292F7D60003BB5B4 /* SecurePersistedStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AEA04A292F7D60003BB5B4 /* SecurePersistedStorage.swift */; }; 29AEA050292F941F003BB5B4 /* PersistedSignaturesStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AEA04F292F941F003BB5B4 /* PersistedSignaturesStorage.swift */; }; 29AEA05529367785003BB5B4 /* UDWallet+Signing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AEA05429367785003BB5B4 /* UDWallet+Signing.swift */; }; @@ -2478,6 +2481,7 @@ 299713CC28F6931E00743003 /* Function.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Function.swift; sourceTree = ""; }; 299713D128F6933F00743003 /* ABIEncoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ABIEncoder.swift; sourceTree = ""; }; 299713D628F693F600743003 /* EIP712TypedData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EIP712TypedData.swift; sourceTree = ""; }; + 29AA6AA32BAAE9F500D24FB5 /* CryptoSenderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoSenderProtocol.swift; sourceTree = ""; }; 29AEA04A292F7D60003BB5B4 /* SecurePersistedStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecurePersistedStorage.swift; sourceTree = ""; }; 29AEA04F292F941F003BB5B4 /* PersistedSignaturesStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistedSignaturesStorage.swift; sourceTree = ""; }; 29AEA05429367785003BB5B4 /* UDWallet+Signing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UDWallet+Signing.swift"; sourceTree = ""; }; @@ -6276,6 +6280,7 @@ C67213DE2BAA96170075B9C7 /* SendCryptoAssetNavigationDestination.swift */, C67213CD2BAA80670075B9C7 /* SelectReceiver */, C67213E72BAA98EC0075B9C7 /* SelectAssetToSend */, + 29AA6AA32BAAE9F500D24FB5 /* CryptoSenderProtocol.swift */, ); path = SendCryptoAsset; sourceTree = ""; @@ -8846,6 +8851,7 @@ C6C995B6289D313D00367362 /* CNavigationControllerDefaultNavigationBarPushAnimation.swift in Sources */, C671E3AC28FEE72B00A2B3A0 /* UnstoppableImagePicker.swift in Sources */, C63A77A828F40B6B00F66E72 /* GhostTertiaryWhiteButton.swift in Sources */, + 29AA6AA42BAAE9F500D24FB5 /* CryptoSenderProtocol.swift in Sources */, C664B6592914EB0400A76154 /* EnterEmailValuePresenter.swift in Sources */, C663538B294319B400EF1DC7 /* ScrollViewOffsetListener.swift in Sources */, C628E36327FDDAA00044E408 /* UILabel.swift in Sources */, @@ -9723,6 +9729,7 @@ C617CFA22B9ED9D100663516 /* TestableUDWalletsService.swift in Sources */, C6D011D22B99A78E0008BF40 /* TestableGenericError.swift in Sources */, C6DF986F290F83020098733A /* TransactionsDecodingTests.swift in Sources */, + 29AA6AA52BAAE9F500D24FB5 /* CryptoSenderProtocol.swift in Sources */, C617CF9E2B9ED96200663516 /* WalletsDataServiceTests.swift in Sources */, C679B61C291CF8C800F543A7 /* BaseTestClass.swift in Sources */, C6DF9877290F83020098733A /* domains_manager_iosTests.swift in Sources */, @@ -9968,6 +9975,7 @@ C6C8F9412B21841B00A9834D /* MessagingServiceProtocol.swift in Sources */, C618083E2B19B0680032E543 /* PreviewDeepLinksService.swift in Sources */, C6D646822B1ED12900D724AC /* DomainProfileGeneralData.swift in Sources */, + 29AA6AA62BAAE9F500D24FB5 /* CryptoSenderProtocol.swift in Sources */, C6C8F9402B2183FA00A9834D /* UIUserInterfaceStyle.swift in Sources */, C6D647922B1EE75A00D724AC /* PreviewCoinRecordsService.swift in Sources */, C6D646C52B1ED24300D724AC /* GroupedCoinRecord.swift in Sources */, diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift new file mode 100644 index 000000000..532f341e1 --- /dev/null +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift @@ -0,0 +1,77 @@ +// +// CryptoSenderProtocol.swift +// domains-manager-ios +// +// Created by Roman Medvid on 20.03.2024. +// + +import Foundation + +protocol StoredSendTransactionProtocol { + var status: TxStatusGroup { get } +} + +protocol CryptoSenderProtocol { + + /// Indicates of Sender supports sending of a token on the chain + /// - Parameters: + /// - token: tokan name + /// - chain: chain + /// - Returns: true if the sending is supported + func canSendCrypto(token: String, chain: BlockchainType) -> Bool + + /// Create TX, send it to the chain and store it to the storage as 'pending' + /// - Parameters: + /// - amount: anount of tokens + /// - address: address of the receiver + /// - chain: chain of the transaction + func sendCrypto(token: String, amount: Double, address: HexAddress, chain: BlockchainType) + + func getSendTxs() -> [StoredSendTransactionProtocol] +} + +extension CryptoSenderProtocol { + func sendCrypto(amount: Double, address: HexAddress, chain: BlockchainType) { + // create the TX + + // send TX to the chain + + // store TX in the storage + } + + private func createTX() { + + } + + private func sendTX() { + + } + + private func storeTX() { + + } +} + +// ========================== + +struct DemoSendTransaction: StoredSendTransactionProtocol { + var status: TxStatusGroup + let token: String + let amount: Double +} + +class DemoCryptoSender: CryptoSenderProtocol { + func sendCrypto(token: String, amount: Double, address: HexAddress, chain: BlockchainType) { + // TODO: + } + + func canSendCrypto(token: String, chain: BlockchainType) -> Bool { + return false + } + + func getSendTxs() -> [StoredSendTransactionProtocol] { + return [DemoSendTransaction(status: .pending, + token: "crypto.LINK.address", + amount: 1.0)] + } +} From f3a95071486182671f08f99ce2ed9a5ba4e4345c Mon Sep 17 00:00:00 2001 From: rommex Date: Wed, 20 Mar 2024 12:40:36 +0200 Subject: [PATCH 02/80] refactoring --- .../CryptoSenderProtocol.swift | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift index 532f341e1..5bd6daf28 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift @@ -7,13 +7,9 @@ import Foundation -protocol StoredSendTransactionProtocol { - var status: TxStatusGroup { get } -} - protocol CryptoSenderProtocol { - /// Indicates of Sender supports sending of a token on the chain + /// Indicates if Sender supports sending of a token on the chain /// - Parameters: /// - token: tokan name /// - chain: chain @@ -25,9 +21,9 @@ protocol CryptoSenderProtocol { /// - amount: anount of tokens /// - address: address of the receiver /// - chain: chain of the transaction - func sendCrypto(token: String, amount: Double, address: HexAddress, chain: BlockchainType) + func sendCrypto(token: String, amount: Double, address: HexAddress, chain: BlockchainType) async - func getSendTxs() -> [StoredSendTransactionProtocol] + func getStoredSendTxs() -> [StoredSendTransactionProtocol] } extension CryptoSenderProtocol { @@ -40,18 +36,23 @@ extension CryptoSenderProtocol { } private func createTX() { - + // TODO: } private func sendTX() { - + // TODO: } private func storeTX() { - + // TODO: } } +protocol StoredSendTransactionProtocol { + var status: TxStatusGroup { get } +} + + // ========================== struct DemoSendTransaction: StoredSendTransactionProtocol { @@ -61,6 +62,9 @@ struct DemoSendTransaction: StoredSendTransactionProtocol { } class DemoCryptoSender: CryptoSenderProtocol { + static let instance: DemoCryptoSender = DemoCryptoSender() + private init() {} + func sendCrypto(token: String, amount: Double, address: HexAddress, chain: BlockchainType) { // TODO: } @@ -69,9 +73,13 @@ class DemoCryptoSender: CryptoSenderProtocol { return false } - func getSendTxs() -> [StoredSendTransactionProtocol] { + func getStoredSendTxs() -> [StoredSendTransactionProtocol] { return [DemoSendTransaction(status: .pending, token: "crypto.LINK.address", amount: 1.0)] } + + func getAllDemoSendTransactions() -> [DemoSendTransaction] { + return getStoredSendTxs() as! [DemoSendTransaction] + } } From 268ea93fc2ce1e5f221276984a168a75e0b8db0e Mon Sep 17 00:00:00 2001 From: rommex Date: Wed, 20 Mar 2024 13:02:42 +0200 Subject: [PATCH 03/80] refact --- .../SendCryptoAsset/CryptoSenderProtocol.swift | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift index 5bd6daf28..8b491e46d 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift @@ -16,18 +16,25 @@ protocol CryptoSenderProtocol { /// - Returns: true if the sending is supported func canSendCrypto(token: String, chain: BlockchainType) -> Bool - /// Create TX, send it to the chain and store it to the storage as 'pending' + /// Create TX, send it to the chain and store it to the storage as 'pending'. + /// Method fails if sending TX failed. Otherwise it returns TX hash /// - Parameters: /// - amount: anount of tokens /// - address: address of the receiver /// - chain: chain of the transaction - func sendCrypto(token: String, amount: Double, address: HexAddress, chain: BlockchainType) async + /// - Returns: TX Hash if success + func sendCrypto(token: String, amount: Double, address: HexAddress, chain: BlockchainType) async throws -> String + /// Get all 'Send' Txs from the persistent storage. + /// - Returns: Array of Txs. ATM only 'status' property can be guaranteed func getStoredSendTxs() -> [StoredSendTransactionProtocol] } + + + extension CryptoSenderProtocol { - func sendCrypto(amount: Double, address: HexAddress, chain: BlockchainType) { + private func _sendCrypto(token: String, amount: Double, address: HexAddress, chain: BlockchainType) async throws { // create the TX // send TX to the chain @@ -65,8 +72,10 @@ class DemoCryptoSender: CryptoSenderProtocol { static let instance: DemoCryptoSender = DemoCryptoSender() private init() {} - func sendCrypto(token: String, amount: Double, address: HexAddress, chain: BlockchainType) { + @discardableResult + func sendCrypto(token: String, amount: Double, address: HexAddress, chain: BlockchainType) -> String { // TODO: + return "0x" } func canSendCrypto(token: String, chain: BlockchainType) -> Bool { From 54332a1c62822c25edaec2eaaf280ab6d03ddb74 Mon Sep 17 00:00:00 2001 From: rommex Date: Wed, 20 Mar 2024 13:13:44 +0200 Subject: [PATCH 04/80] Update CryptoSenderProtocol.swift --- .../Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift index 8b491e46d..1de5d9f4c 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift @@ -57,12 +57,14 @@ extension CryptoSenderProtocol { protocol StoredSendTransactionProtocol { var status: TxStatusGroup { get } + var hash: String { get } } // ========================== struct DemoSendTransaction: StoredSendTransactionProtocol { + let hash: String var status: TxStatusGroup let token: String let amount: Double @@ -83,7 +85,8 @@ class DemoCryptoSender: CryptoSenderProtocol { } func getStoredSendTxs() -> [StoredSendTransactionProtocol] { - return [DemoSendTransaction(status: .pending, + return [DemoSendTransaction(hash: "0x", + status: .pending, token: "crypto.LINK.address", amount: 1.0)] } From 7498e482a05f2f41d284ec485a8ff0de5bbffe48 Mon Sep 17 00:00:00 2001 From: rommex Date: Wed, 20 Mar 2024 13:14:46 +0200 Subject: [PATCH 05/80] added hash property into description --- .../Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift index 1de5d9f4c..216bb6446 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift @@ -26,7 +26,7 @@ protocol CryptoSenderProtocol { func sendCrypto(token: String, amount: Double, address: HexAddress, chain: BlockchainType) async throws -> String /// Get all 'Send' Txs from the persistent storage. - /// - Returns: Array of Txs. ATM only 'status' property can be guaranteed + /// - Returns: Array of Txs. ATM only 'status' and 'hash' properties can be guaranteed func getStoredSendTxs() -> [StoredSendTransactionProtocol] } From fe25d7e0074ed8e91a30055abed447a0e214138c Mon Sep 17 00:00:00 2001 From: rommex Date: Wed, 20 Mar 2024 15:35:18 +0200 Subject: [PATCH 06/80] rm StoredTx --- .../CryptoSenderProtocol.swift | 31 ------------------- 1 file changed, 31 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift index 216bb6446..f4ae577c4 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift @@ -24,10 +24,6 @@ protocol CryptoSenderProtocol { /// - chain: chain of the transaction /// - Returns: TX Hash if success func sendCrypto(token: String, amount: Double, address: HexAddress, chain: BlockchainType) async throws -> String - - /// Get all 'Send' Txs from the persistent storage. - /// - Returns: Array of Txs. ATM only 'status' and 'hash' properties can be guaranteed - func getStoredSendTxs() -> [StoredSendTransactionProtocol] } @@ -49,26 +45,10 @@ extension CryptoSenderProtocol { private func sendTX() { // TODO: } - - private func storeTX() { - // TODO: - } -} - -protocol StoredSendTransactionProtocol { - var status: TxStatusGroup { get } - var hash: String { get } } - // ========================== -struct DemoSendTransaction: StoredSendTransactionProtocol { - let hash: String - var status: TxStatusGroup - let token: String - let amount: Double -} class DemoCryptoSender: CryptoSenderProtocol { static let instance: DemoCryptoSender = DemoCryptoSender() @@ -83,15 +63,4 @@ class DemoCryptoSender: CryptoSenderProtocol { func canSendCrypto(token: String, chain: BlockchainType) -> Bool { return false } - - func getStoredSendTxs() -> [StoredSendTransactionProtocol] { - return [DemoSendTransaction(hash: "0x", - status: .pending, - token: "crypto.LINK.address", - amount: 1.0)] - } - - func getAllDemoSendTransactions() -> [DemoSendTransaction] { - return getStoredSendTxs() as! [DemoSendTransaction] - } } From 73741ead49c2a5ff8f92b56450985f2b728f0d1b Mon Sep 17 00:00:00 2001 From: rommex Date: Thu, 21 Mar 2024 20:59:42 +0200 Subject: [PATCH 07/80] create TX also extracting JRPC methods --- .../project.pbxproj | 16 +++ .../Entities/Wallets/BlockchainType.swift | 4 +- .../Home/SendCryptoAsset/CryptoSender.swift | 103 ++++++++++++++++++ .../CryptoSenderProtocol.swift | 19 ---- .../Home/SendCryptoAsset/JRPC_Client.swift | 50 +++++++++ .../WalletConnectServiceV2.swift | 27 +---- 6 files changed, 174 insertions(+), 45 deletions(-) create mode 100644 unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift create mode 100644 unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift diff --git a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj index bb901927e..027c733d1 100644 --- a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj +++ b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 29018C8D2BACB7BC0004545D /* JRPC_Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29018C8C2BACB7BC0004545D /* JRPC_Client.swift */; }; + 29018C8E2BACB7BC0004545D /* JRPC_Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29018C8C2BACB7BC0004545D /* JRPC_Client.swift */; }; + 29018C8F2BACB7BC0004545D /* JRPC_Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29018C8C2BACB7BC0004545D /* JRPC_Client.swift */; }; 290A60422950A89900882109 /* WalletConnectServiceV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290A60412950A89900882109 /* WalletConnectServiceV2.swift */; }; 290A60482950AA1600882109 /* WalletConnect in Frameworks */ = {isa = PBXBuildFile; productRef = 290A60472950AA1600882109 /* WalletConnect */; }; 290A604A2950AA1600882109 /* WalletConnectAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 290A60492950AA1600882109 /* WalletConnectAuth */; }; @@ -32,6 +35,9 @@ 29AA6AA42BAAE9F500D24FB5 /* CryptoSenderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA32BAAE9F500D24FB5 /* CryptoSenderProtocol.swift */; }; 29AA6AA52BAAE9F500D24FB5 /* CryptoSenderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA32BAAE9F500D24FB5 /* CryptoSenderProtocol.swift */; }; 29AA6AA62BAAE9F500D24FB5 /* CryptoSenderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA32BAAE9F500D24FB5 /* CryptoSenderProtocol.swift */; }; + 29AA6AA82BAC389D00D24FB5 /* CryptoSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA72BAC389D00D24FB5 /* CryptoSender.swift */; }; + 29AA6AA92BAC389D00D24FB5 /* CryptoSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA72BAC389D00D24FB5 /* CryptoSender.swift */; }; + 29AA6AAA2BAC389D00D24FB5 /* CryptoSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA72BAC389D00D24FB5 /* CryptoSender.swift */; }; 29AEA04B292F7D60003BB5B4 /* SecurePersistedStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AEA04A292F7D60003BB5B4 /* SecurePersistedStorage.swift */; }; 29AEA050292F941F003BB5B4 /* PersistedSignaturesStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AEA04F292F941F003BB5B4 /* PersistedSignaturesStorage.swift */; }; 29AEA05529367785003BB5B4 /* UDWallet+Signing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AEA05429367785003BB5B4 /* UDWallet+Signing.swift */; }; @@ -2469,6 +2475,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 29018C8C2BACB7BC0004545D /* JRPC_Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JRPC_Client.swift; sourceTree = ""; }; 290A60412950A89900882109 /* WalletConnectServiceV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletConnectServiceV2.swift; sourceTree = ""; }; 29150FBA2975DA4D00169A1A /* PushSubscriberInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushSubscriberInfo.swift; sourceTree = ""; }; 291A9C6929E444CF00527FF1 /* WC_v1+v2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WC_v1+v2.swift"; sourceTree = ""; }; @@ -2482,6 +2489,7 @@ 299713D128F6933F00743003 /* ABIEncoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ABIEncoder.swift; sourceTree = ""; }; 299713D628F693F600743003 /* EIP712TypedData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EIP712TypedData.swift; sourceTree = ""; }; 29AA6AA32BAAE9F500D24FB5 /* CryptoSenderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoSenderProtocol.swift; sourceTree = ""; }; + 29AA6AA72BAC389D00D24FB5 /* CryptoSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoSender.swift; sourceTree = ""; }; 29AEA04A292F7D60003BB5B4 /* SecurePersistedStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecurePersistedStorage.swift; sourceTree = ""; }; 29AEA04F292F941F003BB5B4 /* PersistedSignaturesStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistedSignaturesStorage.swift; sourceTree = ""; }; 29AEA05429367785003BB5B4 /* UDWallet+Signing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UDWallet+Signing.swift"; sourceTree = ""; }; @@ -6281,6 +6289,8 @@ C67213CD2BAA80670075B9C7 /* SelectReceiver */, C67213E72BAA98EC0075B9C7 /* SelectAssetToSend */, 29AA6AA32BAAE9F500D24FB5 /* CryptoSenderProtocol.swift */, + 29AA6AA72BAC389D00D24FB5 /* CryptoSender.swift */, + 29018C8C2BACB7BC0004545D /* JRPC_Client.swift */, ); path = SendCryptoAsset; sourceTree = ""; @@ -8940,6 +8950,7 @@ C69F99212A9F1264004B1958 /* HexagonShape.swift in Sources */, C66689502811717C002062B4 /* PasscodeButton.swift in Sources */, C669C3682912197C00837F21 /* EnterValueViewPresenter.swift in Sources */, + 29AA6AA82BAC389D00D24FB5 /* CryptoSender.swift in Sources */, C6493A022B63A8B700457363 /* TrackingPressingStateModifier.swift in Sources */, C6D647342B1ED9D600D724AC /* WCRequestUIConfiguration.swift in Sources */, C650AC3D298105C900A398AB /* UDSegmentedControl.swift in Sources */, @@ -9175,6 +9186,7 @@ 307852642771FB030039FF40 /* DeepLinks.swift in Sources */, C606B741286474E000DC562B /* WalletWithInfo.swift in Sources */, C6D3B9BA2A6EB8F80091B279 /* PushMessagingChannelsAPIService.swift in Sources */, + 29018C8D2BACB7BC0004545D /* JRPC_Client.swift in Sources */, C650E28A2B9E97B1002C120A /* UDButtonStyle+Medium.swift in Sources */, C63095CF2B0DA5DE00205054 /* PurchaseDomainsPreferencesStorageEnvironmentKey.swift in Sources */, C68BAC922B919D8E00001CA0 /* ChatViewScrollHandler.swift in Sources */, @@ -9740,12 +9752,14 @@ C617CFA62B9EDA1300663516 /* TestableWalletConnectServiceV2.swift in Sources */, C6B2E2122B970E0900CEA1F9 /* DomainProfileSocialRelationshipDetailsTests.swift in Sources */, C617CFA42B9ED9F200663516 /* TestableDomainTransactionsService.swift in Sources */, + 29018C8E2BACB7BC0004545D /* JRPC_Client.swift in Sources */, C63391782A86819600623188 /* XMTPMessagingAPIServiceTests.swift in Sources */, C67B6D572AE79E8400F74B0B /* ImagesCacheStorageTests.swift in Sources */, C67DE1432B983FD0002374CE /* HomeExploreViewModelTests.swift in Sources */, C6B6B8732B91A4F900565ED2 /* TaskWithDeadlineTests.swift in Sources */, C6B2E2142B9714B100CEA1F9 /* DomainProfilesServiceTests.swift in Sources */, C6DF9870290F83020098733A /* testUpdateRecords.swift in Sources */, + 29AA6AA92BAC389D00D24FB5 /* CryptoSender.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -10094,6 +10108,7 @@ C630E4AD2B7F4B8D008F3269 /* FlippedUpsideDownModifier.swift in Sources */, C6D646E22B1ED49D00D724AC /* TableViewSelectionCell.swift in Sources */, C6FBCAA32B91C6AC00BA39DF /* HomeExploreRecentProfilesSectionView.swift in Sources */, + 29AA6AAA2BAC389D00D24FB5 /* CryptoSender.swift in Sources */, C6C8F8262B217CDD00A9834D /* UDWallet+RecoveryType.swift in Sources */, C6D647182B1ED83800D724AC /* CollectionTextFooterReusableView.swift in Sources */, C61B6C712B9F056C007408FD /* PublicProfileBadgeTileView.swift in Sources */, @@ -10511,6 +10526,7 @@ C61807C52B19A3580032E543 /* PreviewAppContext.swift in Sources */, C6960C3D2B1997F600B79E28 /* Constants.swift in Sources */, C6960C602B199AC900B79E28 /* Version.swift in Sources */, + 29018C8F2BACB7BC0004545D /* JRPC_Client.swift in Sources */, C63AD0982B955BE600BF8C83 /* HomeExploreDomainRowView.swift in Sources */, C6D646532B1ED10100D724AC /* DomainProfileUpdatingRecordsCell.swift in Sources */, C6D646892B1ED12D00D724AC /* DomainProfileGeneralInfoSection.swift in Sources */, diff --git a/unstoppable-ios-app/domains-manager-ios/Entities/Wallets/BlockchainType.swift b/unstoppable-ios-app/domains-manager-ios/Entities/Wallets/BlockchainType.swift index 88d6c638e..2eb4953a2 100644 --- a/unstoppable-ios-app/domains-manager-ios/Entities/Wallets/BlockchainType.swift +++ b/unstoppable-ios-app/domains-manager-ios/Entities/Wallets/BlockchainType.swift @@ -41,12 +41,12 @@ enum BlockchainType: String, CaseIterable, Codable, Hashable { } } - func supportedChainId(isTestNet: Bool) -> Int? { + func supportedChainId(isTestNet: Bool) -> Int { switch self { case .Ethereum: return isTestNet ? 5 : 1 // Goerly or Mainnet case .Matic: - return isTestNet ? 80001 : 137 // Mumbai or + return isTestNet ? 80001 : 137 // Mumbai or Polygon } } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift new file mode 100644 index 000000000..d89743b8a --- /dev/null +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -0,0 +1,103 @@ +// +// CryptoSender.swift +// domains-manager-ios +// +// Created by Roman Medvid on 21.03.2024. +// + +import Foundation +import Boilertalk_Web3 +import BigInt + + +class DemoCryptoSender: CryptoSenderProtocol { + static let instance: DemoCryptoSender = DemoCryptoSender() + private init() {} + + func sendCrypto(token: String, amount: Double, address: HexAddress, chain: BlockchainType) async throws -> String { + + + // create TX + + + // send Tx + + + +// // Set up your Infura URL and Ethereum addresses +// let infuraUrl = "https://mainnet.infura.io/v3/YOUR_INFURA_API_KEY" +// let senderAddress = "0xYourSenderAddress" +// let privateKey = "YourPrivateKey" +// +// // Create a new web3 instance +// let web3 = Web3(rpcURL: "https://mainnet.infura.io/") +// +// // Load your Ethereum address and private key +// let sender = try EthereumAddress(senderAddress) +// let privateKeyData = Data.fromHex(privateKey) +// +// // Set up transaction parameters +// let toAddress = "0xRecipientAddress" +//// let amountToSend = Web3.Utils.parseToBigUInt("1", units: .eth)! +//// let gasPrice = Web3.Utils.parseToBigUInt("50", units: .Gwei)! +// let gasLimit = BigUInt(21000) +// +// // Create a transaction +// let transaction = EthereumTransaction( +// nonce: web3.eth.getTransactionCount(address: sender), +// gasPrice: gasPrice, +// gasLimit: gasLimit, +// to: EthereumAddress(toAddress), +// value: amountToSend, +// data: Data() +// ) +// +// // Sign the transaction +// guard let signedTransaction = try? transaction.sign(with: privateKeyData, chainId: 1) else { +// print("Failed to sign transaction") +// return "" +// } +// +// // Send the transaction +// web3.eth.sendRawTransaction(signedTransaction, withChainId: 1) { result in +// switch result { +// case .success(let txHash): +// print("Transaction sent successfully. TxHash: \(txHash)") +// case .failure(let error): +// print("Failed to send transaction: \(error)") +// } +// } + + + return "" + + } + + private func createTransaction(fromAddress: HexAddress, + toAddress: HexAddress, + chainId: Int) async throws -> EthereumTransaction { + let nonce: EthereumQuantity = try await JRPC_Client.instance.fetchNonce(address: fromAddress, + chainId: chainId) + let gasPrice = try await JRPC_Client.instance.fetchGasPrice(chainId: chainId) + let sender = EthereumAddress(hexString: fromAddress) + let receiver = EthereumAddress(hexString: toAddress) + var transaction = EthereumTransaction(nonce: nonce, + gasPrice: gasPrice, + from: sender, + to: receiver, + value: try EthereumQuantity(ethereumValue: 1)) + + + // TODO: + + + return transaction + } + + + + + func canSendCrypto(token: String, chain: BlockchainType) -> Bool { + return false + } +} diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift index f4ae577c4..0d4a9ab59 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift @@ -35,7 +35,6 @@ extension CryptoSenderProtocol { // send TX to the chain - // store TX in the storage } private func createTX() { @@ -46,21 +45,3 @@ extension CryptoSenderProtocol { // TODO: } } - -// ========================== - - -class DemoCryptoSender: CryptoSenderProtocol { - static let instance: DemoCryptoSender = DemoCryptoSender() - private init() {} - - @discardableResult - func sendCrypto(token: String, amount: Double, address: HexAddress, chain: BlockchainType) -> String { - // TODO: - return "0x" - } - - func canSendCrypto(token: String, chain: BlockchainType) -> Bool { - return false - } -} diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift new file mode 100644 index 000000000..58c72cf2d --- /dev/null +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift @@ -0,0 +1,50 @@ +// +// JRPC_Client.swift +// domains-manager-ios +// +// Created by Roman Medvid on 21.03.2024. +// + +import Foundation +import Boilertalk_Web3 + +struct JRPC_Client { + static let instance = JRPC_Client() + private init() { } + + enum Error: Swift.Error { + case failedFetchGas + } + + func fetchNonce(address: HexAddress, chainId: Int) async throws -> EthereumQuantity { + guard let nonce = await fetchNonce(address: address, chainId: chainId), + let nonceBig = BigUInt(nonce.droppedHexPrefix, radix: 16) else { + throw WalletConnectRequestError.failedFetchNonce + } + return EthereumQuantity(quantity: nonceBig) + } + + func fetchNonce(address: HexAddress, chainId: Int) async -> String? { + guard let nonceString = try? await NetworkService().getTransactionCount(address: address, + chainId: chainId) else { + Debugger.printFailure("Failed to fetch nonce for address: \(address)", critical: true) + return nil + } + Debugger.printInfo(topic: .WalletConnect, "Fetched nonce successfully: \(nonceString)") + return nonceString + } + + func fetchGasPrice(chainId: Int) async throws -> EthereumQuantity { + guard let gasPrice = try? await NetworkService().getGasPrice(chainId: chainId) else { + Debugger.printFailure("Failed to fetch gasPrice", critical: false) + throw Self.Error.failedFetchGas + } + Debugger.printInfo(topic: .WalletConnect, "Fetched gasPrice successfully: \(gasPrice)") + let gasPriceBigUInt = BigUInt(gasPrice.droppedHexPrefix, radix: 16) + + guard let gasPriceBigUInt else { + throw Self.Error.failedFetchGas + } + return EthereumQuantity(quantity: gasPriceBigUInt) + } +} diff --git a/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift b/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift index dc68175fe..9e5183e9a 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift @@ -825,10 +825,8 @@ extension WalletConnectServiceV2: WalletConnectV2RequestHandlingServiceProtocol var txBuilding = transaction if txBuilding.gasPrice == nil { - guard let gasPrice = try await fetchGasPrice(chainId: chainId) else { - throw WalletConnectRequestError.failedFetchGas - } - txBuilding.gasPrice = EthereumQuantity(quantity: gasPrice) + let gasPrice = try await JRPC_Client.instance.fetchGasPrice(chainId: chainId) + txBuilding.gasPrice = gasPrice } txBuilding = try await ensureGasLimit(transaction: txBuilding, chainId: chainId) @@ -840,15 +838,6 @@ extension WalletConnectServiceV2: WalletConnectV2RequestHandlingServiceProtocol return txBuilding } - private func fetchGasPrice(chainId: Int) async throws -> BigUInt? { - guard let gasPrice = try? await NetworkService().getGasPrice(chainId: chainId) else { - Debugger.printFailure("Failed to fetch gasPrice", critical: false) - throw WalletConnectRequestError.failedFetchGas - } - Debugger.printInfo(topic: .WalletConnect, "Fetched gasPrice successfully: \(gasPrice)") - return BigUInt(gasPrice.droppedHexPrefix, radix: 16) - } - private func ensureGasLimit(transaction: EthereumTransaction, chainId: Int) async throws -> EthereumTransaction { guard transaction.gas == nil else { return transaction @@ -876,17 +865,7 @@ extension WalletConnectServiceV2: WalletConnectV2RequestHandlingServiceProtocol private func fetchNonce(transaction: EthereumTransaction, chainId: Int) async -> String? { guard let addressString = transaction.from?.hex() else { return nil } - return await fetchNonce(address: addressString, chainId: chainId) - } - - private func fetchNonce(address: HexAddress, chainId: Int) async -> String? { - guard let nonceString = try? await NetworkService().getTransactionCount(address: address, - chainId: chainId) else { - Debugger.printFailure("Failed to fetch nonce for address: \(address)", critical: true) - return nil - } - Debugger.printInfo(topic: .WalletConnect, "Fetched nonce successfully: \(nonceString)") - return nonceString + return await JRPC_Client.instance.fetchNonce(address: addressString, chainId: chainId) } private func fetchGasLimit(transaction: EthereumTransaction, chainId: Int) async throws -> BigUInt { From 4f12ffd8a5a6002153d99fbaaf94365d8f56247f Mon Sep 17 00:00:00 2001 From: rommex Date: Thu, 21 Mar 2024 22:28:59 +0200 Subject: [PATCH 08/80] refactor protocol --- .../Home/SendCryptoAsset/CryptoSender.swift | 28 +++++++++++++------ .../CryptoSenderProtocol.swift | 3 +- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index d89743b8a..895f76da9 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -11,14 +11,19 @@ import BigInt class DemoCryptoSender: CryptoSenderProtocol { - static let instance: DemoCryptoSender = DemoCryptoSender() - private init() {} - - func sendCrypto(token: String, amount: Double, address: HexAddress, chain: BlockchainType) async throws -> String { + let wallet: UDWallet + + required init(wallet: UDWallet) { + self.wallet = wallet + } + + func sendCrypto(token: String, amount: Double, toAddress: HexAddress, chain: BlockchainType) async throws -> String { // create TX - + let tx = try await createNativeSendTransaction(fromAddress: self.wallet.address, + toAddress: toAddress, + chainId: chain.supportedChainId(isTestNet: true)) // send Tx @@ -73,7 +78,7 @@ class DemoCryptoSender: CryptoSenderProtocol { } - private func createTransaction(fromAddress: HexAddress, + private func createNativeSendTransaction(fromAddress: HexAddress, toAddress: HexAddress, chainId: Int) async throws -> EthereumTransaction { let nonce: EthereumQuantity = try await JRPC_Client.instance.fetchNonce(address: fromAddress, @@ -81,11 +86,13 @@ class DemoCryptoSender: CryptoSenderProtocol { let gasPrice = try await JRPC_Client.instance.fetchGasPrice(chainId: chainId) let sender = EthereumAddress(hexString: fromAddress) let receiver = EthereumAddress(hexString: toAddress) - var transaction = EthereumTransaction(nonce: nonce, + let transaction = EthereumTransaction(nonce: nonce, gasPrice: gasPrice, + gas: try EthereumQuantity(21000.gwei), from: sender, to: receiver, - value: try EthereumQuantity(ethereumValue: 1)) + value: try EthereumQuantity(1.gwei) + ) // TODO: @@ -94,6 +101,11 @@ class DemoCryptoSender: CryptoSenderProtocol { return transaction } + private func send(transaction: EthereumTransaction, chainId: Int) async throws -> String { + // TODO: extract from WC2 + return "" + } + diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift index 0d4a9ab59..f46342882 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift @@ -8,6 +8,7 @@ import Foundation protocol CryptoSenderProtocol { + init(wallet: UDWallet) /// Indicates if Sender supports sending of a token on the chain /// - Parameters: @@ -23,7 +24,7 @@ protocol CryptoSenderProtocol { /// - address: address of the receiver /// - chain: chain of the transaction /// - Returns: TX Hash if success - func sendCrypto(token: String, amount: Double, address: HexAddress, chain: BlockchainType) async throws -> String + func sendCrypto(token: String, amount: Double, toAddress: HexAddress, chain: BlockchainType) async throws -> String } From e289af4212bcd2a3ec154fc8c136e9a94b469727 Mon Sep 17 00:00:00 2001 From: rommex Date: Fri, 22 Mar 2024 16:45:41 +0200 Subject: [PATCH 09/80] extracting sendTx from WC2 --- .../Home/SendCryptoAsset/CryptoSender.swift | 29 +++++----- .../Home/SendCryptoAsset/JRPC_Client.swift | 51 ++++++++++++++++++ .../WalletConnectServiceV2.swift | 53 +------------------ 3 files changed, 64 insertions(+), 69 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index 895f76da9..c82d30138 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -10,24 +10,29 @@ import Boilertalk_Web3 import BigInt -class DemoCryptoSender: CryptoSenderProtocol { +struct DemoCryptoSender: CryptoSenderProtocol { let wallet: UDWallet - required init(wallet: UDWallet) { + init(wallet: UDWallet) { self.wallet = wallet } - func sendCrypto(token: String, amount: Double, toAddress: HexAddress, chain: BlockchainType) async throws -> String { + func sendCrypto(token: String, + amount: Double, + toAddress: HexAddress, + chain: BlockchainType) async throws -> String { + let chainId = chain.supportedChainId(isTestNet: true) // create TX let tx = try await createNativeSendTransaction(fromAddress: self.wallet.address, toAddress: toAddress, - chainId: chain.supportedChainId(isTestNet: true)) + chainId: chainId) // send Tx + let hash = try await JRPC_Client.instance.sendTx(transaction: tx, udWallet: self.wallet, chainIdInt: chainId) - + return hash // // Set up your Infura URL and Ethereum addresses // let infuraUrl = "https://mainnet.infura.io/v3/YOUR_INFURA_API_KEY" @@ -74,8 +79,6 @@ class DemoCryptoSender: CryptoSenderProtocol { // } - return "" - } private func createNativeSendTransaction(fromAddress: HexAddress, @@ -95,20 +98,12 @@ class DemoCryptoSender: CryptoSenderProtocol { ) - // TODO: + // TODO: gas limit, value return transaction } - - private func send(transaction: EthereumTransaction, chainId: Int) async throws -> String { - // TODO: extract from WC2 - return "" - } - - - - + func canSendCrypto(token: String, chain: BlockchainType) -> Bool { return false } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift index 58c72cf2d..7e731f22b 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift @@ -47,4 +47,55 @@ struct JRPC_Client { } return EthereumQuantity(quantity: gasPriceBigUInt) } + + func sendTx(transaction: EthereumTransaction, + udWallet: UDWallet, + chainIdInt: Int) async throws -> String { + + return try await withCheckedThrowingContinuation { continuation in + guard let urlString = NetworkService().getJRPCProviderUrl(chainId: chainIdInt)?.absoluteString else { + Debugger.printFailure("Failed to get net name for chain Id: \(chainIdInt)", critical: true) + continuation.resume(with: .failure(WalletConnectRequestError.failedToDetermineChainId)) + return + } + let web3 = Web3(rpcURL: urlString) + guard let privKeyString = udWallet.getPrivateKey() else { + Debugger.printFailure("No private key in \(udWallet)", critical: true) + continuation.resume(with: .failure(WalletConnectRequestError.failedToGetPrivateKey)) + return + } + guard let privateKey = try? EthereumPrivateKey(hexPrivateKey: privKeyString) else { + Debugger.printFailure("No private key in \(udWallet)", critical: true) + continuation.resume(with: .failure(WalletConnectRequestError.failedToGetPrivateKey)) + return + } + let chainId = EthereumQuantity(quantity: BigUInt(chainIdInt)) + + let gweiAmount = (transaction.gas ?? 0).quantity * (transaction.gasPrice ?? 0).quantity + (transaction.value ?? 0).quantity + Debugger.printInfo(topic: .WalletConnectV2, "Total balance should be \(gweiAmount / ( BigUInt(10).power(12)) ) millionth of eth") + + do { + try transaction.sign(with: privateKey, chainId: chainId).promise + .then { tx in + web3.eth.sendRawTransaction(transaction: tx) } + .done { hash in + guard let result = hash.ethereumValue().string else { + Debugger.printFailure("Failed to parse response from sending: \(transaction)") + continuation.resume(with: .failure(WalletConnectRequestError.failedParseSendTxResponse)) + return + } + continuation.resume(with: .success(result)) + }.catch { error in + Debugger.printFailure("Sending a TX was failed: \(error.localizedDescription)") + continuation.resume(with: .failure(WalletConnectRequestError.failedSendTx)) + return + } + } catch { + Debugger.printFailure("Signing a TX was failed: \(error.localizedDescription)") + continuation.resume(with: .failure(WalletConnectRequestError.failedToSignTransaction)) + return + } + } + + } } diff --git a/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift b/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift index 9e5183e9a..d15bb7b54 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift @@ -750,7 +750,7 @@ extension WalletConnectServiceV2: WalletConnectV2RequestHandlingServiceProtocol return response } - let hash = try await sendTx(transaction: completedTx, + let hash = try await JRPC_Client.instance.sendTx(transaction: completedTx, udWallet: udWallet, chainIdInt: chainIdInt) let hashCodable = WCAnyCodable(hash) @@ -998,57 +998,6 @@ extension WalletConnectServiceV2 { } return String(parts[2]) } - - private func sendTx(transaction: EthereumTransaction, - udWallet: UDWallet, - chainIdInt: Int) async throws -> String { - - return try await withCheckedThrowingContinuation { continuation in - guard let urlString = NetworkService().getJRPCProviderUrl(chainId: chainIdInt)?.absoluteString else { - Debugger.printFailure("Failed to get net name for chain Id: \(chainIdInt)", critical: true) - continuation.resume(with: .failure(WalletConnectRequestError.failedToDetermineChainId)) - return - } - let web3 = Web3(rpcURL: urlString) - guard let privKeyString = udWallet.getPrivateKey() else { - Debugger.printFailure("No private key in \(udWallet)", critical: true) - continuation.resume(with: .failure(WalletConnectRequestError.failedToGetPrivateKey)) - return - } - guard let privateKey = try? EthereumPrivateKey(hexPrivateKey: privKeyString) else { - Debugger.printFailure("No private key in \(udWallet)", critical: true) - continuation.resume(with: .failure(WalletConnectRequestError.failedToGetPrivateKey)) - return - } - let chainId = EthereumQuantity(quantity: BigUInt(chainIdInt)) - - let gweiAmount = (transaction.gas ?? 0).quantity * (transaction.gasPrice ?? 0).quantity + (transaction.value ?? 0).quantity - Debugger.printInfo(topic: .WalletConnectV2, "Total balance should be \(gweiAmount / ( BigUInt(10).power(12)) ) millionth of eth") - - do { - try transaction.sign(with: privateKey, chainId: chainId).promise - .then { tx in - web3.eth.sendRawTransaction(transaction: tx) } - .done { hash in - guard let result = hash.ethereumValue().string else { - Debugger.printFailure("Failed to parse response from sending: \(transaction)") - continuation.resume(with: .failure(WalletConnectRequestError.failedParseSendTxResponse)) - return - } - continuation.resume(with: .success(result)) - }.catch { error in - Debugger.printFailure("Sending a TX was failed: \(error.localizedDescription)") - continuation.resume(with: .failure(WalletConnectRequestError.failedSendTx)) - return - } - } catch { - Debugger.printFailure("Signing a TX was failed: \(error.localizedDescription)") - continuation.resume(with: .failure(WalletConnectRequestError.failedToSignTransaction)) - return - } - } - - } func proceedSendTxViaWC_2(sessions: [SessionV2Proxy], chainId: Int, From 768925345e591f66f8bebd755e7c9ead85ee4f72 Mon Sep 17 00:00:00 2001 From: rommex Date: Fri, 22 Mar 2024 17:45:35 +0200 Subject: [PATCH 10/80] extracted fetchGasLimit() --- .../Home/SendCryptoAsset/JRPC_Client.swift | 28 +++++++++++++++++- .../WalletConnectServiceV2.swift | 29 ++----------------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift index 7e731f22b..3ecf82abb 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift @@ -14,6 +14,7 @@ struct JRPC_Client { enum Error: Swift.Error { case failedFetchGas + case failedFetchGasLimit } func fetchNonce(address: HexAddress, chainId: Int) async throws -> EthereumQuantity { @@ -48,6 +49,31 @@ struct JRPC_Client { return EthereumQuantity(quantity: gasPriceBigUInt) } + func fetchGasLimit(transaction: EthereumTransaction, chainId: Int) async throws -> EthereumQuantity { + do { + let gasPriceString = try await NetworkService().getGasEstimation(tx: transaction, + chainId: chainId) + guard let result = BigUInt(gasPriceString.droppedHexPrefix, radix: 16) else { + Debugger.printFailure("Failed to parse gas Estimate from: \(gasPriceString)", critical: true) + throw Self.Error.failedFetchGasLimit + } + Debugger.printInfo(topic: .WalletConnect, "Fetched gas Estimate successfully: \(gasPriceString)") + return EthereumQuantity(quantity: result) + } catch { + if let jrpcError = error as? NetworkService.JRPCError { + switch jrpcError { + case .gasRequiredExceedsAllowance: + Debugger.printFailure("Failed to fetch gas Estimate because of Low Allowance Error", critical: false) + throw WalletConnectRequestError.lowAllowance + default: throw WalletConnectRequestError.failedFetchGas + } + } else { + Debugger.printFailure("Failed to fetch gas Estimate: \(error.localizedDescription)", critical: false) + throw WalletConnectRequestError.failedFetchGas + } + } + } + func sendTx(transaction: EthereumTransaction, udWallet: UDWallet, chainIdInt: Int) async throws -> String { @@ -86,7 +112,7 @@ struct JRPC_Client { } continuation.resume(with: .success(result)) }.catch { error in - Debugger.printFailure("Sending a TX was failed: \(error.localizedDescription)") + Debugger.printFailure("Sending a TX was failed: \(error.localizedDescription), \(error.displayTitleAndMessage())") continuation.resume(with: .failure(WalletConnectRequestError.failedSendTx)) return } diff --git a/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift b/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift index d15bb7b54..571eeecec 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift @@ -843,9 +843,9 @@ extension WalletConnectServiceV2: WalletConnectV2RequestHandlingServiceProtocol return transaction } - let gas = try await fetchGasLimit(transaction: transaction, chainId: chainId) +// let gas = try await fetchGasLimit(transaction: transaction, chainId: chainId) var newTx = transaction - newTx.gas = EthereumQuantity(quantity: gas) + newTx.gas = try await JRPC_Client.instance.fetchGasLimit(transaction: transaction, chainId: chainId) return newTx } @@ -867,31 +867,6 @@ extension WalletConnectServiceV2: WalletConnectV2RequestHandlingServiceProtocol guard let addressString = transaction.from?.hex() else { return nil } return await JRPC_Client.instance.fetchNonce(address: addressString, chainId: chainId) } - - private func fetchGasLimit(transaction: EthereumTransaction, chainId: Int) async throws -> BigUInt { - do { - let gasPriceString = try await NetworkService().getGasEstimation(tx: transaction, - chainId: chainId) - guard let result = BigUInt(gasPriceString.droppedHexPrefix, radix: 16) else { - Debugger.printFailure("Failed to parse gas Estimate from: \(gasPriceString)", critical: true) - throw WalletConnectRequestError.failedFetchGas - } - Debugger.printInfo(topic: .WalletConnect, "Fetched gas Estimate successfully: \(gasPriceString)") - return result - } catch { - if let jrpcError = error as? NetworkService.JRPCError { - switch jrpcError { - case .gasRequiredExceedsAllowance: - Debugger.printFailure("Failed to fetch gas Estimate because of Low Allowance Error", critical: false) - throw WalletConnectRequestError.lowAllowance - default: throw WalletConnectRequestError.failedFetchGas - } - } else { - Debugger.printFailure("Failed to fetch gas Estimate: \(error.localizedDescription)", critical: false) - throw WalletConnectRequestError.failedFetchGas - } - } - } } // MARK: - Private methods From 6a84efff174fba49f4f6444cead55c611207ec63 Mon Sep 17 00:00:00 2001 From: rommex Date: Fri, 22 Mar 2024 23:23:34 +0200 Subject: [PATCH 11/80] added working demo with Sepolia --- .../Home/SendCryptoAsset/CryptoSender.swift | 65 +++---------------- .../Services/Networking/NetworkService.swift | 4 +- .../SupportingFiles/Constants.swift | 5 ++ 3 files changed, 17 insertions(+), 57 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index c82d30138..af50a42fb 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -23,62 +23,11 @@ struct DemoCryptoSender: CryptoSenderProtocol { chain: BlockchainType) async throws -> String { let chainId = chain.supportedChainId(isTestNet: true) - - // create TX let tx = try await createNativeSendTransaction(fromAddress: self.wallet.address, toAddress: toAddress, chainId: chainId) - - // send Tx let hash = try await JRPC_Client.instance.sendTx(transaction: tx, udWallet: self.wallet, chainIdInt: chainId) - return hash - -// // Set up your Infura URL and Ethereum addresses -// let infuraUrl = "https://mainnet.infura.io/v3/YOUR_INFURA_API_KEY" -// let senderAddress = "0xYourSenderAddress" -// let privateKey = "YourPrivateKey" -// -// // Create a new web3 instance -// let web3 = Web3(rpcURL: "https://mainnet.infura.io/") -// -// // Load your Ethereum address and private key -// let sender = try EthereumAddress(senderAddress) -// let privateKeyData = Data.fromHex(privateKey) -// -// // Set up transaction parameters -// let toAddress = "0xRecipientAddress" -//// let amountToSend = Web3.Utils.parseToBigUInt("1", units: .eth)! -//// let gasPrice = Web3.Utils.parseToBigUInt("50", units: .Gwei)! -// let gasLimit = BigUInt(21000) -// -// // Create a transaction -// let transaction = EthereumTransaction( -// nonce: web3.eth.getTransactionCount(address: sender), -// gasPrice: gasPrice, -// gasLimit: gasLimit, -// to: EthereumAddress(toAddress), -// value: amountToSend, -// data: Data() -// ) -// -// // Sign the transaction -// guard let signedTransaction = try? transaction.sign(with: privateKeyData, chainId: 1) else { -// print("Failed to sign transaction") -// return "" -// } -// -// // Send the transaction -// web3.eth.sendRawTransaction(signedTransaction, withChainId: 1) { result in -// switch result { -// case .success(let txHash): -// print("Transaction sent successfully. TxHash: \(txHash)") -// case .failure(let error): -// print("Failed to send transaction: \(error)") -// } -// } - - } private func createNativeSendTransaction(fromAddress: HexAddress, @@ -89,16 +38,20 @@ struct DemoCryptoSender: CryptoSenderProtocol { let gasPrice = try await JRPC_Client.instance.fetchGasPrice(chainId: chainId) let sender = EthereumAddress(hexString: fromAddress) let receiver = EthereumAddress(hexString: toAddress) - let transaction = EthereumTransaction(nonce: nonce, + + var transaction = EthereumTransaction(nonce: nonce, gasPrice: gasPrice, - gas: try EthereumQuantity(21000.gwei), + gas: EthereumQuantity(21000), from: sender, to: receiver, - value: try EthereumQuantity(1.gwei) - ) + value: try EthereumQuantity(222.gwei) + ) + + let gasEstimate = try await JRPC_Client.instance.fetchGasLimit(transaction: transaction, chainId: chainId) + transaction.gas = gasEstimate - // TODO: gas limit, value + // TODO: value return transaction diff --git a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift index 194ea08d7..f60e19d23 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift @@ -278,7 +278,9 @@ extension NetworkService { } func getJRPCProviderUrl(chainId: Int) -> URL? { - guard let netName = BlockchainNetwork(rawValue: chainId)?.name else { return nil } + guard let netName = BlockchainNetwork(rawValue: chainId)?.name else { + return nil + } return URL(string: "https://\(netName).infura.io/v3/\(NetworkService.chooseInfuraProjectId())")! } diff --git a/unstoppable-ios-app/domains-manager-ios/SupportingFiles/Constants.swift b/unstoppable-ios-app/domains-manager-ios/SupportingFiles/Constants.swift index 2aec9188d..f2ab982a1 100644 --- a/unstoppable-ios-app/domains-manager-ios/SupportingFiles/Constants.swift +++ b/unstoppable-ios-app/domains-manager-ios/SupportingFiles/Constants.swift @@ -187,6 +187,7 @@ enum BlockchainNetwork: Int, CaseIterable { case ethMainnet = 1 case ethRinkby = 4 case ethGoerli = 5 + case ethSepolia = 11155111 case polygonMainnet = 137 case polygonMumbai = 80001 @@ -200,6 +201,8 @@ enum BlockchainNetwork: Int, CaseIterable { return "rinkby" case .ethGoerli: return "goerli" + case .ethSepolia: + return "sepolia" case .polygonMainnet: return "polygon-mainnet" case .polygonMumbai: @@ -215,6 +218,8 @@ enum BlockchainNetwork: Int, CaseIterable { return "Ethereum: Rinkby" case .ethGoerli: return "Ethereum: Goerli" + case .ethSepolia: + return "Ethereum: Sepolia" case .polygonMainnet: return "Polygon" case .polygonMumbai: From 0364832440d7f47c608c0d2b1ac29bcd7e79d370 Mon Sep 17 00:00:00 2001 From: rommex Date: Sat, 23 Mar 2024 14:02:55 +0200 Subject: [PATCH 12/80] factor in amount and chain env --- .../Entities/Wallets/BlockchainType.swift | 5 +++ .../Home/SendCryptoAsset/CryptoSender.swift | 43 +++++++++++++------ .../CryptoSenderProtocol.swift | 8 ++-- 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Entities/Wallets/BlockchainType.swift b/unstoppable-ios-app/domains-manager-ios/Entities/Wallets/BlockchainType.swift index 2eb4953a2..4987f7092 100644 --- a/unstoppable-ios-app/domains-manager-ios/Entities/Wallets/BlockchainType.swift +++ b/unstoppable-ios-app/domains-manager-ios/Entities/Wallets/BlockchainType.swift @@ -50,6 +50,11 @@ enum BlockchainType: String, CaseIterable, Codable, Hashable { } } + func supportedChainId(env: UnsConfigManager.BlockchainEnvironment) -> Int { + supportedChainId(isTestNet: env == .testnet) + } + + enum InitError: Error { case invalidBlockchainAbbreviation } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index af50a42fb..1fe93cf0e 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -9,43 +9,62 @@ import Foundation import Boilertalk_Web3 import BigInt +struct ChainSpec { + let blockchainType: BlockchainType + let env: UnsConfigManager.BlockchainEnvironment +} + +struct CryptoSpec { + let token: String + let amount: Double +} + struct DemoCryptoSender: CryptoSenderProtocol { + enum Error: Swift.Error { + case sendingNotSupported + } + + static let defaultSendTxGasPrice = 21_000 + let wallet: UDWallet init(wallet: UDWallet) { self.wallet = wallet } - func sendCrypto(token: String, - amount: Double, - toAddress: HexAddress, - chain: BlockchainType) async throws -> String { + func sendCrypto(crypto: CryptoSpec, + chain: ChainSpec, + toAddress: HexAddress) async throws -> String { - let chainId = chain.supportedChainId(isTestNet: true) - let tx = try await createNativeSendTransaction(fromAddress: self.wallet.address, + let chainId = chain.blockchainType.supportedChainId(env: chain.env) + let tx = try await createNativeSendTransaction(crypto: crypto, + fromAddress: self.wallet.address, toAddress: toAddress, chainId: chainId) let hash = try await JRPC_Client.instance.sendTx(transaction: tx, udWallet: self.wallet, chainIdInt: chainId) return hash } - private func createNativeSendTransaction(fromAddress: HexAddress, - toAddress: HexAddress, - chainId: Int) async throws -> EthereumTransaction { + private func createNativeSendTransaction(crypto: CryptoSpec, + fromAddress: HexAddress, + toAddress: HexAddress, + chainId: Int) async throws -> EthereumTransaction { let nonce: EthereumQuantity = try await JRPC_Client.instance.fetchNonce(address: fromAddress, chainId: chainId) let gasPrice = try await JRPC_Client.instance.fetchGasPrice(chainId: chainId) let sender = EthereumAddress(hexString: fromAddress) let receiver = EthereumAddress(hexString: toAddress) + let amount = BigUInt ( 1_000_000_000 * crypto.amount ) + var transaction = EthereumTransaction(nonce: nonce, gasPrice: gasPrice, - gas: EthereumQuantity(21000), + gas: try EthereumQuantity(ethereumValue: Self.defaultSendTxGasPrice), from: sender, to: receiver, - value: try EthereumQuantity(222.gwei) - ) + value: try EthereumQuantity(amount) + ) let gasEstimate = try await JRPC_Client.instance.fetchGasLimit(transaction: transaction, chainId: chainId) transaction.gas = gasEstimate diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift index f46342882..4d691a7d7 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift @@ -24,13 +24,13 @@ protocol CryptoSenderProtocol { /// - address: address of the receiver /// - chain: chain of the transaction /// - Returns: TX Hash if success - func sendCrypto(token: String, amount: Double, toAddress: HexAddress, chain: BlockchainType) async throws -> String + func sendCrypto(crypto: CryptoSpec, + chain: ChainSpec, + toAddress: HexAddress) async throws -> String } - - - extension CryptoSenderProtocol { + private func _sendCrypto(token: String, amount: Double, address: HexAddress, chain: BlockchainType) async throws { // create the TX From f6f9c9a5ad67b28d3fbdcf5fe47d3e0790efc80d Mon Sep 17 00:00:00 2001 From: rommex Date: Sat, 23 Mar 2024 14:29:57 +0200 Subject: [PATCH 13/80] fixed amount computation --- .../Modules/Home/SendCryptoAsset/CryptoSender.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index 1fe93cf0e..b96ad1953 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -25,7 +25,7 @@ struct DemoCryptoSender: CryptoSenderProtocol { case sendingNotSupported } - static let defaultSendTxGasPrice = 21_000 + static let defaultSendTxGasPrice: BigUInt = 21_000 let wallet: UDWallet @@ -60,10 +60,10 @@ struct DemoCryptoSender: CryptoSenderProtocol { var transaction = EthereumTransaction(nonce: nonce, gasPrice: gasPrice, - gas: try EthereumQuantity(ethereumValue: Self.defaultSendTxGasPrice), + gas: try EthereumQuantity(Self.defaultSendTxGasPrice), from: sender, to: receiver, - value: try EthereumQuantity(amount) + value: try EthereumQuantity(amount.gwei) ) let gasEstimate = try await JRPC_Client.instance.fetchGasLimit(transaction: transaction, chainId: chainId) From 735db11ae01a21a3567c0dfdcac4ddb96d7a0f73 Mon Sep 17 00:00:00 2001 From: rommex Date: Sat, 23 Mar 2024 14:40:49 +0200 Subject: [PATCH 14/80] helper init --- .../Modules/Home/SendCryptoAsset/CryptoSender.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index b96ad1953..20522a9f3 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -12,6 +12,11 @@ import BigInt struct ChainSpec { let blockchainType: BlockchainType let env: UnsConfigManager.BlockchainEnvironment + + init(blockchainType: BlockchainType, env: UnsConfigManager.BlockchainEnvironment = .mainnet) { + self.blockchainType = blockchainType + self.env = env + } } struct CryptoSpec { From 17388c5ec55e22c661c72f7520a398523dcad09d Mon Sep 17 00:00:00 2001 From: rommex Date: Sat, 23 Mar 2024 14:49:55 +0200 Subject: [PATCH 15/80] check support of native tokens --- .../Home/SendCryptoAsset/CryptoSender.swift | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index 20522a9f3..d9d23a5b5 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -31,6 +31,8 @@ struct DemoCryptoSender: CryptoSenderProtocol { } static let defaultSendTxGasPrice: BigUInt = 21_000 + static let ethTicker = "crypto.ETH.address" + static let maticTicker = "crypto.MATIC.version.MATIC.address" let wallet: UDWallet @@ -41,6 +43,10 @@ struct DemoCryptoSender: CryptoSenderProtocol { func sendCrypto(crypto: CryptoSpec, chain: ChainSpec, toAddress: HexAddress) async throws -> String { + + guard canSendCrypto(token: crypto.token, chain: chain.blockchainType) else { + throw Error.sendingNotSupported + } let chainId = chain.blockchainType.supportedChainId(env: chain.env) let tx = try await createNativeSendTransaction(crypto: crypto, @@ -73,15 +79,12 @@ struct DemoCryptoSender: CryptoSenderProtocol { let gasEstimate = try await JRPC_Client.instance.fetchGasLimit(transaction: transaction, chainId: chainId) transaction.gas = gasEstimate - - - // TODO: value - - return transaction } func canSendCrypto(token: String, chain: BlockchainType) -> Bool { - return false + // only native tokens supported + return (token == Self.ethTicker && chain == .Ethereum) || + (token == Self.maticTicker && chain == .Matic) } } From 5fae2d9686ef18cfbe2f4e2c54d2da7c31d14417 Mon Sep 17 00:00:00 2001 From: rommex Date: Sat, 23 Mar 2024 14:52:37 +0200 Subject: [PATCH 16/80] take use of default gas price --- .../Modules/Home/SendCryptoAsset/CryptoSender.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index d9d23a5b5..06284d053 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -25,7 +25,7 @@ struct CryptoSpec { } -struct DemoCryptoSender: CryptoSenderProtocol { +struct NativeCryptoSender: CryptoSenderProtocol { enum Error: Swift.Error { case sendingNotSupported } @@ -77,8 +77,9 @@ struct DemoCryptoSender: CryptoSenderProtocol { value: try EthereumQuantity(amount.gwei) ) - let gasEstimate = try await JRPC_Client.instance.fetchGasLimit(transaction: transaction, chainId: chainId) - transaction.gas = gasEstimate + if let gasEstimate = try? await JRPC_Client.instance.fetchGasLimit(transaction: transaction, chainId: chainId) { + transaction.gas = gasEstimate + } return transaction } From 0b85a6ed33b774a2953d38d8333301bbb0e3ddf3 Mon Sep 17 00:00:00 2001 From: rommex Date: Sun, 24 Mar 2024 20:03:33 +0200 Subject: [PATCH 17/80] added TxSpeed, top-level CryptoSender --- .../Home/SendCryptoAsset/CryptoSender.swift | 89 ++++++++++++++++--- .../CryptoSenderProtocol.swift | 25 ++---- 2 files changed, 87 insertions(+), 27 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index 06284d053..36f1eb66a 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -19,33 +19,73 @@ struct ChainSpec { } } -struct CryptoSpec { +struct CryptoSendingSpec { + enum TxSpeed { + case normal, fast, urgent + } + let token: String let amount: Double + let speed: TxSpeed + + init(token: String, amount: Double, speed: TxSpeed = .normal) { + self.token = token + self.amount = amount + self.speed = speed + } } - -struct NativeCryptoSender: CryptoSenderProtocol { +struct CryptoSender: CryptoSenderProtocol { enum Error: Swift.Error { case sendingNotSupported } - static let defaultSendTxGasPrice: BigUInt = 21_000 - static let ethTicker = "crypto.ETH.address" - static let maticTicker = "crypto.MATIC.version.MATIC.address" + let wallet: UDWallet + + init(wallet: UDWallet) { + self.wallet = wallet + } + + func canSendCrypto(token: String, chain: BlockchainType) -> Bool { + // only native tokens supported + return (token == Self.ethTicker && chain == .Ethereum) || + (token == Self.maticTicker && chain == .Matic) + } + + func sendCrypto(crypto: CryptoSendingSpec, chain: ChainSpec, toAddress: HexAddress) async throws -> String { + guard canSendCrypto(token: crypto.token, chain: chain.blockchainType) else { + throw Error.sendingNotSupported + } + let cryptoSender: CryptoSenderProtocol = NativeCryptoSender(wallet: wallet) + return try await cryptoSender.sendCrypto(crypto: crypto, chain: chain, toAddress: toAddress) + + } + func computeGasFeeFrom(maxCrypto: CryptoSendingSpec, on chain: ChainSpec, toAddress: HexAddress) async throws -> BigUInt { + guard canSendCrypto(token: maxCrypto.token, chain: chain.blockchainType) else { + throw Error.sendingNotSupported + } + let cryptoSender: CryptoSenderProtocol = NativeCryptoSender(wallet: wallet) + return try await cryptoSender.computeGasFeeFrom(maxCrypto: maxCrypto, + on: chain, + toAddress: toAddress) + } + + +} + +struct NativeCryptoSender: CryptoSenderProtocol { let wallet: UDWallet init(wallet: UDWallet) { self.wallet = wallet } - func sendCrypto(crypto: CryptoSpec, + func sendCrypto(crypto: CryptoSendingSpec, chain: ChainSpec, toAddress: HexAddress) async throws -> String { - guard canSendCrypto(token: crypto.token, chain: chain.blockchainType) else { - throw Error.sendingNotSupported + throw CryptoSender.Error.sendingNotSupported } let chainId = chain.blockchainType.supportedChainId(env: chain.env) @@ -57,7 +97,7 @@ struct NativeCryptoSender: CryptoSenderProtocol { return hash } - private func createNativeSendTransaction(crypto: CryptoSpec, + private func createNativeSendTransaction(crypto: CryptoSendingSpec, fromAddress: HexAddress, toAddress: HexAddress, chainId: Int) async throws -> EthereumTransaction { @@ -67,7 +107,7 @@ struct NativeCryptoSender: CryptoSenderProtocol { let sender = EthereumAddress(hexString: fromAddress) let receiver = EthereumAddress(hexString: toAddress) - let amount = BigUInt ( 1_000_000_000 * crypto.amount ) + let amount = BigUInt(1_000_000_000 * crypto.amount) var transaction = EthereumTransaction(nonce: nonce, gasPrice: gasPrice, @@ -82,6 +122,33 @@ struct NativeCryptoSender: CryptoSenderProtocol { } return transaction } + + func computeGasFeeFrom(maxCrypto: CryptoSendingSpec, + on chain: ChainSpec, + toAddress: HexAddress) async throws -> BigUInt { + let chainId = chain.blockchainType.supportedChainId(env: chain.env) + let fromAddress = self.wallet.address + let nonce: EthereumQuantity = try await JRPC_Client.instance.fetchNonce(address: fromAddress, + chainId: chainId) + let gasPrice = try await JRPC_Client.instance.fetchGasPrice(chainId: chainId) + let sender = EthereumAddress(hexString: fromAddress) + let receiver = EthereumAddress(hexString: toAddress) + + let amount = BigUInt(1_000_000_000 * maxCrypto.amount) + + let transaction = EthereumTransaction(nonce: nonce, + gasPrice: gasPrice, + gas: try EthereumQuantity(Self.defaultSendTxGasPrice), + from: sender, + to: receiver, + value: try EthereumQuantity(amount.gwei) + ) + + guard let gasEstimate = try? await JRPC_Client.instance.fetchGasLimit(transaction: transaction, chainId: chainId) else { + return Self.defaultSendTxGasPrice * gasPrice.quantity + } + return gasEstimate.quantity * gasPrice.quantity + } func canSendCrypto(token: String, chain: BlockchainType) -> Bool { // only native tokens supported diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift index 4d691a7d7..2c2b77097 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift @@ -6,6 +6,7 @@ // import Foundation +import BigInt protocol CryptoSenderProtocol { init(wallet: UDWallet) @@ -24,25 +25,17 @@ protocol CryptoSenderProtocol { /// - address: address of the receiver /// - chain: chain of the transaction /// - Returns: TX Hash if success - func sendCrypto(crypto: CryptoSpec, + func sendCrypto(crypto: CryptoSendingSpec, chain: ChainSpec, toAddress: HexAddress) async throws -> String + + func computeGasFeeFrom(maxCrypto: CryptoSendingSpec, + on chain: ChainSpec, + toAddress: HexAddress) async throws -> BigUInt } extension CryptoSenderProtocol { - - private func _sendCrypto(token: String, amount: Double, address: HexAddress, chain: BlockchainType) async throws { - // create the TX - - // send TX to the chain - - } - - private func createTX() { - // TODO: - } - - private func sendTX() { - // TODO: - } + static var defaultSendTxGasPrice: BigUInt { 21_000 } + static var ethTicker: String { "crypto.ETH.address" } + static var maticTicker: String { "crypto.MATIC.version.MATIC.address" } } From eddd8f9d45c6a16b3c515ebac43b28b8b726fe24 Mon Sep 17 00:00:00 2001 From: rommex Date: Sun, 24 Mar 2024 22:55:55 +0200 Subject: [PATCH 18/80] computeGasFeeFrom() finalyzed --- .../Home/SendCryptoAsset/CryptoSender.swift | 15 +++++++++++---- .../SendCryptoAsset/CryptoSenderProtocol.swift | 8 +++++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index 36f1eb66a..ac254d10c 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -61,7 +61,7 @@ struct CryptoSender: CryptoSenderProtocol { } - func computeGasFeeFrom(maxCrypto: CryptoSendingSpec, on chain: ChainSpec, toAddress: HexAddress) async throws -> BigUInt { + func computeGasFeeFrom(maxCrypto: CryptoSendingSpec, on chain: ChainSpec, toAddress: HexAddress) async throws -> Double { guard canSendCrypto(token: maxCrypto.token, chain: chain.blockchainType) else { throw Error.sendingNotSupported } @@ -125,7 +125,14 @@ struct NativeCryptoSender: CryptoSenderProtocol { func computeGasFeeFrom(maxCrypto: CryptoSendingSpec, on chain: ChainSpec, - toAddress: HexAddress) async throws -> BigUInt { + toAddress: HexAddress) async throws -> Double { + + func downMultiplication (_ a1: BigUInt, _ a2: BigUInt) -> Double { + let m1 = Double(a1) / 1_000_000_000.0 + let m2 = Double(a2) / 1_000_000_000.0 + return m1 * m2 + } + let chainId = chain.blockchainType.supportedChainId(env: chain.env) let fromAddress = self.wallet.address let nonce: EthereumQuantity = try await JRPC_Client.instance.fetchNonce(address: fromAddress, @@ -145,9 +152,9 @@ struct NativeCryptoSender: CryptoSenderProtocol { ) guard let gasEstimate = try? await JRPC_Client.instance.fetchGasLimit(transaction: transaction, chainId: chainId) else { - return Self.defaultSendTxGasPrice * gasPrice.quantity + return downMultiplication(Self.defaultSendTxGasPrice, gasPrice.quantity) } - return gasEstimate.quantity * gasPrice.quantity + return downMultiplication(gasEstimate.quantity, gasPrice.quantity) } func canSendCrypto(token: String, chain: BlockchainType) -> Bool { diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift index 2c2b77097..1c3128691 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift @@ -29,9 +29,15 @@ protocol CryptoSenderProtocol { chain: ChainSpec, toAddress: HexAddress) async throws -> String + /// Calculates the amount of crypto needed to be spent as gas fee + /// - Parameters: + /// - maxCrypto: max crypto available for the send transaction + /// - chain: chain where tx will be placed + /// - toAddress: recepient address of the crypto + /// - Returns: Amount of crypto that must be deducted from maxCrypto as the gas fee in the future tx func computeGasFeeFrom(maxCrypto: CryptoSendingSpec, on chain: ChainSpec, - toAddress: HexAddress) async throws -> BigUInt + toAddress: HexAddress) async throws -> Double } extension CryptoSenderProtocol { From c3d8969d5b7cd5dc9af167ef9856f09c7e217704 Mon Sep 17 00:00:00 2001 From: rommex Date: Sun, 24 Mar 2024 23:01:56 +0200 Subject: [PATCH 19/80] refactoring --- .../Home/SendCryptoAsset/CryptoSender.swift | 17 ++++++----------- .../SendCryptoAsset/CryptoSenderProtocol.swift | 6 ------ 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index ac254d10c..c71da1c71 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -42,14 +42,9 @@ struct CryptoSender: CryptoSenderProtocol { let wallet: UDWallet - init(wallet: UDWallet) { - self.wallet = wallet - } - func canSendCrypto(token: String, chain: BlockchainType) -> Bool { - // only native tokens supported - return (token == Self.ethTicker && chain == .Ethereum) || - (token == Self.maticTicker && chain == .Matic) + // only native tokens supported for Ethereum and Polygon + return NativeCryptoSender(wallet: wallet).canSendCrypto(token: token, chain: chain) } func sendCrypto(crypto: CryptoSendingSpec, chain: ChainSpec, toAddress: HexAddress) async throws -> String { @@ -75,11 +70,11 @@ struct CryptoSender: CryptoSenderProtocol { } struct NativeCryptoSender: CryptoSenderProtocol { - let wallet: UDWallet + static let defaultSendTxGasPrice: BigUInt = 21_000 + static var ethTicker = "crypto.ETH.address" + static var maticTicker = "crypto.MATIC.version.MATIC.address" - init(wallet: UDWallet) { - self.wallet = wallet - } + let wallet: UDWallet func sendCrypto(crypto: CryptoSendingSpec, chain: ChainSpec, diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift index 1c3128691..6a9664817 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift @@ -39,9 +39,3 @@ protocol CryptoSenderProtocol { on chain: ChainSpec, toAddress: HexAddress) async throws -> Double } - -extension CryptoSenderProtocol { - static var defaultSendTxGasPrice: BigUInt { 21_000 } - static var ethTicker: String { "crypto.ETH.address" } - static var maticTicker: String { "crypto.MATIC.version.MATIC.address" } -} From 2b0529948724c85e3c81333002458e1e93a7a315 Mon Sep 17 00:00:00 2001 From: rommex Date: Sun, 24 Mar 2024 23:05:12 +0200 Subject: [PATCH 20/80] refact --- .../Home/SendCryptoAsset/CryptoSender.swift | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index c71da1c71..acd90eae0 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -65,8 +65,6 @@ struct CryptoSender: CryptoSenderProtocol { on: chain, toAddress: toAddress) } - - } struct NativeCryptoSender: CryptoSenderProtocol { @@ -76,6 +74,13 @@ struct NativeCryptoSender: CryptoSenderProtocol { let wallet: UDWallet + + func canSendCrypto(token: String, chain: BlockchainType) -> Bool { + // only native tokens supported + return (token == Self.ethTicker && chain == .Ethereum) || + (token == Self.maticTicker && chain == .Matic) + } + func sendCrypto(crypto: CryptoSendingSpec, chain: ChainSpec, toAddress: HexAddress) async throws -> String { @@ -151,10 +156,4 @@ struct NativeCryptoSender: CryptoSenderProtocol { } return downMultiplication(gasEstimate.quantity, gasPrice.quantity) } - - func canSendCrypto(token: String, chain: BlockchainType) -> Bool { - // only native tokens supported - return (token == Self.ethTicker && chain == .Ethereum) || - (token == Self.maticTicker && chain == .Matic) - } } From d88dfd59b7e93c7a4fb70a0a1af2ce07a8d951d1 Mon Sep 17 00:00:00 2001 From: rommex Date: Sun, 24 Mar 2024 23:22:12 +0200 Subject: [PATCH 21/80] refactor --- .../Modules/Home/SendCryptoAsset/JRPC_Client.swift | 2 ++ .../WalletConnectService_V2/WalletConnectServiceV2.swift | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift index 3ecf82abb..7fb4382f0 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift @@ -85,6 +85,8 @@ struct JRPC_Client { return } let web3 = Web3(rpcURL: urlString) + + // TODO: Handle an external wallet guard let privKeyString = udWallet.getPrivateKey() else { Debugger.printFailure("No private key in \(udWallet)", critical: true) continuation.resume(with: .failure(WalletConnectRequestError.failedToGetPrivateKey)) diff --git a/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift b/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift index 571eeecec..6d2f6aab8 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift @@ -842,8 +842,6 @@ extension WalletConnectServiceV2: WalletConnectV2RequestHandlingServiceProtocol guard transaction.gas == nil else { return transaction } - -// let gas = try await fetchGasLimit(transaction: transaction, chainId: chainId) var newTx = transaction newTx.gas = try await JRPC_Client.instance.fetchGasLimit(transaction: transaction, chainId: chainId) return newTx From cdd62a004a2708d677967b3c1ff80c0cb7c717dc Mon Sep 17 00:00:00 2001 From: rommex Date: Mon, 25 Mar 2024 18:24:27 +0200 Subject: [PATCH 22/80] support ext wallets --- .../Entities/Wallets/UDWallet+Signing.swift | 8 ++++++++ .../Home/SendCryptoAsset/CryptoSender.swift | 7 +++++++ .../Home/SendCryptoAsset/JRPC_Client.swift | 1 - .../WalletConnectRequestError.swift | 4 +++- .../MockWalletConnectServiceV2.swift | 4 ++++ .../WalletConnectServiceV2.swift | 20 +++++++++++++++++++ .../WalletConnectServiceV2Protocol.swift | 5 +++++ 7 files changed, 47 insertions(+), 2 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Entities/Wallets/UDWallet+Signing.swift b/unstoppable-ios-app/domains-manager-ios/Entities/Wallets/UDWallet+Signing.swift index 4a28196c5..73b6e404c 100644 --- a/unstoppable-ios-app/domains-manager-ios/Entities/Wallets/UDWallet+Signing.swift +++ b/unstoppable-ios-app/domains-manager-ios/Entities/Wallets/UDWallet+Signing.swift @@ -172,6 +172,14 @@ extension UDWallet { return try appContext.walletConnectServiceV2.handle(response: response) } + func signViaWalletConnectTransaction(tx: EthereumTransaction) async throws -> String { + let wc2Sessions = try getWC2Session() + let response = try await appContext.walletConnectServiceV2.sendSignTx(sessions: wc2Sessions, + chainId: 1, + tx: tx, address: address, in: self) + return try appContext.walletConnectServiceV2.handle(response: response) + } + func multipleWalletPersonalSigns(messages: [String]) async throws -> [String]{ var sigs = [String]() diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index acd90eae0..85c100431 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -93,6 +93,13 @@ struct NativeCryptoSender: CryptoSenderProtocol { fromAddress: self.wallet.address, toAddress: toAddress, chainId: chainId) + + guard wallet.walletState != .externalLinked else { + let response = try await wallet.signViaWalletConnectTransaction(tx: tx) + return response + } + + let hash = try await JRPC_Client.instance.sendTx(transaction: tx, udWallet: self.wallet, chainIdInt: chainId) return hash } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift index 7fb4382f0..5e4aba22d 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift @@ -86,7 +86,6 @@ struct JRPC_Client { } let web3 = Web3(rpcURL: urlString) - // TODO: Handle an external wallet guard let privKeyString = udWallet.getPrivateKey() else { Debugger.printFailure("No private key in \(udWallet)", critical: true) continuation.resume(with: .failure(WalletConnectRequestError.failedToGetPrivateKey)) diff --git a/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectRequestError.swift b/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectRequestError.swift index f34a487df..595600da1 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectRequestError.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectRequestError.swift @@ -16,6 +16,7 @@ enum WalletConnectRequestError: String, Error, RawValueLocalizable { case failedToDetermineChainId case failedToDetermineIntent case failedToParseMessage + case failedEncodeTransaction case uiHandlerNotSet case networkNotSupported case noWCSessionFound @@ -72,7 +73,8 @@ enum WalletConnectRequestError: String, Error, RawValueLocalizable { .failedOpenExternalApp, .failedToRelayTxToExternalWallet, .failedToFindDomainToConnect, - .failedHashPersonalMessage: return .failedTx + .failedHashPersonalMessage, + .failedEncodeTransaction: return .failedTx case .methodUnsupported: return .methodUnsupported case .networkNotSupported: return .networkNotSupported case .lowAllowance: return .lowAllowance diff --git a/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/MockWalletConnectServiceV2.swift b/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/MockWalletConnectServiceV2.swift index df4256200..b9fe8e275 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/MockWalletConnectServiceV2.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/MockWalletConnectServiceV2.swift @@ -22,6 +22,10 @@ final class MockWalletConnectServiceV2 { // MARK: - WalletConnectServiceProtocol extension MockWalletConnectServiceV2: WalletConnectServiceV2Protocol { + func sendSignTx(sessions: [WCConnectedAppsStorageV2.SessionProxy], chainId: Int, tx: EthereumTransaction, address: HexAddress, in wallet: UDWallet) async throws -> ResponseV2 { + throw WalletConnectRequestError.failedToSignMessage + } + func sendSignTypedData(sessions: [WCConnectedAppsStorageV2.SessionProxy], chainId: Int, dataString: String, address: HexAddress, in wallet: UDWallet) async throws -> WalletConnectSign.Response { throw WalletConnectRequestError.failedToSignMessage } diff --git a/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift b/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift index 6d2f6aab8..b89df3d9e 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift @@ -1234,6 +1234,26 @@ extension WalletConnectServiceV2 { in: wallet) } + func sendSignTx(sessions: [WCConnectedAppsStorageV2.SessionProxy], + chainId: Int, + tx: EthereumTransaction, + address: HexAddress, + in wallet: UDWallet) async throws -> WalletConnectSign.Response { + guard let sessionSettled = pickOnlyActiveSessions(from: sessions).first else { + throw WalletConnectRequestError.noWCSessionFound + } + + guard let txAdapted = TransactionV2(ethTx: tx) else { + throw WalletConnectRequestError.failedEncodeTransaction + } + let params = WalletConnectServiceV2.getParamsSignTx(tx: txAdapted) + return try await sendRequest(method: .personalSign, + session: sessionSettled, + chainId: chainId, + requestParams: params, + in: wallet) + } + func sendSignTypedData(sessions: [WCConnectedAppsStorageV2.SessionProxy], chainId: Int, dataString: String, diff --git a/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2Protocol.swift b/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2Protocol.swift index 4ef84daf2..20ff3f32b 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2Protocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2Protocol.swift @@ -27,6 +27,11 @@ protocol WalletConnectServiceV2Protocol: AnyObject { func sendSignTypedData(sessions: [WCConnectedAppsStorageV2.SessionProxy], chainId: Int, dataString: String, address: HexAddress, in wallet: UDWallet) async throws -> ResponseV2 func sendEthSign(sessions: [WCConnectedAppsStorageV2.SessionProxy], chainId: Int, message: String, address: HexAddress, in wallet: UDWallet) async throws -> ResponseV2 + func sendSignTx(sessions: [WCConnectedAppsStorageV2.SessionProxy], + chainId: Int, + tx: EthereumTransaction, + address: HexAddress, + in wallet: UDWallet) async throws -> ResponseV2 func handle(response: ResponseV2) throws -> String func signTxViaWalletConnect_V2(udWallet: UDWallet, sessions: [SessionV2Proxy], From 81c8899aeba6b56f8f66dbf878b5d3ad696c76c5 Mon Sep 17 00:00:00 2001 From: rommex Date: Mon, 25 Mar 2024 22:35:06 +0200 Subject: [PATCH 23/80] transferring chain to ext wallet --- .../Entities/Wallets/UDWallet+Signing.swift | 4 ++-- .../Modules/Home/SendCryptoAsset/CryptoSender.swift | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Entities/Wallets/UDWallet+Signing.swift b/unstoppable-ios-app/domains-manager-ios/Entities/Wallets/UDWallet+Signing.swift index 73b6e404c..bcd197cbf 100644 --- a/unstoppable-ios-app/domains-manager-ios/Entities/Wallets/UDWallet+Signing.swift +++ b/unstoppable-ios-app/domains-manager-ios/Entities/Wallets/UDWallet+Signing.swift @@ -172,10 +172,10 @@ extension UDWallet { return try appContext.walletConnectServiceV2.handle(response: response) } - func signViaWalletConnectTransaction(tx: EthereumTransaction) async throws -> String { + func signViaWalletConnectTransaction(tx: EthereumTransaction, chainId: Int) async throws -> String { let wc2Sessions = try getWC2Session() let response = try await appContext.walletConnectServiceV2.sendSignTx(sessions: wc2Sessions, - chainId: 1, + chainId: chainId, tx: tx, address: address, in: self) return try appContext.walletConnectServiceV2.handle(response: response) } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index 85c100431..9827f3ab8 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -87,7 +87,7 @@ struct NativeCryptoSender: CryptoSenderProtocol { guard canSendCrypto(token: crypto.token, chain: chain.blockchainType) else { throw CryptoSender.Error.sendingNotSupported } - + let chainId = chain.blockchainType.supportedChainId(env: chain.env) let tx = try await createNativeSendTransaction(crypto: crypto, fromAddress: self.wallet.address, @@ -95,10 +95,10 @@ struct NativeCryptoSender: CryptoSenderProtocol { chainId: chainId) guard wallet.walletState != .externalLinked else { - let response = try await wallet.signViaWalletConnectTransaction(tx: tx) + let response = try await wallet.signViaWalletConnectTransaction(tx: tx, chainId: chainId) return response } - + let hash = try await JRPC_Client.instance.sendTx(transaction: tx, udWallet: self.wallet, chainIdInt: chainId) return hash From bba747064ce611324253e00069b08e4f20a4a62f Mon Sep 17 00:00:00 2001 From: rommex Date: Tue, 26 Mar 2024 14:20:49 +0200 Subject: [PATCH 24/80] ext wallet fix --- .../WalletConnectService_V2/WalletConnectServiceV2.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift b/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift index b89df3d9e..c99c12a0e 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/WalletConnect/WalletConnectService_V2/WalletConnectServiceV2.swift @@ -1247,7 +1247,7 @@ extension WalletConnectServiceV2 { throw WalletConnectRequestError.failedEncodeTransaction } let params = WalletConnectServiceV2.getParamsSignTx(tx: txAdapted) - return try await sendRequest(method: .personalSign, + return try await sendRequest(method: .ethSendTransaction, session: sessionSettled, chainId: chainId, requestParams: params, @@ -1325,7 +1325,7 @@ extension WalletConnectServiceV2 { } static func getParamsSignTx(tx: TransactionV2) -> WCAnyCodable { - WCAnyCodable(tx) + WCAnyCodable([tx]) } static func getParamsPersonalSign(message: String, address: HexAddress) -> WCAnyCodable { From e16a7e051454c2b8bbce7202fa7bc65628e6d5f5 Mon Sep 17 00:00:00 2001 From: rommex Date: Tue, 26 Mar 2024 21:54:05 +0200 Subject: [PATCH 25/80] fetching range of gas prices --- .../Home/SendCryptoAsset/CryptoSender.swift | 4 +++ .../Home/SendCryptoAsset/JRPC_Client.swift | 9 ++++++ .../Services/Networking/NetworkService.swift | 28 +++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index 9827f3ab8..29a9c2b80 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -111,6 +111,10 @@ struct NativeCryptoSender: CryptoSenderProtocol { let nonce: EthereumQuantity = try await JRPC_Client.instance.fetchNonce(address: fromAddress, chainId: chainId) let gasPrice = try await JRPC_Client.instance.fetchGasPrice(chainId: chainId) + + let otherGasPrice = try await JRPC_Client.instance.fetchGasPrice(chainId: chainId, + for: crypto.speed) + let sender = EthereumAddress(hexString: fromAddress) let receiver = EthereumAddress(hexString: toAddress) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift index 5e4aba22d..c779e89ee 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift @@ -49,6 +49,15 @@ struct JRPC_Client { return EthereumQuantity(quantity: gasPriceBigUInt) } + func fetchGasPrice(chainId: Int, for speed: CryptoSendingSpec.TxSpeed) async throws -> Int { + let prices = try await NetworkService().getStatusGasPrices(chainId: chainId) + switch speed { + case .normal: return prices.safeLow + case .fast: return prices.fast + case .urgent: return prices.fastest + } + } + func fetchGasLimit(transaction: EthereumTransaction, chainId: Int) async throws -> EthereumQuantity { do { let gasPriceString = try await NetworkService().getGasEstimation(tx: transaction, diff --git a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift index f60e19d23..85b4e864c 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift @@ -296,6 +296,16 @@ extension NetworkService { let error: ErrorDescription } + struct StatusGasPricesInfo: Decodable { + struct AverageGasPrices: Decodable { + let safeLow: Int + let fast: Int + let fastest: Int + } + let ETH: AverageGasPrices + let MATIC: AverageGasPrices + } + struct JRPCRequestInfo { let name: String let paramsBuilder: ()->String @@ -305,6 +315,7 @@ extension NetworkService { case failedBuildUrl case gasRequiredExceedsAllowance case genericError(String) + case failedGetStatus init(message: String) { if message.lowercased().starts(with: "gas required exceeds allowance") { @@ -357,6 +368,23 @@ extension NetworkService { requestInfo: JRPCRequestInfo(name: "eth_gasPrice", paramsBuilder: { "[]"} )) } + + func getStatusGasPrices() async throws -> StatusGasPricesInfo { + let url = URL(string: "https://unstoppabledomains.com/api/v1/status")! + let data = try await NetworkService().fetchData(for: url, method: .get) + guard let response = try? JSONDecoder().decode(StatusGasPricesInfo.self, from: data) else { + throw JRPCError.failedGetStatus + } + return response + } + + func getStatusGasPrices(chainId: Int) async throws -> StatusGasPricesInfo.AverageGasPrices { + switch chainId { + case BlockchainNetwork.ethMainnet.rawValue: return try await getStatusGasPrices().ETH + case BlockchainNetwork.polygonMainnet.rawValue: return try await getStatusGasPrices().MATIC + default: throw JRPCError.failedGetStatus + } + } } extension NetworkService { From 4109e91e449184026d0dd8b2d97cedbdfb560725 Mon Sep 17 00:00:00 2001 From: Oleg Date: Thu, 28 Mar 2024 14:50:38 +0700 Subject: [PATCH 26/80] Update navigation title --- .../project.pbxproj | 12 ++++ .../SelectCryptoAssetToSendView.swift | 10 +++- .../SendCryptoAssetSelectReceiverView.swift | 9 ++- .../SelectTokenAssetAmountToSendView.swift | 1 - ...SendCryptoAssetNavigationDestination.swift | 9 +++ .../SendCryptoAssetRootView.swift | 25 +++++++- .../SendCryptoReceiverInfoTitleView.swift | 58 +++++++++++++++++++ .../NavigationTrackerViewModifier.swift | 58 +++++++++++++++++++ 8 files changed, 178 insertions(+), 4 deletions(-) create mode 100644 unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoReceiverInfoTitleView.swift create mode 100644 unstoppable-ios-app/domains-manager-ios/SwiftUI/ViewModifiers/NavigationTrackerViewModifier.swift diff --git a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj index eb96a04c0..343947660 100644 --- a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj +++ b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj @@ -1278,6 +1278,10 @@ C6A2D5C228508DA800327C47 /* PaymentTransactionCostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A2D5C128508DA800327C47 /* PaymentTransactionCostView.swift */; }; C6A2D5C728508DB900327C47 /* PaymentTransactionCostView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C6A2D5C628508DB900327C47 /* PaymentTransactionCostView.xib */; }; C6A2D5CD2850EDCD00327C47 /* ConnectServerRequestConfirmationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A2D5CC2850EDCD00327C47 /* ConnectServerRequestConfirmationView.swift */; }; + C6A359272BB52CDC00B1209A /* SendCryptoReceiverInfoTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A359262BB52CDC00B1209A /* SendCryptoReceiverInfoTitleView.swift */; }; + C6A359282BB52CDC00B1209A /* SendCryptoReceiverInfoTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A359262BB52CDC00B1209A /* SendCryptoReceiverInfoTitleView.swift */; }; + C6A3592A2BB5586700B1209A /* NavigationTrackerViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A359292BB5586700B1209A /* NavigationTrackerViewModifier.swift */; }; + C6A3592B2BB5586700B1209A /* NavigationTrackerViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A359292BB5586700B1209A /* NavigationTrackerViewModifier.swift */; }; C6A474C729D149560073415F /* LoginFlowNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A474C629D149560073415F /* LoginFlowNavigationController.swift */; }; C6A474D029D150A40073415F /* NoParkedDomainsFoundViewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A474CD29D150A40073415F /* NoParkedDomainsFoundViewPresenter.swift */; }; C6A474D429D150A40073415F /* NoParkedDomainsFoundViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A474CE29D150A40073415F /* NoParkedDomainsFoundViewController.swift */; }; @@ -3524,6 +3528,8 @@ C6A2D5C128508DA800327C47 /* PaymentTransactionCostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentTransactionCostView.swift; sourceTree = ""; }; C6A2D5C628508DB900327C47 /* PaymentTransactionCostView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PaymentTransactionCostView.xib; sourceTree = ""; }; C6A2D5CC2850EDCD00327C47 /* ConnectServerRequestConfirmationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectServerRequestConfirmationView.swift; sourceTree = ""; }; + C6A359262BB52CDC00B1209A /* SendCryptoReceiverInfoTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendCryptoReceiverInfoTitleView.swift; sourceTree = ""; }; + C6A359292BB5586700B1209A /* NavigationTrackerViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationTrackerViewModifier.swift; sourceTree = ""; }; C6A474C629D149560073415F /* LoginFlowNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginFlowNavigationController.swift; sourceTree = ""; }; C6A474CD29D150A40073415F /* NoParkedDomainsFoundViewPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoParkedDomainsFoundViewPresenter.swift; sourceTree = ""; }; C6A474CE29D150A40073415F /* NoParkedDomainsFoundViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoParkedDomainsFoundViewController.swift; sourceTree = ""; }; @@ -6387,6 +6393,7 @@ C621A43B2BB10D2900CB5CB9 /* QRWalletAddressScannerView.swift */, C6F433502BB27AEF000C5E46 /* SendCryptoAssetSuccessView.swift */, C6F433532BB28809000C5E46 /* TransactionStatusTracker.swift */, + C6A359262BB52CDC00B1209A /* SendCryptoReceiverInfoTitleView.swift */, C67213CD2BAA80670075B9C7 /* SelectReceiver */, C67213E72BAA98EC0075B9C7 /* SelectAssetToSend */, C62900F32BAAC821008B35A2 /* SelectTokenAssetAmount */, @@ -6958,6 +6965,7 @@ C6DA0B742B7C5F68009920B5 /* SectionSpacingModifier.swift */, C6DF46262AA18F8900D124E7 /* DisplayError.swift */, C64939F72B6373EC00457363 /* DeviceShakeViewModifier.swift */, + C6A359292BB5586700B1209A /* NavigationTrackerViewModifier.swift */, C630E4AB2B7F4B8D008F3269 /* FlippedUpsideDownModifier.swift */, C62900EA2BAAC135008B35A2 /* NavigationTopSafeAreaOffset.swift */, C685803A2B357EF400907568 /* NavBarVisibleModifier.swift */, @@ -9403,6 +9411,7 @@ C6F060C42ACAA35B00BA2E7E /* MessagingService+Tools.swift in Sources */, C67213E22BAA967C0075B9C7 /* SendCryptoAssetRootView.swift in Sources */, C69F997B2A9F2206004B1958 /* PublicProfileViewModel.swift in Sources */, + C6A359272BB52CDC00B1209A /* SendCryptoReceiverInfoTitleView.swift in Sources */, C6D646452B1E084C00D724AC /* ViewPullUp.swift in Sources */, C688C1862B846F1B00BD233A /* ChannelView.swift in Sources */, C62C3FC6283F70970094FC93 /* SelectorButton.swift in Sources */, @@ -9810,6 +9819,7 @@ C68BEC142A397D9B00A0B809 /* MessagingChatUserProfileDisplayInfo.swift in Sources */, C6C9958A289D313D00367362 /* CNavigationBarBackButton.swift in Sources */, C6E6A001288AEAC6000A8346 /* ExternalEventsStorage.swift in Sources */, + C6A3592A2BB5586700B1209A /* NavigationTrackerViewModifier.swift in Sources */, C666E0A129CB3FEA0003DECB /* ParkedDomainCell.swift in Sources */, 29AEA04B292F7D60003BB5B4 /* SecurePersistedStorage.swift in Sources */, C624D79A281BCA3100F55530 /* SettingsPresenter.swift in Sources */, @@ -10533,6 +10543,7 @@ C6D647A52B1F189800D724AC /* PurchaseDomainsCheckoutViewController.swift in Sources */, C6D647532B1EDB7C00D724AC /* SelectorButton.swift in Sources */, C6C8F8AB2B2182CF00A9834D /* EnterEmailViewController.swift in Sources */, + C6A3592B2BB5586700B1209A /* NavigationTrackerViewModifier.swift in Sources */, C6D646ED2B1ED5A900D724AC /* ImportExistingExternalWalletPresenter.swift in Sources */, C61807C32B19A2F80032E543 /* SafeContinuation.swift in Sources */, C6C8F8DF2B21836400A9834D /* ParkedDomainCell.swift in Sources */, @@ -10762,6 +10773,7 @@ C6C8F85B2B217FB500A9834D /* WalletBackUpPassword.swift in Sources */, C6D647812B1EE1FC00D724AC /* UDCheckBox.swift in Sources */, C6D646522B1ED10100D724AC /* DomainProfileSocialCell.swift in Sources */, + C6A359282BB52CDC00B1209A /* SendCryptoReceiverInfoTitleView.swift in Sources */, C6C8F8632B21802C00A9834D /* TestsEnum.swift in Sources */, C6D645CA2B1DBD3C00D724AC /* CNavigationControllerChild.swift in Sources */, C6D645C72B1DBD3900D724AC /* CNavigationBarScrollingController.swift in Sources */, diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectAssetToSend/SelectCryptoAssetToSendView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectAssetToSend/SelectCryptoAssetToSendView.swift index 625810633..4982bc40c 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectAssetToSend/SelectCryptoAssetToSendView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectAssetToSend/SelectCryptoAssetToSendView.swift @@ -33,7 +33,6 @@ struct SelectCryptoAssetToSendView: View { } .addNavigationTopSafeAreaOffset() .listRowSpacing(0) - .navigationTitle(String.Constants.send.localized()) .listStyle(.plain) .animation(.default, value: UUID()) .onAppear(perform: onAppear) @@ -53,6 +52,15 @@ private extension SelectCryptoAssetToSendView { allDomains = viewModel.sourceWallet.domains.filter { $0.isUDDomain && $0.isAbleToTransfer } setDomainsData() + setupTitle() + } + + func setupTitle() { + withAnimation { + viewModel.navigationState?.setCustomTitle(customTitle: { SendCryptoReceiverInfoTitleView(receiver: receiver) }, + id: UUID().uuidString) + viewModel.navigationState?.isTitleVisible = true + } } func setDomainsData() { diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectReceiver/SendCryptoAssetSelectReceiverView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectReceiver/SendCryptoAssetSelectReceiverView.swift index 055faacbe..461261ad1 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectReceiver/SendCryptoAssetSelectReceiverView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectReceiver/SendCryptoAssetSelectReceiverView.swift @@ -54,11 +54,18 @@ struct SendCryptoAssetSelectReceiverView: View, ViewAnalyticsLogger { followingList = relationshipDetails.socialDetails?.getFollowersListFor(relationshipType: .following) ?? [] } } + .onAppear(perform: onAppear) } } // MARK: - Private methods private extension SendCryptoAssetSelectReceiverView { + func onAppear() { + withAnimation { + viewModel.navigationState?.isTitleVisible = false + } + } + @ViewBuilder func closeButton() -> some View { CloseButtonView { @@ -287,5 +294,5 @@ private extension SendCryptoAssetSelectReceiverView { NavigationStack { SendCryptoAssetSelectReceiverView() } - .environmentObject(MockEntitiesFabric.SendCrypto.mockViewModel()) + .environmentObject(MockEntitiesFabric.SendCrypto.mockViewModel()) } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift index 6704856d2..379821705 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift @@ -38,7 +38,6 @@ struct SelectTokenAssetAmountToSendView: View { .animation(.default, value: UUID()) .addNavigationTopSafeAreaOffset() .viewPullUp($pullUp) - .navigationTitle(String.Constants.send.localized()) } } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetNavigationDestination.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetNavigationDestination.swift index 32148b79e..75d50a5ee 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetNavigationDestination.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetNavigationDestination.swift @@ -16,6 +16,15 @@ extension SendCryptoAsset { case confirmTransferDomain(TransferDomainData) case domainTransferSuccess(DomainDisplayInfo) + + var isWithCustomTitle: Bool { + switch self { + case .selectTokenAmountToSend, .selectAssetToSend: + return true + case .scanWalletAddress, .confirmTransferDomain, .domainTransferSuccess, .confirmSendToken: + return false + } + } } struct LinkNavigationDestination { diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetRootView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetRootView.swift index c7a907a84..7dfbc874e 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetRootView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetRootView.swift @@ -22,6 +22,10 @@ struct SendCryptoAssetRootView: View { .ignoresSafeArea() .environmentObject(viewModel) } + .onChange(of: viewModel.navPath) { _ in + updateTitleView() + } + .trackNavigationControllerEvents(onDidNotFinishNavigationBack: updateTitleView) }, navigationStateProvider: { navigationState in self.viewModel.navigationState = navigationState }, path: $viewModel.navPath) @@ -30,6 +34,25 @@ struct SendCryptoAssetRootView: View { } +// MARK: - Private methods +private extension SendCryptoAssetRootView { + func updateTitleView() { + viewModel.navigationState?.yOffset = -2 + withAnimation { + viewModel.navigationState?.isTitleVisible = viewModel.navPath.last?.isWithCustomTitle == true + } + } +} + #Preview { - SendCryptoAssetRootView(viewModel: SendCryptoAssetViewModel(initialData: .init(sourceWallet: MockEntitiesFabric.Wallet.mockEntities()[0]))) + struct PresenterView: View { + var body: some View { + Text("") + .sheet(isPresented: .constant(true), content: { + SendCryptoAssetRootView(viewModel: SendCryptoAssetViewModel(initialData: .init(sourceWallet: MockEntitiesFabric.Wallet.mockEntities()[0]))) + }) + } + } + + return PresenterView() } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoReceiverInfoTitleView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoReceiverInfoTitleView.swift new file mode 100644 index 000000000..08150a809 --- /dev/null +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoReceiverInfoTitleView.swift @@ -0,0 +1,58 @@ +// +// SendCryptoReceiverInfoTitleView.swift +// domains-manager-ios +// +// Created by Oleg Kuplin on 28.03.2024. +// + +import SwiftUI + +struct SendCryptoReceiverInfoTitleView: View { + + @Environment(\.imageLoadingService) var imageLoadingService + @Environment(\.domainProfilesService) var domainProfilesService + + let receiver: SendCryptoAsset.AssetReceiver + + @State private var icon: UIImage? + + var body: some View { + HStack(spacing: 8) { + iconView() + Text(receiver.walletAddress.walletAddressTruncated) + .font(.currentFont(size: 16, weight: .semibold)) + } + .animation(.default, value: UUID()) + .onAppear(perform: onAppear) + } +} + +// MARK: - Private methods +private extension SendCryptoReceiverInfoTitleView { + func onAppear() { + loadIcon() + } + + func loadIcon() { + if let domainName = receiver.domainName, + let profile = domainProfilesService.getCachedDomainProfileDisplayInfo(for: domainName), + let pfpURL = profile.pfpURL { + Task { + icon = await imageLoadingService.loadImage(from: .url(pfpURL, maxSize: nil), downsampleDescription: .icon) + } + } + } + + @ViewBuilder + func iconView() -> some View { + if let icon { + UIImageBridgeView(image: icon) + .squareFrame(20) + .clipShape(Circle()) + } + } +} + +#Preview { + SendCryptoReceiverInfoTitleView(receiver: .init(walletAddress: "0x1234567890abcdef1234567890abcdef12345678")) +} diff --git a/unstoppable-ios-app/domains-manager-ios/SwiftUI/ViewModifiers/NavigationTrackerViewModifier.swift b/unstoppable-ios-app/domains-manager-ios/SwiftUI/ViewModifiers/NavigationTrackerViewModifier.swift new file mode 100644 index 000000000..8e8818374 --- /dev/null +++ b/unstoppable-ios-app/domains-manager-ios/SwiftUI/ViewModifiers/NavigationTrackerViewModifier.swift @@ -0,0 +1,58 @@ +// +// NavigationTrackerViewModifier.swift +// domains-manager-ios +// +// Created by Oleg Kuplin on 28.03.2024. +// + +import SwiftUI + +struct NavigationTrackerViewModifier: ViewModifier { + + var onDidNotFinishNavigationBack: EmptyCallback? = nil + @StateObject private var navigationTracker = UINavigationViewControllerTracker() + + func body(content: Content) -> some View { + content + .onAppear { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + if let topVC = appContext.coreAppCoordinator.topVC, + let nav = topVC.children.first as? UINavigationController { + navigationTracker.handler = self + navigationTracker.trackNavigationController(nav) + } + } + } + } + +} + +extension NavigationTrackerViewModifier: UINavigationControllerTrackerHandler { + func didNotFinishNavigationBack() { + onDidNotFinishNavigationBack?() + } +} + +extension View { + func trackNavigationControllerEvents(onDidNotFinishNavigationBack: EmptyCallback? = nil) -> some View { + modifier(NavigationTrackerViewModifier(onDidNotFinishNavigationBack: onDidNotFinishNavigationBack)) + } +} + +protocol UINavigationControllerTrackerHandler { + func didNotFinishNavigationBack() +} + +final class UINavigationViewControllerTracker: NSObject, ObservableObject, UINavigationControllerDelegate { + var handler: UINavigationControllerTrackerHandler? + + func trackNavigationController(_ navigationController: UINavigationController?) { + guard navigationController?.delegate !== self else { return } + + navigationController?.transitionCoordinator?.notifyWhenInteractionChanges({ [weak self] context in + if context.completionVelocity < 0 { // will restore current view controller + self?.handler?.didNotFinishNavigationBack() + } + }) + } +} From edcf64657cfafe6f5054dcbea764b4cd89a95ea6 Mon Sep 17 00:00:00 2001 From: Oleg Date: Thu, 28 Mar 2024 14:52:40 +0700 Subject: [PATCH 27/80] Will use domain name for wallet on receiver selection screen if available --- .../SendCryptoAssetSelectReceiverWalletRowView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectReceiver/SendCryptoAssetSelectReceiverWalletRowView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectReceiver/SendCryptoAssetSelectReceiverWalletRowView.swift index 0ee6a72c3..8aae7872b 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectReceiver/SendCryptoAssetSelectReceiverWalletRowView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectReceiver/SendCryptoAssetSelectReceiverWalletRowView.swift @@ -15,7 +15,7 @@ struct SendCryptoAssetSelectReceiverWalletRowView: View { @State private var domainAvatarImage: UIImage? var body: some View { - UDListItemView(title: wallet.displayName, + UDListItemView(title: wallet.domainOrDisplayName, titleColor: .foregroundDefault, subtitle: subtitle, subtitleStyle: .default, From 0c9fbc6ceb01cc5042651e7aedae860a6eafe255 Mon Sep 17 00:00:00 2001 From: rommex Date: Thu, 28 Mar 2024 11:42:43 +0200 Subject: [PATCH 28/80] new enum SupportedToken --- .../Home/SendCryptoAsset/CryptoSender.swift | 37 +++++++++++-------- .../CryptoSenderProtocol.swift | 2 +- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index 29a9c2b80..1c056133d 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -24,11 +24,11 @@ struct CryptoSendingSpec { case normal, fast, urgent } - let token: String + let token: CryptoSender.SupportedToken let amount: Double let speed: TxSpeed - init(token: String, amount: Double, speed: TxSpeed = .normal) { + init(token: CryptoSender.SupportedToken, amount: Double, speed: TxSpeed = .normal) { self.token = token self.amount = amount self.speed = speed @@ -39,16 +39,21 @@ struct CryptoSender: CryptoSenderProtocol { enum Error: Swift.Error { case sendingNotSupported } + + enum SupportedToken: String { + case eth = "ETH" + case matic = "MATIC" + } let wallet: UDWallet - func canSendCrypto(token: String, chain: BlockchainType) -> Bool { + func canSendCrypto(token: CryptoSender.SupportedToken, chainType: BlockchainType) -> Bool { // only native tokens supported for Ethereum and Polygon - return NativeCryptoSender(wallet: wallet).canSendCrypto(token: token, chain: chain) + return NativeCryptoSender(wallet: wallet).canSendCrypto(token: token, chainType: chainType) } func sendCrypto(crypto: CryptoSendingSpec, chain: ChainSpec, toAddress: HexAddress) async throws -> String { - guard canSendCrypto(token: crypto.token, chain: chain.blockchainType) else { + guard canSendCrypto(token: crypto.token, chainType: chain.blockchainType) else { throw Error.sendingNotSupported } let cryptoSender: CryptoSenderProtocol = NativeCryptoSender(wallet: wallet) @@ -57,7 +62,7 @@ struct CryptoSender: CryptoSenderProtocol { } func computeGasFeeFrom(maxCrypto: CryptoSendingSpec, on chain: ChainSpec, toAddress: HexAddress) async throws -> Double { - guard canSendCrypto(token: maxCrypto.token, chain: chain.blockchainType) else { + guard canSendCrypto(token: maxCrypto.token, chainType: chain.blockchainType) else { throw Error.sendingNotSupported } let cryptoSender: CryptoSenderProtocol = NativeCryptoSender(wallet: wallet) @@ -69,22 +74,20 @@ struct CryptoSender: CryptoSenderProtocol { struct NativeCryptoSender: CryptoSenderProtocol { static let defaultSendTxGasPrice: BigUInt = 21_000 - static var ethTicker = "crypto.ETH.address" - static var maticTicker = "crypto.MATIC.version.MATIC.address" let wallet: UDWallet - func canSendCrypto(token: String, chain: BlockchainType) -> Bool { + func canSendCrypto(token: CryptoSender.SupportedToken, chainType: BlockchainType) -> Bool { // only native tokens supported - return (token == Self.ethTicker && chain == .Ethereum) || - (token == Self.maticTicker && chain == .Matic) + return (token == CryptoSender.SupportedToken.eth && chainType == .Ethereum) || + (token == CryptoSender.SupportedToken.matic && chainType == .Matic) } func sendCrypto(crypto: CryptoSendingSpec, chain: ChainSpec, toAddress: HexAddress) async throws -> String { - guard canSendCrypto(token: crypto.token, chain: chain.blockchainType) else { + guard canSendCrypto(token: crypto.token, chainType: chain.blockchainType) else { throw CryptoSender.Error.sendingNotSupported } @@ -112,13 +115,15 @@ struct NativeCryptoSender: CryptoSenderProtocol { chainId: chainId) let gasPrice = try await JRPC_Client.instance.fetchGasPrice(chainId: chainId) - let otherGasPrice = try await JRPC_Client.instance.fetchGasPrice(chainId: chainId, - for: crypto.speed) +// let otherGasPrice = try await JRPC_Client.instance.fetchGasPrice(chainId: chainId, +// for: crypto.speed) +// +// let otherGasPrice = 54 let sender = EthereumAddress(hexString: fromAddress) let receiver = EthereumAddress(hexString: toAddress) - let amount = BigUInt(1_000_000_000 * crypto.amount) + let amount = BigUInt(1_000_000_000.0 * crypto.amount) var transaction = EthereumTransaction(nonce: nonce, gasPrice: gasPrice, @@ -152,7 +157,7 @@ struct NativeCryptoSender: CryptoSenderProtocol { let sender = EthereumAddress(hexString: fromAddress) let receiver = EthereumAddress(hexString: toAddress) - let amount = BigUInt(1_000_000_000 * maxCrypto.amount) + let amount = BigUInt(1_000_000_000.0 * maxCrypto.amount) let transaction = EthereumTransaction(nonce: nonce, gasPrice: gasPrice, diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift index 6a9664817..c12d7273a 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift @@ -16,7 +16,7 @@ protocol CryptoSenderProtocol { /// - token: tokan name /// - chain: chain /// - Returns: true if the sending is supported - func canSendCrypto(token: String, chain: BlockchainType) -> Bool + func canSendCrypto(token: CryptoSender.SupportedToken, chainType: BlockchainType) -> Bool /// Create TX, send it to the chain and store it to the storage as 'pending'. /// Method fails if sending TX failed. Otherwise it returns TX hash From 35f24398ee56842d6289419a5b86e06ba7823384 Mon Sep 17 00:00:00 2001 From: rommex Date: Thu, 28 Mar 2024 11:43:06 +0200 Subject: [PATCH 29/80] default body nil --- .../Services/Networking/NetworkService.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift index 85b4e864c..6a3494ce6 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift @@ -126,7 +126,7 @@ struct NetworkService { } func fetchDataHandlingThrottle(for url: URL, - body: String = "", + body: String? = nil, method: HttpRequestMethod = .post, extraHeaders: [String: String] = [:]) async throws -> Data { let data: Data @@ -157,7 +157,7 @@ struct NetworkService { } func fetchData(for url: URL, - body: String = "", + body: String? = nil, method: HttpRequestMethod = .post, extraHeaders: [String: String] = [:]) async throws -> Data { let urlRequest = urlRequest(for: url, body: body, method: method, extraHeaders: extraHeaders) @@ -232,13 +232,13 @@ struct NetworkService { } private func urlRequest(for url: URL, - body: String = "", + body: String? = nil, method: HttpRequestMethod = .post, extraHeaders: [String: String] = [:]) -> URLRequest { var urlRequest = URLRequest(url: url) urlRequest.httpMethod = method.string - urlRequest.httpBody = body.data(using: .utf8) + urlRequest.httpBody = body?.data(using: .utf8) Self.headers.forEach { urlRequest.addValue($0.value, forHTTPHeaderField: $0.key)} extraHeaders.forEach { urlRequest.addValue($0.value, forHTTPHeaderField: $0.key)} From 19cb5fa48a65c0c6f7dc1cc387b2c097f24568de Mon Sep 17 00:00:00 2001 From: rommex Date: Thu, 28 Mar 2024 12:59:22 +0200 Subject: [PATCH 30/80] removed double-checking --- .../Home/SendCryptoAsset/CryptoSender.swift | 77 ++++++++++--------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index 1c056133d..a2beeab80 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -53,18 +53,12 @@ struct CryptoSender: CryptoSenderProtocol { } func sendCrypto(crypto: CryptoSendingSpec, chain: ChainSpec, toAddress: HexAddress) async throws -> String { - guard canSendCrypto(token: crypto.token, chainType: chain.blockchainType) else { - throw Error.sendingNotSupported - } let cryptoSender: CryptoSenderProtocol = NativeCryptoSender(wallet: wallet) return try await cryptoSender.sendCrypto(crypto: crypto, chain: chain, toAddress: toAddress) } func computeGasFeeFrom(maxCrypto: CryptoSendingSpec, on chain: ChainSpec, toAddress: HexAddress) async throws -> Double { - guard canSendCrypto(token: maxCrypto.token, chainType: chain.blockchainType) else { - throw Error.sendingNotSupported - } let cryptoSender: CryptoSenderProtocol = NativeCryptoSender(wallet: wallet) return try await cryptoSender.computeGasFeeFrom(maxCrypto: maxCrypto, on: chain, @@ -106,39 +100,7 @@ struct NativeCryptoSender: CryptoSenderProtocol { let hash = try await JRPC_Client.instance.sendTx(transaction: tx, udWallet: self.wallet, chainIdInt: chainId) return hash } - - private func createNativeSendTransaction(crypto: CryptoSendingSpec, - fromAddress: HexAddress, - toAddress: HexAddress, - chainId: Int) async throws -> EthereumTransaction { - let nonce: EthereumQuantity = try await JRPC_Client.instance.fetchNonce(address: fromAddress, - chainId: chainId) - let gasPrice = try await JRPC_Client.instance.fetchGasPrice(chainId: chainId) - -// let otherGasPrice = try await JRPC_Client.instance.fetchGasPrice(chainId: chainId, -// for: crypto.speed) -// -// let otherGasPrice = 54 - - let sender = EthereumAddress(hexString: fromAddress) - let receiver = EthereumAddress(hexString: toAddress) - let amount = BigUInt(1_000_000_000.0 * crypto.amount) - - var transaction = EthereumTransaction(nonce: nonce, - gasPrice: gasPrice, - gas: try EthereumQuantity(Self.defaultSendTxGasPrice), - from: sender, - to: receiver, - value: try EthereumQuantity(amount.gwei) - ) - - if let gasEstimate = try? await JRPC_Client.instance.fetchGasLimit(transaction: transaction, chainId: chainId) { - transaction.gas = gasEstimate - } - return transaction - } - func computeGasFeeFrom(maxCrypto: CryptoSendingSpec, on chain: ChainSpec, toAddress: HexAddress) async throws -> Double { @@ -149,6 +111,10 @@ struct NativeCryptoSender: CryptoSenderProtocol { return m1 * m2 } + guard canSendCrypto(token: maxCrypto.token, chainType: chain.blockchainType) else { + throw CryptoSender.Error.sendingNotSupported + } + let chainId = chain.blockchainType.supportedChainId(env: chain.env) let fromAddress = self.wallet.address let nonce: EthereumQuantity = try await JRPC_Client.instance.fetchNonce(address: fromAddress, @@ -172,4 +138,39 @@ struct NativeCryptoSender: CryptoSenderProtocol { } return downMultiplication(gasEstimate.quantity, gasPrice.quantity) } + + // Private methods + + private func createNativeSendTransaction(crypto: CryptoSendingSpec, + fromAddress: HexAddress, + toAddress: HexAddress, + chainId: Int) async throws -> EthereumTransaction { + let nonce: EthereumQuantity = try await JRPC_Client.instance.fetchNonce(address: fromAddress, + chainId: chainId) + let gasPrice = try await JRPC_Client.instance.fetchGasPrice(chainId: chainId) + +// let otherGasPrice = try await JRPC_Client.instance.fetchGasPrice(chainId: chainId, +// for: crypto.speed) +// +// let otherGasPrice = 54 + + let sender = EthereumAddress(hexString: fromAddress) + let receiver = EthereumAddress(hexString: toAddress) + + let amount = BigUInt(1_000_000_000.0 * crypto.amount) + + var transaction = EthereumTransaction(nonce: nonce, + gasPrice: gasPrice, + gas: try EthereumQuantity(Self.defaultSendTxGasPrice), + from: sender, + to: receiver, + value: try EthereumQuantity(amount.gwei) + ) + + if let gasEstimate = try? await JRPC_Client.instance.fetchGasLimit(transaction: transaction, chainId: chainId) { + transaction.gas = gasEstimate + } + return transaction + } + } From c16100cb3d1ae3974411deea4fbdb587918a1452 Mon Sep 17 00:00:00 2001 From: rommex Date: Thu, 28 Mar 2024 13:10:14 +0200 Subject: [PATCH 31/80] removed repeated code --- .../Home/SendCryptoAsset/CryptoSender.swift | 26 ++++--------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index a2beeab80..e203bf444 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -116,27 +116,11 @@ struct NativeCryptoSender: CryptoSenderProtocol { } let chainId = chain.blockchainType.supportedChainId(env: chain.env) - let fromAddress = self.wallet.address - let nonce: EthereumQuantity = try await JRPC_Client.instance.fetchNonce(address: fromAddress, - chainId: chainId) - let gasPrice = try await JRPC_Client.instance.fetchGasPrice(chainId: chainId) - let sender = EthereumAddress(hexString: fromAddress) - let receiver = EthereumAddress(hexString: toAddress) - - let amount = BigUInt(1_000_000_000.0 * maxCrypto.amount) - - let transaction = EthereumTransaction(nonce: nonce, - gasPrice: gasPrice, - gas: try EthereumQuantity(Self.defaultSendTxGasPrice), - from: sender, - to: receiver, - value: try EthereumQuantity(amount.gwei) - ) - - guard let gasEstimate = try? await JRPC_Client.instance.fetchGasLimit(transaction: transaction, chainId: chainId) else { - return downMultiplication(Self.defaultSendTxGasPrice, gasPrice.quantity) - } - return downMultiplication(gasEstimate.quantity, gasPrice.quantity) + let transaction = try await createNativeSendTransaction(crypto: maxCrypto, + fromAddress: self.wallet.address, + toAddress: toAddress, + chainId: chainId) + return downMultiplication(transaction.gas?.quantity ?? 0, transaction.gasPrice?.quantity ?? 0) } // Private methods From ddcbb8da54c41470d97246cd4f2e2b306e904583 Mon Sep 17 00:00:00 2001 From: rommex Date: Thu, 28 Mar 2024 13:47:11 +0200 Subject: [PATCH 32/80] refactored computeGasPrice() --- .../Home/SendCryptoAsset/CryptoSender.swift | 22 +++++++++---------- .../CryptoSenderProtocol.swift | 2 +- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index e203bf444..eb06240b2 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -38,6 +38,7 @@ struct CryptoSendingSpec { struct CryptoSender: CryptoSenderProtocol { enum Error: Swift.Error { case sendingNotSupported + case failedFetchGasPrice } enum SupportedToken: String { @@ -71,7 +72,6 @@ struct NativeCryptoSender: CryptoSenderProtocol { let wallet: UDWallet - func canSendCrypto(token: CryptoSender.SupportedToken, chainType: BlockchainType) -> Bool { // only native tokens supported return (token == CryptoSender.SupportedToken.eth && chainType == .Ethereum) || @@ -105,12 +105,6 @@ struct NativeCryptoSender: CryptoSenderProtocol { on chain: ChainSpec, toAddress: HexAddress) async throws -> Double { - func downMultiplication (_ a1: BigUInt, _ a2: BigUInt) -> Double { - let m1 = Double(a1) / 1_000_000_000.0 - let m2 = Double(a2) / 1_000_000_000.0 - return m1 * m2 - } - guard canSendCrypto(token: maxCrypto.token, chainType: chain.blockchainType) else { throw CryptoSender.Error.sendingNotSupported } @@ -120,7 +114,13 @@ struct NativeCryptoSender: CryptoSenderProtocol { fromAddress: self.wallet.address, toAddress: toAddress, chainId: chainId) - return downMultiplication(transaction.gas?.quantity ?? 0, transaction.gasPrice?.quantity ?? 0) + + guard let gasPriceWei = transaction.gasPrice?.quantity else { + throw CryptoSender.Error.failedFetchGasPrice + } + let gas = transaction.gas?.quantity ?? Self.defaultSendTxGasPrice + + return (Double(gasPriceWei) / 1_000_000_000.0) * Double(gas) // in Gwei } // Private methods @@ -141,20 +141,18 @@ struct NativeCryptoSender: CryptoSenderProtocol { let sender = EthereumAddress(hexString: fromAddress) let receiver = EthereumAddress(hexString: toAddress) - let amount = BigUInt(1_000_000_000.0 * crypto.amount) + let amountInGwei = BigUInt(1_000_000_000.0 * crypto.amount) var transaction = EthereumTransaction(nonce: nonce, gasPrice: gasPrice, gas: try EthereumQuantity(Self.defaultSendTxGasPrice), from: sender, to: receiver, - value: try EthereumQuantity(amount.gwei) - ) + value: try EthereumQuantity(amountInGwei.gwei) ) if let gasEstimate = try? await JRPC_Client.instance.fetchGasLimit(transaction: transaction, chainId: chainId) { transaction.gas = gasEstimate } return transaction } - } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift index c12d7273a..0211d2eae 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift @@ -34,7 +34,7 @@ protocol CryptoSenderProtocol { /// - maxCrypto: max crypto available for the send transaction /// - chain: chain where tx will be placed /// - toAddress: recepient address of the crypto - /// - Returns: Amount of crypto that must be deducted from maxCrypto as the gas fee in the future tx + /// - Returns: Amount of crypto that must be deducted from maxCrypto as the gas fee in the future tx, in Gwei func computeGasFeeFrom(maxCrypto: CryptoSendingSpec, on chain: ChainSpec, toAddress: HexAddress) async throws -> Double From 362dfdb7e1a7f06fe9ea8aca8769d861a741000c Mon Sep 17 00:00:00 2001 From: rommex Date: Thu, 28 Mar 2024 14:34:11 +0200 Subject: [PATCH 33/80] computed gas fee in token units (Double) --- .../Modules/Home/SendCryptoAsset/CryptoSender.swift | 5 ++++- .../Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index eb06240b2..c65853c54 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -120,7 +120,10 @@ struct NativeCryptoSender: CryptoSenderProtocol { } let gas = transaction.gas?.quantity ?? Self.defaultSendTxGasPrice - return (Double(gasPriceWei) / 1_000_000_000.0) * Double(gas) // in Gwei + let gasPriceGwei = (Double(gasPriceWei) / 1_000_000_000.0) + let gasFee = gasPriceGwei * Double(gas) / 1_000_000_000.0 // in token units + + return gasFee } // Private methods diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift index 0211d2eae..416145299 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift @@ -34,7 +34,7 @@ protocol CryptoSenderProtocol { /// - maxCrypto: max crypto available for the send transaction /// - chain: chain where tx will be placed /// - toAddress: recepient address of the crypto - /// - Returns: Amount of crypto that must be deducted from maxCrypto as the gas fee in the future tx, in Gwei + /// - Returns: Amount of crypto that must be deducted from maxCrypto as the gas fee in the future tx, in token units func computeGasFeeFrom(maxCrypto: CryptoSendingSpec, on chain: ChainSpec, toAddress: HexAddress) async throws -> Double From a512af1da692b844288933667c67276c9ae54a9f Mon Sep 17 00:00:00 2001 From: rommex Date: Thu, 28 Mar 2024 17:54:49 +0200 Subject: [PATCH 34/80] use speed-based gas price --- .../Home/SendCryptoAsset/CryptoSender.swift | 11 +-- .../Home/SendCryptoAsset/JRPC_Client.swift | 9 --- .../Services/Networking/NetworkService.swift | 67 ++++++++++++------- 3 files changed, 46 insertions(+), 41 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index c65853c54..c32d95682 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -134,20 +134,15 @@ struct NativeCryptoSender: CryptoSenderProtocol { chainId: Int) async throws -> EthereumTransaction { let nonce: EthereumQuantity = try await JRPC_Client.instance.fetchNonce(address: fromAddress, chainId: chainId) - let gasPrice = try await JRPC_Client.instance.fetchGasPrice(chainId: chainId) - -// let otherGasPrice = try await JRPC_Client.instance.fetchGasPrice(chainId: chainId, -// for: crypto.speed) -// -// let otherGasPrice = 54 + let speedBasedGasPriceGwei = try await NetworkService().fetchGasPrice(chainId: chainId, + for: crypto.speed) let sender = EthereumAddress(hexString: fromAddress) let receiver = EthereumAddress(hexString: toAddress) - let amountInGwei = BigUInt(1_000_000_000.0 * crypto.amount) var transaction = EthereumTransaction(nonce: nonce, - gasPrice: gasPrice, + gasPrice: try EthereumQuantity(speedBasedGasPriceGwei.gwei), gas: try EthereumQuantity(Self.defaultSendTxGasPrice), from: sender, to: receiver, diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift index c779e89ee..5e4aba22d 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift @@ -49,15 +49,6 @@ struct JRPC_Client { return EthereumQuantity(quantity: gasPriceBigUInt) } - func fetchGasPrice(chainId: Int, for speed: CryptoSendingSpec.TxSpeed) async throws -> Int { - let prices = try await NetworkService().getStatusGasPrices(chainId: chainId) - switch speed { - case .normal: return prices.safeLow - case .fast: return prices.fast - case .urgent: return prices.fastest - } - } - func fetchGasLimit(transaction: EthereumTransaction, chainId: Int) async throws -> EthereumQuantity { do { let gasPriceString = try await NetworkService().getGasEstimation(tx: transaction, diff --git a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift index 6a3494ce6..76df5ba1e 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift @@ -159,8 +159,13 @@ struct NetworkService { func fetchData(for url: URL, body: String? = nil, method: HttpRequestMethod = .post, - extraHeaders: [String: String] = [:]) async throws -> Data { - let urlRequest = urlRequest(for: url, body: body, method: method, extraHeaders: extraHeaders) + extraHeaders: [String: String] = [:], + includeStandardJsonHeaders: Bool = true) async throws -> Data { + let urlRequest = urlRequest(for: url, + body: body, + method: method, + extraHeaders: extraHeaders, + includeStandardJsonHeaders: includeStandardJsonHeaders) do { let (data, response) = try await URLSession.shared.data(for: urlRequest, delegate: nil) @@ -234,14 +239,18 @@ struct NetworkService { private func urlRequest(for url: URL, body: String? = nil, method: HttpRequestMethod = .post, - extraHeaders: [String: String] = [:]) -> URLRequest { + extraHeaders: [String: String] = [:], + includeStandardJsonHeaders: Bool) -> URLRequest { var urlRequest = URLRequest(url: url) urlRequest.httpMethod = method.string urlRequest.httpBody = body?.data(using: .utf8) - Self.headers.forEach { urlRequest.addValue($0.value, forHTTPHeaderField: $0.key)} - extraHeaders.forEach { urlRequest.addValue($0.value, forHTTPHeaderField: $0.key)} + if includeStandardJsonHeaders { + Self.headers.forEach { urlRequest.addValue($0.value, forHTTPHeaderField: $0.key)} + } + + extraHeaders.forEach { urlRequest.addValue($0.value, forHTTPHeaderField: $0.key)} urlRequest.addValue(Version.getCurrentAppVersionString() ?? "version n/a", forHTTPHeaderField: Self.appVersionHeaderKey) Debugger.printInfo(topic: .Network, "--- REQUEST TO ENDPOINT") @@ -296,16 +305,6 @@ extension NetworkService { let error: ErrorDescription } - struct StatusGasPricesInfo: Decodable { - struct AverageGasPrices: Decodable { - let safeLow: Int - let fast: Int - let fastest: Int - } - let ETH: AverageGasPrices - let MATIC: AverageGasPrices - } - struct JRPCRequestInfo { let name: String let paramsBuilder: ()->String @@ -316,6 +315,8 @@ extension NetworkService { case gasRequiredExceedsAllowance case genericError(String) case failedGetStatus + case failedParseStatusPrices + case unknownChain init(message: String) { if message.lowercased().starts(with: "gas required exceeds allowance") { @@ -369,20 +370,38 @@ extension NetworkService { paramsBuilder: { "[]"} )) } - func getStatusGasPrices() async throws -> StatusGasPricesInfo { - let url = URL(string: "https://unstoppabledomains.com/api/v1/status")! + func getStatusGasPrices(env: UnsConfigManager.BlockchainEnvironment) async throws -> [String: [String: Int]]{ + let url = env == .mainnet ? URL(string: "https://api.unstoppabledomains.com/api/v1/status")! : + URL(string: "https://api.ud-staging.com/api/v1/status")! let data = try await NetworkService().fetchData(for: url, method: .get) - guard let response = try? JSONDecoder().decode(StatusGasPricesInfo.self, from: data) else { - throw JRPCError.failedGetStatus + + guard let jsonPrices = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any], + let eth = jsonPrices["ETH"] as? [String: Any], + let ethPrices = eth["averageGasPrices"] as? [String: Int], + let matic = jsonPrices["MATIC"] as? [String: Any], + let maticPrices = matic["averageGasPrices"] as? [String: Int] else { + throw JRPCError.failedGetStatus + } + return ["ETH": ethPrices, "MATIC": maticPrices] + } + + func fetchGasPrice(chainId: Int, for speed: CryptoSendingSpec.TxSpeed) async throws -> Int { + let prices = try await getStatusGasPrices(chainId: chainId) + switch speed { + case .normal: return prices["safeLow"]! + case .fast: return prices["fast"]! + case .urgent: return prices["fastest"]! } - return response } - func getStatusGasPrices(chainId: Int) async throws -> StatusGasPricesInfo.AverageGasPrices { + func getStatusGasPrices(chainId: Int) async throws -> [String: Int] { switch chainId { - case BlockchainNetwork.ethMainnet.rawValue: return try await getStatusGasPrices().ETH - case BlockchainNetwork.polygonMainnet.rawValue: return try await getStatusGasPrices().MATIC - default: throw JRPCError.failedGetStatus + case BlockchainNetwork.ethMainnet.rawValue: return try await getStatusGasPrices(env: .mainnet)["ETH"]! + case BlockchainNetwork.polygonMainnet.rawValue: return try await getStatusGasPrices(env: .mainnet)["MATIC"]! + case BlockchainNetwork.ethGoerli.rawValue: return try await getStatusGasPrices(env: .testnet)["ETH"]! + case BlockchainNetwork.ethSepolia.rawValue: return try await getStatusGasPrices(env: .testnet)["ETH"]! + case BlockchainNetwork.polygonMumbai.rawValue: return try await getStatusGasPrices(env: .testnet)["MATIC"]! + default: throw JRPCError.unknownChain } } } From a412015eebde859d301ac16a17c8fa26a7fb780e Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 29 Mar 2024 12:43:00 +0700 Subject: [PATCH 35/80] Fixed preview target --- .../AppContext/PreviewWalletConnectServiceV2.swift | 4 ++++ .../domains-manager-ios.xcodeproj/project.pbxproj | 6 ------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios-preview/AppContext/PreviewWalletConnectServiceV2.swift b/unstoppable-ios-app/domains-manager-ios-preview/AppContext/PreviewWalletConnectServiceV2.swift index 60d1964e2..f8e535601 100644 --- a/unstoppable-ios-app/domains-manager-ios-preview/AppContext/PreviewWalletConnectServiceV2.swift +++ b/unstoppable-ios-app/domains-manager-ios-preview/AppContext/PreviewWalletConnectServiceV2.swift @@ -11,6 +11,10 @@ typealias ResponseV2 = String typealias SessionV2Proxy = String final class WalletConnectServiceV2: WalletConnectServiceV2Protocol { + func sendSignTx(sessions: [WCConnectedAppsStorageV2.SessionProxy], chainId: Int, tx: EthereumTransaction, address: HexAddress, in wallet: UDWallet) async throws -> ResponseV2 { + throw NSError() + } + var delegate: WalletConnectDelegate? func getWCV2Request(for code: QRCode) throws -> WalletConnectURI { diff --git a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj index 98b8f31b9..5f51c6b6a 100644 --- a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj +++ b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj @@ -9,7 +9,6 @@ /* Begin PBXBuildFile section */ 29018C8D2BACB7BC0004545D /* JRPC_Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29018C8C2BACB7BC0004545D /* JRPC_Client.swift */; }; 29018C8E2BACB7BC0004545D /* JRPC_Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29018C8C2BACB7BC0004545D /* JRPC_Client.swift */; }; - 29018C8F2BACB7BC0004545D /* JRPC_Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29018C8C2BACB7BC0004545D /* JRPC_Client.swift */; }; 290A60422950A89900882109 /* WalletConnectServiceV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 290A60412950A89900882109 /* WalletConnectServiceV2.swift */; }; 290A60482950AA1600882109 /* WalletConnect in Frameworks */ = {isa = PBXBuildFile; productRef = 290A60472950AA1600882109 /* WalletConnect */; }; 290A604A2950AA1600882109 /* WalletConnectAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 290A60492950AA1600882109 /* WalletConnectAuth */; }; @@ -34,10 +33,8 @@ 299713D728F693F600743003 /* EIP712TypedData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 299713D628F693F600743003 /* EIP712TypedData.swift */; }; 29AA6AA42BAAE9F500D24FB5 /* CryptoSenderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA32BAAE9F500D24FB5 /* CryptoSenderProtocol.swift */; }; 29AA6AA52BAAE9F500D24FB5 /* CryptoSenderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA32BAAE9F500D24FB5 /* CryptoSenderProtocol.swift */; }; - 29AA6AA62BAAE9F500D24FB5 /* CryptoSenderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA32BAAE9F500D24FB5 /* CryptoSenderProtocol.swift */; }; 29AA6AA82BAC389D00D24FB5 /* CryptoSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA72BAC389D00D24FB5 /* CryptoSender.swift */; }; 29AA6AA92BAC389D00D24FB5 /* CryptoSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA72BAC389D00D24FB5 /* CryptoSender.swift */; }; - 29AA6AAA2BAC389D00D24FB5 /* CryptoSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA72BAC389D00D24FB5 /* CryptoSender.swift */; }; 29AEA04B292F7D60003BB5B4 /* SecurePersistedStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AEA04A292F7D60003BB5B4 /* SecurePersistedStorage.swift */; }; 29AEA050292F941F003BB5B4 /* PersistedSignaturesStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AEA04F292F941F003BB5B4 /* PersistedSignaturesStorage.swift */; }; 29AEA05529367785003BB5B4 /* UDWallet+Signing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AEA05429367785003BB5B4 /* UDWallet+Signing.swift */; }; @@ -10178,7 +10175,6 @@ C6C8F9412B21841B00A9834D /* MessagingServiceProtocol.swift in Sources */, C618083E2B19B0680032E543 /* PreviewDeepLinksService.swift in Sources */, C6D646822B1ED12900D724AC /* DomainProfileGeneralData.swift in Sources */, - 29AA6AA62BAAE9F500D24FB5 /* CryptoSenderProtocol.swift in Sources */, C6C8F9402B2183FA00A9834D /* UIUserInterfaceStyle.swift in Sources */, C6D647922B1EE75A00D724AC /* PreviewCoinRecordsService.swift in Sources */, C6D646C52B1ED24300D724AC /* GroupedCoinRecord.swift in Sources */, @@ -10301,7 +10297,6 @@ C630E4AD2B7F4B8D008F3269 /* FlippedUpsideDownModifier.swift in Sources */, C6D646E22B1ED49D00D724AC /* TableViewSelectionCell.swift in Sources */, C6FBCAA32B91C6AC00BA39DF /* HomeExploreRecentProfilesSectionView.swift in Sources */, - 29AA6AAA2BAC389D00D24FB5 /* CryptoSender.swift in Sources */, C6C8F8262B217CDD00A9834D /* UDWallet+RecoveryType.swift in Sources */, C6D647182B1ED83800D724AC /* CollectionTextFooterReusableView.swift in Sources */, C61B6C712B9F056C007408FD /* PublicProfileBadgeTileView.swift in Sources */, @@ -10735,7 +10730,6 @@ C61807C52B19A3580032E543 /* PreviewAppContext.swift in Sources */, C6960C3D2B1997F600B79E28 /* Constants.swift in Sources */, C6960C602B199AC900B79E28 /* Version.swift in Sources */, - 29018C8F2BACB7BC0004545D /* JRPC_Client.swift in Sources */, C63AD0982B955BE600BF8C83 /* HomeExploreDomainRowView.swift in Sources */, C6D646532B1ED10100D724AC /* DomainProfileUpdatingRecordsCell.swift in Sources */, C6D646892B1ED12D00D724AC /* DomainProfileGeneralInfoSection.swift in Sources */, From dd23dcab59e9b7494919eaedeb9689543bab9b0c Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 29 Mar 2024 12:43:58 +0700 Subject: [PATCH 36/80] Implemented use max crypto to send calculations. Updated show pull up logic --- .../Entities/NumberPadInputInterpreter.swift | 4 +++ .../Extensions/UserDefaults.swift | 2 -- .../SelectTokenAssetAmountToSendView.swift | 36 +++++++++++++------ 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Entities/NumberPadInputInterpreter.swift b/unstoppable-ios-app/domains-manager-ios/Entities/NumberPadInputInterpreter.swift index 578624df5..784df26b1 100644 --- a/unstoppable-ios-app/domains-manager-ios/Entities/NumberPadInputInterpreter.swift +++ b/unstoppable-ios-app/domains-manager-ios/Entities/NumberPadInputInterpreter.swift @@ -48,4 +48,8 @@ struct NumberPadInputInterpreter { } return input } + + mutating func setInput(_ input: Double) { + self.input = String(input) + } } diff --git a/unstoppable-ios-app/domains-manager-ios/Extensions/UserDefaults.swift b/unstoppable-ios-app/domains-manager-ios/Extensions/UserDefaults.swift index 7838a41c6..5589f89ba 100644 --- a/unstoppable-ios-app/domains-manager-ios/Extensions/UserDefaults.swift +++ b/unstoppable-ios-app/domains-manager-ios/Extensions/UserDefaults.swift @@ -31,7 +31,6 @@ public enum UserDefaultsKey: String { case selectedProfileId case firebaseUser case didUpdateToWalletVersion - case didShowSendMaxCryptoInfoPullUp // Purchase domains case purchasedDomains @@ -45,7 +44,6 @@ extension UserDefaults { @UserDefaultsValue(key: UserDefaultsKey.homeScreenSettingsButtonPressed, defaultValue: false) static var homeScreenSettingsButtonPressed: Bool @UserDefaultsValue(key: UserDefaultsKey.didMigrateXMTPConsentsListFromUD, defaultValue: false) static var didMigrateXMTPConsentsListFromUD: Bool @UserDefaultsValue(key: UserDefaultsKey.didUpdateToWalletVersion, defaultValue: false) static var didUpdateToWalletVersion: Bool - @UserDefaultsValue(key: UserDefaultsKey.didShowSendMaxCryptoInfoPullUp, defaultValue: false) static var didShowSendMaxCryptoInfoPullUp: Bool @UserDefaultsValue(key: UserDefaultsKey.buildVersion, defaultValue: "") static var buildVersion: String @UserDefaultsRawRepresentableValue(key: .appearanceStyle, defaultValue: .unspecified) static var appearanceStyle: UIUserInterfaceStyle @UserDefaultsRawRepresentableValue(key: .selectedBlockchainType, defaultValue: .Ethereum) static var selectedBlockchainType: BlockchainType diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift index 379821705..dc79b180a 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift @@ -89,8 +89,7 @@ private extension SelectTokenAssetAmountToSendView { // MARK: - Converted value private extension SelectTokenAssetAmountToSendView { var isSufficientFunds: Bool { - getCurrentInput().valueOf(type: .tokenAmount, - for: token) <= token.balance + inputValueFor(inputType: .tokenAmount) <= token.balance } @ViewBuilder @@ -148,14 +147,17 @@ private extension SelectTokenAssetAmountToSendView { } } + func inputValueFor(inputType: SendCryptoAsset.TokenAssetAmountInputType) -> Double { + getCurrentInput().valueOf(type: inputType, + for: token) + } + var usdConvertedString: String { - formatCartPrice(getCurrentInput().valueOf(type: .usdAmount, - for: token)) + formatCartPrice(inputValueFor(inputType: .usdAmount)) } var tokenConvertedString: String { - BalanceStringFormatter.tokenFullBalanceString(balance: getCurrentInput().valueOf(type: .tokenAmount, - for: token), + BalanceStringFormatter.tokenFullBalanceString(balance: inputValueFor(inputType: .tokenAmount), symbol: token.symbol) } @@ -212,7 +214,14 @@ private extension SelectTokenAssetAmountToSendView { } } - var isUsingMax: Bool { false } + var isUsingMax: Bool { + switch inputType { + case .usdAmount: + return inputValueFor(inputType: .usdAmount) == token.balanceUsd + case .tokenAmount: + return inputValueFor(inputType: .tokenAmount) == token.balance + } + } var useMaxButtonTitle: String { if isUsingMax { String.Constants.usingMax.localized() @@ -235,10 +244,15 @@ private extension SelectTokenAssetAmountToSendView { } func maxButtonPressed() { - if !UserDefaults.didShowSendMaxCryptoInfoPullUp { - UserDefaults.didShowSendMaxCryptoInfoPullUp = true - pullUp = .default(.maxCryptoSendInfoPullUp(token: token)) - } + guard !isUsingMax else { return } + + pullUp = .default(.maxCryptoSendInfoPullUp(token: token)) + setMaxInputValue() + } + + func setMaxInputValue() { + self.inputType = .tokenAmount + interpreter.setInput(token.balance) } } From d264335945d2c0a127bb6abe65625f0a2e66ccc6 Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 29 Mar 2024 13:01:11 +0700 Subject: [PATCH 37/80] Filter tokens by can send --- .../domains-manager-ios.xcodeproj/project.pbxproj | 2 ++ .../Entities/BalanceTokenUIDescription.swift | 3 +++ .../Home/SendCryptoAsset/CryptoSenderProtocol.swift | 1 - .../SelectCryptoAssetToSendView.swift | 1 + .../SendCryptoAsset/SendCryptoAssetViewModel.swift | 13 +++++++++++++ 5 files changed, 19 insertions(+), 1 deletion(-) diff --git a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj index 5f51c6b6a..0885c37e5 100644 --- a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj +++ b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj @@ -1288,6 +1288,7 @@ C6A359282BB52CDC00B1209A /* SendCryptoReceiverInfoTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A359262BB52CDC00B1209A /* SendCryptoReceiverInfoTitleView.swift */; }; C6A3592A2BB5586700B1209A /* NavigationTrackerViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A359292BB5586700B1209A /* NavigationTrackerViewModifier.swift */; }; C6A3592B2BB5586700B1209A /* NavigationTrackerViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A359292BB5586700B1209A /* NavigationTrackerViewModifier.swift */; }; + C6A3592F2BB68D9B00B1209A /* CryptoSenderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA32BAAE9F500D24FB5 /* CryptoSenderProtocol.swift */; }; C6A474C729D149560073415F /* LoginFlowNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A474C629D149560073415F /* LoginFlowNavigationController.swift */; }; C6A474D029D150A40073415F /* NoParkedDomainsFoundViewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A474CD29D150A40073415F /* NoParkedDomainsFoundViewPresenter.swift */; }; C6A474D429D150A40073415F /* NoParkedDomainsFoundViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A474CE29D150A40073415F /* NoParkedDomainsFoundViewController.swift */; }; @@ -10282,6 +10283,7 @@ C6A89C5E2B316540008AB043 /* HotFeaturesSuggestionsFetcher.swift in Sources */, C6D646CA2B1ED26300D724AC /* EnterValueViewPresenter.swift in Sources */, C6D645CD2B1DBD4100D724AC /* SetupReverseResolutionNavBarPopAnimation.swift in Sources */, + C6A3592F2BB68D9B00B1209A /* CryptoSenderProtocol.swift in Sources */, C6E28F3E2B33486700026E6C /* PreviewDomainProfileActionCover.swift in Sources */, C6C8F9782B2188FA00A9834D /* QRScannerViewController.swift in Sources */, C6203A4F2B882959000A1A8E /* HomeExploreView.swift in Sources */, diff --git a/unstoppable-ios-app/domains-manager-ios/Entities/BalanceTokenUIDescription.swift b/unstoppable-ios-app/domains-manager-ios/Entities/BalanceTokenUIDescription.swift index cc3172a50..7055f977f 100644 --- a/unstoppable-ios-app/domains-manager-ios/Entities/BalanceTokenUIDescription.swift +++ b/unstoppable-ios-app/domains-manager-ios/Entities/BalanceTokenUIDescription.swift @@ -78,6 +78,9 @@ struct BalanceTokenUIDescription: Hashable, Identifiable { return [tokenDescription] + subTokenDescriptions } + func blockchainType() -> BlockchainType? { + BlockchainType(rawValue: chain) + } } // MARK: - Open methods diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift index 416145299..5328a61fb 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift @@ -6,7 +6,6 @@ // import Foundation -import BigInt protocol CryptoSenderProtocol { init(wallet: UDWallet) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectAssetToSend/SelectCryptoAssetToSendView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectAssetToSend/SelectCryptoAssetToSendView.swift index 4982bc40c..f62a84804 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectAssetToSend/SelectCryptoAssetToSendView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectAssetToSend/SelectCryptoAssetToSendView.swift @@ -45,6 +45,7 @@ private extension SelectCryptoAssetToSendView { tokens = viewModel.sourceWallet.balance .map { BalanceTokenUIDescription.extractFrom(walletBalance: $0) } .flatMap({ $0 }) + .filter { viewModel.canSendToken($0) } .filter { $0.balanceUsd > 1 } .sorted(by: { lhs, rhs in lhs.balanceUsd > rhs.balanceUsd diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift index 88a9308c3..7b160586d 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift @@ -12,9 +12,11 @@ final class SendCryptoAssetViewModel: ObservableObject { @Published var sourceWallet: WalletEntity @Published var navigationState: NavigationStateManager? @Published var navPath: [SendCryptoAsset.NavigationDestination] = [] + private let cryptoSender: CryptoSenderProtocol init(initialData: SendCryptoAsset.InitialData) { self.sourceWallet = initialData.sourceWallet + self.cryptoSender = CryptoSender.init(wallet: initialData.sourceWallet.udWallet) } func handleAction(_ action: SendCryptoAsset.FlowAction) { @@ -43,4 +45,15 @@ final class SendCryptoAssetViewModel: ObservableObject { } } + func canSendToken(_ token: BalanceTokenUIDescription) -> Bool { + guard let supportedToken = supportedTokenFrom(token: token), + let chainType = token.blockchainType() else { return false } + + + return cryptoSender.canSendCrypto(token: supportedToken, chainType: chainType) + } + + func supportedTokenFrom(token: BalanceTokenUIDescription) -> CryptoSender.SupportedToken? { + CryptoSender.SupportedToken(rawValue: token.symbol.uppercased()) + } } From e0722fdcedf0a0c7016583c5e4bdaad034caef8a Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 29 Mar 2024 13:13:35 +0700 Subject: [PATCH 38/80] Refactoring --- .../Entities/BalanceTokenUIDescription.swift | 2 +- .../Home/SendCryptoAsset/SendCryptoAssetViewModel.swift | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Entities/BalanceTokenUIDescription.swift b/unstoppable-ios-app/domains-manager-ios/Entities/BalanceTokenUIDescription.swift index 7055f977f..d9959d24d 100644 --- a/unstoppable-ios-app/domains-manager-ios/Entities/BalanceTokenUIDescription.swift +++ b/unstoppable-ios-app/domains-manager-ios/Entities/BalanceTokenUIDescription.swift @@ -78,7 +78,7 @@ struct BalanceTokenUIDescription: Hashable, Identifiable { return [tokenDescription] + subTokenDescriptions } - func blockchainType() -> BlockchainType? { + var blockchainType: BlockchainType? { BlockchainType(rawValue: chain) } } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift index 7b160586d..158a6cee3 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift @@ -46,14 +46,13 @@ final class SendCryptoAssetViewModel: ObservableObject { } func canSendToken(_ token: BalanceTokenUIDescription) -> Bool { - guard let supportedToken = supportedTokenFrom(token: token), - let chainType = token.blockchainType() else { return false } - + guard let supportedToken = getSupportedTokenFor(balanceToken: token), + let chainType = token.blockchainType else { return false } return cryptoSender.canSendCrypto(token: supportedToken, chainType: chainType) } - func supportedTokenFrom(token: BalanceTokenUIDescription) -> CryptoSender.SupportedToken? { - CryptoSender.SupportedToken(rawValue: token.symbol.uppercased()) + private func getSupportedTokenFor(balanceToken: BalanceTokenUIDescription) -> CryptoSender.SupportedToken? { + CryptoSender.SupportedToken(rawValue: balanceToken.symbol.uppercased()) } } From b78d56375144b978c4a1bc3269f9f4d49bd9bd42 Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 29 Mar 2024 13:30:36 +0700 Subject: [PATCH 39/80] Created bridge functions to send crypto in view model --- .../SendCryptoAsset/SendCryptoAsset.swift | 12 ++++ .../SendCryptoAssetViewModel.swift | 64 ++++++++++++++++++- 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift index 4a18db37c..8f5b8c714 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift @@ -98,6 +98,18 @@ extension SendCryptoAsset { let receiver: AssetReceiver let token: BalanceTokenUIDescription let amount: TokenAssetAmountInput + + var receiverAddress: HexAddress { + receiver.walletAddress + } + + func getTokenAmountValue() -> Double { + amount.valueOf(type: .tokenAmount, for: token) + } + } + + enum TransactionSpeed { + case normal, fast, urgent } } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift index 158a6cee3..43165a552 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift @@ -46,13 +46,71 @@ final class SendCryptoAssetViewModel: ObservableObject { } func canSendToken(_ token: BalanceTokenUIDescription) -> Bool { - guard let supportedToken = getSupportedTokenFor(balanceToken: token), + guard let supportedToken = try? getSupportedTokenFor(balanceToken: token), let chainType = token.blockchainType else { return false } return cryptoSender.canSendCrypto(token: supportedToken, chainType: chainType) } + + func sendCryptoTokenWith(sendData: SendCryptoAsset.SendTokenAssetData, + txSpeed: SendCryptoAsset.TransactionSpeed) async throws -> String { + let crypto = try getCryptoSendingSpecFor(sendData: sendData, txSpeed: txSpeed) + let chain = try getChainSpecFor(balanceToken: sendData.token) + let toAddress = sendData.receiverAddress + + return try await cryptoSender.sendCrypto(crypto: crypto, chain: chain, toAddress: toAddress) + } + + func computeGasFeeFor(sendData: SendCryptoAsset.SendTokenAssetData, + txSpeed: SendCryptoAsset.TransactionSpeed) async throws -> Double { + let crypto = try getCryptoSendingSpecFor(sendData: sendData, txSpeed: txSpeed) + let chain = try getChainSpecFor(balanceToken: sendData.token) + let toAddress = sendData.receiverAddress + + return try await cryptoSender.computeGasFeeFrom(maxCrypto: crypto, on: chain, toAddress: toAddress) + } + + private func getCryptoSendingSpecFor(sendData: SendCryptoAsset.SendTokenAssetData, + txSpeed: SendCryptoAsset.TransactionSpeed) throws -> CryptoSendingSpec { + let token = try getSupportedTokenFor(balanceToken: sendData.token) + let amount = sendData.getTokenAmountValue() + let speed = getSpecTransactionSpeedFor(txSpeed: txSpeed) + + return CryptoSendingSpec(token: token, + amount: amount, + speed: speed) + } + + private func getChainSpecFor(balanceToken: BalanceTokenUIDescription) throws -> ChainSpec { + guard let blockchainType = balanceToken.blockchainType else { + throw CryptoSender.Error.sendingNotSupported + } + let env = getCurrentEnvironment() - private func getSupportedTokenFor(balanceToken: BalanceTokenUIDescription) -> CryptoSender.SupportedToken? { - CryptoSender.SupportedToken(rawValue: balanceToken.symbol.uppercased()) + return ChainSpec(blockchainType: blockchainType, + env: env) + } + + private func getCurrentEnvironment() -> UnsConfigManager.BlockchainEnvironment { + .mainnet + } + + private func getSupportedTokenFor(balanceToken: BalanceTokenUIDescription) throws -> CryptoSender.SupportedToken { + guard let token = CryptoSender.SupportedToken(rawValue: balanceToken.symbol.uppercased()) else { + throw CryptoSender.Error.sendingNotSupported + } + return token } + + private func getSpecTransactionSpeedFor(txSpeed: SendCryptoAsset.TransactionSpeed) -> CryptoSendingSpec.TxSpeed { + switch txSpeed { + case .normal: + return .normal + case .fast: + return .fast + case .urgent: + return .urgent + } + } + } From f1b449926c80edcf3f68dd07f7bb0dcdab5086e5 Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 29 Mar 2024 13:39:00 +0700 Subject: [PATCH 40/80] Added ui entity for transaction speed --- .../ConfirmSendAssetReviewInfoView.swift | 36 ++++++++++--------- .../ConfirmSendTokenView.swift | 1 + .../SendCryptoAsset/SendCryptoAsset.swift | 36 ++++++++++++++++++- 3 files changed, 56 insertions(+), 17 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift index f6901db13..9037328e7 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift @@ -204,24 +204,28 @@ private extension ConfirmSendAssetReviewInfoView { } func getTransactionSpeedActions() -> [InfoActionDescription] { - [.init(title: "Urgent", - subtitle: "41-53 Gwei (~1 sec)", - iconName: "flame", - tintColor: .foregroundDanger, - action: { didSelectTransactionSpeed() }), - .init(title: "Fast", - subtitle: "44-54 Gwei (~4 sec)", - iconName: "bolt", - tintColor: .foregroundWarning, - action: { didSelectTransactionSpeed() }), - .init(title: "Normal", - subtitle: "43-51 Gwei (~12 sec)", - iconName: "clock", - tintColor: .foregroundDefault, - action: { didSelectTransactionSpeed() })].reversed() + SendCryptoAsset.TransactionSpeed.allCases.map { txSpeed in + InfoActionDescription(title: txSpeed.title, + subtitle: txSpeed.subtitle, + iconName: txSpeed.iconName, + tintColor: tintColorFor(txSpeed: txSpeed), + action: { didSelectTransactionSpeed(txSpeed) }) + } } - func didSelectTransactionSpeed() { + func tintColorFor(txSpeed: SendCryptoAsset.TransactionSpeed) -> UIColor { + switch txSpeed { + case .normal: + .foregroundDefault + case .fast: + .foregroundWarning + case .urgent: + .foregroundDanger + } + } + + + func didSelectTransactionSpeed(_ transactionSpeed: SendCryptoAsset.TransactionSpeed) { } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift index d11674920..292391628 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift @@ -13,6 +13,7 @@ struct ConfirmSendTokenView: View { let data: SendCryptoAsset.SendTokenAssetData + @State private var txSpeed: SendCryptoAsset.TransactionSpeed = .normal private var token: BalanceTokenUIDescription { data.token } private var receiver: SendCryptoAsset.AssetReceiver { data.receiver } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift index 8f5b8c714..933b4a571 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift @@ -108,8 +108,42 @@ extension SendCryptoAsset { } } - enum TransactionSpeed { + enum TransactionSpeed: CaseIterable { case normal, fast, urgent + + var title: String { + switch self { + case .normal: + return "Normal" + case .fast: + return "Fast" + case .urgent: + return "Urgent" + } + } + + var subtitle: String { + switch self { + case .normal: + return "43-51 Gwei (~12 sec)" + case .fast: + return "44-54 Gwei (~4 sec)" + case .urgent: + return "41-53 Gwei (~1 sec)" + } + } + + var iconName: String { + switch self { + case .normal: + "clock" + case .fast: + "bolt" + case .urgent: + "flame" + } + } + } } From e8c1e46ec277bef13e74d34b4ddcda9c93c960ba Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 29 Mar 2024 14:44:24 +0700 Subject: [PATCH 41/80] Fetch gas fee and display on the UI. --- .../project.pbxproj | 6 ++ .../ConfirmSendAssetReviewInfoView.swift | 67 ++++++++++------- .../ConfirmSendTokenDataModel.swift | 43 +++++++++++ .../ConfirmSendTokenView.swift | 72 ++++++++++++++++--- 4 files changed, 153 insertions(+), 35 deletions(-) create mode 100644 unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenDataModel.swift diff --git a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj index 0885c37e5..cf37adc17 100644 --- a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj +++ b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj @@ -1289,6 +1289,8 @@ C6A3592A2BB5586700B1209A /* NavigationTrackerViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A359292BB5586700B1209A /* NavigationTrackerViewModifier.swift */; }; C6A3592B2BB5586700B1209A /* NavigationTrackerViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A359292BB5586700B1209A /* NavigationTrackerViewModifier.swift */; }; C6A3592F2BB68D9B00B1209A /* CryptoSenderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA32BAAE9F500D24FB5 /* CryptoSenderProtocol.swift */; }; + C6A359322BB699FC00B1209A /* ConfirmSendTokenDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A359312BB699FC00B1209A /* ConfirmSendTokenDataModel.swift */; }; + C6A359332BB699FC00B1209A /* ConfirmSendTokenDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A359312BB699FC00B1209A /* ConfirmSendTokenDataModel.swift */; }; C6A474C729D149560073415F /* LoginFlowNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A474C629D149560073415F /* LoginFlowNavigationController.swift */; }; C6A474D029D150A40073415F /* NoParkedDomainsFoundViewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A474CD29D150A40073415F /* NoParkedDomainsFoundViewPresenter.swift */; }; C6A474D429D150A40073415F /* NoParkedDomainsFoundViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A474CE29D150A40073415F /* NoParkedDomainsFoundViewController.swift */; }; @@ -3540,6 +3542,7 @@ C6A2D5CC2850EDCD00327C47 /* ConnectServerRequestConfirmationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectServerRequestConfirmationView.swift; sourceTree = ""; }; C6A359262BB52CDC00B1209A /* SendCryptoReceiverInfoTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendCryptoReceiverInfoTitleView.swift; sourceTree = ""; }; C6A359292BB5586700B1209A /* NavigationTrackerViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationTrackerViewModifier.swift; sourceTree = ""; }; + C6A359312BB699FC00B1209A /* ConfirmSendTokenDataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmSendTokenDataModel.swift; sourceTree = ""; }; C6A474C629D149560073415F /* LoginFlowNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginFlowNavigationController.swift; sourceTree = ""; }; C6A474CD29D150A40073415F /* NoParkedDomainsFoundViewPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoParkedDomainsFoundViewPresenter.swift; sourceTree = ""; }; C6A474CE29D150A40073415F /* NoParkedDomainsFoundViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoParkedDomainsFoundViewController.swift; sourceTree = ""; }; @@ -5339,6 +5342,7 @@ isa = PBXGroup; children = ( C62AC1FB2BABE565003FB69E /* ConfirmSendTokenView.swift */, + C6A359312BB699FC00B1209A /* ConfirmSendTokenDataModel.swift */, ); path = ConfirmSendToken; sourceTree = ""; @@ -9146,6 +9150,7 @@ C6B65F902B565C70006D1812 /* HomeWalletSortingSelectorView.swift in Sources */, C688C19E2B848D0600BD233A /* ChatRequestsListView.swift in Sources */, C6C995BA289D313D00367362 /* CNavigationControllerDefaultPushAnimation.swift in Sources */, + C6A359322BB699FC00B1209A /* ConfirmSendTokenDataModel.swift in Sources */, C6D6E5B2281A215D008C66BB /* SegmentPicker.swift in Sources */, C6DDF2502B147F1A006D1F0B /* UDButtonStyle.swift in Sources */, C63DD36129C89FEC002D45B2 /* LoginViewPresenter.swift in Sources */, @@ -10550,6 +10555,7 @@ C6B761D72BB3CDD900773943 /* WalletTransactionsService.swift in Sources */, C6C8F83E2B217E9600A9834D /* RecoveryPhraseViewController.swift in Sources */, C6C8F8742B21822700A9834D /* TutorialStepViewController.swift in Sources */, + C6A359332BB699FC00B1209A /* ConfirmSendTokenDataModel.swift in Sources */, C621A4432BB1137900CB5CB9 /* QRScannerState.swift in Sources */, C6C8F8BF2B2182E400A9834D /* ChangeWalletsReverseResolutionDomainViewPresenter.swift in Sources */, C62900FC2BAACCD2008B35A2 /* UDNumberPadView.swift in Sources */, diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift index 9037328e7..bfa83e227 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift @@ -13,9 +13,10 @@ struct ConfirmSendAssetReviewInfoView: View { private let lineWidth: CGFloat = 1 private let sectionHeight: CGFloat = 48 - + let asset: Asset let sourceWallet: WalletEntity + @State private var fromUserAvatar: UIImage? var body: some View { @@ -173,22 +174,10 @@ private extension ConfirmSendAssetReviewInfoView { } - func getFromWalletInfoSection() -> SectionType { - .infoValue(.init(title: String.Constants.from.localized(), - icon: fromUserAvatar ?? sourceWallet.displayInfo.source.listIcon, - value: sourceWallet.domainOrDisplayName)) - } - - func getChainInfoSection() -> SectionType { - .infoValue(.init(title: String.Constants.chain.localized(), - icon: chainIcon, - value: getBlockchainType().fullName)) - } - func getBlockchainType() -> BlockchainType { switch asset { - case .token(let token): - BlockchainType(rawValue: token.symbol) ?? .Matic + case .token(let dataModel): + BlockchainType(rawValue: dataModel.token.symbol) ?? .Matic case .domain(let domain): domain.blockchain ?? .Matic } @@ -196,8 +185,9 @@ private extension ConfirmSendAssetReviewInfoView { func getCurrentSections() -> [SectionType] { switch asset { - case .token: - getSectionsForToken() + case .token(let dataModel): + getSectionsForToken(selectedTxSpeed: dataModel.txSpeed, + gasUsd: dataModel.gasUsd) case .domain: getSectionsForDomain() } @@ -226,25 +216,50 @@ private extension ConfirmSendAssetReviewInfoView { func didSelectTransactionSpeed(_ transactionSpeed: SendCryptoAsset.TransactionSpeed) { - + switch asset { + case .token(let dataModel): + dataModel.txSpeed = transactionSpeed + case .domain: + Debugger.printFailure("Incorrect state", critical: true) + } } - func getSectionsForToken() -> [SectionType] { + func getSectionsForToken(selectedTxSpeed: SendCryptoAsset.TransactionSpeed, + gasUsd: Double?) -> [SectionType] { [getFromWalletInfoSection(), getChainInfoSection(), .infoValue(.init(title: String.Constants.speed.localized(), icon: .chevronGrabberVertical, iconColor: .foregroundSecondary, - value: "Fast", - valueColor: .foregroundWarning, - subValue: "~ 4 sec", + value: selectedTxSpeed.title, + valueColor: Color(uiColor: tintColorFor(txSpeed: selectedTxSpeed)), + subValue: selectedTxSpeed.subtitle, actions: getTransactionSpeedActions())), .infoValue(.init(title: String.Constants.feeEstimate.localized(), icon: .tildaIcon, - value: "$4.20")), + value: gasUsdTitleFor(gasUsd: gasUsd))), .info(String.Constants.sendCryptoReviewPromptMessage.localized())] } + func getFromWalletInfoSection() -> SectionType { + .infoValue(.init(title: String.Constants.from.localized(), + icon: fromUserAvatar ?? sourceWallet.displayInfo.source.listIcon, + value: sourceWallet.domainOrDisplayName)) + } + + func getChainInfoSection() -> SectionType { + .infoValue(.init(title: String.Constants.chain.localized(), + icon: chainIcon, + value: getBlockchainType().fullName)) + } + + func gasUsdTitleFor(gasUsd: Double?) -> String { + if let gasUsd { + return formatCartPrice(gasUsd) + } + return "" + } + func getSectionsForDomain() -> [SectionType] { [getFromWalletInfoSection(), getChainInfoSection(), @@ -398,12 +413,14 @@ private extension ConfirmSendAssetReviewInfoView { // MARK: - Open methods extension ConfirmSendAssetReviewInfoView { enum Asset { - case token(BalanceTokenUIDescription) + case token(ConfirmSendTokenDataModel) case domain(DomainDisplayInfo) } } #Preview { - ConfirmSendAssetReviewInfoView(asset: .token(MockEntitiesFabric.Tokens.mockUIToken()), + ConfirmSendAssetReviewInfoView(asset: .token(.init(data: .init(receiver: MockEntitiesFabric.SendCrypto.mockReceiver(), + token: MockEntitiesFabric.Tokens.mockUIToken(), + amount: .usdAmount(3998234.3)))), sourceWallet: MockEntitiesFabric.Wallet.mockEntities()[0]) } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenDataModel.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenDataModel.swift new file mode 100644 index 000000000..32acd4f66 --- /dev/null +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenDataModel.swift @@ -0,0 +1,43 @@ +// +// ConfirmSendTokenDataModel.swift +// domains-manager-ios +// +// Created by Oleg Kuplin on 29.03.2024. +// + +import SwiftUI + +final class ConfirmSendTokenDataModel: ObservableObject, Hashable { + + let data: SendCryptoAsset.SendTokenAssetData + @Published var txSpeed: SendCryptoAsset.TransactionSpeed = .normal + @Published var gasAmount: Double? = nil + + var token: BalanceTokenUIDescription { data.token } + var receiver: SendCryptoAsset.AssetReceiver { data.receiver } + var amount: SendCryptoAsset.TokenAssetAmountInput { data.amount } + var gasUsd: Double? { + if let gasAmount, + let marketUsd = data.token.marketUsd { + return gasAmount * marketUsd + } + return nil + } + + init(data: SendCryptoAsset.SendTokenAssetData) { + self.data = data + } + + static func == (lhs: ConfirmSendTokenDataModel, rhs: ConfirmSendTokenDataModel) -> Bool { + lhs.data == rhs.data && + lhs.txSpeed == rhs.txSpeed && + lhs.gasAmount == rhs.gasAmount + } + + func hash(into hasher: inout Hasher) { + hasher.combine(data) + hasher.combine(txSpeed) + hasher.combine(gasAmount) + } + +} diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift index 292391628..7a71369aa 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift @@ -10,13 +10,14 @@ import SwiftUI struct ConfirmSendTokenView: View { @EnvironmentObject var viewModel: SendCryptoAssetViewModel - - let data: SendCryptoAsset.SendTokenAssetData - - @State private var txSpeed: SendCryptoAsset.TransactionSpeed = .normal - private var token: BalanceTokenUIDescription { data.token } - private var receiver: SendCryptoAsset.AssetReceiver { data.receiver } + @ObservedObject private var dataModel: ConfirmSendTokenDataModel + @State private var error: Error? + @State private var isLoading = false + @State private var stateId = UUID() + private var token: BalanceTokenUIDescription { dataModel.token } + private var receiver: SendCryptoAsset.AssetReceiver { dataModel.receiver } + var body: some View { VStack(spacing: 4) { sendingTokenInfoView() @@ -26,11 +27,60 @@ struct ConfirmSendTokenView: View { Spacer() confirmButton() } + .onChange(of: dataModel.txSpeed) { _ in + refreshGasAmount() + } .padding(16) .background(Color.backgroundDefault) .animation(.default, value: UUID()) .addNavigationTopSafeAreaOffset() .navigationTitle(String.Constants.youAreSending.localized()) + .displayError($error) + .onAppear(perform: onAppear) + } + + init(data: SendCryptoAsset.SendTokenAssetData) { + self.dataModel = ConfirmSendTokenDataModel(data: data) + } + +} + +// MARK: - Private methods +private extension ConfirmSendTokenView { + func onAppear() { + refreshGasAmount() + } + + func refreshGasAmount() { + dataModel.gasAmount = nil + updateStateId() + Task { + isLoading = true + do { + dataModel.gasAmount = try await viewModel.computeGasFeeFor(sendData: dataModel.data, + txSpeed: dataModel.txSpeed) + updateStateId() + } catch { + self.error = error + } + isLoading = false + } + } + + func confirmSending() { + Task { + isLoading = true + do { +// try await viewModel.sendToken(data: dataModel.data) + } catch { + self.error = error + } + isLoading = false + } + } + + func updateStateId() { + stateId = UUID() } } @@ -39,7 +89,7 @@ private extension ConfirmSendTokenView { @ViewBuilder func sendingTokenInfoView() -> some View { ConfirmSendAssetSendingInfoView(asset: .token(token: token, - amount: data.amount)) + amount: dataModel.amount)) } @ViewBuilder @@ -54,8 +104,9 @@ private extension ConfirmSendTokenView { @ViewBuilder func reviewInfoView() -> some View { - ConfirmSendAssetReviewInfoView(asset: .token(token), + ConfirmSendAssetReviewInfoView(asset: .token(dataModel), sourceWallet: viewModel.sourceWallet) + .id(stateId) } var isSufficientFunds: Bool { true } @@ -68,8 +119,9 @@ private extension ConfirmSendTokenView { } UDButtonView(text: String.Constants.confirm.localized(), icon: confirmIcon, - style: .large(.raisedPrimary)) { - + style: .large(.raisedPrimary), + isLoading: isLoading) { + confirmSending() } .disabled(!isSufficientFunds) } From 03256554f7988574fa92c8534dc01dc90b59bb82 Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 29 Mar 2024 14:56:30 +0700 Subject: [PATCH 42/80] Updated gas fee price formatting --- .../ConfirmSend/ConfirmSendAssetReviewInfoView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift index bfa83e227..3033aecd4 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift @@ -255,7 +255,7 @@ private extension ConfirmSendAssetReviewInfoView { func gasUsdTitleFor(gasUsd: Double?) -> String { if let gasUsd { - return formatCartPrice(gasUsd) + return "$\(gasUsd.formatted(toMaxNumberAfterComa: 4))" } return "" } From 34187cfce8575a22d749ab63a2bfb1431cff333d Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 29 Mar 2024 15:01:54 +0700 Subject: [PATCH 43/80] Updated token formatting and saved value --- .../domains-manager-ios/Entities/BalanceStringFormatter.swift | 2 +- .../Entities/BalanceTokenUIDescription.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Entities/BalanceStringFormatter.swift b/unstoppable-ios-app/domains-manager-ios/Entities/BalanceStringFormatter.swift index e5ef93bd3..769c35e51 100644 --- a/unstoppable-ios-app/domains-manager-ios/Entities/BalanceStringFormatter.swift +++ b/unstoppable-ios-app/domains-manager-ios/Entities/BalanceStringFormatter.swift @@ -16,7 +16,7 @@ struct BalanceStringFormatter { static func tokenBalanceString(balance: Double, symbol: String) -> String { - "\(balance.formattedBalance()) \(symbol)" + "\(balance.formatted(toMaxNumberAfterComa: 5)) \(symbol)" } static func tokenFullBalanceString(balance: Double, diff --git a/unstoppable-ios-app/domains-manager-ios/Entities/BalanceTokenUIDescription.swift b/unstoppable-ios-app/domains-manager-ios/Entities/BalanceTokenUIDescription.swift index d9959d24d..6c3358c87 100644 --- a/unstoppable-ios-app/domains-manager-ios/Entities/BalanceTokenUIDescription.swift +++ b/unstoppable-ios-app/domains-manager-ios/Entities/BalanceTokenUIDescription.swift @@ -29,7 +29,7 @@ struct BalanceTokenUIDescription: Hashable, Identifiable { self.chain = walletBalance.symbol self.symbol = walletBalance.symbol self.name = walletBalance.name - self.balance = walletBalance.balanceAmt.rounded(toDecimalPlaces: 2) + self.balance = walletBalance.balanceAmt self.balanceUsd = walletBalance.value.walletUsdAmt self.marketUsd = walletBalance.value.marketUsdAmt ?? 0 self.marketPctChange24Hr = walletBalance.value.marketPctChange24Hr @@ -56,7 +56,7 @@ struct BalanceTokenUIDescription: Hashable, Identifiable { self.chain = chain self.symbol = walletToken.symbol self.name = walletToken.name - self.balance = walletToken.balanceAmt.rounded(toDecimalPlaces: 2) + self.balance = walletToken.balanceAmt self.balanceUsd = walletToken.value?.walletUsdAmt ?? 0 self.marketUsd = walletToken.value?.marketUsdAmt ?? 0 self.marketPctChange24Hr = walletToken.value?.marketPctChange24Hr From 3b4f55497f2d93188ea5c79743a2ea65a031b01a Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 29 Mar 2024 15:17:14 +0700 Subject: [PATCH 44/80] Refactoring --- .../Services/Networking/NetworkService+ProfilesApi.swift | 2 +- .../WalletTransactionsService/WalletTransactionsService.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService+ProfilesApi.swift b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService+ProfilesApi.swift index 94b8a8f87..05e9c1a13 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService+ProfilesApi.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService+ProfilesApi.swift @@ -454,7 +454,7 @@ extension NetworkService: WalletTransactionsNetworkServiceProtocol { cursor: String?, chain: String?, forceRefresh: Bool) async throws -> [WalletTransactionsPerChainResponse] { - let endpoint = Endpoint.getProfileWalletTransactions(for: wallet, + let endpoint = Endpoint.getProfileWalletTransactions(for: wallet, cursor: cursor, chain: chain, forceRefresh: forceRefresh) diff --git a/unstoppable-ios-app/domains-manager-ios/Services/WalletTransactionsService/WalletTransactionsService.swift b/unstoppable-ios-app/domains-manager-ios/Services/WalletTransactionsService/WalletTransactionsService.swift index 3fc921dc3..f7512a711 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/WalletTransactionsService/WalletTransactionsService.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/WalletTransactionsService/WalletTransactionsService.swift @@ -49,7 +49,7 @@ private extension WalletTransactionsService { for response in responses { if let cursor = response.cursor { group.addTask { - try await self.fetchAndCacheTransactions(for: wallet, cursor: response.cursor, chain: response.chain, forceReload: false) + try await self.fetchAndCacheTransactions(for: wallet, cursor: cursor, chain: response.chain, forceReload: false) } } else { result.append(response) From baedd368a8a025b46734c11b99810bff71c8bfd8 Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 29 Mar 2024 15:19:25 +0700 Subject: [PATCH 45/80] Restore limited scope of requested porfolio data --- .../NetworkEnvironment/ApiRequestBuilder.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/unstoppable-ios-app/domains-manager-ios/NetworkEnvironment/ApiRequestBuilder.swift b/unstoppable-ios-app/domains-manager-ios/NetworkEnvironment/ApiRequestBuilder.swift index f37df8b35..0579269c9 100644 --- a/unstoppable-ios-app/domains-manager-ios/NetworkEnvironment/ApiRequestBuilder.swift +++ b/unstoppable-ios-app/domains-manager-ios/NetworkEnvironment/ApiRequestBuilder.swift @@ -890,10 +890,11 @@ extension Endpoint { extension Endpoint { static func getCryptoPortfolio(for wallet: String) -> Endpoint { //https://api.ud-staging.com/profile/user/0xcd0dadab45baf9a06ce1279d1342ecc3f44845af/wallets + let queryItems: [URLQueryItem] = [.init(name: "walletFields", value: "native,token")] return Endpoint( host: NetworkConfig.baseProfileHost, path: "/profile/user/\(wallet)/wallets", - queryItems: [], + queryItems: queryItems, body: "", headers: NetworkService.profilesAPIHeader ) From 8d146dd6175299929ac09a766813ce26e27a5866 Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 29 Mar 2024 15:56:51 +0700 Subject: [PATCH 46/80] Remove speed estimation in sec from UI --- .../ConfirmSend/ConfirmSendAssetReviewInfoView.swift | 1 - .../Modules/Home/SendCryptoAsset/SendCryptoAsset.swift | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift index 3033aecd4..f1db8b4d8 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift @@ -233,7 +233,6 @@ private extension ConfirmSendAssetReviewInfoView { iconColor: .foregroundSecondary, value: selectedTxSpeed.title, valueColor: Color(uiColor: tintColorFor(txSpeed: selectedTxSpeed)), - subValue: selectedTxSpeed.subtitle, actions: getTransactionSpeedActions())), .infoValue(.init(title: String.Constants.feeEstimate.localized(), icon: .tildaIcon, diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift index 933b4a571..a3510c3b5 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift @@ -125,11 +125,11 @@ extension SendCryptoAsset { var subtitle: String { switch self { case .normal: - return "43-51 Gwei (~12 sec)" + return "43-51 Gwei" case .fast: - return "44-54 Gwei (~4 sec)" + return "44-54 Gwei " case .urgent: - return "41-53 Gwei (~1 sec)" + return "41-53 Gwei" } } From e710036cabab9a29f9a5f31adaf43b6943914acd Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 29 Mar 2024 16:00:36 +0700 Subject: [PATCH 47/80] Run refresh gas timer --- .../ConfirmSendToken/ConfirmSendTokenView.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift index 7a71369aa..55004d57a 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift @@ -15,8 +15,10 @@ struct ConfirmSendTokenView: View { @State private var error: Error? @State private var isLoading = false @State private var stateId = UUID() + @State private var lastRefreshGasTime = Date() private var token: BalanceTokenUIDescription { dataModel.token } private var receiver: SendCryptoAsset.AssetReceiver { dataModel.receiver } + private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() var body: some View { VStack(spacing: 4) { @@ -30,6 +32,9 @@ struct ConfirmSendTokenView: View { .onChange(of: dataModel.txSpeed) { _ in refreshGasAmount() } + .onReceive(timer) { _ in + refreshGasIfNeeded() + } .padding(16) .background(Color.backgroundDefault) .animation(.default, value: UUID()) @@ -52,6 +57,7 @@ private extension ConfirmSendTokenView { } func refreshGasAmount() { + lastRefreshGasTime = Date() dataModel.gasAmount = nil updateStateId() Task { @@ -82,6 +88,13 @@ private extension ConfirmSendTokenView { func updateStateId() { stateId = UUID() } + + func refreshGasIfNeeded() { + let timeSinceLastRefresh = Date().timeIntervalSince(lastRefreshGasTime) + if timeSinceLastRefresh >= 60 { + refreshGasAmount() + } + } } // MARK: - Private methods From 764a591ac8e94273473477ad0be757ccf2ab0bf6 Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 29 Mar 2024 16:16:30 +0700 Subject: [PATCH 48/80] Submit send crypto --- .../domains-manager-ios.xcodeproj/project.pbxproj | 6 ++++++ .../domains-manager-ios/Entities/TxHash.swift | 10 ++++++++++ .../ConfirmSendToken/ConfirmSendTokenView.swift | 4 +++- .../Home/SendCryptoAsset/SendCryptoAsset.swift | 1 + .../SendCryptoAssetNavigationDestination.swift | 7 ++++++- .../SendCryptoAssetSuccessView.swift | 11 ++++++----- .../SendCryptoAsset/SendCryptoAssetViewModel.swift | 7 ++++++- .../SendCryptoAsset/TransactionStatusTracker.swift | 14 +++++++++----- 8 files changed, 47 insertions(+), 13 deletions(-) create mode 100644 unstoppable-ios-app/domains-manager-ios/Entities/TxHash.swift diff --git a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj index cf37adc17..0e5f1f487 100644 --- a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj +++ b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj @@ -1291,6 +1291,8 @@ C6A3592F2BB68D9B00B1209A /* CryptoSenderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA32BAAE9F500D24FB5 /* CryptoSenderProtocol.swift */; }; C6A359322BB699FC00B1209A /* ConfirmSendTokenDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A359312BB699FC00B1209A /* ConfirmSendTokenDataModel.swift */; }; C6A359332BB699FC00B1209A /* ConfirmSendTokenDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A359312BB699FC00B1209A /* ConfirmSendTokenDataModel.swift */; }; + C6A359352BB6BB2100B1209A /* TxHash.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A359342BB6BB2000B1209A /* TxHash.swift */; }; + C6A359362BB6BB2100B1209A /* TxHash.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A359342BB6BB2000B1209A /* TxHash.swift */; }; C6A474C729D149560073415F /* LoginFlowNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A474C629D149560073415F /* LoginFlowNavigationController.swift */; }; C6A474D029D150A40073415F /* NoParkedDomainsFoundViewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A474CD29D150A40073415F /* NoParkedDomainsFoundViewPresenter.swift */; }; C6A474D429D150A40073415F /* NoParkedDomainsFoundViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A474CE29D150A40073415F /* NoParkedDomainsFoundViewController.swift */; }; @@ -3543,6 +3545,7 @@ C6A359262BB52CDC00B1209A /* SendCryptoReceiverInfoTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendCryptoReceiverInfoTitleView.swift; sourceTree = ""; }; C6A359292BB5586700B1209A /* NavigationTrackerViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationTrackerViewModifier.swift; sourceTree = ""; }; C6A359312BB699FC00B1209A /* ConfirmSendTokenDataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmSendTokenDataModel.swift; sourceTree = ""; }; + C6A359342BB6BB2000B1209A /* TxHash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TxHash.swift; sourceTree = ""; }; C6A474C629D149560073415F /* LoginFlowNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginFlowNavigationController.swift; sourceTree = ""; }; C6A474CD29D150A40073415F /* NoParkedDomainsFoundViewPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoParkedDomainsFoundViewPresenter.swift; sourceTree = ""; }; C6A474CE29D150A40073415F /* NoParkedDomainsFoundViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoParkedDomainsFoundViewController.swift; sourceTree = ""; }; @@ -7359,6 +7362,7 @@ C6BF6BDB2B8F11CC006CC2BD /* TaskWithDeadline.swift */, C6109EA128E6B1CC0027D5D8 /* Toast.swift */, 307EC19A25383EF600D62BA1 /* TransactionItem.swift */, + C6A359342BB6BB2000B1209A /* TxHash.swift */, C6C8F98E2B2189CB00A9834D /* TxOperation.swift */, C61808812B19BC680032E543 /* TransactionError.swift */, 306486CC25273C4400388026 /* User.swift */, @@ -9664,6 +9668,7 @@ C664B679291551E600A76154 /* LinkPresentationService.swift in Sources */, C6F9FBBE2A25C32700102F81 /* MessagingWebSocketEvent.swift in Sources */, C691CFA828C06F120077C59F /* ReverseResolutionIllustrationView.swift in Sources */, + C6A359352BB6BB2100B1209A /* TxHash.swift in Sources */, C6E041CE29554C370080F8E3 /* GhostTertiaryButton.swift in Sources */, C60E9B9C2821347F00CD7593 /* WalletsListHeaderView.swift in Sources */, C6B65FA42B5791B4006D1812 /* HomeWalletDomainCellView.swift in Sources */, @@ -10538,6 +10543,7 @@ C63AD09B2B95657C00BF8C83 /* LineView.swift in Sources */, C6D6463B2B1DC50600D724AC /* AppReviewActionEvent.swift in Sources */, C6C8F8B32B2182CF00A9834D /* EnterEmailVerificationCodeToMintDomainsPresenter.swift in Sources */, + C6A359362BB6BB2100B1209A /* TxHash.swift in Sources */, C61808052B19A9C20032E543 /* Toast.swift in Sources */, C6C8F8452B217E9600A9834D /* BackupWalletViewController.swift in Sources */, C6C8F9502B21856200A9834D /* PreviewKeychainPrivateKeyStorage.swift in Sources */, diff --git a/unstoppable-ios-app/domains-manager-ios/Entities/TxHash.swift b/unstoppable-ios-app/domains-manager-ios/Entities/TxHash.swift new file mode 100644 index 000000000..b45aac7c9 --- /dev/null +++ b/unstoppable-ios-app/domains-manager-ios/Entities/TxHash.swift @@ -0,0 +1,10 @@ +// +// TxHash.swift +// domains-manager-ios +// +// Created by Oleg Kuplin on 29.03.2024. +// + +import Foundation + +typealias TxHash = String diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift index 55004d57a..d3f7fb1c7 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift @@ -77,7 +77,9 @@ private extension ConfirmSendTokenView { Task { isLoading = true do { -// try await viewModel.sendToken(data: dataModel.data) + let txHash = try await viewModel.sendCryptoTokenWith(sendData: dataModel.data, + txSpeed: dataModel.txSpeed) + viewModel.handleAction(.didSendCrypto(data: dataModel.data, txHash: txHash)) } catch { self.error = error } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift index a3510c3b5..d7d49de88 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift @@ -46,6 +46,7 @@ extension SendCryptoAsset { case userTokenToSendSelected(SelectTokenAmountToSendData) case userTokenValueSelected(SendTokenAssetData) + case didSendCrypto(data: SendTokenAssetData, txHash: String) case userDomainSelected(TransferDomainData) case didTransferDomain(DomainDisplayInfo) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetNavigationDestination.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetNavigationDestination.swift index 75d50a5ee..83af45203 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetNavigationDestination.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetNavigationDestination.swift @@ -13,6 +13,7 @@ extension SendCryptoAsset { case selectAssetToSend(AssetReceiver) case selectTokenAmountToSend(SelectTokenAmountToSendData) case confirmSendToken(SendTokenAssetData) + case cryptoSendSuccess(data: SendTokenAssetData, txHash: String) case confirmTransferDomain(TransferDomainData) case domainTransferSuccess(DomainDisplayInfo) @@ -21,7 +22,7 @@ extension SendCryptoAsset { switch self { case .selectTokenAmountToSend, .selectAssetToSend: return true - case .scanWalletAddress, .confirmTransferDomain, .domainTransferSuccess, .confirmSendToken: + case .scanWalletAddress, .confirmTransferDomain, .domainTransferSuccess, .confirmSendToken, .cryptoSendSuccess: return false } } @@ -40,6 +41,10 @@ extension SendCryptoAsset { SelectTokenAssetAmountToSendView(data: data) case .confirmSendToken(let data): ConfirmSendTokenView(data: data) + case .cryptoSendSuccess(let data, let txHash): + SendCryptoAssetSuccessView(asset: .token(token: data.token, + amount: data.amount, + txHash: txHash)) case .confirmTransferDomain(let data): ConfirmTransferDomainView(data: data) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetSuccessView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetSuccessView.swift index 47b8f40c4..d69cbf4f6 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetSuccessView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetSuccessView.swift @@ -48,8 +48,8 @@ private extension SendCryptoAssetSuccessView { switch asset { case .domain(let domain): transactionTracker.trackTransactionOf(type: .domainTransfer(domain.name)) - case .token(let token, _): - return + case .token(_, _, let txHash): + transactionTracker.trackTransactionOf(type: .txHash(txHash)) } } } @@ -87,7 +87,7 @@ private extension SendCryptoAssetSuccessView { switch asset { case .domain(let domain): domain.name - case .token(let token, let amount): + case .token(let token, let amount, _): "\(formatCartPrice(amount.valueOf(type: .usdAmount, for: token))) · \(amount.valueOf(type: .tokenAmount, for: token).formatted(toMaxNumberAfterComa: 6)) \(token.symbol)" } } @@ -155,13 +155,14 @@ private extension SendCryptoAssetSuccessView { // MARK: - Open methods extension SendCryptoAssetSuccessView { enum Asset { - case token(token: BalanceTokenUIDescription, amount: SendCryptoAsset.TokenAssetAmountInput) + case token(token: BalanceTokenUIDescription, amount: SendCryptoAsset.TokenAssetAmountInput, txHash: TxHash) case domain(DomainDisplayInfo) } } #Preview { SendCryptoAssetSuccessView(asset: .token(token: MockEntitiesFabric.Tokens.mockUIToken(), - amount: .tokenAmount(0.0324))) + amount: .tokenAmount(0.0324), + txHash: "")) // SendCryptoAssetSuccessView(asset: .domain(MockEntitiesFabric.Domains.mockDomainDisplayInfo())) } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift index 43165a552..009112556 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift @@ -21,6 +21,7 @@ final class SendCryptoAssetViewModel: ObservableObject { func handleAction(_ action: SendCryptoAsset.FlowAction) { switch action { + // Common path case .scanQRSelected: navPath.append(.scanWalletAddress) case .userWalletSelected(let walletEntity): @@ -33,11 +34,15 @@ final class SendCryptoAssetViewModel: ObservableObject { case .globalWalletAddressSelected(let address): navPath.append(.selectAssetToSend(.init(walletAddress: address))) + // Send crypto case .userTokenToSendSelected(let data): navPath.append(.selectTokenAmountToSend(data)) case .userTokenValueSelected(let data): navPath.append(.confirmSendToken(data)) + case .didSendCrypto(let data, let txHash): + navPath.append(.cryptoSendSuccess(data: data, txHash: txHash)) + // Transfer domain case .userDomainSelected(let data): navPath.append(.confirmTransferDomain(data)) case.didTransferDomain(let domain): @@ -92,7 +97,7 @@ final class SendCryptoAssetViewModel: ObservableObject { } private func getCurrentEnvironment() -> UnsConfigManager.BlockchainEnvironment { - .mainnet + User.instance.getSettings().isTestnetUsed ? .testnet : .mainnet } private func getSupportedTokenFor(balanceToken: BalanceTokenUIDescription) throws -> CryptoSender.SupportedToken { diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/TransactionStatusTracker.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/TransactionStatusTracker.swift index b7de11389..0c41a215b 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/TransactionStatusTracker.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/TransactionStatusTracker.swift @@ -21,19 +21,19 @@ final class TransactionStatusTracker: ObservableObject { self.trackingTransaction = type refreshTransactionStatus() - startRefreshTransactionsTimer() + startRefreshTimer() } func stopTracking() { trackingTransaction = nil - stopRefreshDomainsTimer() + stopRefreshTimer() } } // MARK: - Private methods private extension TransactionStatusTracker { - func startRefreshTransactionsTimer() { + func startRefreshTimer() { refreshTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(refreshTransactionStatus), @@ -41,7 +41,7 @@ private extension TransactionStatusTracker { repeats: true) } - func stopRefreshDomainsTimer() { + func stopRefreshTimer() { refreshTimer?.invalidate() refreshTimer = nil } @@ -52,8 +52,11 @@ private extension TransactionStatusTracker { switch trackingTransaction { case .domainTransfer(let domainName): try await refreshTransactionStatusForDomain(domainName) + case .txHash(let txHash): + self.txHash = txHash + stopRefreshTimer() case .none: - stopRefreshDomainsTimer() + stopRefreshTimer() } } catch { @@ -79,5 +82,6 @@ private extension TransactionStatusTracker { extension TransactionStatusTracker { enum TransactionType { case domainTransfer(DomainName) + case txHash(TxHash) } } From 43efa4a028d988097d83531bcaaf3c42ba8509b0 Mon Sep 17 00:00:00 2001 From: rommex Date: Fri, 29 Mar 2024 12:59:29 +0200 Subject: [PATCH 49/80] introduced EVMTokenAmount, extra helper method fetchGasPrices() --- .../Home/SendCryptoAsset/CryptoSender.swift | 46 +++++++++------ .../CryptoSenderProtocol.swift | 56 ++++++++++++++++++- .../Services/Networking/NetworkService.swift | 25 ++++++--- 3 files changed, 100 insertions(+), 27 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index c32d95682..238919348 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -17,6 +17,10 @@ struct ChainSpec { self.blockchainType = blockchainType self.env = env } + + var id: Int { + self.blockchainType.supportedChainId(env: self.env) + } } struct CryptoSendingSpec { @@ -25,10 +29,10 @@ struct CryptoSendingSpec { } let token: CryptoSender.SupportedToken - let amount: Double + let amount: EVMTokenAmount let speed: TxSpeed - init(token: CryptoSender.SupportedToken, amount: Double, speed: TxSpeed = .normal) { + init(token: CryptoSender.SupportedToken, amount: EVMTokenAmount, speed: TxSpeed = .normal) { self.token = token self.amount = amount self.speed = speed @@ -59,12 +63,17 @@ struct CryptoSender: CryptoSenderProtocol { } - func computeGasFeeFrom(maxCrypto: CryptoSendingSpec, on chain: ChainSpec, toAddress: HexAddress) async throws -> Double { + func computeGasFeeFrom(maxCrypto: CryptoSendingSpec, on chain: ChainSpec, toAddress: HexAddress) async throws -> EVMTokenAmount { let cryptoSender: CryptoSenderProtocol = NativeCryptoSender(wallet: wallet) return try await cryptoSender.computeGasFeeFrom(maxCrypto: maxCrypto, on: chain, toAddress: toAddress) } + + func fetchGasPrices(on chain: ChainSpec) async throws -> TransactionEstimatedGasFee { + let cryptoSender: CryptoSenderProtocol = NativeCryptoSender(wallet: wallet) + return try await cryptoSender.fetchGasPrices(on: chain) + } } struct NativeCryptoSender: CryptoSenderProtocol { @@ -85,47 +94,48 @@ struct NativeCryptoSender: CryptoSenderProtocol { throw CryptoSender.Error.sendingNotSupported } - let chainId = chain.blockchainType.supportedChainId(env: chain.env) let tx = try await createNativeSendTransaction(crypto: crypto, fromAddress: self.wallet.address, toAddress: toAddress, - chainId: chainId) + chainId: chain.id) guard wallet.walletState != .externalLinked else { - let response = try await wallet.signViaWalletConnectTransaction(tx: tx, chainId: chainId) + let response = try await wallet.signViaWalletConnectTransaction(tx: tx, chainId: chain.id) return response } - let hash = try await JRPC_Client.instance.sendTx(transaction: tx, udWallet: self.wallet, chainIdInt: chainId) + let hash = try await JRPC_Client.instance.sendTx(transaction: tx, udWallet: self.wallet, chainIdInt: chain.id) return hash } func computeGasFeeFrom(maxCrypto: CryptoSendingSpec, on chain: ChainSpec, - toAddress: HexAddress) async throws -> Double { + toAddress: HexAddress) async throws -> EVMTokenAmount { guard canSendCrypto(token: maxCrypto.token, chainType: chain.blockchainType) else { throw CryptoSender.Error.sendingNotSupported } - let chainId = chain.blockchainType.supportedChainId(env: chain.env) let transaction = try await createNativeSendTransaction(crypto: maxCrypto, fromAddress: self.wallet.address, toAddress: toAddress, - chainId: chainId) + chainId: chain.id) guard let gasPriceWei = transaction.gasPrice?.quantity else { throw CryptoSender.Error.failedFetchGasPrice } + let gasPrice = EVMTokenAmount(wei: gasPriceWei) + let gas = transaction.gas?.quantity ?? Self.defaultSendTxGasPrice - - let gasPriceGwei = (Double(gasPriceWei) / 1_000_000_000.0) - let gasFee = gasPriceGwei * Double(gas) / 1_000_000_000.0 // in token units - + let gasFee = EVMTokenAmount(gwei: gasPrice.gwei * Double(gas)) return gasFee } + func fetchGasPrices(on chain: ChainSpec) async throws -> TransactionEstimatedGasFee { + try await NetworkService().getStatusGasPrices(chainId: chain.id) + } + // Private methods private func createNativeSendTransaction(crypto: CryptoSendingSpec, @@ -134,19 +144,19 @@ struct NativeCryptoSender: CryptoSenderProtocol { chainId: Int) async throws -> EthereumTransaction { let nonce: EthereumQuantity = try await JRPC_Client.instance.fetchNonce(address: fromAddress, chainId: chainId) - let speedBasedGasPriceGwei = try await NetworkService().fetchGasPrice(chainId: chainId, + let speedBasedGasPrice = try await NetworkService().fetchGasPrice(chainId: chainId, for: crypto.speed) let sender = EthereumAddress(hexString: fromAddress) let receiver = EthereumAddress(hexString: toAddress) - let amountInGwei = BigUInt(1_000_000_000.0 * crypto.amount) var transaction = EthereumTransaction(nonce: nonce, - gasPrice: try EthereumQuantity(speedBasedGasPriceGwei.gwei), + gasPrice: try EthereumQuantity(BigUInt(speedBasedGasPrice.gwei)), gas: try EthereumQuantity(Self.defaultSendTxGasPrice), from: sender, to: receiver, - value: try EthereumQuantity(amountInGwei.gwei) ) + value: try EthereumQuantity(BigUInt(crypto.amount.gwei)) + ) if let gasEstimate = try? await JRPC_Client.instance.fetchGasLimit(transaction: transaction, chainId: chainId) { transaction.gas = gasEstimate diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift index 416145299..26dc4f643 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift @@ -8,6 +8,58 @@ import Foundation import BigInt +struct EVMTokenAmount { + static let Billion = 1_000_000_000.0 + private let gweiTotal: Double + + init(units: Double) { + self.gweiTotal = units * Self.Billion + } + + init(gwei: Double) { + self.gweiTotal = gwei + } + + init(gwei: Int) { + self.gweiTotal = Double(gwei) + } + + init(wei: BigUInt) { + self.gweiTotal = Double(wei) / Self.Billion + } + + var units: Double { + gweiTotal / Self.Billion + } + + var gwei: Double { + gweiTotal + } + + var wei: Double { + gweiTotal * Self.Billion + } +} + +struct TransactionEstimatedGasFee { + let normalFee: EVMTokenAmount + let fastFee: EVMTokenAmount + let urgentFee: EVMTokenAmount + + func feeForSpeed(_ txSpeed: CryptoSendingSpec.TxSpeed) -> EVMTokenAmount { + switch txSpeed { + case .normal: + return normalFee + case .fast: + return fastFee + case .urgent: + return urgentFee + } + } +} + + + protocol CryptoSenderProtocol { init(wallet: UDWallet) @@ -37,5 +89,7 @@ protocol CryptoSenderProtocol { /// - Returns: Amount of crypto that must be deducted from maxCrypto as the gas fee in the future tx, in token units func computeGasFeeFrom(maxCrypto: CryptoSendingSpec, on chain: ChainSpec, - toAddress: HexAddress) async throws -> Double + toAddress: HexAddress) async throws -> EVMTokenAmount + + func fetchGasPrices(on chain: ChainSpec) async throws -> TransactionEstimatedGasFee } diff --git a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift index 76df5ba1e..52aac0af8 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift @@ -385,16 +385,25 @@ extension NetworkService { return ["ETH": ethPrices, "MATIC": maticPrices] } - func fetchGasPrice(chainId: Int, for speed: CryptoSendingSpec.TxSpeed) async throws -> Int { - let prices = try await getStatusGasPrices(chainId: chainId) - switch speed { - case .normal: return prices["safeLow"]! - case .fast: return prices["fast"]! - case .urgent: return prices["fastest"]! - } + func fetchGasPrice(chainId: Int, for speed: CryptoSendingSpec.TxSpeed) async throws -> EVMTokenAmount { + let prices: TransactionEstimatedGasFee = try await getStatusGasPrices(chainId: chainId) + return prices.feeForSpeed(speed) } - func getStatusGasPrices(chainId: Int) async throws -> [String: Int] { + func getStatusGasPrices(chainId: Int) async throws -> TransactionEstimatedGasFee { + let prices: [String: Int] = try await getStatusGasPrices(chainId: chainId) + + guard let normal = prices["safeLow"], + let fast = prices["fast"], + let urgent = prices["fastest"] else { + throw CryptoSender.Error.failedFetchGasPrice + } + return TransactionEstimatedGasFee(normalFee: EVMTokenAmount(gwei: normal), + fastFee: EVMTokenAmount(gwei: fast), + urgentFee: EVMTokenAmount(gwei: urgent)) + } + + private func getStatusGasPrices(chainId: Int) async throws -> [String: Int] { switch chainId { case BlockchainNetwork.ethMainnet.rawValue: return try await getStatusGasPrices(env: .mainnet)["ETH"]! case BlockchainNetwork.polygonMainnet.rawValue: return try await getStatusGasPrices(env: .mainnet)["MATIC"]! From ae88a8348bd1c1a82eff403dba172ebd944c8334 Mon Sep 17 00:00:00 2001 From: rommex Date: Fri, 29 Mar 2024 13:55:05 +0200 Subject: [PATCH 50/80] fix: Eth Tx is build with WEIs only --- .../Modules/Home/SendCryptoAsset/CryptoSender.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index 238919348..9f951d365 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -151,11 +151,11 @@ struct NativeCryptoSender: CryptoSenderProtocol { let receiver = EthereumAddress(hexString: toAddress) var transaction = EthereumTransaction(nonce: nonce, - gasPrice: try EthereumQuantity(BigUInt(speedBasedGasPrice.gwei)), + gasPrice: try EthereumQuantity(BigUInt(speedBasedGasPrice.wei)), gas: try EthereumQuantity(Self.defaultSendTxGasPrice), from: sender, to: receiver, - value: try EthereumQuantity(BigUInt(crypto.amount.gwei)) + value: try EthereumQuantity(BigUInt(crypto.amount.wei)) ) if let gasEstimate = try? await JRPC_Client.instance.fetchGasLimit(transaction: transaction, chainId: chainId) { From 53951ffca9d91e1e5d249df1653ac01129d63dd9 Mon Sep 17 00:00:00 2001 From: rommex Date: Fri, 29 Mar 2024 14:08:57 +0200 Subject: [PATCH 51/80] refactoring --- .../Modules/Home/SendCryptoAsset/CryptoSender.swift | 8 ++++---- .../Home/SendCryptoAsset/CryptoSenderProtocol.swift | 12 +++++++----- .../Services/Networking/NetworkService.swift | 6 +++--- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index 9f951d365..b2a74032f 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -70,7 +70,7 @@ struct CryptoSender: CryptoSenderProtocol { toAddress: toAddress) } - func fetchGasPrices(on chain: ChainSpec) async throws -> TransactionEstimatedGasFee { + func fetchGasPrices(on chain: ChainSpec) async throws -> EstimatedGasPrices { let cryptoSender: CryptoSenderProtocol = NativeCryptoSender(wallet: wallet) return try await cryptoSender.fetchGasPrices(on: chain) } @@ -132,7 +132,7 @@ struct NativeCryptoSender: CryptoSenderProtocol { return gasFee } - func fetchGasPrices(on chain: ChainSpec) async throws -> TransactionEstimatedGasFee { + func fetchGasPrices(on chain: ChainSpec) async throws -> EstimatedGasPrices { try await NetworkService().getStatusGasPrices(chainId: chain.id) } @@ -151,11 +151,11 @@ struct NativeCryptoSender: CryptoSenderProtocol { let receiver = EthereumAddress(hexString: toAddress) var transaction = EthereumTransaction(nonce: nonce, - gasPrice: try EthereumQuantity(BigUInt(speedBasedGasPrice.wei)), + gasPrice: try EthereumQuantity(speedBasedGasPrice.wei), gas: try EthereumQuantity(Self.defaultSendTxGasPrice), from: sender, to: receiver, - value: try EthereumQuantity(BigUInt(crypto.amount.wei)) + value: try EthereumQuantity(crypto.amount.wei) ) if let gasEstimate = try? await JRPC_Client.instance.fetchGasLimit(transaction: transaction, chainId: chainId) { diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift index 26dc4f643..10185827d 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift @@ -8,6 +8,9 @@ import Foundation import BigInt +// Unified container for the token amount. +// Init with units, gwei's or wei's +// Read in units, gwei's or wei's struct EVMTokenAmount { static let Billion = 1_000_000_000.0 private let gweiTotal: Double @@ -36,12 +39,12 @@ struct EVMTokenAmount { gweiTotal } - var wei: Double { - gweiTotal * Self.Billion + var wei: BigUInt { // can only be integer and may be very big + BigUInt(gweiTotal * Self.Billion) } } -struct TransactionEstimatedGasFee { +struct EstimatedGasPrices { let normalFee: EVMTokenAmount let fastFee: EVMTokenAmount let urgentFee: EVMTokenAmount @@ -59,7 +62,6 @@ struct TransactionEstimatedGasFee { } - protocol CryptoSenderProtocol { init(wallet: UDWallet) @@ -91,5 +93,5 @@ protocol CryptoSenderProtocol { on chain: ChainSpec, toAddress: HexAddress) async throws -> EVMTokenAmount - func fetchGasPrices(on chain: ChainSpec) async throws -> TransactionEstimatedGasFee + func fetchGasPrices(on chain: ChainSpec) async throws -> EstimatedGasPrices } diff --git a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift index 52aac0af8..743a8dda0 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift @@ -386,11 +386,11 @@ extension NetworkService { } func fetchGasPrice(chainId: Int, for speed: CryptoSendingSpec.TxSpeed) async throws -> EVMTokenAmount { - let prices: TransactionEstimatedGasFee = try await getStatusGasPrices(chainId: chainId) + let prices: EstimatedGasPrices = try await getStatusGasPrices(chainId: chainId) return prices.feeForSpeed(speed) } - func getStatusGasPrices(chainId: Int) async throws -> TransactionEstimatedGasFee { + func getStatusGasPrices(chainId: Int) async throws -> EstimatedGasPrices { let prices: [String: Int] = try await getStatusGasPrices(chainId: chainId) guard let normal = prices["safeLow"], @@ -398,7 +398,7 @@ extension NetworkService { let urgent = prices["fastest"] else { throw CryptoSender.Error.failedFetchGasPrice } - return TransactionEstimatedGasFee(normalFee: EVMTokenAmount(gwei: normal), + return EstimatedGasPrices(normalFee: EVMTokenAmount(gwei: normal), fastFee: EVMTokenAmount(gwei: fast), urgentFee: EVMTokenAmount(gwei: urgent)) } From b688505b71625c9731415acd9834a9b5fcf53885 Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 29 Mar 2024 20:55:59 +0700 Subject: [PATCH 52/80] Update gas fee and prices calculations --- .../ConfirmSendAssetReviewInfoView.swift | 11 ++++-- .../ConfirmSendTokenDataModel.swift | 35 ++++++++++++------- .../ConfirmSendTokenView.swift | 7 ++-- .../CryptoSenderProtocol.swift | 1 + .../SendCryptoAsset/SendCryptoAsset.swift | 11 ------ .../SendCryptoAssetViewModel.swift | 16 ++++++--- 6 files changed, 48 insertions(+), 33 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift index f1db8b4d8..119dc193d 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift @@ -187,7 +187,7 @@ private extension ConfirmSendAssetReviewInfoView { switch asset { case .token(let dataModel): getSectionsForToken(selectedTxSpeed: dataModel.txSpeed, - gasUsd: dataModel.gasUsd) + gasUsd: dataModel.gasFeeUsd) case .domain: getSectionsForDomain() } @@ -196,13 +196,20 @@ private extension ConfirmSendAssetReviewInfoView { func getTransactionSpeedActions() -> [InfoActionDescription] { SendCryptoAsset.TransactionSpeed.allCases.map { txSpeed in InfoActionDescription(title: txSpeed.title, - subtitle: txSpeed.subtitle, + subtitle: txSpeedSubtitleFor(txSpeed: txSpeed), iconName: txSpeed.iconName, tintColor: tintColorFor(txSpeed: txSpeed), action: { didSelectTransactionSpeed(txSpeed) }) } } + func txSpeedSubtitleFor(txSpeed: SendCryptoAsset.TransactionSpeed) -> String { + guard case .token(let dataModel) = asset, + let gwei = dataModel.gasGweiFor(speed: txSpeed) else { return "" } + + return "\(gwei) Gwei" + } + func tintColorFor(txSpeed: SendCryptoAsset.TransactionSpeed) -> UIColor { switch txSpeed { case .normal: diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenDataModel.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenDataModel.swift index 32acd4f66..519af32de 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenDataModel.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenDataModel.swift @@ -7,19 +7,20 @@ import SwiftUI -final class ConfirmSendTokenDataModel: ObservableObject, Hashable { +final class ConfirmSendTokenDataModel: ObservableObject { let data: SendCryptoAsset.SendTokenAssetData @Published var txSpeed: SendCryptoAsset.TransactionSpeed = .normal - @Published var gasAmount: Double? = nil + @Published var gasFee: Double? = nil + @Published var gasPrices: EstimatedGasPrices? = nil var token: BalanceTokenUIDescription { data.token } var receiver: SendCryptoAsset.AssetReceiver { data.receiver } var amount: SendCryptoAsset.TokenAssetAmountInput { data.amount } - var gasUsd: Double? { - if let gasAmount, + var gasFeeUsd: Double? { + if let gasFee, let marketUsd = data.token.marketUsd { - return gasAmount * marketUsd + return gasFee * marketUsd } return nil } @@ -28,16 +29,24 @@ final class ConfirmSendTokenDataModel: ObservableObject, Hashable { self.data = data } - static func == (lhs: ConfirmSendTokenDataModel, rhs: ConfirmSendTokenDataModel) -> Bool { - lhs.data == rhs.data && - lhs.txSpeed == rhs.txSpeed && - lhs.gasAmount == rhs.gasAmount + func gasGweiFor(speed: SendCryptoAsset.TransactionSpeed) -> Int? { + guard let gasPrices else { return nil } + + let evmSpeed = evmTxSpeedFor(transactionSpeed: speed) + let feeForSpeed = gasPrices.feeForSpeed(evmSpeed) + + return Int(feeForSpeed.gwei) } - func hash(into hasher: inout Hasher) { - hasher.combine(data) - hasher.combine(txSpeed) - hasher.combine(gasAmount) + private func evmTxSpeedFor(transactionSpeed: SendCryptoAsset.TransactionSpeed) -> CryptoSendingSpec.TxSpeed { + switch transactionSpeed { + case .normal: + return .normal + case .fast: + return .fast + case .urgent: + return .urgent + } } } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift index d3f7fb1c7..1b19ba2cf 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift @@ -58,13 +58,14 @@ private extension ConfirmSendTokenView { func refreshGasAmount() { lastRefreshGasTime = Date() - dataModel.gasAmount = nil + dataModel.gasPrices = nil updateStateId() Task { isLoading = true do { - dataModel.gasAmount = try await viewModel.computeGasFeeFor(sendData: dataModel.data, - txSpeed: dataModel.txSpeed) + dataModel.gasFee = try await viewModel.computeGasFeeFor(sendData: dataModel.data, + txSpeed: dataModel.txSpeed) + dataModel.gasPrices = try await viewModel.getGasPrices(sendData: dataModel.data) updateStateId() } catch { self.error = error diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift index f17e5c6c0..10185827d 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift @@ -6,6 +6,7 @@ // import Foundation +import BigInt // Unified container for the token amount. // Init with units, gwei's or wei's diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift index d7d49de88..45fe9d395 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift @@ -123,17 +123,6 @@ extension SendCryptoAsset { } } - var subtitle: String { - switch self { - case .normal: - return "43-51 Gwei" - case .fast: - return "44-54 Gwei " - case .urgent: - return "41-53 Gwei" - } - } - var iconName: String { switch self { case .normal: diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift index 009112556..2c6e5cddf 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift @@ -72,18 +72,26 @@ final class SendCryptoAssetViewModel: ObservableObject { let chain = try getChainSpecFor(balanceToken: sendData.token) let toAddress = sendData.receiverAddress - return try await cryptoSender.computeGasFeeFrom(maxCrypto: crypto, on: chain, toAddress: toAddress) + return try await cryptoSender.computeGasFeeFrom(maxCrypto: crypto, on: chain, toAddress: toAddress).units + } + + func getGasPrices(sendData: SendCryptoAsset.SendTokenAssetData) async throws -> EstimatedGasPrices { + let chain = try getChainSpecFor(balanceToken: sendData.token) + let toAddress = sendData.receiverAddress + + return try await cryptoSender.fetchGasPrices(on: chain) } private func getCryptoSendingSpecFor(sendData: SendCryptoAsset.SendTokenAssetData, txSpeed: SendCryptoAsset.TransactionSpeed) throws -> CryptoSendingSpec { let token = try getSupportedTokenFor(balanceToken: sendData.token) - let amount = sendData.getTokenAmountValue() + let tokenAmount = sendData.getTokenAmountValue() + let amount = EVMTokenAmount(units: tokenAmount) let speed = getSpecTransactionSpeedFor(txSpeed: txSpeed) return CryptoSendingSpec(token: token, - amount: amount, - speed: speed) + amount: amount, + speed: speed) } private func getChainSpecFor(balanceToken: BalanceTokenUIDescription) throws -> ChainSpec { From af1dfdd47a1609fa2c4c7a0c5227a6e31570014c Mon Sep 17 00:00:00 2001 From: rommex Date: Fri, 29 Mar 2024 17:06:11 +0200 Subject: [PATCH 53/80] renaming (#452) --- .../SendCryptoAsset/CryptoSenderProtocol.swift | 14 +++++++------- .../Services/Networking/NetworkService.swift | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift index 10185827d..e4969f675 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift @@ -45,18 +45,18 @@ struct EVMTokenAmount { } struct EstimatedGasPrices { - let normalFee: EVMTokenAmount - let fastFee: EVMTokenAmount - let urgentFee: EVMTokenAmount + let normal: EVMTokenAmount + let fast: EVMTokenAmount + let urgent: EVMTokenAmount - func feeForSpeed(_ txSpeed: CryptoSendingSpec.TxSpeed) -> EVMTokenAmount { + func getPriceForSpeed(_ txSpeed: CryptoSendingSpec.TxSpeed) -> EVMTokenAmount { switch txSpeed { case .normal: - return normalFee + return normal case .fast: - return fastFee + return fast case .urgent: - return urgentFee + return urgent } } } diff --git a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift index c89233994..1c54cacd6 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift @@ -387,7 +387,7 @@ extension NetworkService { func fetchGasPrice(chainId: Int, for speed: CryptoSendingSpec.TxSpeed) async throws -> EVMTokenAmount { let prices: EstimatedGasPrices = try await getStatusGasPrices(chainId: chainId) - return prices.feeForSpeed(speed) + return prices.getPriceForSpeed(speed) } func getStatusGasPrices(chainId: Int) async throws -> EstimatedGasPrices { @@ -398,9 +398,9 @@ extension NetworkService { let urgent = prices["fastest"] else { throw CryptoSender.Error.failedFetchGasPrice } - return EstimatedGasPrices(normalFee: EVMTokenAmount(gwei: normal), - fastFee: EVMTokenAmount(gwei: fast), - urgentFee: EVMTokenAmount(gwei: urgent)) + return EstimatedGasPrices(normal: EVMTokenAmount(gwei: normal), + fast: EVMTokenAmount(gwei: fast), + urgent: EVMTokenAmount(gwei: urgent)) } private func getStatusGasPrices(chainId: Int) async throws -> [String: Int] { From c904ffb2ee3ff4ca468d9c76f5e661ea1c913005 Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 29 Mar 2024 22:08:50 +0700 Subject: [PATCH 54/80] Fix merging issue --- .../ConfirmSendToken/ConfirmSendTokenDataModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenDataModel.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenDataModel.swift index 519af32de..2f12fda07 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenDataModel.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenDataModel.swift @@ -33,7 +33,7 @@ final class ConfirmSendTokenDataModel: ObservableObject { guard let gasPrices else { return nil } let evmSpeed = evmTxSpeedFor(transactionSpeed: speed) - let feeForSpeed = gasPrices.feeForSpeed(evmSpeed) + let feeForSpeed = gasPrices.getPriceForSpeed(evmSpeed) return Int(feeForSpeed.gwei) } From 7b4c73de2e8c522c4e40d162dd9d076d770e3536 Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 29 Mar 2024 22:15:49 +0700 Subject: [PATCH 55/80] Gas fee and gas prices refactoring --- .../ConfirmSendTokenView.swift | 62 +++++++++++++------ 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift index 1b19ba2cf..1997ad7fc 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift @@ -15,10 +15,12 @@ struct ConfirmSendTokenView: View { @State private var error: Error? @State private var isLoading = false @State private var stateId = UUID() - @State private var lastRefreshGasTime = Date() + @State private var lastRefreshGasFeeTime = Date() + @State private var lastRefreshGasPricesTime = Date() private var token: BalanceTokenUIDescription { dataModel.token } private var receiver: SendCryptoAsset.AssetReceiver { dataModel.receiver } - private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() + private let refreshGasFeeTimer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() + private let refreshGasPricesTimer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() var body: some View { VStack(spacing: 4) { @@ -30,10 +32,13 @@ struct ConfirmSendTokenView: View { confirmButton() } .onChange(of: dataModel.txSpeed) { _ in - refreshGasAmount() + refreshGasFeeForSelectedSpeed() } - .onReceive(timer) { _ in - refreshGasIfNeeded() + .onReceive(refreshGasFeeTimer) { _ in + refreshGasFeeForSelectedSpeedIfNeeded() + } + .onReceive(refreshGasPricesTimer) { _ in + refreshGasPricesIfNeeded() } .padding(16) .background(Color.backgroundDefault) @@ -53,11 +58,12 @@ struct ConfirmSendTokenView: View { // MARK: - Private methods private extension ConfirmSendTokenView { func onAppear() { - refreshGasAmount() + refreshGasFeeForSelectedSpeed() + refreshGasPrices() } - func refreshGasAmount() { - lastRefreshGasTime = Date() + func refreshGasFeeForSelectedSpeed() { + lastRefreshGasFeeTime = Date() dataModel.gasPrices = nil updateStateId() Task { @@ -65,7 +71,6 @@ private extension ConfirmSendTokenView { do { dataModel.gasFee = try await viewModel.computeGasFeeFor(sendData: dataModel.data, txSpeed: dataModel.txSpeed) - dataModel.gasPrices = try await viewModel.getGasPrices(sendData: dataModel.data) updateStateId() } catch { self.error = error @@ -74,6 +79,34 @@ private extension ConfirmSendTokenView { } } + func refreshGasFeeForSelectedSpeedIfNeeded() { + let timeSinceLastRefresh = Date().timeIntervalSince(lastRefreshGasFeeTime) + if timeSinceLastRefresh >= 60 { + refreshGasFeeForSelectedSpeed() + } + } + + func refreshGasPrices() { + lastRefreshGasPricesTime = Date() + Task { + do { + dataModel.gasPrices = try await viewModel.getGasPrices(sendData: dataModel.data) + updateStateId() + } catch { } + } + } + + func refreshGasPricesIfNeeded() { + let timeSinceLastRefresh = Date().timeIntervalSince(lastRefreshGasPricesTime) + if timeSinceLastRefresh >= 60 { + refreshGasPrices() + } + } + + func updateStateId() { + stateId = UUID() + } + func confirmSending() { Task { isLoading = true @@ -87,17 +120,6 @@ private extension ConfirmSendTokenView { isLoading = false } } - - func updateStateId() { - stateId = UUID() - } - - func refreshGasIfNeeded() { - let timeSinceLastRefresh = Date().timeIntervalSince(lastRefreshGasTime) - if timeSinceLastRefresh >= 60 { - refreshGasAmount() - } - } } // MARK: - Private methods From 6b57c8094248ad9484fa9e6b263308b46f26633b Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 29 Mar 2024 22:20:18 +0700 Subject: [PATCH 56/80] Fixed gas prices preserved and refactoring --- .../ConfirmSendToken/ConfirmSendTokenView.swift | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift index 1997ad7fc..4acd0d093 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift @@ -19,8 +19,7 @@ struct ConfirmSendTokenView: View { @State private var lastRefreshGasPricesTime = Date() private var token: BalanceTokenUIDescription { dataModel.token } private var receiver: SendCryptoAsset.AssetReceiver { dataModel.receiver } - private let refreshGasFeeTimer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() - private let refreshGasPricesTimer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() + private let refreshGasTimer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() var body: some View { VStack(spacing: 4) { @@ -34,10 +33,8 @@ struct ConfirmSendTokenView: View { .onChange(of: dataModel.txSpeed) { _ in refreshGasFeeForSelectedSpeed() } - .onReceive(refreshGasFeeTimer) { _ in + .onReceive(refreshGasTimer) { _ in refreshGasFeeForSelectedSpeedIfNeeded() - } - .onReceive(refreshGasPricesTimer) { _ in refreshGasPricesIfNeeded() } .padding(16) @@ -64,7 +61,7 @@ private extension ConfirmSendTokenView { func refreshGasFeeForSelectedSpeed() { lastRefreshGasFeeTime = Date() - dataModel.gasPrices = nil + dataModel.gasFee = nil updateStateId() Task { isLoading = true From 65fbde332d1937287e399ea21b2c13a9a4ca6e37 Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 29 Mar 2024 22:35:11 +0700 Subject: [PATCH 57/80] Handle send all tokens --- .../Home/SendCryptoAsset/CryptoSender.swift | 1 + .../SendCryptoAsset/SendCryptoAsset.swift | 4 ++ .../SendCryptoAssetViewModel.swift | 37 +++++++++++++++++-- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift index b2a74032f..d5182d850 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift @@ -43,6 +43,7 @@ struct CryptoSender: CryptoSenderProtocol { enum Error: Swift.Error { case sendingNotSupported case failedFetchGasPrice + case insufficientFunds } enum SupportedToken: String { diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift index 45fe9d395..148cdb416 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift @@ -107,6 +107,10 @@ extension SendCryptoAsset { func getTokenAmountValue() -> Double { amount.valueOf(type: .tokenAmount, for: token) } + + func isSendingAllTokens() -> Bool { + getTokenAmountValue() >= token.balance + } } enum TransactionSpeed: CaseIterable { diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift index 2c6e5cddf..9c8b55beb 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift @@ -59,13 +59,35 @@ final class SendCryptoAssetViewModel: ObservableObject { func sendCryptoTokenWith(sendData: SendCryptoAsset.SendTokenAssetData, txSpeed: SendCryptoAsset.TransactionSpeed) async throws -> String { - let crypto = try getCryptoSendingSpecFor(sendData: sendData, txSpeed: txSpeed) + let crypto: CryptoSendingSpec + + if sendData.isSendingAllTokens() { + crypto = try await getMaxTokenAmountToSendConsideringGasFee(sendData: sendData, txSpeed: txSpeed) + } else { + crypto = try getCryptoSendingSpecFor(sendData: sendData, txSpeed: txSpeed) + } + let chain = try getChainSpecFor(balanceToken: sendData.token) let toAddress = sendData.receiverAddress return try await cryptoSender.sendCrypto(crypto: crypto, chain: chain, toAddress: toAddress) } + private func getMaxTokenAmountToSendConsideringGasFee(sendData: SendCryptoAsset.SendTokenAssetData, + txSpeed: SendCryptoAsset.TransactionSpeed) async throws -> CryptoSendingSpec { + let gasPrice = try await computeGasFeeFor(sendData: sendData, txSpeed: txSpeed) + let tokenAmountToSend = sendData.getTokenAmountValue() - gasPrice + guard tokenAmountToSend > 0 else { + throw CryptoSender.Error.insufficientFunds + } + + let token = sendData.token + + return try getCryptoSendingSpecFor(token: token, + tokenAmount: tokenAmountToSend, + txSpeed: txSpeed) + } + func computeGasFeeFor(sendData: SendCryptoAsset.SendTokenAssetData, txSpeed: SendCryptoAsset.TransactionSpeed) async throws -> Double { let crypto = try getCryptoSendingSpecFor(sendData: sendData, txSpeed: txSpeed) @@ -77,15 +99,24 @@ final class SendCryptoAssetViewModel: ObservableObject { func getGasPrices(sendData: SendCryptoAsset.SendTokenAssetData) async throws -> EstimatedGasPrices { let chain = try getChainSpecFor(balanceToken: sendData.token) - let toAddress = sendData.receiverAddress return try await cryptoSender.fetchGasPrices(on: chain) } private func getCryptoSendingSpecFor(sendData: SendCryptoAsset.SendTokenAssetData, txSpeed: SendCryptoAsset.TransactionSpeed) throws -> CryptoSendingSpec { - let token = try getSupportedTokenFor(balanceToken: sendData.token) + let token = sendData.token let tokenAmount = sendData.getTokenAmountValue() + + return try getCryptoSendingSpecFor(token: token, + tokenAmount: tokenAmount, + txSpeed: txSpeed) + } + + private func getCryptoSendingSpecFor(token: BalanceTokenUIDescription, + tokenAmount: Double, + txSpeed: SendCryptoAsset.TransactionSpeed) throws -> CryptoSendingSpec { + let token = try getSupportedTokenFor(balanceToken: token) let amount = EVMTokenAmount(units: tokenAmount) let speed = getSpecTransactionSpeedFor(txSpeed: txSpeed) From ea6aca34faa9c176a6fcf385181056f88b27cc12 Mon Sep 17 00:00:00 2001 From: Oleg Date: Fri, 29 Mar 2024 22:45:08 +0700 Subject: [PATCH 58/80] Check for user's fund is enough to cover gas before sending. --- .../ConfirmSendToken/ConfirmSendTokenView.swift | 13 ++++++++++++- .../Home/SendCryptoAsset/SendCryptoAsset.swift | 4 ++-- .../SendCryptoAsset/SendCryptoAssetViewModel.swift | 4 ++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift index 4acd0d093..9c9c01502 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift @@ -144,7 +144,18 @@ private extension ConfirmSendTokenView { .id(stateId) } - var isSufficientFunds: Bool { true } + var isSufficientFunds: Bool { + guard let gasFee = dataModel.gasFee else { return true } + + let sendData = dataModel.data + let balance = sendData.token.balance + if sendData.isSendingAllTokens() { + return balance >= gasFee + } + + let tokenAmountToSend = sendData.getTokenAmountValueToSend() + return balance > tokenAmountToSend + gasFee + } @ViewBuilder func confirmButton() -> some View { diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift index 148cdb416..fa2708a10 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift @@ -104,12 +104,12 @@ extension SendCryptoAsset { receiver.walletAddress } - func getTokenAmountValue() -> Double { + func getTokenAmountValueToSend() -> Double { amount.valueOf(type: .tokenAmount, for: token) } func isSendingAllTokens() -> Bool { - getTokenAmountValue() >= token.balance + getTokenAmountValueToSend() >= token.balance } } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift index 9c8b55beb..fe09ea8ab 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetViewModel.swift @@ -76,7 +76,7 @@ final class SendCryptoAssetViewModel: ObservableObject { private func getMaxTokenAmountToSendConsideringGasFee(sendData: SendCryptoAsset.SendTokenAssetData, txSpeed: SendCryptoAsset.TransactionSpeed) async throws -> CryptoSendingSpec { let gasPrice = try await computeGasFeeFor(sendData: sendData, txSpeed: txSpeed) - let tokenAmountToSend = sendData.getTokenAmountValue() - gasPrice + let tokenAmountToSend = sendData.getTokenAmountValueToSend() - gasPrice guard tokenAmountToSend > 0 else { throw CryptoSender.Error.insufficientFunds } @@ -106,7 +106,7 @@ final class SendCryptoAssetViewModel: ObservableObject { private func getCryptoSendingSpecFor(sendData: SendCryptoAsset.SendTokenAssetData, txSpeed: SendCryptoAsset.TransactionSpeed) throws -> CryptoSendingSpec { let token = sendData.token - let tokenAmount = sendData.getTokenAmountValue() + let tokenAmount = sendData.getTokenAmountValueToSend() return try getCryptoSendingSpecFor(token: token, tokenAmount: tokenAmount, From 4e78172015c023e8c0f223ba5f66360a6d5e513a Mon Sep 17 00:00:00 2001 From: Oleg Date: Sat, 30 Mar 2024 12:36:10 +0700 Subject: [PATCH 59/80] Force refresh wallet update --- .../NetworkEnvironment/ApiRequestBuilder.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/unstoppable-ios-app/domains-manager-ios/NetworkEnvironment/ApiRequestBuilder.swift b/unstoppable-ios-app/domains-manager-ios/NetworkEnvironment/ApiRequestBuilder.swift index 0579269c9..f4610fa8f 100644 --- a/unstoppable-ios-app/domains-manager-ios/NetworkEnvironment/ApiRequestBuilder.swift +++ b/unstoppable-ios-app/domains-manager-ios/NetworkEnvironment/ApiRequestBuilder.swift @@ -890,7 +890,8 @@ extension Endpoint { extension Endpoint { static func getCryptoPortfolio(for wallet: String) -> Endpoint { //https://api.ud-staging.com/profile/user/0xcd0dadab45baf9a06ce1279d1342ecc3f44845af/wallets - let queryItems: [URLQueryItem] = [.init(name: "walletFields", value: "native,token")] + let queryItems: [URLQueryItem] = [.init(name: "walletFields", value: "native,token"), + .init(name: "forceRefresh", value: String(Int(Date().timeIntervalSince1970)))] return Endpoint( host: NetworkConfig.baseProfileHost, path: "/profile/user/\(wallet)/wallets", From 6ee14fe19020e3bf43cac1cf5e6f6e50517f5ee7 Mon Sep 17 00:00:00 2001 From: Oleg Date: Mon, 1 Apr 2024 09:57:32 +0700 Subject: [PATCH 60/80] Add biometric verification to confirm send transaction. --- .../ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift index 9c9c01502..478639416 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift @@ -106,6 +106,9 @@ private extension ConfirmSendTokenView { func confirmSending() { Task { + guard let view = appContext.coreAppCoordinator.topVC else { return } + try await appContext.authentificationService.verifyWith(uiHandler: view, purpose: .confirm) + isLoading = true do { let txHash = try await viewModel.sendCryptoTokenWith(sendData: dataModel.data, From 63be7a7c5fab688add0588b120a04c2daabdf067 Mon Sep 17 00:00:00 2001 From: Oleg Date: Mon, 1 Apr 2024 10:00:04 +0700 Subject: [PATCH 61/80] Updated using max button title --- .../SelectTokenAssetAmountToSendView.swift | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift index dc79b180a..18b1d9b0e 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift @@ -222,13 +222,6 @@ private extension SelectTokenAssetAmountToSendView { return inputValueFor(inputType: .tokenAmount) == token.balance } } - var useMaxButtonTitle: String { - if isUsingMax { - String.Constants.usingMax.localized() - } else { - String.Constants.max.localized() - } - } @ViewBuilder func usingMaxButton() -> some View { @@ -236,7 +229,7 @@ private extension SelectTokenAssetAmountToSendView { UDVibration.buttonTap.vibrate() maxButtonPressed() } label: { - Text(useMaxButtonTitle) + Text(String.Constants.max.localized()) .foregroundStyle(isUsingMax ? Color.foregroundAccentMuted : Color.foregroundAccent) } .buttonStyle(.plain) From e00b09c3ff5bd5b58d4ec638341060663a6a0ce1 Mon Sep 17 00:00:00 2001 From: Oleg Date: Mon, 1 Apr 2024 10:29:19 +0700 Subject: [PATCH 62/80] Added crypto views lifecycle analytic events --- .../Modules/Home/Explore/HomeExploreView.swift | 1 + .../ConfirmSendToken/ConfirmSendTokenView.swift | 12 ++++++++++-- .../ConfirmTransferDomainView.swift | 9 +++++++-- .../SelectCryptoAssetToSendView.swift | 4 +++- .../SendCryptoAssetSelectReceiverView.swift | 6 ++++++ .../SelectTokenAssetAmountToSendView.swift | 4 +++- .../AnalyticsServiceEnvironment.swift | 5 +++-- 7 files changed, 33 insertions(+), 8 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Explore/HomeExploreView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Explore/HomeExploreView.swift index a841d27f0..2a72eb53d 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Explore/HomeExploreView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Explore/HomeExploreView.swift @@ -29,6 +29,7 @@ struct HomeExploreView: View, ViewAnalyticsLogger { .navigationTitle("") .navigationBarTitleDisplayMode(.inline) .environmentObject(viewModel) + .trackAppearanceAnalytics(analyticsLogger: self) .passViewAnalyticsDetails(logger: self) .displayError($viewModel.error) .background(Color.backgroundMuted2) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift index 478639416..3b5577eb4 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift @@ -7,7 +7,7 @@ import SwiftUI -struct ConfirmSendTokenView: View { +struct ConfirmSendTokenView: View, ViewAnalyticsLogger { @EnvironmentObject var viewModel: SendCryptoAssetViewModel @@ -20,7 +20,13 @@ struct ConfirmSendTokenView: View { private var token: BalanceTokenUIDescription { dataModel.token } private var receiver: SendCryptoAsset.AssetReceiver { dataModel.receiver } private let refreshGasTimer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() - + var analyticsName: Analytics.ViewName { .sendCryptoTokenConfirmation } + var additionalAppearAnalyticParameters: Analytics.EventParameters { [.token: token.symbol, + .value: String(dataModel.amount.valueOf(type: .tokenAmount, + for: token)), + .toWallet: dataModel.receiver.walletAddress, + .fromWallet: viewModel.sourceWallet.address] } + var body: some View { VStack(spacing: 4) { sendingTokenInfoView() @@ -40,6 +46,8 @@ struct ConfirmSendTokenView: View { .padding(16) .background(Color.backgroundDefault) .animation(.default, value: UUID()) + .trackAppearanceAnalytics(analyticsLogger: self) + .passViewAnalyticsDetails(logger: self) .addNavigationTopSafeAreaOffset() .navigationTitle(String.Constants.youAreSending.localized()) .displayError($error) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmTransferDomain/ConfirmTransferDomainView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmTransferDomain/ConfirmTransferDomainView.swift index bf884ddde..10f0843c9 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmTransferDomain/ConfirmTransferDomainView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmTransferDomain/ConfirmTransferDomainView.swift @@ -7,7 +7,7 @@ import SwiftUI -struct ConfirmTransferDomainView: View { +struct ConfirmTransferDomainView: View, ViewAnalyticsLogger { @EnvironmentObject var viewModel: SendCryptoAssetViewModel @@ -15,7 +15,11 @@ struct ConfirmTransferDomainView: View { @State private var pullUp: ViewPullUpConfigurationType? @State private var isLoading: Bool = false @State private var error: Error? - + var analyticsName: Analytics.ViewName { .sendCryptoDomainTransferConfirmation } + var additionalAppearAnalyticParameters: Analytics.EventParameters { [.domainName: data.domain.name, + .toWallet: data.receiver.walletAddress, + .fromWallet: viewModel.sourceWallet.address] } + var body: some View { VStack(spacing: 4) { sendingTokenInfoView() @@ -28,6 +32,7 @@ struct ConfirmTransferDomainView: View { .padding(16) .background(Color.backgroundDefault) .animation(.default, value: UUID()) + .trackAppearanceAnalytics(analyticsLogger: self) .addNavigationTopSafeAreaOffset() .viewPullUp($pullUp) .displayError($error) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectAssetToSend/SelectCryptoAssetToSendView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectAssetToSend/SelectCryptoAssetToSendView.swift index f62a84804..f3b267c36 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectAssetToSend/SelectCryptoAssetToSendView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectAssetToSend/SelectCryptoAssetToSendView.swift @@ -7,7 +7,7 @@ import SwiftUI -struct SelectCryptoAssetToSendView: View { +struct SelectCryptoAssetToSendView: View, ViewAnalyticsLogger { @EnvironmentObject var viewModel: SendCryptoAssetViewModel @EnvironmentObject var tabRouter: HomeTabRouter @@ -21,6 +21,7 @@ struct SelectCryptoAssetToSendView: View { @State private var allDomains: [DomainDisplayInfo] = [] let receiver: SendCryptoAsset.AssetReceiver + var analyticsName: Analytics.ViewName { .sendCryptoAssetSelection } var body: some View { List { @@ -31,6 +32,7 @@ struct SelectCryptoAssetToSendView: View { .listRowSeparator(.hidden) .listRowInsets(.init(horizontal: 16)) } + .trackAppearanceAnalytics(analyticsLogger: self) .addNavigationTopSafeAreaOffset() .listRowSpacing(0) .listStyle(.plain) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectReceiver/SendCryptoAssetSelectReceiverView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectReceiver/SendCryptoAssetSelectReceiverView.swift index 461261ad1..905882024 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectReceiver/SendCryptoAssetSelectReceiverView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectReceiver/SendCryptoAssetSelectReceiverView.swift @@ -17,6 +17,7 @@ struct SendCryptoAssetSelectReceiverView: View, ViewAnalyticsLogger { @EnvironmentObject var viewModel: SendCryptoAssetViewModel var analyticsName: Analytics.ViewName { .sendCryptoReceiverSelection } + var additionalAppearAnalyticParameters: Analytics.EventParameters { [.fromWallet: viewModel.sourceWallet.address] } @State private var userWallets: [WalletEntity] = [] @State private var followingList: [DomainName] = [] @@ -104,6 +105,7 @@ private extension SendCryptoAssetSelectReceiverView { bordered: true), rightViewStyle: nil) } callback: { + logButtonPressedAnalyticEvents(button: .qrCode) viewModel.handleAction(.scanQRSelected) } .listRowSeparator(.hidden) @@ -142,6 +144,7 @@ private extension SendCryptoAssetSelectReceiverView { selectableRowView { SendCryptoAssetSelectReceiverWalletRowView(wallet: wallet) } callback: { + logAnalytic(event: .userWalletPressed, parameters: [.wallet: wallet.address]) viewModel.handleAction(.userWalletSelected(wallet)) } } @@ -165,6 +168,7 @@ private extension SendCryptoAssetSelectReceiverView { selectableRowView { SendCryptoAssetSelectReceiverFollowingRowView(domainName: following) } callback: { + logAnalytic(event: .followingProfilePressed, parameters: [.domainName : domainName]) guard let profile = domainProfilesService.getCachedDomainProfileDisplayInfo(for: following) else { Debugger.printFailure("Failed to get cached domain profile for following: \(following)") return @@ -231,6 +235,7 @@ private extension SendCryptoAssetSelectReceiverView { bordered: true), rightViewStyle: nil) }, callback: { + logAnalytic(event: .searchWalletAddressPressed, parameters: [.wallet : address]) viewModel.handleAction(.globalWalletAddressSelected(address)) }) } @@ -240,6 +245,7 @@ private extension SendCryptoAssetSelectReceiverView { selectableRowView { DomainSearchResultProfileRowView(profile: profile) } callback: { + logAnalytic(event: .searchProfilePressed, parameters: [.domainName : profile.name]) viewModel.handleAction(.globalProfileSelected(profile)) } } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift index 18b1d9b0e..9343c892f 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift @@ -7,7 +7,7 @@ import SwiftUI -struct SelectTokenAssetAmountToSendView: View { +struct SelectTokenAssetAmountToSendView: View, ViewAnalyticsLogger { @EnvironmentObject var viewModel: SendCryptoAssetViewModel @@ -17,6 +17,7 @@ struct SelectTokenAssetAmountToSendView: View { @State private var pullUp: ViewPullUpConfigurationType? @State private var inputType: SendCryptoAsset.TokenAssetAmountInputType = .usdAmount @State private var interpreter = NumberPadInputInterpreter() + var analyticsName: Analytics.ViewName { .sendCryptoTokenAmountInput } var body: some View { VStack(spacing: isIPSE ? 8 : 57) { @@ -36,6 +37,7 @@ struct SelectTokenAssetAmountToSendView: View { } .padding(16) .animation(.default, value: UUID()) + .trackAppearanceAnalytics(analyticsLogger: self) .addNavigationTopSafeAreaOffset() .viewPullUp($pullUp) } diff --git a/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift b/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift index c7709707a..662e48e37 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift @@ -44,7 +44,7 @@ extension Analytics { case makeScreenshot, screenRecording case didConnectDApp, didDisconnectDApp case didTransferDomain - case searchProfilePressed, recentSearchProfilePressed + case searchProfilePressed, recentSearchProfilePressed, searchWalletAddressPressed, followingProfilePressed, userWalletPressed case trendingProfilePressed // Domains Collection @@ -139,6 +139,7 @@ extension Analytics { case numberOfItemsInSection case success case tab + case token } } @@ -225,7 +226,7 @@ extension Analytics { case homeExplore case homeActivity - case sendCryptoReceiverSelection + case sendCryptoReceiverSelection, sendCryptoAssetSelection, sendCryptoTokenAmountInput, sendCryptoDomainTransferConfirmation, sendCryptoTokenConfirmation } } From 196bb0920d79efa8ede5cedc2685ea8b3a0dad22 Mon Sep 17 00:00:00 2001 From: Oleg Date: Mon, 1 Apr 2024 10:30:10 +0700 Subject: [PATCH 63/80] Fixed typo --- .../SelectReceiver/SendCryptoAssetSelectReceiverView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectReceiver/SendCryptoAssetSelectReceiverView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectReceiver/SendCryptoAssetSelectReceiverView.swift index 905882024..79134ac94 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectReceiver/SendCryptoAssetSelectReceiverView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectReceiver/SendCryptoAssetSelectReceiverView.swift @@ -168,7 +168,7 @@ private extension SendCryptoAssetSelectReceiverView { selectableRowView { SendCryptoAssetSelectReceiverFollowingRowView(domainName: following) } callback: { - logAnalytic(event: .followingProfilePressed, parameters: [.domainName : domainName]) + logAnalytic(event: .followingProfilePressed, parameters: [.domainName : following]) guard let profile = domainProfilesService.getCachedDomainProfileDisplayInfo(for: following) else { Debugger.printFailure("Failed to get cached domain profile for following: \(following)") return From caa103adb3e9f3b878479c71d06ded5144140835 Mon Sep 17 00:00:00 2001 From: Oleg Date: Mon, 1 Apr 2024 10:47:36 +0700 Subject: [PATCH 64/80] Added analytic events for scan qr code and select receiver views --- .../domains-manager-ios.xcodeproj/project.pbxproj | 12 ++++++------ .../SendCryptoAssetSelectReceiverView.swift | 6 +++++- .../SendCryptoAssetNavigationDestination.swift | 2 +- ...=> SendCryptoQRWalletAddressScannerView.swift} | 15 ++++++++++----- .../AnalyticsServiceEnvironment.swift | 5 +++-- .../SwiftUI/CommonViews/UDTextFieldView.swift | 13 +++++++------ 6 files changed, 32 insertions(+), 21 deletions(-) rename unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/{QRWalletAddressScannerView.swift => SendCryptoQRWalletAddressScannerView.swift} (81%) diff --git a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj index 0e5f1f487..5edd70347 100644 --- a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj +++ b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj @@ -533,8 +533,8 @@ C6203A632B884018000A1A8E /* HomeExploreFollowerCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6203A622B884018000A1A8E /* HomeExploreFollowerCellView.swift */; }; C6203A642B884018000A1A8E /* HomeExploreFollowerCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6203A622B884018000A1A8E /* HomeExploreFollowerCellView.swift */; }; C6209F9929D41B9700D573EB /* LocalNotificationsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6209F9729D4180500D573EB /* LocalNotificationsService.swift */; }; - C621A43C2BB10D2900CB5CB9 /* QRWalletAddressScannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C621A43B2BB10D2900CB5CB9 /* QRWalletAddressScannerView.swift */; }; - C621A43D2BB10D2900CB5CB9 /* QRWalletAddressScannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C621A43B2BB10D2900CB5CB9 /* QRWalletAddressScannerView.swift */; }; + C621A43C2BB10D2900CB5CB9 /* SendCryptoQRWalletAddressScannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C621A43B2BB10D2900CB5CB9 /* SendCryptoQRWalletAddressScannerView.swift */; }; + C621A43D2BB10D2900CB5CB9 /* SendCryptoQRWalletAddressScannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C621A43B2BB10D2900CB5CB9 /* SendCryptoQRWalletAddressScannerView.swift */; }; C621A43F2BB10EFD00CB5CB9 /* QRScannerPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C621A43E2BB10EFD00CB5CB9 /* QRScannerPreviewView.swift */; }; C621A4402BB10EFD00CB5CB9 /* QRScannerPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C621A43E2BB10EFD00CB5CB9 /* QRScannerPreviewView.swift */; }; C621A4422BB1137900CB5CB9 /* QRScannerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = C621A4412BB1137900CB5CB9 /* QRScannerState.swift */; }; @@ -2935,7 +2935,7 @@ C6203A5E2B883EF3000A1A8E /* HomeExploreFollowersSectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeExploreFollowersSectionView.swift; sourceTree = ""; }; C6203A622B884018000A1A8E /* HomeExploreFollowerCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeExploreFollowerCellView.swift; sourceTree = ""; }; C6209F9729D4180500D573EB /* LocalNotificationsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotificationsService.swift; sourceTree = ""; }; - C621A43B2BB10D2900CB5CB9 /* QRWalletAddressScannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRWalletAddressScannerView.swift; sourceTree = ""; }; + C621A43B2BB10D2900CB5CB9 /* SendCryptoQRWalletAddressScannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendCryptoQRWalletAddressScannerView.swift; sourceTree = ""; }; C621A43E2BB10EFD00CB5CB9 /* QRScannerPreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRScannerPreviewView.swift; sourceTree = ""; }; C621A4412BB1137900CB5CB9 /* QRScannerState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRScannerState.swift; sourceTree = ""; }; C621A4442BB11B0100CB5CB9 /* QRScannerHint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRScannerHint.swift; sourceTree = ""; }; @@ -6407,7 +6407,7 @@ C67213E12BAA967C0075B9C7 /* SendCryptoAssetRootView.swift */, C62900E12BAAB392008B35A2 /* SendCryptoAsset.swift */, C67213DE2BAA96170075B9C7 /* SendCryptoAssetNavigationDestination.swift */, - C621A43B2BB10D2900CB5CB9 /* QRWalletAddressScannerView.swift */, + C621A43B2BB10D2900CB5CB9 /* SendCryptoQRWalletAddressScannerView.swift */, C6F433502BB27AEF000C5E46 /* SendCryptoAssetSuccessView.swift */, C6F433532BB28809000C5E46 /* TransactionStatusTracker.swift */, C6A359262BB52CDC00B1209A /* SendCryptoReceiverInfoTitleView.swift */, @@ -9221,7 +9221,7 @@ C66A9DC828BCCE6600F8BA3F /* CNavigationControllerChildNavigationHandler.swift in Sources */, C621A43F2BB10EFD00CB5CB9 /* QRScannerPreviewView.swift in Sources */, C66FCCF32844863E009B9525 /* UDGradientCoverView.swift in Sources */, - C621A43C2BB10D2900CB5CB9 /* QRWalletAddressScannerView.swift in Sources */, + C621A43C2BB10D2900CB5CB9 /* SendCryptoQRWalletAddressScannerView.swift in Sources */, 29BECD132979BDF800662FC1 /* BackedUpWallet.swift in Sources */, C62BDA412B5E4104008E21AD /* MessagingBlockUserInChatType.swift in Sources */, C62900DF2BAAB1A8008B35A2 /* UDTabsPickerView.swift in Sources */, @@ -10044,7 +10044,7 @@ C61808302B19AD9D0032E543 /* PreviewStripeService.swift in Sources */, C6C837A22B5FF307000A6AF5 /* TabBarVisibleModifier.swift in Sources */, C6D645BC2B1DBCE400D724AC /* ScrollViewOffsetListener.swift in Sources */, - C621A43D2BB10D2900CB5CB9 /* QRWalletAddressScannerView.swift in Sources */, + C621A43D2BB10D2900CB5CB9 /* SendCryptoQRWalletAddressScannerView.swift in Sources */, C6C8F8352B217E9600A9834D /* CreateLocalWalletRecoveryWordsPresenter.swift in Sources */, C6D645E82B1DBE6B00D724AC /* RaisedTertiaryButton.swift in Sources */, C62AC2032BABF532003FB69E /* ConfirmSendAssetReviewInfoView.swift in Sources */, diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectReceiver/SendCryptoAssetSelectReceiverView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectReceiver/SendCryptoAssetSelectReceiverView.swift index 79134ac94..4aa1d0c64 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectReceiver/SendCryptoAssetSelectReceiverView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectReceiver/SendCryptoAssetSelectReceiverView.swift @@ -83,8 +83,12 @@ private extension SendCryptoAssetSelectReceiverView { rightViewType: .paste, rightViewMode: .always, autocapitalization: .never, - autocorrectionDisabled: true) + autocorrectionDisabled: true, + focusedStateChangedCallback: { isFocused in + logAnalytic(event: isFocused ? .didStartSearching : .didStopSearching) + }) .onChange(of: debounceObject.debouncedText) { text in + logAnalytic(event: .didSearch, parameters: [.value : text]) inputText = text.lowercased().trimmedSpaces searchForGlobalProfiles() } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetNavigationDestination.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetNavigationDestination.swift index 83af45203..d98610272 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetNavigationDestination.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetNavigationDestination.swift @@ -34,7 +34,7 @@ extension SendCryptoAsset { static func viewFor(navigationDestination: NavigationDestination) -> some View { switch navigationDestination { case .scanWalletAddress: - QRWalletAddressScannerView() + SendCryptoQRWalletAddressScannerView() case .selectAssetToSend(let receiver): SelectCryptoAssetToSendView(receiver: receiver) case .selectTokenAmountToSend(let data): diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/QRWalletAddressScannerView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoQRWalletAddressScannerView.swift similarity index 81% rename from unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/QRWalletAddressScannerView.swift rename to unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoQRWalletAddressScannerView.swift index 4dce9a7ee..c55982d64 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/QRWalletAddressScannerView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoQRWalletAddressScannerView.swift @@ -7,11 +7,12 @@ import SwiftUI -struct QRWalletAddressScannerView: View { +struct SendCryptoQRWalletAddressScannerView: View, ViewAnalyticsLogger { @Environment(\.dismiss) var dismiss @EnvironmentObject var viewModel: SendCryptoAssetViewModel - + var analyticsName: Analytics.ViewName { .sendCryptoScanQRCode } + @State private var isTorchAvailable = false @State private var isTorchOn = false @State private var didRecognizeAddress = false @@ -31,12 +32,14 @@ struct QRWalletAddressScannerView: View { } } .animation(.default, value: UUID()) + .trackAppearanceAnalytics(analyticsLogger: self) + .passViewAnalyticsDetails(logger: self) .navigationTitle(String.Constants.scanQRCodeTitle.localized()) } } // MARK: - Private methods -private extension QRWalletAddressScannerView { +private extension SendCryptoQRWalletAddressScannerView { func handleQRScannerViewEvent(_ event: QRScannerPreviewView.Event) { switch event { case .didChangeState(let state): @@ -56,6 +59,7 @@ private extension QRWalletAddressScannerView { guard !didRecognizeAddress else { return } didRecognizeAddress = true + logAnalytic(event: .didRecognizeQRWalletAddress, parameters: [.wallet: walletAddress]) dismiss() Vibration.success.vibrate() DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) { @@ -65,12 +69,13 @@ private extension QRWalletAddressScannerView { } // MARK: - Private methods -private extension QRWalletAddressScannerView { +private extension SendCryptoQRWalletAddressScannerView { @ViewBuilder func torchButton() -> some View { Button { UDVibration.buttonTap.vibrate() isTorchOn.toggle() + logButtonPressedAnalyticEvents(button: .cameraTorch, parameters: [.isOn: String(isTorchOn)]) } label: { currentTorchIcon() .squareFrame(24) @@ -99,7 +104,7 @@ private extension QRWalletAddressScannerView { #Preview { NavigationStack { - QRWalletAddressScannerView() + SendCryptoQRWalletAddressScannerView() } } diff --git a/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift b/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift index 662e48e37..d188a1a38 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift @@ -32,7 +32,7 @@ extension Analytics { case didStartSearching, didStopSearching, didSearch case didSelectExportDomainPFPStyle case didChangeTheme - case didRecognizeQRCode + case didRecognizeQRCode, didRecognizeQRWalletAddress case didSelectChainNetwork case didReceivePushNotification case didReceiveLocalPushNotification @@ -226,7 +226,7 @@ extension Analytics { case homeExplore case homeActivity - case sendCryptoReceiverSelection, sendCryptoAssetSelection, sendCryptoTokenAmountInput, sendCryptoDomainTransferConfirmation, sendCryptoTokenConfirmation + case sendCryptoReceiverSelection, sendCryptoAssetSelection, sendCryptoTokenAmountInput, sendCryptoDomainTransferConfirmation, sendCryptoTokenConfirmation, sendCryptoScanQRCode } } @@ -399,6 +399,7 @@ extension Analytics { case exploreNoProfile, exploreNoFollowers, exploreNoFollowing case sendCrypto + case cameraTorch } } diff --git a/unstoppable-ios-app/domains-manager-ios/SwiftUI/CommonViews/UDTextFieldView.swift b/unstoppable-ios-app/domains-manager-ios/SwiftUI/CommonViews/UDTextFieldView.swift index c698e7802..063d48fd1 100644 --- a/unstoppable-ios-app/domains-manager-ios/SwiftUI/CommonViews/UDTextFieldView.swift +++ b/unstoppable-ios-app/domains-manager-ios/SwiftUI/CommonViews/UDTextFieldView.swift @@ -7,7 +7,10 @@ import SwiftUI -struct UDTextFieldView: View { +struct UDTextFieldView: View, ViewAnalyticsLogger { + + @Environment(\.analyticsViewName) var analyticsName + @Environment(\.analyticsAdditionalProperties) var additionalAppearAnalyticParameters @Binding var text: String let placeholder: String @@ -20,6 +23,7 @@ struct UDTextFieldView: View { var autocapitalization: TextInputAutocapitalization = .sentences var autocorrectionDisabled: Bool = false var height: CGFloat = 56 + var focusedStateChangedCallback: ((Bool)->())? = nil @State private var state: TextFieldState = .rest @State private var isInspiring = false @FocusState private var isTextFieldFocused: Bool @@ -128,11 +132,7 @@ private extension UDTextFieldView { .textInputAutocapitalization(autocapitalization) .autocorrectionDisabled(autocorrectionDisabled) .onChange(of: isTextFieldFocused) { isFocused in - if isFocused { - // began editing... - } else { - // ended editing... - } + focusedStateChangedCallback?(isFocused) setState() } .frame(height: 24) @@ -151,6 +151,7 @@ private extension UDTextFieldView { case .clear: text = "" case .paste: + logButtonPressedAnalyticEvents(button: .pasteFromClipboard) text = UIPasteboard.general.string ?? "" case .cancel(let callback): if isInspiring { From 832646e0a4bc5ff5c3c94b6f3b3fd0265a4c45d0 Mon Sep 17 00:00:00 2001 From: Oleg Date: Mon, 1 Apr 2024 11:12:18 +0700 Subject: [PATCH 65/80] Added analytics to asset selection and token input views --- .../ConfirmSendToken/ConfirmSendTokenView.swift | 2 +- .../SelectCryptoAssetToSendView.swift | 17 ++++++++++++++--- .../SelectTokenAssetAmountToSendView.swift | 7 +++++++ .../HomeWalletsDomainsSectionView.swift | 2 +- .../AnalyticsServiceEnvironment.swift | 4 ++++ 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift index 3b5577eb4..8639f9306 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift @@ -21,7 +21,7 @@ struct ConfirmSendTokenView: View, ViewAnalyticsLogger { private var receiver: SendCryptoAsset.AssetReceiver { dataModel.receiver } private let refreshGasTimer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() var analyticsName: Analytics.ViewName { .sendCryptoTokenConfirmation } - var additionalAppearAnalyticParameters: Analytics.EventParameters { [.token: token.symbol, + var additionalAppearAnalyticParameters: Analytics.EventParameters { [.token: token.id, .value: String(dataModel.amount.valueOf(type: .tokenAmount, for: token)), .toWallet: dataModel.receiver.walletAddress, diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectAssetToSend/SelectCryptoAssetToSendView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectAssetToSend/SelectCryptoAssetToSendView.swift index f3b267c36..308a06ec6 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectAssetToSend/SelectCryptoAssetToSendView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectAssetToSend/SelectCryptoAssetToSendView.swift @@ -22,7 +22,8 @@ struct SelectCryptoAssetToSendView: View, ViewAnalyticsLogger { let receiver: SendCryptoAsset.AssetReceiver var analyticsName: Analytics.ViewName { .sendCryptoAssetSelection } - + var additionalAppearAnalyticParameters: Analytics.EventParameters { [.toWallet: receiver.walletAddress, + .fromWallet: viewModel.sourceWallet.address] } var body: some View { List { assetTypePickerView() @@ -32,6 +33,10 @@ struct SelectCryptoAssetToSendView: View, ViewAnalyticsLogger { .listRowSeparator(.hidden) .listRowInsets(.init(horizontal: 16)) } + .onChange(of: selectedType, perform: { newValue in + logButtonPressedAnalyticEvents(button: .assetTypeSwitcher, + parameters: [.assetType : newValue.rawValue]) + }) .trackAppearanceAnalytics(analyticsLogger: self) .addNavigationTopSafeAreaOffset() .listRowSpacing(0) @@ -102,6 +107,8 @@ private extension SelectCryptoAssetToSendView { func selectableTokenRow(_ token: BalanceTokenUIDescription) -> some View { Button { UDVibration.buttonTap.vibrate() + logButtonPressedAnalyticEvents(button: .cryptoToken, + parameters: [.token : token.id]) viewModel.handleAction(.userTokenToSendSelected(.init(receiver: receiver, token: token))) } label: { @@ -147,8 +154,12 @@ private extension SelectCryptoAssetToSendView { leftViewType: .search, autocapitalization: .never, autocorrectionDisabled: true, - height: 36) - .onChange(of: searchDomainsKey, perform: { _ in + height: 36, + focusedStateChangedCallback: { isFocused in + logAnalytic(event: isFocused ? .didStartSearching : .didStopSearching) + }) + .onChange(of: searchDomainsKey, perform: { text in + logAnalytic(event: .didSearch, parameters: [.value : text]) setDomainsData() }) } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift index 9343c892f..b4dca46b5 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift @@ -18,6 +18,9 @@ struct SelectTokenAssetAmountToSendView: View, ViewAnalyticsLogger { @State private var inputType: SendCryptoAsset.TokenAssetAmountInputType = .usdAmount @State private var interpreter = NumberPadInputInterpreter() var analyticsName: Analytics.ViewName { .sendCryptoTokenAmountInput } + var additionalAppearAnalyticParameters: Analytics.EventParameters { [.token: token.id, + .toWallet: data.receiver.walletAddress, + .fromWallet: viewModel.sourceWallet.address] } var body: some View { VStack(spacing: isIPSE ? 8 : 57) { @@ -229,11 +232,13 @@ private extension SelectTokenAssetAmountToSendView { func usingMaxButton() -> some View { Button { UDVibration.buttonTap.vibrate() + logButtonPressedAnalyticEvents(button: .useMax) maxButtonPressed() } label: { Text(String.Constants.max.localized()) .foregroundStyle(isUsingMax ? Color.foregroundAccentMuted : Color.foregroundAccent) } + .disabled(isUsingMax) .buttonStyle(.plain) .padding(.init(horizontal: 16)) } @@ -257,6 +262,8 @@ private extension SelectTokenAssetAmountToSendView { func confirmButton() -> some View { UDButtonView(text: String.Constants.review.localized(), style: .large(.raisedPrimary)) { + logButtonPressedAnalyticEvents(button: .confirm, + parameters: [.value: String(getCurrentInput().valueOf(type: .tokenAmount, for: token))]) viewModel.handleAction(.userTokenValueSelected(.init(receiver: data.receiver, token: token, amount: getCurrentInput()))) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Wallet/HomeWalletView/Subviews/HomeWalletsDomainsSectionView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Wallet/HomeWalletView/Subviews/HomeWalletsDomainsSectionView.swift index 34a1d0ed9..8f6f8a10d 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Wallet/HomeWalletView/Subviews/HomeWalletsDomainsSectionView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Wallet/HomeWalletView/Subviews/HomeWalletsDomainsSectionView.swift @@ -91,7 +91,7 @@ private extension HomeWalletsDomainsSectionView { var numberOfVisibleSubdomains: Int { let numberOfSubdomains = subdomains.count - return isSubdomainsVisible ? numberOfSubdomains : min(numberOfSubdomains, minNumOfVisibleSubdomains) //Take no more than minNumOfVisibleSubdomains Subdomains + return isSubdomainsVisible ? numberOfSubdomains : min(numberOfSubdomains, minNumOfVisibleSubdomains) // Take no more than minNumOfVisibleSubdomains Subdomains } @ViewBuilder diff --git a/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift b/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift index d188a1a38..47de147fb 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift @@ -140,6 +140,7 @@ extension Analytics { case success case tab case token + case assetType } } @@ -400,6 +401,9 @@ extension Analytics { case sendCrypto case cameraTorch + case assetTypeSwitcher + case cryptoToken + case useMax } } From ce65705c5b57604abeb50628414658a9a59b0a74 Mon Sep 17 00:00:00 2001 From: Oleg Date: Mon, 1 Apr 2024 13:03:16 +0700 Subject: [PATCH 66/80] Added analytics to transfer domain view --- .../ConfirmTransferDomainView.swift | 8 +++++-- ...TransferDomainConfirmationPullUpView.swift | 22 ++++++++++++------- .../AnalyticsServiceEnvironment.swift | 2 ++ .../SwiftUI/CommonViews/UDCheckBoxView.swift | 6 +++++ 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmTransferDomain/ConfirmTransferDomainView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmTransferDomain/ConfirmTransferDomainView.swift index 10f0843c9..ea1f21248 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmTransferDomain/ConfirmTransferDomainView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmTransferDomain/ConfirmTransferDomainView.swift @@ -72,6 +72,7 @@ private extension ConfirmTransferDomainView { } func continueButtonPressed() { + logButtonPressedAnalyticEvents(button: .confirm) pullUp = .custom(.transferDomainConfirmationPullUp(confirmCallback: transferConfirmed)) } @@ -84,14 +85,17 @@ private extension ConfirmTransferDomainView { do { let domain = self.data.domain let recipientAddress = self.data.receiver.walletAddress - let configuration = TransferDomainConfiguration(resetRecords: confirmationData.shouldClearRecords) + let shouldClearRecords = confirmationData.shouldClearRecords + + let configuration = TransferDomainConfiguration(resetRecords: shouldClearRecords) try await appContext.domainTransferService.transferDomain(domain: domain.toDomainItem(), to: recipientAddress, configuration: configuration) appContext.analyticsService.log(event: .didTransferDomain, withParameters: [.domainName: domain.name, .fromWallet: domain.ownerWallet ?? "", - .toWallet: recipientAddress]) + .toWallet: recipientAddress, + .didClearRecords: String(shouldClearRecords)]) Task.detached { try? await appContext.walletsDataService.refreshDataForWallet(viewModel.sourceWallet) } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/TransferDomainConfirmationPullUpView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/TransferDomainConfirmationPullUpView.swift index afd9d2127..cf7c296c2 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/TransferDomainConfirmationPullUpView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/TransferDomainConfirmationPullUpView.swift @@ -20,14 +20,17 @@ struct TransferDomainConfirmationPullUpView: View { DismissIndicatorView(color: .foregroundMuted) VStack(alignment: .leading, spacing: 16) { confirmRowWith(isOn: $confirmationData.isConsentNotExchangeConfirmed, - title: String.Constants.transferConsentNotExchange.localized()) + title: String.Constants.transferConsentNotExchange.localized(), + analyticsName: .transferDomainExchangeToggle) HomeExploreSeparatorView() confirmRowWith(isOn: $confirmationData.isConsentValidAddressConfirmed, - title: String.Constants.transferConsentValidAddress.localized()) + title: String.Constants.transferConsentValidAddress.localized(), + analyticsName: .transferDomainConfirmAddressToggle) HomeExploreSeparatorView() confirmRowWith(isOn: $confirmationData.resetRecords, title: String.Constants.clearRecordsUponTransfer.localized(), - subtitle: String.Constants.optional.localized()) + subtitle: String.Constants.optional.localized(), + analyticsName: .transferDomainClearRecordsToggle) HomeExploreSeparatorView() warningText() } @@ -46,12 +49,12 @@ struct TransferDomainConfirmationPullUpView: View { private extension TransferDomainConfirmationPullUpView { @ViewBuilder func confirmRowWith(isOn: Binding, - title: String, - subtitle: String? = nil) -> some View { + title: String, + subtitle: String? = nil, + analyticsName: Analytics.Button) -> some View { HStack(spacing: 16) { - // TODO: - Pass analytics button to UDCheckBoxView - - UDCheckBoxView(isOn: isOn) + UDCheckBoxView(isOn: isOn, + analyticsName: analyticsName) VStack(alignment: .leading, spacing: 0) { Text(title) .font(.currentFont(size: 16, weight: .medium)) @@ -106,6 +109,9 @@ private extension TransferDomainConfirmationPullUpView { func confirmTransfer() { Task { + appContext.analyticsService.log(event: .buttonPressed, + withParameters: [.button: Analytics.Button.confirm.rawValue, + .pullUpName: Analytics.PullUp.transferDomainConfirmation.rawValue]) guard let view = await appContext.coreAppCoordinator.topVC else { return } do { try await appContext.authentificationService.verifyWith(uiHandler: view, purpose: .confirm) diff --git a/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift b/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift index 47de147fb..c55a72f30 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift @@ -141,6 +141,7 @@ extension Analytics { case tab case token case assetType + case didClearRecords } } @@ -404,6 +405,7 @@ extension Analytics { case assetTypeSwitcher case cryptoToken case useMax + case transferDomainExchangeToggle, transferDomainConfirmAddressToggle, transferDomainClearRecordsToggle } } diff --git a/unstoppable-ios-app/domains-manager-ios/SwiftUI/CommonViews/UDCheckBoxView.swift b/unstoppable-ios-app/domains-manager-ios/SwiftUI/CommonViews/UDCheckBoxView.swift index 2bb504374..01773bbc5 100644 --- a/unstoppable-ios-app/domains-manager-ios/SwiftUI/CommonViews/UDCheckBoxView.swift +++ b/unstoppable-ios-app/domains-manager-ios/SwiftUI/CommonViews/UDCheckBoxView.swift @@ -12,11 +12,17 @@ struct UDCheckBoxView: View { @Environment(\.isEnabled) private var isEnabled @Binding var isOn: Bool + var analyticsName: Analytics.Button? = nil var body: some View { Button { UDVibration.buttonTap.vibrate() isOn.toggle() + if let analyticsName { + appContext.analyticsService.log(event: .buttonPressed, + withParameters: [.button: analyticsName.rawValue, + .value: String(isOn)]) + } } label: { ZStack { if isOn { From a219aa912697fba78708af394774493544c48a49 Mon Sep 17 00:00:00 2001 From: Oleg Date: Mon, 1 Apr 2024 13:28:12 +0700 Subject: [PATCH 67/80] Added analytics to send crypto view --- .../Extensions/Extension-String+Preview.swift | 3 +++ .../ConfirmSendAssetReviewInfoView.swift | 18 +++++++++++++++--- .../ConfirmSendTokenView.swift | 5 +++++ .../Home/SendCryptoAsset/SendCryptoAsset.swift | 8 ++++---- .../AnalyticsServiceEnvironment.swift | 4 ++++ .../Localization/en.lproj/Localizable.strings | 3 +++ 6 files changed, 34 insertions(+), 7 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Extensions/Extension-String+Preview.swift b/unstoppable-ios-app/domains-manager-ios/Extensions/Extension-String+Preview.swift index 9cdbc8997..aa04b2ac7 100644 --- a/unstoppable-ios-app/domains-manager-ios/Extensions/Extension-String+Preview.swift +++ b/unstoppable-ios-app/domains-manager-ios/Extensions/Extension-String+Preview.swift @@ -1173,6 +1173,9 @@ extension String { static let sendMaxCryptoInfoPullUpTitle = "SEND_MAX_CRYPTO_INFO_PULL_UP_TITLE" static let notEnoughToken = "NOT_ENOUGH_TOKEN" static let activity = "ACTIVITY" + static let normal = "NORMAL" + static let fast = "FAST" + static let urgent = "URGENT" } enum BlockChainIcons: String { diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift index 119dc193d..02bea5a2e 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendAssetReviewInfoView.swift @@ -7,8 +7,10 @@ import SwiftUI -struct ConfirmSendAssetReviewInfoView: View { +struct ConfirmSendAssetReviewInfoView: View, ViewAnalyticsLogger { + @Environment(\.analyticsViewName) var analyticsName + @Environment(\.analyticsAdditionalProperties) var additionalAppearAnalyticParameters @Environment(\.imageLoadingService) var imageLoadingService private let lineWidth: CGFloat = 1 @@ -74,6 +76,8 @@ private extension ConfirmSendAssetReviewInfoView { ForEach(info.actions, id: \.self) { action in Button { UDVibration.buttonTap.vibrate() + logButtonPressedAnalyticEvents(button: action.analyticName, + parameters: action.analyticParameters) action.action() } label: { Label( @@ -87,7 +91,9 @@ private extension ConfirmSendAssetReviewInfoView { viewForInfoValueSection(info) } .onButtonTap { - + if let analyticName = info.analyticName { + logButtonPressedAnalyticEvents(button: analyticName) + } } } } @@ -150,6 +156,7 @@ private extension ConfirmSendAssetReviewInfoView { var valueColor: Color = .foregroundDefault var subValue: String? = nil var actions: [InfoActionDescription] = [] + var analyticName: Analytics.Button? = nil } struct InfoActionDescription: Hashable { @@ -158,6 +165,8 @@ private extension ConfirmSendAssetReviewInfoView { let subtitle: String let iconName: String let tintColor: UIColor + var analyticName: Analytics.Button + var analyticParameters: Analytics.EventParameters let action: EmptyCallback static func == (lhs: ConfirmSendAssetReviewInfoView.InfoActionDescription, rhs: ConfirmSendAssetReviewInfoView.InfoActionDescription) -> Bool { @@ -199,6 +208,8 @@ private extension ConfirmSendAssetReviewInfoView { subtitle: txSpeedSubtitleFor(txSpeed: txSpeed), iconName: txSpeed.iconName, tintColor: tintColorFor(txSpeed: txSpeed), + analyticName: .selectTransactionSpeed, + analyticParameters: [.transactionSpeed: txSpeed.rawValue], action: { didSelectTransactionSpeed(txSpeed) }) } } @@ -240,7 +251,8 @@ private extension ConfirmSendAssetReviewInfoView { iconColor: .foregroundSecondary, value: selectedTxSpeed.title, valueColor: Color(uiColor: tintColorFor(txSpeed: selectedTxSpeed)), - actions: getTransactionSpeedActions())), + actions: getTransactionSpeedActions(), + analyticName: .transactionSpeedSelection)), .infoValue(.init(title: String.Constants.feeEstimate.localized(), icon: .tildaIcon, value: gasUsdTitleFor(gasUsd: gasUsd))), diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift index 8639f9306..7106798f0 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift @@ -113,6 +113,9 @@ private extension ConfirmSendTokenView { } func confirmSending() { + logButtonPressedAnalyticEvents(button: .confirm, + parameters: [.transactionSpeed: dataModel.txSpeed.rawValue]) + Task { guard let view = appContext.coreAppCoordinator.topVC else { return } try await appContext.authentificationService.verifyWith(uiHandler: view, purpose: .confirm) @@ -121,6 +124,8 @@ private extension ConfirmSendTokenView { do { let txHash = try await viewModel.sendCryptoTokenWith(sendData: dataModel.data, txSpeed: dataModel.txSpeed) + logAnalytic(event: .didSendCrypto, + parameters: [.transactionSpeed: dataModel.txSpeed.rawValue]) viewModel.handleAction(.didSendCrypto(data: dataModel.data, txHash: txHash)) } catch { self.error = error diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift index fa2708a10..38e8acfb2 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAsset.swift @@ -113,17 +113,17 @@ extension SendCryptoAsset { } } - enum TransactionSpeed: CaseIterable { + enum TransactionSpeed: String, CaseIterable { case normal, fast, urgent var title: String { switch self { case .normal: - return "Normal" + return String.Constants.normal.localized() case .fast: - return "Fast" + return String.Constants.fast.localized() case .urgent: - return "Urgent" + return String.Constants.urgent.localized() } } diff --git a/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift b/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift index c55a72f30..1058f6361 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift @@ -77,6 +77,8 @@ extension Analytics { case shareResult, didSelectHomeTab case didPullToRefresh + + case didSendCrypto } } @@ -142,6 +144,7 @@ extension Analytics { case token case assetType case didClearRecords + case transactionSpeed } } @@ -406,6 +409,7 @@ extension Analytics { case cryptoToken case useMax case transferDomainExchangeToggle, transferDomainConfirmAddressToggle, transferDomainClearRecordsToggle + case transactionSpeedSelection, selectTransactionSpeed } } diff --git a/unstoppable-ios-app/domains-manager-ios/SupportingFiles/Localization/en.lproj/Localizable.strings b/unstoppable-ios-app/domains-manager-ios/SupportingFiles/Localization/en.lproj/Localizable.strings index 1853f9db2..77ac3d747 100644 --- a/unstoppable-ios-app/domains-manager-ios/SupportingFiles/Localization/en.lproj/Localizable.strings +++ b/unstoppable-ios-app/domains-manager-ios/SupportingFiles/Localization/en.lproj/Localizable.strings @@ -1073,3 +1073,6 @@ More tabs are coming in the next updates."; "SEND_MAX_CRYPTO_INFO_PULL_UP_TITLE" = "We will send all %@ less required network costs"; "NOT_ENOUGH_TOKEN" = "Not enough %@"; "ACTIVITY" = "Activity"; +"NORMAL" = "Normal"; +"FAST" = "Fast"; +"URGENT" = "Urgent"; From 6e3581d5eebbdca1a50f6f09ac134e4ee7aa9b15 Mon Sep 17 00:00:00 2001 From: Oleg Date: Mon, 1 Apr 2024 13:43:01 +0700 Subject: [PATCH 68/80] Added analytics to success screen --- .../SendCryptoAssetSuccessView.swift | 16 ++++++++++++++-- .../AnalyticsServiceEnvironment.swift | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetSuccessView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetSuccessView.swift index d69cbf4f6..77ba6a53b 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetSuccessView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SendCryptoAssetSuccessView.swift @@ -7,13 +7,13 @@ import SwiftUI -struct SendCryptoAssetSuccessView: View { +struct SendCryptoAssetSuccessView: View, ViewAnalyticsLogger { @EnvironmentObject var viewModel: SendCryptoAssetViewModel @EnvironmentObject var tabRouter: HomeTabRouter var asset: Asset - + var analyticsName: Analytics.ViewName { asset.viewName } @ObservedObject private var transactionTracker = TransactionStatusTracker() var body: some View { @@ -36,6 +36,7 @@ struct SendCryptoAssetSuccessView: View { .animation(.default, value: UUID()) .toolbar(.hidden, for: .navigationBar) .onAppear(perform: onAppear) + .trackAppearanceAnalytics(analyticsLogger: self) .onDisappear(perform: { transactionTracker.stopTracking() }) @@ -132,6 +133,7 @@ private extension SendCryptoAssetSuccessView { UDButtonView(text: String.Constants.viewTransaction.localized(), style: .large(.ghostPrimary), callback: { + logButtonPressedAnalyticEvents(button: .viewTransaction) viewTransaction(txHash: txHash) }) } @@ -148,6 +150,7 @@ private extension SendCryptoAssetSuccessView { } func doneAction() { + logButtonPressedAnalyticEvents(button: .done) tabRouter.sendCryptoInitialData = nil } } @@ -157,6 +160,15 @@ extension SendCryptoAssetSuccessView { enum Asset { case token(token: BalanceTokenUIDescription, amount: SendCryptoAsset.TokenAssetAmountInput, txHash: TxHash) case domain(DomainDisplayInfo) + + var viewName: Analytics.ViewName { + switch self { + case .token: + return .sendCryptoSuccess + case .domain: + return .transferDomainSuccess + } + } } } diff --git a/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift b/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift index 1058f6361..a74e22feb 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift @@ -232,6 +232,7 @@ extension Analytics { case homeActivity case sendCryptoReceiverSelection, sendCryptoAssetSelection, sendCryptoTokenAmountInput, sendCryptoDomainTransferConfirmation, sendCryptoTokenConfirmation, sendCryptoScanQRCode + case transferDomainSuccess, sendCryptoSuccess } } From 6cc28cf9192f5d2fe00bea079ad93ba98048d378 Mon Sep 17 00:00:00 2001 From: Oleg Date: Mon, 1 Apr 2024 13:55:22 +0700 Subject: [PATCH 69/80] Log send asset error --- .../ConfirmSendToken/ConfirmSendTokenView.swift | 3 +++ .../ConfirmTransferDomainView.swift | 15 +++++++-------- .../AnalyticsServiceEnvironment.swift | 4 ++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift index 7106798f0..76e8f4681 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift @@ -128,6 +128,9 @@ private extension ConfirmSendTokenView { parameters: [.transactionSpeed: dataModel.txSpeed.rawValue]) viewModel.handleAction(.didSendCrypto(data: dataModel.data, txHash: txHash)) } catch { + logAnalytic(event: .didFailToSendCrypto, + parameters: [.transactionSpeed: dataModel.txSpeed.rawValue, + .error: error.localizedDescription]) self.error = error } isLoading = false diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmTransferDomain/ConfirmTransferDomainView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmTransferDomain/ConfirmTransferDomainView.swift index ea1f21248..ad6f49d9c 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmTransferDomain/ConfirmTransferDomainView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmTransferDomain/ConfirmTransferDomainView.swift @@ -82,26 +82,25 @@ private extension ConfirmTransferDomainView { isLoading = true await Task.sleep(seconds: 0.35) + let domain = self.data.domain + let recipientAddress = self.data.receiver.walletAddress + let shouldClearRecords = confirmationData.shouldClearRecords do { - let domain = self.data.domain - let recipientAddress = self.data.receiver.walletAddress - let shouldClearRecords = confirmationData.shouldClearRecords let configuration = TransferDomainConfiguration(resetRecords: shouldClearRecords) try await appContext.domainTransferService.transferDomain(domain: domain.toDomainItem(), to: recipientAddress, configuration: configuration) - appContext.analyticsService.log(event: .didTransferDomain, - withParameters: [.domainName: domain.name, - .fromWallet: domain.ownerWallet ?? "", - .toWallet: recipientAddress, - .didClearRecords: String(shouldClearRecords)]) + logAnalytic(event: .didTransferDomain, parameters: [.didClearRecords: String(shouldClearRecords)]) Task.detached { try? await appContext.walletsDataService.refreshDataForWallet(viewModel.sourceWallet) } viewModel.handleAction(.didTransferDomain(domain)) } catch { + logAnalytic(event: .didFailToTransferDomain, + parameters: [.didClearRecords: String(shouldClearRecords), + .error: error.localizedDescription]) self.error = error } diff --git a/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift b/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift index a74e22feb..efd6056f7 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift @@ -43,7 +43,7 @@ extension Analytics { case websiteLoginOptionSelected case makeScreenshot, screenRecording case didConnectDApp, didDisconnectDApp - case didTransferDomain + case didTransferDomain, didFailToTransferDomain case searchProfilePressed, recentSearchProfilePressed, searchWalletAddressPressed, followingProfilePressed, userWalletPressed case trendingProfilePressed @@ -78,7 +78,7 @@ extension Analytics { case shareResult, didSelectHomeTab case didPullToRefresh - case didSendCrypto + case didSendCrypto, didFailToSendCrypto } } From f9b2800e554c19f00c9be912928acea490b5649e Mon Sep 17 00:00:00 2001 From: Oleg Date: Mon, 1 Apr 2024 14:02:15 +0700 Subject: [PATCH 70/80] Refactoring --- .../ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift | 6 +++--- .../SelectTokenAssetAmountToSendView.swift | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift index 76e8f4681..36cbf883e 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/ConfirmSend/ConfirmSendToken/ConfirmSendTokenView.swift @@ -163,7 +163,7 @@ private extension ConfirmSendTokenView { .id(stateId) } - var isSufficientFunds: Bool { + var hasSufficientFunds: Bool { guard let gasFee = dataModel.gasFee else { return true } let sendData = dataModel.data @@ -179,7 +179,7 @@ private extension ConfirmSendTokenView { @ViewBuilder func confirmButton() -> some View { VStack(spacing: isIPSE ? 6 : 24) { - if !isSufficientFunds { + if !hasSufficientFunds { insufficientFundsLabel() } UDButtonView(text: String.Constants.confirm.localized(), @@ -188,7 +188,7 @@ private extension ConfirmSendTokenView { isLoading: isLoading) { confirmSending() } - .disabled(!isSufficientFunds) + .disabled(!hasSufficientFunds) } } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift index b4dca46b5..d6f4fac15 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/SelectTokenAssetAmount/SelectTokenAssetAmountToSendView.swift @@ -93,7 +93,7 @@ private extension SelectTokenAssetAmountToSendView { // MARK: - Converted value private extension SelectTokenAssetAmountToSendView { - var isSufficientFunds: Bool { + var hasSufficientFunds: Bool { inputValueFor(inputType: .tokenAmount) <= token.balance } @@ -103,7 +103,7 @@ private extension SelectTokenAssetAmountToSendView { UDVibration.buttonTap.vibrate() switchInputType() } label: { - if isSufficientFunds { + if hasSufficientFunds { convertedValueLabel() } else { insufficientFundsLabel() @@ -268,7 +268,7 @@ private extension SelectTokenAssetAmountToSendView { token: token, amount: getCurrentInput()))) } - .disabled(interpreter.getInterpretedNumber() <= 0 || !isSufficientFunds) + .disabled(interpreter.getInterpretedNumber() <= 0 || !hasSufficientFunds) } func getCurrentInput() -> SendCryptoAsset.TokenAssetAmountInput { From 40eb0ac4094412e16753397bce090ed2394194a5 Mon Sep 17 00:00:00 2001 From: Oleg Date: Mon, 1 Apr 2024 14:29:31 +0700 Subject: [PATCH 71/80] Refactoring --- .../Entities/PreviewCryptoSender.swift | 31 ++++++ .../project.pbxproj | 26 ++++- .../CryptoSender/CryptoSender+Entities.swift | 104 ++++++++++++++++++ .../{ => CryptoSender}/CryptoSender.swift | 42 +------ .../CryptoSenderProtocol.swift | 55 --------- .../{ => CryptoSender}/JRPC_Client.swift | 0 6 files changed, 157 insertions(+), 101 deletions(-) create mode 100644 unstoppable-ios-app/domains-manager-ios-preview/Entities/PreviewCryptoSender.swift create mode 100644 unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender+Entities.swift rename unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/{ => CryptoSender}/CryptoSender.swift (85%) rename unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/{ => CryptoSender}/CryptoSenderProtocol.swift (58%) rename unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/{ => CryptoSender}/JRPC_Client.swift (100%) diff --git a/unstoppable-ios-app/domains-manager-ios-preview/Entities/PreviewCryptoSender.swift b/unstoppable-ios-app/domains-manager-ios-preview/Entities/PreviewCryptoSender.swift new file mode 100644 index 000000000..266243502 --- /dev/null +++ b/unstoppable-ios-app/domains-manager-ios-preview/Entities/PreviewCryptoSender.swift @@ -0,0 +1,31 @@ +// +// PreviewCryptoSender.swift +// domains-manager-ios +// +// Created by Oleg Kuplin on 01.04.2024. +// + +import Foundation + +typealias BigUInt = Int + +struct CryptoSender: CryptoSenderProtocol { + + let wallet: UDWallet + + func canSendCrypto(token: CryptoSender.SupportedToken, chainType: BlockchainType) -> Bool { + true + } + + func sendCrypto(crypto: CryptoSendingSpec, chain: ChainSpec, toAddress: HexAddress) async throws -> String { + "" + } + + func computeGasFeeFrom(maxCrypto: CryptoSendingSpec, on chain: ChainSpec, toAddress: HexAddress) async throws -> EVMTokenAmount { + .init(wei: 12300) + } + + func fetchGasPrices(on chain: ChainSpec) async throws -> EstimatedGasPrices { + .init(normal: .init(gwei: 123), fast: .init(gwei: 432), urgent: .init(gwei: 742)) + } +} diff --git a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj index 5edd70347..db6c477d9 100644 --- a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj +++ b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj @@ -34,7 +34,6 @@ 29AA6AA42BAAE9F500D24FB5 /* CryptoSenderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA32BAAE9F500D24FB5 /* CryptoSenderProtocol.swift */; }; 29AA6AA52BAAE9F500D24FB5 /* CryptoSenderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA32BAAE9F500D24FB5 /* CryptoSenderProtocol.swift */; }; 29AA6AA82BAC389D00D24FB5 /* CryptoSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA72BAC389D00D24FB5 /* CryptoSender.swift */; }; - 29AA6AA92BAC389D00D24FB5 /* CryptoSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AA6AA72BAC389D00D24FB5 /* CryptoSender.swift */; }; 29AEA04B292F7D60003BB5B4 /* SecurePersistedStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AEA04A292F7D60003BB5B4 /* SecurePersistedStorage.swift */; }; 29AEA050292F941F003BB5B4 /* PersistedSignaturesStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AEA04F292F941F003BB5B4 /* PersistedSignaturesStorage.swift */; }; 29AEA05529367785003BB5B4 /* UDWallet+Signing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29AEA05429367785003BB5B4 /* UDWallet+Signing.swift */; }; @@ -1351,6 +1350,9 @@ C6B40E912B5F83370038CEB0 /* UserProfileSelectionRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6B40E8F2B5F83370038CEB0 /* UserProfileSelectionRowView.swift */; }; C6B435002A52884F00BC644B /* MessagingPrivateChatBlockingStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6B434FF2A52884F00BC644B /* MessagingPrivateChatBlockingStatus.swift */; }; C6B435072A53F42400BC644B /* BaseDiffableCollectionViewControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6B435062A53F42400BC644B /* BaseDiffableCollectionViewControllerProtocol.swift */; }; + C6B45DB02BBA97B600B44C33 /* CryptoSender+Entities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6B45DAF2BBA97B600B44C33 /* CryptoSender+Entities.swift */; }; + C6B45DB12BBA97B600B44C33 /* CryptoSender+Entities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6B45DAF2BBA97B600B44C33 /* CryptoSender+Entities.swift */; }; + C6B45DB42BBA97FD00B44C33 /* PreviewCryptoSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6B45DB22BBA97FD00B44C33 /* PreviewCryptoSender.swift */; }; C6B5136228320707001E99B5 /* EnterBackupToRestoreWalletsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6B5136128320707001E99B5 /* EnterBackupToRestoreWalletsPresenter.swift */; }; C6B5136828322EA1001E99B5 /* MainWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6B5136728322EA1001E99B5 /* MainWindow.swift */; }; C6B540C12BA3F77200A41D42 /* ViewPullUpListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6B540C02BA3F77200A41D42 /* ViewPullUpListItemView.swift */; }; @@ -3587,6 +3589,8 @@ C6B40E8F2B5F83370038CEB0 /* UserProfileSelectionRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileSelectionRowView.swift; sourceTree = ""; }; C6B434FF2A52884F00BC644B /* MessagingPrivateChatBlockingStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagingPrivateChatBlockingStatus.swift; sourceTree = ""; }; C6B435062A53F42400BC644B /* BaseDiffableCollectionViewControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseDiffableCollectionViewControllerProtocol.swift; sourceTree = ""; }; + C6B45DAF2BBA97B600B44C33 /* CryptoSender+Entities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CryptoSender+Entities.swift"; sourceTree = ""; }; + C6B45DB22BBA97FD00B44C33 /* PreviewCryptoSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewCryptoSender.swift; sourceTree = ""; }; C6B5136128320707001E99B5 /* EnterBackupToRestoreWalletsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnterBackupToRestoreWalletsPresenter.swift; sourceTree = ""; }; C6B5136728322EA1001E99B5 /* MainWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainWindow.swift; sourceTree = ""; }; C6B540C02BA3F77200A41D42 /* ViewPullUpListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewPullUpListItemView.swift; sourceTree = ""; }; @@ -5044,6 +5048,7 @@ C61808292B19ACF20032E543 /* PreviewAPIRequest.swift */, C6D646232B1DC28600D724AC /* PreviewAppReviewService.swift */, C6D645B12B1DBBCE00D724AC /* PreviewConnectedAppsImageCache.swift */, + C6B45DB22BBA97FD00B44C33 /* PreviewCryptoSender.swift */, C61807BA2B199FD10032E543 /* PreviewData.swift */, C6960C302B1994EC00B79E28 /* PreviewDebugger.swift */, C6960C652B199B6F00B79E28 /* PreviewDomainItem.swift */, @@ -6415,9 +6420,7 @@ C67213E72BAA98EC0075B9C7 /* SelectAssetToSend */, C62900F32BAAC821008B35A2 /* SelectTokenAssetAmount */, C6F4333A2BB2598A000C5E46 /* ConfirmSend */, - 29AA6AA32BAAE9F500D24FB5 /* CryptoSenderProtocol.swift */, - 29AA6AA72BAC389D00D24FB5 /* CryptoSender.swift */, - 29018C8C2BACB7BC0004545D /* JRPC_Client.swift */, + C6B45DAE2BBA978900B44C33 /* CryptoSender */, ); path = SendCryptoAsset; sourceTree = ""; @@ -7321,6 +7324,17 @@ path = UserProfileSelectionView; sourceTree = ""; }; + C6B45DAE2BBA978900B44C33 /* CryptoSender */ = { + isa = PBXGroup; + children = ( + 29AA6AA32BAAE9F500D24FB5 /* CryptoSenderProtocol.swift */, + C6B45DAF2BBA97B600B44C33 /* CryptoSender+Entities.swift */, + 29AA6AA72BAC389D00D24FB5 /* CryptoSender.swift */, + 29018C8C2BACB7BC0004545D /* JRPC_Client.swift */, + ); + path = CryptoSender; + sourceTree = ""; + }; C6B51366283225C6001E99B5 /* Entities */ = { isa = PBXGroup; children = ( @@ -9126,6 +9140,7 @@ 3026FA1C27E76F6600FE058B /* ProtectWalletViewController.swift in Sources */, C6F9FBC52A25C38900102F81 /* MessagingAPIServiceProtocol.swift in Sources */, C671E3C829014D7700A2B3A0 /* RaisedWhiteButton.swift in Sources */, + C6B45DB02BBA97B600B44C33 /* CryptoSender+Entities.swift in Sources */, C62396B32A28DE3B00363F60 /* PushEnvironment.swift in Sources */, C6E1189928F8391800C6AD4D /* DomainProfileViewControllerItemsEntities.swift in Sources */, C6B6859028007C09000F9037 /* UDCheckBox.swift in Sources */, @@ -9950,7 +9965,6 @@ C6B6B8732B91A4F900565ED2 /* TaskWithDeadlineTests.swift in Sources */, C6B2E2142B9714B100CEA1F9 /* DomainProfilesServiceTests.swift in Sources */, C6DF9870290F83020098733A /* testUpdateRecords.swift in Sources */, - 29AA6AA92BAC389D00D24FB5 /* CryptoSender.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -10048,6 +10062,7 @@ C6C8F8352B217E9600A9834D /* CreateLocalWalletRecoveryWordsPresenter.swift in Sources */, C6D645E82B1DBE6B00D724AC /* RaisedTertiaryButton.swift in Sources */, C62AC2032BABF532003FB69E /* ConfirmSendAssetReviewInfoView.swift in Sources */, + C6B45DB42BBA97FD00B44C33 /* PreviewCryptoSender.swift in Sources */, C61807E52B19A5890032E543 /* MintingDomain.swift in Sources */, C6D647602B1EDDBD00D724AC /* BaseTransactionInProgressViewPresenter.swift in Sources */, C6C8F8A92B2182CF00A9834D /* MintDomainsConfigurationListHeaderView.swift in Sources */, @@ -10437,6 +10452,7 @@ C6D645CC2B1DBD3C00D724AC /* CNavigationControllerChildNavigationHandler.swift in Sources */, C61808772B19BC290032E543 /* UnstoppableListRowInset.swift in Sources */, C6D647412B1EDA1900D724AC /* WalletConnectServiceV2+Entities.swift in Sources */, + C6B45DB12BBA97B600B44C33 /* CryptoSender+Entities.swift in Sources */, C61808612B19BBFE0032E543 /* PositionObservingView.swift in Sources */, C6960C622B199AE600B79E28 /* String+Localization.swift in Sources */, C6B65FAF2B580F40006D1812 /* WalletWithInfo.swift in Sources */, diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender+Entities.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender+Entities.swift new file mode 100644 index 000000000..0ac554732 --- /dev/null +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender+Entities.swift @@ -0,0 +1,104 @@ +// +// CryptoSender+Entities.swift +// domains-manager-ios +// +// Created by Oleg Kuplin on 01.04.2024. +// + +import Foundation + +// Unified container for the token amount. +// Init with units, gwei's or wei's +// Read in units, gwei's or wei's +struct EVMTokenAmount { + static let Billion = 1_000_000_000.0 + private let gweiTotal: Double + + init(units: Double) { + self.gweiTotal = units * Self.Billion + } + + init(gwei: Double) { + self.gweiTotal = gwei + } + + init(gwei: Int) { + self.gweiTotal = Double(gwei) + } + + init(wei: BigUInt) { + self.gweiTotal = Double(wei) / Self.Billion + } + + var units: Double { + gweiTotal / Self.Billion + } + + var gwei: Double { + gweiTotal + } + + var wei: BigUInt { // can only be integer and may be very big + BigUInt(gweiTotal * Self.Billion) + } +} + +struct EstimatedGasPrices { + let normal: EVMTokenAmount + let fast: EVMTokenAmount + let urgent: EVMTokenAmount + + func getPriceForSpeed(_ txSpeed: CryptoSendingSpec.TxSpeed) -> EVMTokenAmount { + switch txSpeed { + case .normal: + return normal + case .fast: + return fast + case .urgent: + return urgent + } + } +} + +struct ChainSpec { + let blockchainType: BlockchainType + let env: UnsConfigManager.BlockchainEnvironment + + init(blockchainType: BlockchainType, env: UnsConfigManager.BlockchainEnvironment = .mainnet) { + self.blockchainType = blockchainType + self.env = env + } + + var id: Int { + self.blockchainType.supportedChainId(env: self.env) + } +} + +struct CryptoSendingSpec { + enum TxSpeed { + case normal, fast, urgent + } + + let token: CryptoSender.SupportedToken + let amount: EVMTokenAmount + let speed: TxSpeed + + init(token: CryptoSender.SupportedToken, amount: EVMTokenAmount, speed: TxSpeed = .normal) { + self.token = token + self.amount = amount + self.speed = speed + } +} + +extension CryptoSender { + enum Error: Swift.Error { + case sendingNotSupported + case failedFetchGasPrice + case insufficientFunds + } + + enum SupportedToken: String { + case eth = "ETH" + case matic = "MATIC" + } +} diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender.swift similarity index 85% rename from unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift rename to unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender.swift index d5182d850..7d3dbd6f3 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender.swift @@ -9,48 +9,8 @@ import Foundation import Boilertalk_Web3 import BigInt -struct ChainSpec { - let blockchainType: BlockchainType - let env: UnsConfigManager.BlockchainEnvironment - - init(blockchainType: BlockchainType, env: UnsConfigManager.BlockchainEnvironment = .mainnet) { - self.blockchainType = blockchainType - self.env = env - } - - var id: Int { - self.blockchainType.supportedChainId(env: self.env) - } -} - -struct CryptoSendingSpec { - enum TxSpeed { - case normal, fast, urgent - } - - let token: CryptoSender.SupportedToken - let amount: EVMTokenAmount - let speed: TxSpeed - - init(token: CryptoSender.SupportedToken, amount: EVMTokenAmount, speed: TxSpeed = .normal) { - self.token = token - self.amount = amount - self.speed = speed - } -} - struct CryptoSender: CryptoSenderProtocol { - enum Error: Swift.Error { - case sendingNotSupported - case failedFetchGasPrice - case insufficientFunds - } - - enum SupportedToken: String { - case eth = "ETH" - case matic = "MATIC" - } - + let wallet: UDWallet func canSendCrypto(token: CryptoSender.SupportedToken, chainType: BlockchainType) -> Bool { diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSenderProtocol.swift similarity index 58% rename from unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift rename to unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSenderProtocol.swift index e4969f675..5b7b10bdd 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSenderProtocol.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSenderProtocol.swift @@ -6,61 +6,6 @@ // import Foundation -import BigInt - -// Unified container for the token amount. -// Init with units, gwei's or wei's -// Read in units, gwei's or wei's -struct EVMTokenAmount { - static let Billion = 1_000_000_000.0 - private let gweiTotal: Double - - init(units: Double) { - self.gweiTotal = units * Self.Billion - } - - init(gwei: Double) { - self.gweiTotal = gwei - } - - init(gwei: Int) { - self.gweiTotal = Double(gwei) - } - - init(wei: BigUInt) { - self.gweiTotal = Double(wei) / Self.Billion - } - - var units: Double { - gweiTotal / Self.Billion - } - - var gwei: Double { - gweiTotal - } - - var wei: BigUInt { // can only be integer and may be very big - BigUInt(gweiTotal * Self.Billion) - } -} - -struct EstimatedGasPrices { - let normal: EVMTokenAmount - let fast: EVMTokenAmount - let urgent: EVMTokenAmount - - func getPriceForSpeed(_ txSpeed: CryptoSendingSpec.TxSpeed) -> EVMTokenAmount { - switch txSpeed { - case .normal: - return normal - case .fast: - return fast - case .urgent: - return urgent - } - } -} - protocol CryptoSenderProtocol { init(wallet: UDWallet) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/JRPC_Client.swift similarity index 100% rename from unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/JRPC_Client.swift rename to unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/JRPC_Client.swift From 37c6b01e8dd1d879036343e57ee0615985521058 Mon Sep 17 00:00:00 2001 From: Oleg Date: Mon, 1 Apr 2024 14:29:46 +0700 Subject: [PATCH 72/80] Updated and localized tx list items --- .../Extensions/Extension-String+Preview.swift | 3 +++ .../Home/Activity/WalletTransactionDisplayInfo.swift | 12 ++++++++---- .../WalletTransactionDisplayInfoListItemView.swift | 10 +++++----- .../Localization/en.lproj/Localizable.strings | 3 +++ 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Extensions/Extension-String+Preview.swift b/unstoppable-ios-app/domains-manager-ios/Extensions/Extension-String+Preview.swift index aa04b2ac7..0e9bbd8b8 100644 --- a/unstoppable-ios-app/domains-manager-ios/Extensions/Extension-String+Preview.swift +++ b/unstoppable-ios-app/domains-manager-ios/Extensions/Extension-String+Preview.swift @@ -1176,6 +1176,9 @@ extension String { static let normal = "NORMAL" static let fast = "FAST" static let urgent = "URGENT" + static let received = "RECEIVED" + static let sent = "SENT" + static let txFee = "TX_FEE" } enum BlockChainIcons: String { diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/WalletTransactionDisplayInfo.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/WalletTransactionDisplayInfo.swift index 755228ca6..c84a49a84 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/WalletTransactionDisplayInfo.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/WalletTransactionDisplayInfo.swift @@ -23,22 +23,26 @@ struct WalletTransactionDisplayInfo: Hashable, Identifiable { struct Participant: Hashable { let address: String - let domainName: String? + let label: String? let link: URL? var displayName: String { - domainName ?? address.walletAddressTruncated + if let label, + label.isValidDomainName() { + return label + } + return address.walletAddressTruncated } init(address: String, domainName: String?, link: URL?) { self.address = address - self.domainName = domainName + self.label = domainName self.link = link } init(serializedParticipant: SerializedWalletTransaction.Participant) { self.address = serializedParticipant.address - self.domainName = serializedParticipant.label + self.label = serializedParticipant.label self.link = URL(string: serializedParticipant.link) } } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/WalletTransactionDisplayInfoListItemView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/WalletTransactionDisplayInfoListItemView.swift index 74c6e7c2f..7ed052455 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/WalletTransactionDisplayInfoListItemView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/WalletTransactionDisplayInfoListItemView.swift @@ -67,18 +67,18 @@ private extension WalletTransactionDisplayInfoListItemView { var transactionTitle: String { switch transaction.type { case .tokenDeposit, .nftDeposit: - "Received" + String.Constants.received.localized() case .tokenWithdrawal, .nftWithdrawal: - "Sent" + String.Constants.sent.localized() } } var sourceText: String { switch transaction.type { case .tokenDeposit, .nftDeposit: - "From \(transaction.from.displayName)" + String.Constants.from.localized() + " \(transaction.from.displayName)" case .tokenWithdrawal, .nftWithdrawal: - "To \(transaction.to.displayName)" + String.Constants.to.localized() + " \(transaction.to.displayName)" } } @@ -112,7 +112,7 @@ private extension WalletTransactionDisplayInfoListItemView { @ViewBuilder func gasFeeLabel() -> some View { if transaction.gas > 0 { - Text("-\(transaction.gas.formatted(toMaxNumberAfterComa: 4)) Tx fee") + Text("-\(transaction.gas.formatted(toMaxNumberAfterComa: 4)) " + String.Constants.txFee.localized()) .font(.currentFont(size: 14)) .foregroundStyle(Color.foregroundSecondary) } diff --git a/unstoppable-ios-app/domains-manager-ios/SupportingFiles/Localization/en.lproj/Localizable.strings b/unstoppable-ios-app/domains-manager-ios/SupportingFiles/Localization/en.lproj/Localizable.strings index 77ac3d747..76c4f477c 100644 --- a/unstoppable-ios-app/domains-manager-ios/SupportingFiles/Localization/en.lproj/Localizable.strings +++ b/unstoppable-ios-app/domains-manager-ios/SupportingFiles/Localization/en.lproj/Localizable.strings @@ -1076,3 +1076,6 @@ More tabs are coming in the next updates."; "NORMAL" = "Normal"; "FAST" = "Fast"; "URGENT" = "Urgent"; +"RECEIVED" = "Received"; +"SENT" = "Sent"; +"TX_FEE" = "Tx fee"; From 4dece3f5a2347d1e2449b558f1081f572d5dc2e8 Mon Sep 17 00:00:00 2001 From: Oleg Date: Mon, 1 Apr 2024 14:34:00 +0700 Subject: [PATCH 73/80] Adjust UI for long values in tx list --- .../Entities/Mock/MockEntitiesFabric+Txs.swift | 2 +- .../Activity/WalletTransactionDisplayInfoListItemView.swift | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Entities/Mock/MockEntitiesFabric+Txs.swift b/unstoppable-ios-app/domains-manager-ios/Entities/Mock/MockEntitiesFabric+Txs.swift index c21f0595f..df9c8fd18 100644 --- a/unstoppable-ios-app/domains-manager-ios/Entities/Mock/MockEntitiesFabric+Txs.swift +++ b/unstoppable-ios-app/domains-manager-ios/Entities/Mock/MockEntitiesFabric+Txs.swift @@ -59,7 +59,7 @@ extension MockEntitiesFabric { imageUrl: "", symbol: "", type: "", - from: .init(address: "1", label: nil, link: ""), + from: .init(address: "0", label: "ksdjhfskdjfhsdkfjhsdkjfhsdkjfhsdkjfhsdkjfh.x", link: ""), to: .init(address: "2", label: nil, link: "")) } } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/WalletTransactionDisplayInfoListItemView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/WalletTransactionDisplayInfoListItemView.swift index 7ed052455..6ae4bd2d8 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/WalletTransactionDisplayInfoListItemView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/WalletTransactionDisplayInfoListItemView.swift @@ -16,12 +16,13 @@ struct WalletTransactionDisplayInfoListItemView: View { @State private var icon: UIImage? var body: some View { - HStack { + HStack(spacing: 24) { iconView() transactionTypeDetails() Spacer() transactionValueDetails() } + .lineLimit(1) .onAppear(perform: onAppear) } } @@ -61,6 +62,7 @@ private extension WalletTransactionDisplayInfoListItemView { Text(sourceText) .font(.currentFont(size: 14)) .foregroundStyle(Color.foregroundSecondary) + .truncationMode(.middle) } } From aa8d09149cee47bc673f491a26e171eec7d75cdc Mon Sep 17 00:00:00 2001 From: Oleg Date: Mon, 1 Apr 2024 14:40:06 +0700 Subject: [PATCH 74/80] Fixed targets --- .../Entities/PreviewCryptoSender.swift | 2 +- .../CryptoSender/CryptoSender+Entities.swift | 6 +++--- .../Home/SendCryptoAsset/CryptoSender/CryptoSender.swift | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios-preview/Entities/PreviewCryptoSender.swift b/unstoppable-ios-app/domains-manager-ios-preview/Entities/PreviewCryptoSender.swift index 266243502..fd0ddb649 100644 --- a/unstoppable-ios-app/domains-manager-ios-preview/Entities/PreviewCryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios-preview/Entities/PreviewCryptoSender.swift @@ -7,7 +7,7 @@ import Foundation -typealias BigUInt = Int +typealias UDBigUInt = Int struct CryptoSender: CryptoSenderProtocol { diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender+Entities.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender+Entities.swift index 0ac554732..a4cd883e1 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender+Entities.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender+Entities.swift @@ -26,7 +26,7 @@ struct EVMTokenAmount { self.gweiTotal = Double(gwei) } - init(wei: BigUInt) { + init(wei: UDBigUInt) { self.gweiTotal = Double(wei) / Self.Billion } @@ -38,8 +38,8 @@ struct EVMTokenAmount { gweiTotal } - var wei: BigUInt { // can only be integer and may be very big - BigUInt(gweiTotal * Self.Billion) + var wei: UDBigUInt { // can only be integer and may be very big + UDBigUInt(gweiTotal * Self.Billion) } } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender.swift index 7d3dbd6f3..a1ba5979c 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender.swift @@ -9,6 +9,8 @@ import Foundation import Boilertalk_Web3 import BigInt +typealias UDBigUInt = BigUInt + struct CryptoSender: CryptoSenderProtocol { let wallet: UDWallet From 5ba8f9098856c365b357ea0445576127a6629317 Mon Sep 17 00:00:00 2001 From: Oleg Date: Mon, 1 Apr 2024 14:40:19 +0700 Subject: [PATCH 75/80] Extend tx id --- .../Modules/Home/Activity/WalletTransactionDisplayInfo.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/WalletTransactionDisplayInfo.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/WalletTransactionDisplayInfo.swift index c84a49a84..7cd7da667 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/WalletTransactionDisplayInfo.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/WalletTransactionDisplayInfo.swift @@ -53,7 +53,7 @@ struct WalletTransactionDisplayInfo: Hashable, Identifiable { extension WalletTransactionDisplayInfo { init(serializedTransaction: SerializedWalletTransaction, userWallet: String) { - self.id = serializedTransaction.id + self.id = serializedTransaction.id + serializedTransaction.method self.time = serializedTransaction.timestamp self.success = serializedTransaction.success self.value = serializedTransaction.value From 3d38268a5938f415cc10c3c0368f65c3f8a18ca3 Mon Sep 17 00:00:00 2001 From: Oleg Date: Mon, 1 Apr 2024 14:51:37 +0700 Subject: [PATCH 76/80] Updated tx icons ui --- .../WalletTransactionDisplayInfoListItemView.swift | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/WalletTransactionDisplayInfoListItemView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/WalletTransactionDisplayInfoListItemView.swift index 6ae4bd2d8..13df7e8a5 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/WalletTransactionDisplayInfoListItemView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/Activity/WalletTransactionDisplayInfoListItemView.swift @@ -47,10 +47,21 @@ private extension WalletTransactionDisplayInfoListItemView { private extension WalletTransactionDisplayInfoListItemView { @ViewBuilder func iconView() -> some View { + switch transaction.type { + case .tokenDeposit, .tokenWithdrawal: + currentIconView() + .clipShape(Circle()) + case .nftDeposit, .nftWithdrawal: + currentIconView() + .clipShape(RoundedRectangle(cornerRadius: 8)) + } + } + + @ViewBuilder + func currentIconView() -> some View { Image(uiImage: icon ?? .appleIcon) .resizable() .squareFrame(40) - .clipShape(Circle()) } @ViewBuilder From bdefadc9431dbd2d757d0f8ba75871ff590455a8 Mon Sep 17 00:00:00 2001 From: rommex Date: Tue, 2 Apr 2024 13:27:52 +0300 Subject: [PATCH 77/80] bump version to 5.3.0(1) --- .../domains-manager-ios.xcodeproj/project.pbxproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj index db6c477d9..3191b146d 100644 --- a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj +++ b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj @@ -11227,7 +11227,7 @@ ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CODE_SIGN_ENTITLEMENTS = "domains-manager-ios/entitlements/domains-manager-iosDebug.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 22QSRWWPVJ; INFOPLIST_FILE = "Dev-Env-Info.plist"; INFOPLIST_KEY_CFBundleDisplayName = Unstoppable; @@ -11237,7 +11237,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 5.0.0; + MARKETING_VERSION = 5.3.0; MERGED_BINARY_TYPE = automatic; PRODUCT_BUNDLE_IDENTIFIER = com.unstoppabledomains.manager; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -11261,7 +11261,7 @@ CODE_SIGN_ENTITLEMENTS = "domains-manager-ios/entitlements/domains-manager-iosRelease.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 22QSRWWPVJ; INFOPLIST_FILE = "domains-manager-ios/Info.plist"; INFOPLIST_KEY_CFBundleDisplayName = Unstoppable; @@ -11271,7 +11271,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 5.0.0; + MARKETING_VERSION = 5.3.0; MERGED_BINARY_TYPE = automatic; PRODUCT_BUNDLE_IDENTIFIER = com.unstoppabledomains.manager; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -11437,7 +11437,7 @@ CODE_SIGN_ENTITLEMENTS = "domains-manager-ios/entitlements/domains-manager-iosRelease.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 22QSRWWPVJ; INFOPLIST_FILE = "domains-manager-ios/Info.plist"; INFOPLIST_KEY_CFBundleDisplayName = Unstoppable; @@ -11447,7 +11447,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 5.0.0; + MARKETING_VERSION = 5.3.0; MERGED_BINARY_TYPE = automatic; PRODUCT_BUNDLE_IDENTIFIER = com.unstoppabledomains.manager; PRODUCT_NAME = "$(TARGET_NAME)"; From 8c43ec256fb97f170c2291cc48cb244076cf075d Mon Sep 17 00:00:00 2001 From: rommex Date: Tue, 2 Apr 2024 16:54:24 +0300 Subject: [PATCH 78/80] added fetchInfuraGasPrices() --- .../Services/Networking/NetworkService.swift | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift index 1c54cacd6..202b32820 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift @@ -316,6 +316,7 @@ extension NetworkService { case genericError(String) case failedGetStatus case failedParseStatusPrices + case failedParseInfuraPrices case unknownChain init(message: String) { @@ -385,6 +386,31 @@ extension NetworkService { return ["ETH": ethPrices, "MATIC": maticPrices] } + enum InfuraSpeedCase: String, CaseIterable { + case low, medium, high + } + + func fetchInfuraGasPrices(chain: ChainSpec) async throws -> EstimatedGasPrices { + let url = URL(string: "https://gas.api.infura.io/networks/\(chain.id)/suggestedGasFees")! + let data = try await NetworkService().fetchData(for: url, method: .get, extraHeaders: ["Authorization": "Basic ODdmOWFlNjcxNmI1NGYyNTkyYjU2YTNkYTI5MDc1MmM6NWFkZThkNTRjNWQyNGRjMjhlN2U0ZGYwZDI1OTMwNDE="]) + let jsonInf = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any] + + var priceDict: [InfuraSpeedCase: Double] = [:] + try Self.InfuraSpeedCase.allCases.forEach { + guard let section = jsonInf[$0.rawValue] as? [String: Any], + let priceString = section["suggestedMaxFeePerGas"] as? String, + let price = Double(priceString) else { + throw JRPCError.failedParseInfuraPrices + } + priceDict[$0] = price + } + assert(priceDict.count == Self.InfuraSpeedCase.allCases.count) // always true after forEach + + return EstimatedGasPrices(normal: EVMTokenAmount(gwei: priceDict[InfuraSpeedCase.low]!), + fast: EVMTokenAmount(gwei: priceDict[InfuraSpeedCase.medium]!), + urgent: EVMTokenAmount(gwei: priceDict[InfuraSpeedCase.high]!)) + } + func fetchGasPrice(chainId: Int, for speed: CryptoSendingSpec.TxSpeed) async throws -> EVMTokenAmount { let prices: EstimatedGasPrices = try await getStatusGasPrices(chainId: chainId) return prices.getPriceForSpeed(speed) From 75417a85fa83bbb04e25b8c7401c657c30259bb9 Mon Sep 17 00:00:00 2001 From: rommex Date: Tue, 2 Apr 2024 18:26:14 +0300 Subject: [PATCH 79/80] refactoring --- .../CryptoSender/CryptoSender.swift | 17 +++++++++++++---- .../Services/Networking/NetworkService.swift | 11 +++++------ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender.swift index a1ba5979c..2b5364e16 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Home/SendCryptoAsset/CryptoSender/CryptoSender.swift @@ -96,9 +96,9 @@ struct NativeCryptoSender: CryptoSenderProtocol { } func fetchGasPrices(on chain: ChainSpec) async throws -> EstimatedGasPrices { - try await NetworkService().getStatusGasPrices(chainId: chain.id) + try await fetchGasPrices(chainId: chain.id) } - + // Private methods private func createNativeSendTransaction(crypto: CryptoSendingSpec, @@ -107,8 +107,7 @@ struct NativeCryptoSender: CryptoSenderProtocol { chainId: Int) async throws -> EthereumTransaction { let nonce: EthereumQuantity = try await JRPC_Client.instance.fetchNonce(address: fromAddress, chainId: chainId) - let speedBasedGasPrice = try await NetworkService().fetchGasPrice(chainId: chainId, - for: crypto.speed) + let speedBasedGasPrice = try await fetchGasPrice(chainId: chainId, for: crypto.speed) let sender = EthereumAddress(hexString: fromAddress) let receiver = EthereumAddress(hexString: toAddress) @@ -126,4 +125,14 @@ struct NativeCryptoSender: CryptoSenderProtocol { } return transaction } + + private func fetchGasPrice(chainId: Int, for speed: CryptoSendingSpec.TxSpeed) async throws -> EVMTokenAmount { + let prices: EstimatedGasPrices = try await fetchGasPrices(chainId: chainId) + return prices.getPriceForSpeed(speed) + } + + private func fetchGasPrices(chainId: Int) async throws -> EstimatedGasPrices { + // here routes to Status or Infura source + try await NetworkService().fetchInfuraGasPrices(chainId: chainId) + } } diff --git a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift index 202b32820..592072a4f 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift @@ -391,7 +391,11 @@ extension NetworkService { } func fetchInfuraGasPrices(chain: ChainSpec) async throws -> EstimatedGasPrices { - let url = URL(string: "https://gas.api.infura.io/networks/\(chain.id)/suggestedGasFees")! + try await fetchInfuraGasPrices(chainId: chain.id) + } + + func fetchInfuraGasPrices(chainId: Int) async throws -> EstimatedGasPrices { + let url = URL(string: "https://gas.api.infura.io/networks/\(chainId)/suggestedGasFees")! let data = try await NetworkService().fetchData(for: url, method: .get, extraHeaders: ["Authorization": "Basic ODdmOWFlNjcxNmI1NGYyNTkyYjU2YTNkYTI5MDc1MmM6NWFkZThkNTRjNWQyNGRjMjhlN2U0ZGYwZDI1OTMwNDE="]) let jsonInf = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any] @@ -411,11 +415,6 @@ extension NetworkService { urgent: EVMTokenAmount(gwei: priceDict[InfuraSpeedCase.high]!)) } - func fetchGasPrice(chainId: Int, for speed: CryptoSendingSpec.TxSpeed) async throws -> EVMTokenAmount { - let prices: EstimatedGasPrices = try await getStatusGasPrices(chainId: chainId) - return prices.getPriceForSpeed(speed) - } - func getStatusGasPrices(chainId: Int) async throws -> EstimatedGasPrices { let prices: [String: Int] = try await getStatusGasPrices(chainId: chainId) From b1755cb82da11e1c890877f485373c959e8afdd2 Mon Sep 17 00:00:00 2001 From: rommex Date: Tue, 2 Apr 2024 19:28:46 +0300 Subject: [PATCH 80/80] rm auth key --- .../Services/Networking/NetworkService.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift index 592072a4f..b4698ec08 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift @@ -396,7 +396,7 @@ extension NetworkService { func fetchInfuraGasPrices(chainId: Int) async throws -> EstimatedGasPrices { let url = URL(string: "https://gas.api.infura.io/networks/\(chainId)/suggestedGasFees")! - let data = try await NetworkService().fetchData(for: url, method: .get, extraHeaders: ["Authorization": "Basic ODdmOWFlNjcxNmI1NGYyNTkyYjU2YTNkYTI5MDc1MmM6NWFkZThkNTRjNWQyNGRjMjhlN2U0ZGYwZDI1OTMwNDE="]) + let data = try await NetworkService().fetchData(for: url, method: .get, extraHeaders: Self.infuraBasicAuthHeader) let jsonInf = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any] var priceDict: [InfuraSpeedCase: Double] = [:]