diff --git a/Adamant.xcodeproj/project.pbxproj b/Adamant.xcodeproj/project.pbxproj index 19cfcd91d..40e0a9baa 100644 --- a/Adamant.xcodeproj/project.pbxproj +++ b/Adamant.xcodeproj/project.pbxproj @@ -189,15 +189,11 @@ 41E3C9CC2A0E20F500AF0985 /* AdamantCoinTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41E3C9CB2A0E20F500AF0985 /* AdamantCoinTools.swift */; }; 4E9EE86F28CE793D008359F7 /* SafeDecimalRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E9EE86E28CE793D008359F7 /* SafeDecimalRow.swift */; }; 551F66E628959A5300DE5D69 /* LoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 551F66E528959A5200DE5D69 /* LoadingView.swift */; }; - 551F66E82895B3DA00DE5D69 /* AdamantHealthCheckServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 551F66E72895B3DA00DE5D69 /* AdamantHealthCheckServiceTests.swift */; }; - 5551CC8F28A8B75300B52AD0 /* ApiServiceStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5551CC8E28A8B75300B52AD0 /* ApiServiceStub.swift */; }; 557AC306287B10D8004699D7 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 557AC305287B10D8004699D7 /* SnapKit */; }; 557AC308287B1365004699D7 /* CheckmarkRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 557AC307287B1365004699D7 /* CheckmarkRowView.swift */; }; 55D1D84F287B78F200F94A4E /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 55D1D84E287B78F200F94A4E /* SnapKit */; }; 55D1D851287B78FC00F94A4E /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 55D1D850287B78FC00F94A4E /* SnapKit */; }; - 55D1D855287B890300F94A4E /* AddressGeneratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55D1D854287B890300F94A4E /* AddressGeneratorTests.swift */; }; 55E69E172868D7920025D82E /* CheckmarkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E69E162868D7920025D82E /* CheckmarkView.swift */; }; - 55FBAAFB28C550920066E629 /* NodesAllowanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55FBAAFA28C550920066E629 /* NodesAllowanceTests.swift */; }; 6403F5DB2272389800D58779 /* (null) in Sources */ = {isa = PBXBuildFile; }; 6403F5DE22723C6800D58779 /* DashMainnet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6403F5DD22723C6800D58779 /* DashMainnet.swift */; }; 6403F5E022723F6400D58779 /* DashWalletFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6403F5DF22723F6400D58779 /* DashWalletFactory.swift */; }; @@ -464,6 +460,18 @@ A5F929AF262C857D00C3E60A /* MarkdownKit in Frameworks */ = {isa = PBXBuildFile; productRef = A5F929AE262C857D00C3E60A /* MarkdownKit */; }; A5F929B6262C858700C3E60A /* MarkdownKit in Frameworks */ = {isa = PBXBuildFile; productRef = A5F929B5262C858700C3E60A /* MarkdownKit */; }; A5F929B8262C858F00C3E60A /* MarkdownKit in Frameworks */ = {isa = PBXBuildFile; productRef = A5F929B7262C858F00C3E60A /* MarkdownKit */; }; + AA33BEB22D303C730083E59C /* BtcWalletServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA33BEB12D303C5F0083E59C /* BtcWalletServiceTests.swift */; }; + AA33BEB62D303E240083E59C /* APICoreProtocolMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA33BEB52D303DB60083E59C /* APICoreProtocolMock.swift */; }; + AA33BEB72D3041A30083E59C /* AddressConverterMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA33BEB42D303CBD0083E59C /* AddressConverterMock.swift */; }; + AA33BEB92D3044760083E59C /* BtcApiServiceProtocolMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA33BEB82D30446F0083E59C /* BtcApiServiceProtocolMock.swift */; }; + AAFB3C8B2D31C0DD000CCCE9 /* Actor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAFB3C8A2D31C0D0000CCCE9 /* Actor+Extensions.swift */; }; + AAFB3C8D2D31C0EE000CCCE9 /* Result+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAFB3C8C2D31C0EA000CCCE9 /* Result+Extensions.swift */; }; + AAFB3C8F2D31C119000CCCE9 /* WalletServiceError+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAFB3C8E2D31C110000CCCE9 /* WalletServiceError+Equatable.swift */; }; + AAFB3C912D31C14A000CCCE9 /* BitcoinKitTransaction+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAFB3C902D31C140000CCCE9 /* BitcoinKitTransaction+Equatable.swift */; }; + AAFB3C932D31C3DC000CCCE9 /* BitcoinKitTransactionFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAFB3C922D31C3CD000CCCE9 /* BitcoinKitTransactionFactoryProtocol.swift */; }; + AAFB3C952D31C58B000CCCE9 /* BitcoinKitTransactionFactoryProtocolMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAFB3C942D31C587000CCCE9 /* BitcoinKitTransactionFactoryProtocolMock.swift */; }; + AAFB3C972D31CB72000CCCE9 /* UnspentTransaction+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAFB3C962D31CB6B000CCCE9 /* UnspentTransaction+Equatable.swift */; }; + AAFB3C992D357E1D000CCCE9 /* BtcWalletServiceIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAFB3C982D357E16000CCCE9 /* BtcWalletServiceIntegrationTests.swift */; }; E90055F520EBF5DA00D0CB2D /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E90055F420EBF5DA00D0CB2D /* AboutViewController.swift */; }; E90055F720EC200900D0CB2D /* SecurityViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E90055F620EC200900D0CB2D /* SecurityViewController.swift */; }; E90055F920ECD86800D0CB2D /* SecurityViewController+StayIn.swift in Sources */ = {isa = PBXBuildFile; fileRef = E90055F820ECD86800D0CB2D /* SecurityViewController+StayIn.swift */; }; @@ -540,13 +548,11 @@ E9484B79227C617E008E10F0 /* BalanceTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9484B77227C617D008E10F0 /* BalanceTableViewCell.swift */; }; E9484B7A227CA93B008E10F0 /* BalanceTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E9484B78227C617E008E10F0 /* BalanceTableViewCell.xib */; }; E9484B7D2285BAD9008E10F0 /* PrivateKeyGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9484B7C2285BAD8008E10F0 /* PrivateKeyGenerator.swift */; }; - E94883E7203F07CD00F6E1B0 /* PassphraseValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E94883E6203F07CD00F6E1B0 /* PassphraseValidation.swift */; }; E948E03B20235E2300975D6B /* SettingsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = E948E03A20235E2300975D6B /* SettingsFactory.swift */; }; E94E7B01205D3F090042B639 /* ChatListViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E94E7B00205D3F090042B639 /* ChatListViewController.xib */; }; E94E7B08205D4CB80042B639 /* ShareQRFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = E94E7B07205D4CB80042B639 /* ShareQRFactory.swift */; }; E94E7B0C205D5E4A0042B639 /* TransactionsListViewControllerBase.xib in Resources */ = {isa = PBXBuildFile; fileRef = E94E7B0B205D5E4A0042B639 /* TransactionsListViewControllerBase.xib */; }; E9502740202E257E002C1098 /* RepeaterService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E950273F202E257E002C1098 /* RepeaterService.swift */; }; - E950652120404BF0008352E5 /* AdamantUriBuilding.swift in Sources */ = {isa = PBXBuildFile; fileRef = E950652020404BF0008352E5 /* AdamantUriBuilding.swift */; }; E950652320404C84008352E5 /* AdamantUriTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = E950652220404C84008352E5 /* AdamantUriTools.swift */; }; E957E107229AF7CB0019732A /* adamant_notificationContent.png in Resources */ = {isa = PBXBuildFile; fileRef = E957E103229AF7CA0019732A /* adamant_notificationContent.png */; }; E957E108229AF7CB0019732A /* doge_notificationContent.png in Resources */ = {isa = PBXBuildFile; fileRef = E957E104229AF7CA0019732A /* doge_notificationContent.png */; }; @@ -557,27 +563,13 @@ E957E132229B10F80019732A /* NotificationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E957E131229B10F80019732A /* NotificationViewController.swift */; }; E957E135229B10F80019732A /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E957E133229B10F80019732A /* MainInterface.storyboard */; }; E957E139229B10F80019732A /* TransferNotificationContentExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = E957E12D229B10F80019732A /* TransferNotificationContentExtension.appex */; platformFilter = ios; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - E95F85712007D98D0070534A /* CurrencyFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85702007D98D0070534A /* CurrencyFormatterTests.swift */; }; - E95F85752007E4790070534A /* HexAndBytesUtilitiesTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85742007E4790070534A /* HexAndBytesUtilitiesTest.swift */; }; E95F85802008C8D70070534A /* ChatListFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F857E2008C8D60070534A /* ChatListFactory.swift */; }; E95F85852008CB3A0070534A /* ChatListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85842008CB3A0070534A /* ChatListViewController.swift */; }; - E95F85B7200A4D8F0070534A /* TestTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85B6200A4D8F0070534A /* TestTools.swift */; }; - E95F85BC200A4E670070534A /* ParsingModelsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85BB200A4E670070534A /* ParsingModelsTests.swift */; }; - E95F85C0200A51BB0070534A /* Account.json in Resources */ = {isa = PBXBuildFile; fileRef = E95F85BF200A51BB0070534A /* Account.json */; }; E95F85C7200A9B070070534A /* ChatTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85C5200A9B070070534A /* ChatTableViewCell.swift */; }; E95F85C8200A9B070070534A /* ChatTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E95F85C6200A9B070070534A /* ChatTableViewCell.xib */; }; E96BBE3121F70F5E009AA738 /* ReadonlyTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E96BBE3021F70F5E009AA738 /* ReadonlyTextView.swift */; }; E96BBE3321F71290009AA738 /* BuyAndSellViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E96BBE3221F71290009AA738 /* BuyAndSellViewController.swift */; }; - E96D64BE2295C06400CA5587 /* JSAdamantCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9220E0221983155009C9642 /* JSAdamantCore.swift */; }; - E96D64BF2295C06400CA5587 /* adamant-core.js in Resources */ = {isa = PBXBuildFile; fileRef = E9220E0121983155009C9642 /* adamant-core.js */; }; - E96D64C02295C06400CA5587 /* JSModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F856A200789450070534A /* JSModels.swift */; }; - E96D64C12295C06400CA5587 /* JSAdamantCoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95F85762007E8EC0070534A /* JSAdamantCoreTests.swift */; }; - E96D64C22295C06400CA5587 /* NativeCoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9220E07219879B9009C9642 /* NativeCoreTests.swift */; }; E96D64C82295C44400CA5587 /* Data+utilites.swift in Sources */ = {isa = PBXBuildFile; fileRef = E96D64C72295C44400CA5587 /* Data+utilites.swift */; }; - E96D64CF2295C82B00CA5587 /* Chat.json in Resources */ = {isa = PBXBuildFile; fileRef = E95F85C3200A540B0070534A /* Chat.json */; }; - E96D64D02295C82B00CA5587 /* NormalizedTransaction.json in Resources */ = {isa = PBXBuildFile; fileRef = E95F85C1200A53E90070534A /* NormalizedTransaction.json */; }; - E96D64D12295C82B00CA5587 /* TransactionChat.json in Resources */ = {isa = PBXBuildFile; fileRef = E95F85BD200A503A0070534A /* TransactionChat.json */; }; - E96D64D22295C82B00CA5587 /* TransactionSend.json in Resources */ = {isa = PBXBuildFile; fileRef = E95F85B9200A4DC90070534A /* TransactionSend.json */; }; E96D64DE2295CD4700CA5587 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E96D64DD2295CD4700CA5587 /* NotificationService.swift */; }; E96D64E22295CD4700CA5587 /* NotificationServiceExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = E96D64DB2295CD4700CA5587 /* NotificationServiceExtension.appex */; platformFilter = ios; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; E96E86B821679C120061F80A /* EthTransactionDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E96E86B721679C120061F80A /* EthTransactionDetailsViewController.swift */; }; @@ -624,7 +616,6 @@ E9B3D3A9202082450019EB36 /* AdamantTransfersProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9B3D3A8202082450019EB36 /* AdamantTransfersProvider.swift */; }; E9B4E1A8210F079E007E77FC /* DoubleDetailsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9B4E1A7210F079E007E77FC /* DoubleDetailsTableViewCell.swift */; }; E9B4E1AA210F1803007E77FC /* DoubleDetailsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = E9B4E1A9210F08BE007E77FC /* DoubleDetailsTableViewCell.xib */; }; - E9C51ECF200E2D1100385EB7 /* FeeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9C51ECE200E2D1100385EB7 /* FeeTests.swift */; }; E9C51EF12013F18000385EB7 /* NewChatViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9C51EF02013F18000385EB7 /* NewChatViewController.swift */; }; E9D1BE1C211DABE100E86B72 /* WalletPagingItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9D1BE1B211DABE100E86B72 /* WalletPagingItem.swift */; }; E9DFB71C21624C9200CF8C7C /* AdmTransactionDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9DFB71B21624C9200CF8C7C /* AdmTransactionDetailsViewController.swift */; }; @@ -641,7 +632,6 @@ E9E7CDC22003F5A400DFC4DB /* TransactionsListViewControllerBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E7CDC12003F5A400DFC4DB /* TransactionsListViewControllerBase.swift */; }; E9E7CDC72003F6D200DFC4DB /* TransactionTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E7CDC52003F6D200DFC4DB /* TransactionTableViewCell.swift */; }; E9EC342120052ABB00C0E546 /* TransferViewControllerBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9EC342020052ABB00C0E546 /* TransferViewControllerBase.swift */; }; - E9EC344720066D4A00C0E546 /* AddressValidationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9EC344620066D4A00C0E546 /* AddressValidationTests.swift */; }; E9FAE5DA203DBFEF008D3A6B /* Comparable+clamped.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9FAE5D9203DBFEF008D3A6B /* Comparable+clamped.swift */; }; E9FAE5E2203ED1AE008D3A6B /* ShareQrViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9FAE5E0203ED1AE008D3A6B /* ShareQrViewController.swift */; }; E9FAE5E3203ED1AE008D3A6B /* ShareQrViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E9FAE5E1203ED1AE008D3A6B /* ShareQrViewController.xib */; }; @@ -1104,6 +1094,18 @@ A5E04226282A8BDC0076CD13 /* BtcBalanceResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BtcBalanceResponse.swift; sourceTree = ""; }; A5E04228282A998C0076CD13 /* BtcTransactionResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BtcTransactionResponse.swift; sourceTree = ""; }; A5E0422A282AB18B0076CD13 /* BtcUnspentTransactionResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BtcUnspentTransactionResponse.swift; sourceTree = ""; }; + AA33BEB12D303C5F0083E59C /* BtcWalletServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BtcWalletServiceTests.swift; sourceTree = ""; }; + AA33BEB42D303CBD0083E59C /* AddressConverterMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressConverterMock.swift; sourceTree = ""; }; + AA33BEB52D303DB60083E59C /* APICoreProtocolMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APICoreProtocolMock.swift; sourceTree = ""; }; + AA33BEB82D30446F0083E59C /* BtcApiServiceProtocolMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BtcApiServiceProtocolMock.swift; sourceTree = ""; }; + AAFB3C8A2D31C0D0000CCCE9 /* Actor+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Actor+Extensions.swift"; sourceTree = ""; }; + AAFB3C8C2D31C0EA000CCCE9 /* Result+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Result+Extensions.swift"; sourceTree = ""; }; + AAFB3C8E2D31C110000CCCE9 /* WalletServiceError+Equatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WalletServiceError+Equatable.swift"; sourceTree = ""; }; + AAFB3C902D31C140000CCCE9 /* BitcoinKitTransaction+Equatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BitcoinKitTransaction+Equatable.swift"; sourceTree = ""; }; + AAFB3C922D31C3CD000CCCE9 /* BitcoinKitTransactionFactoryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BitcoinKitTransactionFactoryProtocol.swift; sourceTree = ""; }; + AAFB3C942D31C587000CCCE9 /* BitcoinKitTransactionFactoryProtocolMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BitcoinKitTransactionFactoryProtocolMock.swift; sourceTree = ""; }; + AAFB3C962D31CB6B000CCCE9 /* UnspentTransaction+Equatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UnspentTransaction+Equatable.swift"; sourceTree = ""; }; + AAFB3C982D357E16000CCCE9 /* BtcWalletServiceIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BtcWalletServiceIntegrationTests.swift; sourceTree = ""; }; AD258997F050B24C0051CC8D /* Pods-Adamant.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Adamant.release.xcconfig"; path = "Target Support Files/Pods-Adamant/Pods-Adamant.release.xcconfig"; sourceTree = ""; }; ADDFD2FA17E41CCBD11A1733 /* Pods-Adamant.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Adamant.debug.xcconfig"; path = "Target Support Files/Pods-Adamant/Pods-Adamant.debug.xcconfig"; sourceTree = ""; }; E90055F420EBF5DA00D0CB2D /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = ""; }; @@ -2184,6 +2186,7 @@ A50A41022822F8CE006BDFE1 /* Bitcoin */ = { isa = PBXGroup; children = ( + AAFB3C922D31C3CD000CCCE9 /* BitcoinKitTransactionFactoryProtocol.swift */, A5E04225282A8BC70076CD13 /* DTO */, A50A41062822F8CE006BDFE1 /* BtcWallet.swift */, 93294B812AAD0BB400911109 /* BtcWalletFactory.swift */, @@ -2213,6 +2216,66 @@ path = DTO; sourceTree = ""; }; + AA33BEAE2D303C260083E59C /* DisabledTests */ = { + isa = PBXGroup; + children = ( + 5551CC8D28A8B72D00B52AD0 /* Stubs */, + E95F85B8200A4D9C0070534A /* Parsing */, + E96D64C42295C0AF00CA5587 /* Core */, + E950652020404BF0008352E5 /* AdamantUriBuilding.swift */, + E9EC344620066D4A00C0E546 /* AddressValidationTests.swift */, + E94883E6203F07CD00F6E1B0 /* PassphraseValidation.swift */, + E95F85702007D98D0070534A /* CurrencyFormatterTests.swift */, + E9C51ECE200E2D1100385EB7 /* FeeTests.swift */, + E95F85742007E4790070534A /* HexAndBytesUtilitiesTest.swift */, + E95F85B6200A4D8F0070534A /* TestTools.swift */, + 55D1D854287B890300F94A4E /* AddressGeneratorTests.swift */, + 551F66E72895B3DA00DE5D69 /* AdamantHealthCheckServiceTests.swift */, + 55FBAAFA28C550920066E629 /* NodesAllowanceTests.swift */, + ); + path = DisabledTests; + sourceTree = ""; + }; + AA33BEAF2D303C410083E59C /* Modules */ = { + isa = PBXGroup; + children = ( + AA33BEB02D303C470083E59C /* Wallets */, + ); + path = Modules; + sourceTree = ""; + }; + AA33BEB02D303C470083E59C /* Wallets */ = { + isa = PBXGroup; + children = ( + AAFB3C982D357E16000CCCE9 /* BtcWalletServiceIntegrationTests.swift */, + AA33BEB12D303C5F0083E59C /* BtcWalletServiceTests.swift */, + ); + path = Wallets; + sourceTree = ""; + }; + AA33BEB32D303CA30083E59C /* Stubs */ = { + isa = PBXGroup; + children = ( + AAFB3C942D31C587000CCCE9 /* BitcoinKitTransactionFactoryProtocolMock.swift */, + AA33BEB82D30446F0083E59C /* BtcApiServiceProtocolMock.swift */, + AA33BEB52D303DB60083E59C /* APICoreProtocolMock.swift */, + AA33BEB42D303CBD0083E59C /* AddressConverterMock.swift */, + ); + path = Stubs; + sourceTree = ""; + }; + AAFB3C892D31C0C4000CCCE9 /* Extensions */ = { + isa = PBXGroup; + children = ( + AAFB3C962D31CB6B000CCCE9 /* UnspentTransaction+Equatable.swift */, + AAFB3C902D31C140000CCCE9 /* BitcoinKitTransaction+Equatable.swift */, + AAFB3C8E2D31C110000CCCE9 /* WalletServiceError+Equatable.swift */, + AAFB3C8C2D31C0EA000CCCE9 /* Result+Extensions.swift */, + AAFB3C8A2D31C0D0000CCCE9 /* Actor+Extensions.swift */, + ); + path = Extensions; + sourceTree = ""; + }; B92CFC9A479739E2046C81E9 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -2812,19 +2875,10 @@ E9EC344520066D4A00C0E546 /* AdamantTests */ = { isa = PBXGroup; children = ( - 5551CC8D28A8B72D00B52AD0 /* Stubs */, - E95F85B8200A4D9C0070534A /* Parsing */, - E96D64C42295C0AF00CA5587 /* Core */, - E950652020404BF0008352E5 /* AdamantUriBuilding.swift */, - E9EC344620066D4A00C0E546 /* AddressValidationTests.swift */, - E94883E6203F07CD00F6E1B0 /* PassphraseValidation.swift */, - E95F85702007D98D0070534A /* CurrencyFormatterTests.swift */, - E9C51ECE200E2D1100385EB7 /* FeeTests.swift */, - E95F85742007E4790070534A /* HexAndBytesUtilitiesTest.swift */, - E95F85B6200A4D8F0070534A /* TestTools.swift */, - 55D1D854287B890300F94A4E /* AddressGeneratorTests.swift */, - 551F66E72895B3DA00DE5D69 /* AdamantHealthCheckServiceTests.swift */, - 55FBAAFA28C550920066E629 /* NodesAllowanceTests.swift */, + AAFB3C892D31C0C4000CCCE9 /* Extensions */, + AA33BEB32D303CA30083E59C /* Stubs */, + AA33BEAF2D303C410083E59C /* Modules */, + AA33BEAE2D303C260083E59C /* DisabledTests */, E9EC344820066D4A00C0E546 /* Info.plist */, ); path = AdamantTests; @@ -2862,8 +2916,8 @@ isa = PBXNativeTarget; buildConfigurationList = E913C9001FFFA51E001A83F7 /* Build configuration list for PBXNativeTarget "Adamant" */; buildPhases = ( - 9372E0412C9BC178006DF0B3 /* Run Script - Git Data */, 47866E9AB7D201F2CED0064C /* [CP] Check Pods Manifest.lock */, + 9372E0412C9BC178006DF0B3 /* Run Script - Git Data */, E913C8EA1FFFA51D001A83F7 /* Sources */, E913C8EB1FFFA51D001A83F7 /* Frameworks */, E913C8EC1FFFA51D001A83F7 /* Resources */, @@ -3207,12 +3261,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - E96D64BF2295C06400CA5587 /* adamant-core.js in Resources */, - E96D64D02295C82B00CA5587 /* NormalizedTransaction.json in Resources */, - E96D64D22295C82B00CA5587 /* TransactionSend.json in Resources */, - E96D64D12295C82B00CA5587 /* TransactionChat.json in Resources */, - E96D64CF2295C82B00CA5587 /* Chat.json in Resources */, - E95F85C0200A51BB0070534A /* Account.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3723,6 +3771,7 @@ E908472D2196FEA80095825D /* CoreDataAccount+CoreDataProperties.swift in Sources */, 64FA53D120E24942006783C9 /* TransactionDetailsViewControllerBase.swift in Sources */, 41047B76294C62710039E956 /* AdamantVisibleWalletsService.swift in Sources */, + AAFB3C932D31C3DC000CCCE9 /* BitcoinKitTransactionFactoryProtocol.swift in Sources */, 93294B982AAD364F00911109 /* AdamantScreensFactory.swift in Sources */, 64BD2B7720E2820300E2CD36 /* TransactionDetails.swift in Sources */, 3A9015A52A614A18002A2464 /* EmojiService.swift in Sources */, @@ -3809,22 +3858,17 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - E9C51ECF200E2D1100385EB7 /* FeeTests.swift in Sources */, - 5551CC8F28A8B75300B52AD0 /* ApiServiceStub.swift in Sources */, - E9EC344720066D4A00C0E546 /* AddressValidationTests.swift in Sources */, - E94883E7203F07CD00F6E1B0 /* PassphraseValidation.swift in Sources */, - E95F85B7200A4D8F0070534A /* TestTools.swift in Sources */, - E95F85BC200A4E670070534A /* ParsingModelsTests.swift in Sources */, - 55FBAAFB28C550920066E629 /* NodesAllowanceTests.swift in Sources */, - E96D64C02295C06400CA5587 /* JSModels.swift in Sources */, - E96D64BE2295C06400CA5587 /* JSAdamantCore.swift in Sources */, - E95F85752007E4790070534A /* HexAndBytesUtilitiesTest.swift in Sources */, - E95F85712007D98D0070534A /* CurrencyFormatterTests.swift in Sources */, - 55D1D855287B890300F94A4E /* AddressGeneratorTests.swift in Sources */, - 551F66E82895B3DA00DE5D69 /* AdamantHealthCheckServiceTests.swift in Sources */, - E96D64C12295C06400CA5587 /* JSAdamantCoreTests.swift in Sources */, - E96D64C22295C06400CA5587 /* NativeCoreTests.swift in Sources */, - E950652120404BF0008352E5 /* AdamantUriBuilding.swift in Sources */, + AAFB3C972D31CB72000CCCE9 /* UnspentTransaction+Equatable.swift in Sources */, + AAFB3C992D357E1D000CCCE9 /* BtcWalletServiceIntegrationTests.swift in Sources */, + AAFB3C8F2D31C119000CCCE9 /* WalletServiceError+Equatable.swift in Sources */, + AA33BEB92D3044760083E59C /* BtcApiServiceProtocolMock.swift in Sources */, + AA33BEB22D303C730083E59C /* BtcWalletServiceTests.swift in Sources */, + AA33BEB72D3041A30083E59C /* AddressConverterMock.swift in Sources */, + AAFB3C8D2D31C0EE000CCCE9 /* Result+Extensions.swift in Sources */, + AA33BEB62D303E240083E59C /* APICoreProtocolMock.swift in Sources */, + AAFB3C912D31C14A000CCCE9 /* BitcoinKitTransaction+Equatable.swift in Sources */, + AAFB3C8B2D31C0DD000CCCE9 /* Actor+Extensions.swift in Sources */, + AAFB3C952D31C58B000CCCE9 /* BitcoinKitTransactionFactoryProtocolMock.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Adamant/App/DI/AppAssembly.swift b/Adamant/App/DI/AppAssembly.swift index 8f0287660..1d9fe12a2 100644 --- a/Adamant/App/DI/AppAssembly.swift +++ b/Adamant/App/DI/AppAssembly.swift @@ -173,6 +173,11 @@ struct AppAssembly: MainThreadAssembly { )) }.inObjectScope(.container) + // MARK: BitcointTransactionFactoryProtocol + container.register(BitcoinKitTransactionFactoryProtocol.self) { _ in + BitcoinKitTransactionFactory() + }.inObjectScope(.transient) + // MARK: DogeApiService container.register(DogeApiService.self) { r in DogeApiService(api: .init( diff --git a/Adamant/Modules/Wallets/Bitcoin/BitcoinKitTransactionFactoryProtocol.swift b/Adamant/Modules/Wallets/Bitcoin/BitcoinKitTransactionFactoryProtocol.swift new file mode 100644 index 000000000..f7e3e75bd --- /dev/null +++ b/Adamant/Modules/Wallets/Bitcoin/BitcoinKitTransactionFactoryProtocol.swift @@ -0,0 +1,43 @@ +// +// BitcoinKitTransactionFactoryProtocol.swift +// Adamant +// +// Created by Christian Benua on 11.01.2025. +// Copyright © 2025 Adamant. All rights reserved. +// + +import BitcoinKit + +protocol BitcoinKitTransactionFactoryProtocol { + func createTransaction( + toAddress: Address, + amount: UInt64, + fee: UInt64, + changeAddress: Address, + utxos: [UnspentTransaction], + lockTime: UInt32, + keys: [PrivateKey] + ) -> Transaction +} + +final class BitcoinKitTransactionFactory: BitcoinKitTransactionFactoryProtocol { + func createTransaction( + toAddress address: Address, + amount: UInt64, + fee: UInt64, + changeAddress: Address, + utxos: [UnspentTransaction], + lockTime: UInt32, + keys: [PrivateKey] + ) -> Transaction { + BitcoinKit.Transaction.createNewTransaction( + toAddress: address, + amount: amount, + fee: fee, + changeAddress: changeAddress, + utxos: utxos, + lockTime: lockTime, + keys: keys + ) + } +} diff --git a/Adamant/Modules/Wallets/Bitcoin/BtcApiService.swift b/Adamant/Modules/Wallets/Bitcoin/BtcApiService.swift index 7c8e9b528..057453d32 100644 --- a/Adamant/Modules/Wallets/Bitcoin/BtcApiService.swift +++ b/Adamant/Modules/Wallets/Bitcoin/BtcApiService.swift @@ -64,7 +64,16 @@ final class BtcApiCore: BlockchainHealthCheckableService, Sendable { } } -final class BtcApiService: ApiServiceProtocol { +protocol BtcApiServiceProtocol: ApiServiceProtocol { + func request( + waitsForConnectivity: Bool, + _ request: @Sendable @escaping (APICoreProtocol, NodeOrigin) async -> ApiServiceResult + ) async -> WalletServiceResult + + func getStatusInfo() async -> WalletServiceResult +} + +final class BtcApiService: BtcApiServiceProtocol { let api: BlockchainHealthCheckWrapper @MainActor diff --git a/Adamant/Modules/Wallets/Bitcoin/BtcWallet.swift b/Adamant/Modules/Wallets/Bitcoin/BtcWallet.swift index d220e1835..57a866dad 100644 --- a/Adamant/Modules/Wallets/Bitcoin/BtcWallet.swift +++ b/Adamant/Modules/Wallets/Bitcoin/BtcWallet.swift @@ -34,4 +34,18 @@ final class BtcWallet: WalletAccount, @unchecked Sendable { self.publicKey = privateKey.publicKey() self.addressEntity = try addressConverter.convert(publicKey: publicKey, type: .p2pkh) } + +#if DEBUG + @available(*, deprecated, message: "For testing purposes only") + init( + unicId: String, + privateKey: PrivateKey, + address: Address + ) { + self.unicId = unicId + self.privateKey = privateKey + self.publicKey = privateKey.publicKey() + self.addressEntity = address + } +#endif } diff --git a/Adamant/Modules/Wallets/Bitcoin/BtcWalletService+Send.swift b/Adamant/Modules/Wallets/Bitcoin/BtcWalletService+Send.swift index 4056fe11d..977ffe2f0 100644 --- a/Adamant/Modules/Wallets/Bitcoin/BtcWalletService+Send.swift +++ b/Adamant/Modules/Wallets/Bitcoin/BtcWalletService+Send.swift @@ -47,12 +47,13 @@ extension BtcWalletService: WalletServiceTwoStepSend { // MARK: 4. Create local transaction - let transaction = BitcoinKit.Transaction.createNewTransaction( + let transaction = btcTransactionFactory.createTransaction( toAddress: toAddress, amount: rawAmount, fee: fee, changeAddress: wallet.addressEntity, utxos: utxos, + lockTime: 0, keys: [key] ) diff --git a/Adamant/Modules/Wallets/Bitcoin/BtcWalletService.swift b/Adamant/Modules/Wallets/Bitcoin/BtcWalletService.swift index 94be6f35e..e5dc6f778 100644 --- a/Adamant/Modules/Wallets/Bitcoin/BtcWalletService.swift +++ b/Adamant/Modules/Wallets/Bitcoin/BtcWalletService.swift @@ -123,7 +123,8 @@ final class BtcWalletService: WalletCoreProtocol, @unchecked Sendable { // MARK: - Dependencies var apiService: AdamantApiServiceProtocol! - var btcApiService: BtcApiService! + var btcApiService: BtcApiServiceProtocol! + var btcTransactionFactory: BitcoinKitTransactionFactoryProtocol! var accountService: AccountService! var dialogService: DialogService! var increaseFeeService: IncreaseFeeService! @@ -518,6 +519,7 @@ extension BtcWalletService: SwinjectDependentService { increaseFeeService = container.resolve(IncreaseFeeService.self) addressConverter = container.resolve(AddressConverterFactory.self)?.make(network: network) btcApiService = container.resolve(BtcApiService.self) + btcTransactionFactory = container.resolve(BitcoinKitTransactionFactoryProtocol.self) vibroService = container.resolve(VibroService.self) coreDataStack = container.resolve(CoreDataStack.self) @@ -777,6 +779,17 @@ extension BtcWalletService: PrivateKeyGenerator { } } +// MARK: test helpers + +#if DEBUG +extension BtcWalletService { + @available(*, deprecated, message: "For testing purposes only") + func setWalletForTests(_ wallet: BtcWallet?) { + self.btcWallet = wallet + } +} +#endif + final class BtcTransaction: BaseBtcTransaction { override var defaultCurrencySymbol: String? { BtcWalletService.currencySymbol } } diff --git a/AdamantTests/AdamantHealthCheckServiceTests.swift b/AdamantTests/DisabledTests/AdamantHealthCheckServiceTests.swift similarity index 100% rename from AdamantTests/AdamantHealthCheckServiceTests.swift rename to AdamantTests/DisabledTests/AdamantHealthCheckServiceTests.swift diff --git a/AdamantTests/AdamantUriBuilding.swift b/AdamantTests/DisabledTests/AdamantUriBuilding.swift similarity index 100% rename from AdamantTests/AdamantUriBuilding.swift rename to AdamantTests/DisabledTests/AdamantUriBuilding.swift diff --git a/AdamantTests/AddressGeneratorTests.swift b/AdamantTests/DisabledTests/AddressGeneratorTests.swift similarity index 100% rename from AdamantTests/AddressGeneratorTests.swift rename to AdamantTests/DisabledTests/AddressGeneratorTests.swift diff --git a/AdamantTests/AddressValidationTests.swift b/AdamantTests/DisabledTests/AddressValidationTests.swift similarity index 100% rename from AdamantTests/AddressValidationTests.swift rename to AdamantTests/DisabledTests/AddressValidationTests.swift diff --git a/AdamantTests/Core/JSAdamantCore.swift b/AdamantTests/DisabledTests/Core/JSAdamantCore.swift similarity index 100% rename from AdamantTests/Core/JSAdamantCore.swift rename to AdamantTests/DisabledTests/Core/JSAdamantCore.swift diff --git a/AdamantTests/Core/JSAdamantCoreTests.swift b/AdamantTests/DisabledTests/Core/JSAdamantCoreTests.swift similarity index 100% rename from AdamantTests/Core/JSAdamantCoreTests.swift rename to AdamantTests/DisabledTests/Core/JSAdamantCoreTests.swift diff --git a/AdamantTests/Core/JSModels.swift b/AdamantTests/DisabledTests/Core/JSModels.swift similarity index 100% rename from AdamantTests/Core/JSModels.swift rename to AdamantTests/DisabledTests/Core/JSModels.swift diff --git a/AdamantTests/Core/NativeCoreTests.swift b/AdamantTests/DisabledTests/Core/NativeCoreTests.swift similarity index 100% rename from AdamantTests/Core/NativeCoreTests.swift rename to AdamantTests/DisabledTests/Core/NativeCoreTests.swift diff --git a/AdamantTests/Core/adamant-core.js b/AdamantTests/DisabledTests/Core/adamant-core.js similarity index 100% rename from AdamantTests/Core/adamant-core.js rename to AdamantTests/DisabledTests/Core/adamant-core.js diff --git a/AdamantTests/CurrencyFormatterTests.swift b/AdamantTests/DisabledTests/CurrencyFormatterTests.swift similarity index 100% rename from AdamantTests/CurrencyFormatterTests.swift rename to AdamantTests/DisabledTests/CurrencyFormatterTests.swift diff --git a/AdamantTests/FeeTests.swift b/AdamantTests/DisabledTests/FeeTests.swift similarity index 100% rename from AdamantTests/FeeTests.swift rename to AdamantTests/DisabledTests/FeeTests.swift diff --git a/AdamantTests/HexAndBytesUtilitiesTest.swift b/AdamantTests/DisabledTests/HexAndBytesUtilitiesTest.swift similarity index 100% rename from AdamantTests/HexAndBytesUtilitiesTest.swift rename to AdamantTests/DisabledTests/HexAndBytesUtilitiesTest.swift diff --git a/AdamantTests/NodesAllowanceTests.swift b/AdamantTests/DisabledTests/NodesAllowanceTests.swift similarity index 100% rename from AdamantTests/NodesAllowanceTests.swift rename to AdamantTests/DisabledTests/NodesAllowanceTests.swift diff --git a/AdamantTests/Parsing/Account.json b/AdamantTests/DisabledTests/Parsing/Account.json similarity index 100% rename from AdamantTests/Parsing/Account.json rename to AdamantTests/DisabledTests/Parsing/Account.json diff --git a/AdamantTests/Parsing/Chat.json b/AdamantTests/DisabledTests/Parsing/Chat.json similarity index 100% rename from AdamantTests/Parsing/Chat.json rename to AdamantTests/DisabledTests/Parsing/Chat.json diff --git a/AdamantTests/Parsing/NormalizedTransaction.json b/AdamantTests/DisabledTests/Parsing/NormalizedTransaction.json similarity index 100% rename from AdamantTests/Parsing/NormalizedTransaction.json rename to AdamantTests/DisabledTests/Parsing/NormalizedTransaction.json diff --git a/AdamantTests/Parsing/ParsingModelsTests.swift b/AdamantTests/DisabledTests/Parsing/ParsingModelsTests.swift similarity index 100% rename from AdamantTests/Parsing/ParsingModelsTests.swift rename to AdamantTests/DisabledTests/Parsing/ParsingModelsTests.swift diff --git a/AdamantTests/Parsing/TransactionChat.json b/AdamantTests/DisabledTests/Parsing/TransactionChat.json similarity index 100% rename from AdamantTests/Parsing/TransactionChat.json rename to AdamantTests/DisabledTests/Parsing/TransactionChat.json diff --git a/AdamantTests/Parsing/TransactionSend.json b/AdamantTests/DisabledTests/Parsing/TransactionSend.json similarity index 100% rename from AdamantTests/Parsing/TransactionSend.json rename to AdamantTests/DisabledTests/Parsing/TransactionSend.json diff --git a/AdamantTests/PassphraseValidation.swift b/AdamantTests/DisabledTests/PassphraseValidation.swift similarity index 100% rename from AdamantTests/PassphraseValidation.swift rename to AdamantTests/DisabledTests/PassphraseValidation.swift diff --git a/AdamantTests/Stubs/ApiServiceStub.swift b/AdamantTests/DisabledTests/Stubs/ApiServiceStub.swift similarity index 100% rename from AdamantTests/Stubs/ApiServiceStub.swift rename to AdamantTests/DisabledTests/Stubs/ApiServiceStub.swift diff --git a/AdamantTests/TestTools.swift b/AdamantTests/DisabledTests/TestTools.swift similarity index 100% rename from AdamantTests/TestTools.swift rename to AdamantTests/DisabledTests/TestTools.swift diff --git a/AdamantTests/Extensions/Actor+Extensions.swift b/AdamantTests/Extensions/Actor+Extensions.swift new file mode 100644 index 000000000..076eb923b --- /dev/null +++ b/AdamantTests/Extensions/Actor+Extensions.swift @@ -0,0 +1,13 @@ +// +// Actor+Extensions.swift +// Adamant +// +// Created by Christian Benua on 10.01.2025. +// Copyright © 2025 Adamant. All rights reserved. +// + +extension Actor { + func isolated(_ closure: (isolated Self) -> T) -> T { + return closure(self) + } +} diff --git a/AdamantTests/Extensions/BitcoinKitTransaction+Equatable.swift b/AdamantTests/Extensions/BitcoinKitTransaction+Equatable.swift new file mode 100644 index 000000000..2ae6524f5 --- /dev/null +++ b/AdamantTests/Extensions/BitcoinKitTransaction+Equatable.swift @@ -0,0 +1,40 @@ +// +// BitcoinKitTransaction+Equatable.swift +// Adamant +// +// Created by Christian Benua on 10.01.2025. +// Copyright © 2025 Adamant. All rights reserved. +// + +import BitcoinKit + +extension BitcoinKit.Transaction: Equatable { + public static func == (lhs: BitcoinKit.Transaction, rhs: BitcoinKit.Transaction) -> Bool { + lhs.version == rhs.version && + lhs.inputs == rhs.inputs && + lhs.outputs == rhs.outputs && + lhs.lockTime == rhs.lockTime + } +} + +extension BitcoinKit.TransactionInput: Equatable { + public static func == (lhs: BitcoinKit.TransactionInput, rhs: BitcoinKit.TransactionInput) -> Bool { + lhs.previousOutput == rhs.previousOutput && + lhs.signatureScript == rhs.signatureScript && + lhs.sequence == rhs.sequence + } +} + +extension BitcoinKit.TransactionOutPoint: Equatable { + public static func == (lhs: BitcoinKit.TransactionOutPoint, rhs: BitcoinKit.TransactionOutPoint) -> Bool { + lhs.hash == rhs.hash && + lhs.index == rhs.index + } +} + +extension BitcoinKit.TransactionOutput: Equatable { + public static func == (lhs: BitcoinKit.TransactionOutput, rhs: BitcoinKit.TransactionOutput) -> Bool { + lhs.value == rhs.value && + lhs.lockingScript == rhs.lockingScript + } +} diff --git a/AdamantTests/Extensions/Result+Extensions.swift b/AdamantTests/Extensions/Result+Extensions.swift new file mode 100644 index 000000000..a42643cf3 --- /dev/null +++ b/AdamantTests/Extensions/Result+Extensions.swift @@ -0,0 +1,38 @@ +// +// Result+Extensions.swift +// Adamant +// +// Created by Christian Benua on 10.01.2025. +// Copyright © 2025 Adamant. All rights reserved. +// + +extension Result { + var error: Failure? { + switch self { + case let .failure(error): + return error + case .success: + return nil + } + } + + var value: Success? { + switch self { + case .failure: + return nil + case let .success(value): + return value + } + } +} + +extension Result where Failure == Error { + init(catchingAsync run: @escaping () async throws -> Success) async { + do { + let value = try await run() + self = .success(value) + } catch { + self = .failure(error) + } + } +} diff --git a/AdamantTests/Extensions/UnspentTransaction+Equatable.swift b/AdamantTests/Extensions/UnspentTransaction+Equatable.swift new file mode 100644 index 000000000..33face01b --- /dev/null +++ b/AdamantTests/Extensions/UnspentTransaction+Equatable.swift @@ -0,0 +1,16 @@ +// +// UnspentTransaction+Equatable.swift +// Adamant +// +// Created by Christian Benua on 11.01.2025. +// Copyright © 2025 Adamant. All rights reserved. +// + +import BitcoinKit + +extension UnspentTransaction: Equatable { + public static func == (lhs: UnspentTransaction, rhs: UnspentTransaction) -> Bool { + lhs.output == rhs.output && + lhs.outpoint == rhs.outpoint + } +} diff --git a/AdamantTests/Extensions/WalletServiceError+Equatable.swift b/AdamantTests/Extensions/WalletServiceError+Equatable.swift new file mode 100644 index 000000000..cc86f17f0 --- /dev/null +++ b/AdamantTests/Extensions/WalletServiceError+Equatable.swift @@ -0,0 +1,34 @@ +// +// WalletServiceError+Equatable.swift +// Adamant +// +// Created by Christian Benua on 10.01.2025. +// Copyright © 2025 Adamant. All rights reserved. +// + +@testable import Adamant + +extension WalletServiceError: Equatable { + public static func == (lhs: Adamant.WalletServiceError, rhs: Adamant.WalletServiceError) -> Bool { + switch (lhs, rhs) { + case (.notLogged, .notLogged), (.notEnoughMoney, .notEnoughMoney), (.networkError, .networkError), + (.accountNotFound, .accountNotFound), (.walletNotInitiated, .walletNotInitiated), + (.requestCancelled, .requestCancelled), (.dustAmountError, .dustAmountError): + return true + case let (.invalidAmount(lhsValue), invalidAmount(rhsValue)): + return lhsValue == rhsValue + case let (.transactionNotFound(lhsValue), transactionNotFound(rhsValue)): + return lhsValue == rhsValue + case (.apiError, .apiError): + return true + case let (.remoteServiceError(lhsValue, _), .remoteServiceError(rhsValue, _)): + return lhsValue == rhsValue + case let (.internalError(lhsValue, _), .internalError(rhsValue, _)): + return lhsValue == rhsValue + case (.notLogged, _), (.notEnoughMoney, _), (.networkError, _), (.accountNotFound, _), (.walletNotInitiated, _), + (.requestCancelled, _), (.dustAmountError, _), (.invalidAmount, _), (.transactionNotFound, _), + (.apiError, _), (.remoteServiceError, _), (.internalError, _): + return false + } + } +} diff --git a/AdamantTests/Modules/Wallets/BtcWalletServiceIntegrationTests.swift b/AdamantTests/Modules/Wallets/BtcWalletServiceIntegrationTests.swift new file mode 100644 index 000000000..4588841ac --- /dev/null +++ b/AdamantTests/Modules/Wallets/BtcWalletServiceIntegrationTests.swift @@ -0,0 +1,119 @@ +// +// BtcWalletServiceIntegrationTests.swift +// Adamant +// +// Created by Christian Benua on 13.01.2025. +// Copyright © 2025 Adamant. All rights reserved. +// + +import XCTest +@testable import Adamant +import Swinject +import BitcoinKit +import CommonKit + +final class BtcWalletServiceIntegrationTests: XCTestCase { + + private var apiCoreMock: APICoreProtocolMock! + private var btcApiServiceProtocolMock: BtcApiServiceProtocolMock! + private var sut: BtcWalletService! + + override func setUp() { + super.setUp() + apiCoreMock = APICoreProtocolMock() + btcApiServiceProtocolMock = BtcApiServiceProtocolMock() + btcApiServiceProtocolMock.api = BtcApiCore(apiCore: apiCoreMock) + + sut = BtcWalletService() + sut.addressConverter = AddressConverterFactory().make(network: .mainnetBTC) + sut.btcApiService = btcApiServiceProtocolMock + sut.btcTransactionFactory = BitcoinKitTransactionFactory() + } + + override func tearDown() { + apiCoreMock = nil + btcApiServiceProtocolMock = nil + sut = nil + super.tearDown() + } + + func test_createAndSendTransaction_createsValidTxIdAndHash() async throws { + // given + sut.setWalletForTests(try makeWallet()) + let data = Constants.unspentTranscationsData + await apiCoreMock.isolated { mock in + mock.stubbedSendRequestBasicGenericResult = APIResponseModel(result: .success(data), data: data, code: 200) + } + + // when 1 + let result = await Result(catchingAsync: { + try await self.sut.createTransaction( + recipient: "1K4hFg49PaEt5pHCym7yb5B446Vb3roSMp", + amount: 0.00009, + fee: 0.00002159542, + comment: nil + ) + }) + + // then 1 + let transaction = try XCTUnwrap(result.value) + XCTAssertEqual(transaction.serialized().hex, Constants.expectedTransactionHex) + XCTAssertEqual(transaction.txID, Constants.expectedTransactionID) + + // given 2 + let txData = try XCTUnwrap(transaction.txID.data(using: .utf8)) + await apiCoreMock.isolated { mock in + mock.stubbedSendRequestBasicGenericResult = APIResponseModel(result: .success(txData), data: txData, code: 200) + } + + // when 2 + let result2 = await Result { + try await self.sut.sendTransaction(transaction) + } + // then 3 + XCTAssertNil(result2.error) + await apiCoreMock.isolated { mock in + XCTAssertEqual(mock.invokedSendRequestBasicGenericCount, 2) + } + } + + private func makeWallet() throws -> BtcWallet { + let privateKeyData = Constants.passphrase + .data(using: .utf8)! + .sha256() + let privateKey = PrivateKey( + data: privateKeyData, + network: .mainnetBTC, + isPublicKeyCompressed: true + ) + return try BtcWallet( + unicId: "BTCBTC", + privateKey: privateKey, + addressConverter: AddressConverterFactory().make(network: .mainnetBTC) + ) + } +} + +private enum Constants { + + static let passphrase = "mystery category decide music raw damage render style decorate glad million know" + + static let expectedTransactionID = "8b2654793f94539e5c66b87dee6d0908fb9728eb25c90396e25286c6d4b8a371" + + static let expectedTransactionHex = "0100000001a0d73e3bd0aa2025d91eabd8512d5e19ad80752892f415480f75b97966b06f0e010000006a473044022072c8ecd3143e663520807c496dba3dc8010478f3cae09fcb65995be29737a55702206d23617cad2f88a3bd28757be956c731dbde06615fb9bb9fabf2d55e6a8f67ba0121037ec9f6126013088b3d1e8f844f3e755144756a4e9a7da6b0094c189f55031934ffffffff0228230000000000001976a914c6251d0e16c0e1946b745b69caa3a7c36014381088ac38560200000000001976a914931ef5cbdad28723ba9596de5da1145ae969a71888ac00000000" + + static let unspentTranscationsData = unspentTranscationsRawJSON.data(using: .utf8)! + + static let unspentTranscationsRawJSON: String = """ +[{ + "txid":"0e6fb06679b9750f4815f492287580ad195e2d51d8ab1ed92520aad03b3ed7a0", + "vout":1, + "status":{ + "confirmed":true, + "block_height":879091, + "block_hash":"00000000000000000001e0da09b0792ff69dcd98af264b1750cbf9ef2deab73d", + "block_time":1736786953}, + "value":164303 +}] +""" +} diff --git a/AdamantTests/Modules/Wallets/BtcWalletServiceTests.swift b/AdamantTests/Modules/Wallets/BtcWalletServiceTests.swift new file mode 100644 index 000000000..79589333c --- /dev/null +++ b/AdamantTests/Modules/Wallets/BtcWalletServiceTests.swift @@ -0,0 +1,336 @@ +// +// BtcWalletServiceTests.swift +// Adamant +// +// Created by Christian Benua on 09.01.2025. +// Copyright © 2025 Adamant. All rights reserved. +// + +import XCTest +@testable import Adamant +import Swinject +import BitcoinKit +import CommonKit + +final class BtcWalletServiceTests: XCTestCase { + + private var addressConverterMock: AddressConverterMock! + private var apiCoreMock: APICoreProtocolMock! + private var btcApiServiceProtocolMock: BtcApiServiceProtocolMock! + private var transactionFactoryMock: BitcoinKitTransactionFactoryProtocolMock! + private var sut: BtcWalletService! + + override func setUp() { + super.setUp() + addressConverterMock = AddressConverterMock() + apiCoreMock = APICoreProtocolMock() + btcApiServiceProtocolMock = BtcApiServiceProtocolMock() + btcApiServiceProtocolMock.api = BtcApiCore(apiCore: apiCoreMock) + + sut = BtcWalletService() + sut.addressConverter = addressConverterMock + sut.btcApiService = btcApiServiceProtocolMock + transactionFactoryMock = BitcoinKitTransactionFactoryProtocolMock() + transactionFactoryMock.stubbedTransactionFactory = { + BitcoinKit.Transaction.createNewTransaction( + toAddress: $0, + amount: $1, + fee: $2, + changeAddress: $3, + utxos: $4, + lockTime: $5, + keys: $6 + ) + } + sut.btcTransactionFactory = transactionFactoryMock + } + + override func tearDown() { + addressConverterMock = nil + apiCoreMock = nil + btcApiServiceProtocolMock = nil + transactionFactoryMock = nil + sut = nil + super.tearDown() + } + + func test_createTransaction_noWalletThrowsError() async throws { + // given + sut.setWalletForTests(nil) + + // when + let result = await Result(catchingAsync: { + try await self.sut.createTransaction( + recipient: "recipient", + amount: 10, + fee: 0.1, + comment: nil + ) + }) + + // then + XCTAssertEqual(result.error as? WalletServiceError, .notLogged) + } + + func test_createTransaction_accountNotFoundThrowsError() async throws { + // given + sut.setWalletForTests(try makeWallet()) + addressConverterMock.stubbedInvokedConvertAddressResult = .failure(NSError()) + + // when + let result = await Result(catchingAsync: { + try await self.sut.createTransaction( + recipient: "recipient", + amount: 10, + fee: 1, + comment: nil + ) + }) + + // then + XCTAssertEqual(result.error as? WalletServiceError, .accountNotFound) + } + + func test_createTransaction_notEnoughMoneyThrowsError() async throws { + // given + sut.setWalletForTests(try makeWallet()) + let data = Constants.unspentTranscationsData + await apiCoreMock.isolated { mock in + mock.stubbedSendRequestBasicGenericResult = APIResponseModel( + result: .success(data), + data: data, + code: 200 + ) + } + addressConverterMock.stubbedInvokedConvertAddressResult = .success(try makeDefaultAddress()) + + // when + let result = await Result(catchingAsync: { + try await self.sut.createTransaction( + recipient: "recipient", + amount: 30 / BtcWalletService.multiplier, + fee: 1 / BtcWalletService.multiplier, + comment: nil) + }) + + // then + XCTAssertEqual(result.error as? WalletServiceError, .notEnoughMoney) + } + + func test_createTransaction_badUnspentTransactionResponseDataThrowsError() async throws { + // given + sut.setWalletForTests(try makeWallet()) + let data = Constants.unspentTranscationsCorruptedData + await apiCoreMock.isolated { mock in + mock.stubbedSendRequestBasicGenericResult = APIResponseModel(result: .success(data), data: data, code: 200) + } + addressConverterMock.stubbedInvokedConvertAddressResult = .success(try makeDefaultAddress()) + + // when + let result = await Result(catchingAsync: { + try await self.sut.createTransaction( + recipient: "recipient", + amount: 30 / BtcWalletService.multiplier, + fee: 1 / BtcWalletService.multiplier, + comment: nil) + }) + + // then + switch result.error as? WalletServiceError { + case .internalError?: + break + default: + XCTFail("Expected `internalError`, but got \(String(describing: result.error))") + } + } + + func test_createTransaction_enoughMoneyReturnsRealTransaction() async throws { + // given + sut.setWalletForTests(try makeWallet(address: Constants.anotherBtcAddress)) + let data = Constants.unspentTranscationsData + await apiCoreMock.isolated { mock in + mock.stubbedSendRequestBasicGenericResult = APIResponseModel(result: .success(data), data: data, code: 200) + } + let expectedToAddress = try makeDefaultAddress() + addressConverterMock.stubbedInvokedConvertAddressResult = .success(expectedToAddress) + + // when + let result = await Result(catchingAsync: { + try await self.sut.createTransaction( + recipient: "recipient", + amount: 10 / BtcWalletService.multiplier, + fee: 1 / BtcWalletService.multiplier, + comment: nil + ) + }) + + // then + XCTAssertNil(result.error) + XCTAssertEqual(result.value, Constants.expectedTransaction) + XCTAssertEqual( + transactionFactoryMock.invokedCreateTransactionParameters?.amount, + 10 + ) + XCTAssertEqual( + transactionFactoryMock.invokedCreateTransactionParameters?.fee, + 1 + ) + XCTAssertEqual( + transactionFactoryMock.invokedCreateTransactionParameters?.utxos, + Constants.expectedUnspentTransactions + ) + assertAddressesEqual( + try XCTUnwrap(transactionFactoryMock.invokedCreateTransactionParameters?.toAddress), + expectedToAddress + ) + assertAddressesEqual( + try XCTUnwrap(transactionFactoryMock.invokedCreateTransactionParameters?.changeAddress), + try XCTUnwrap(sut.btcWallet?.addressEntity) + ) + } + + func test_sendTransaction_failIfTxIdCorrupted() async throws { + // given + let txData = try XCTUnwrap(Constants.anotherTransactionId.data(using: .utf8)) + await apiCoreMock.isolated { mock in + mock.stubbedSendRequestBasicGenericResult = APIResponseModel(result: .success(txData), data: txData, code: 200) + } + + // when + let result = await Result { + try await self.sut.sendTransaction(BitcoinKit.Transaction.deserialize(Data(hex: Constants.transactionHex)!)) + } + + // then + XCTAssertEqual( + result.error as? WalletServiceError, + WalletServiceError.remoteServiceError(message: Constants.anotherTransactionId) + ) + } + + func test_sendTransaction_successIfTxIdMatches() async throws { + // given + let txData = try XCTUnwrap(Constants.transactionId.data(using: .utf8)) + await apiCoreMock.isolated { mock in + mock.stubbedSendRequestBasicGenericResult = APIResponseModel(result: .success(txData), data: txData, code: 200) + } + + // when + let result = await Result { + try await self.sut.sendTransaction(BitcoinKit.Transaction.deserialize(Data(hex: Constants.transactionHex)!)) + } + + // then + XCTAssertNil(result.error) + } + + private func makeWallet(address: String = Constants.btcAddress) throws -> BtcWallet { + let privateKeyData = "my long passphrase" + .data(using: .utf8)! + .sha256() + let privateKey = PrivateKey( + data: privateKeyData, + network: .testnet, + isPublicKeyCompressed: true + ) + + return try BtcWallet( + unicId: "unicId", + privateKey: privateKey, + address: makeDefaultAddress(address: address) + ) + } + + private func makeDefaultAddress(address: String = Constants.btcAddress) throws -> Address { + try AddressConverterFactory() + .make(network: .mainnetBTC) + .convert(address: address) + } + + private func assertAddressesEqual(_ lhs: Address, _ rhs: Address, file: StaticString = #file, line: UInt = #line) { + XCTAssertEqual(lhs.lockingScript, rhs.lockingScript, file: file, line: line) + XCTAssertEqual(lhs.stringValue, rhs.stringValue, file: file, line: line) + XCTAssertEqual(lhs.lockingScriptPayload, rhs.lockingScriptPayload, file: file, line: line) + XCTAssertEqual(lhs.scriptType, rhs.scriptType, file: file, line: line) + } +} + +private enum Constants { + + static let btcAddress = "1DU8Hi1sbHTpEP9vViBEkEw6noeUrgKkJH" + + static let anotherBtcAddress = "1JFHeK6mv8g7EmLj6g5MRgbQ58B9udtHC5" + + static let lockingScript = Data([118, 169, 20, 136, 194, 210, 250, 132, 98, 130, 200, 112, 167, 108, 173, 236, 190, 69, 196, 172, 215, 43, 182, 136, 172]) + + static let lockingScript2 = Data([118, 169, 20, 189, 45, 218, 220, 109, 190, 133, 34, 44, 61, 83, 31, 41, 204, 37, 209, 62, 168, 11, 45, 136, 172]) + + static let transactionId = "8b2654793f94539e5c66b87dee6d0908fb9728eb25c90396e25286c6d4b8a371" + + static let anotherTransactionId = String("8b2654793f94539e5c66b87dee6d0908fb9728eb25c90396e25286c6d4b8a371".reversed()) + + static let transactionHex = "0100000001a0d73e3bd0aa2025d91eabd8512d5e19ad80752892f415480f75b97966b06f0e010000006a473044022072c8ecd3143e663520807c496dba3dc8010478f3cae09fcb65995be29737a55702206d23617cad2f88a3bd28757be956c731dbde06615fb9bb9fabf2d55e6a8f67ba0121037ec9f6126013088b3d1e8f844f3e755144756a4e9a7da6b0094c189f55031934ffffffff0228230000000000001976a914c6251d0e16c0e1946b745b69caa3a7c36014381088ac38560200000000001976a914931ef5cbdad28723ba9596de5da1145ae969a71888ac00000000" + + static let expectedTransaction = BitcoinKit.Transaction( + version: 1, + inputs: [ + TransactionInput(previousOutput: TransactionOutPoint(hash: Data(), index: 1), signatureScript: Data(), sequence: 4294967295), + TransactionInput(previousOutput: TransactionOutPoint(hash: Data(), index: 2), signatureScript: Data(), sequence: 4294967295) + ], + outputs: [ + TransactionOutput( + value: 10, + lockingScript: Constants.lockingScript + ), + TransactionOutput( + value: 19, + lockingScript: Constants.lockingScript2 + ) + ], + lockTime: 0 + ) + + static let expectedUnspentTransactions = [ + UnspentTransaction( + output: TransactionOutput(value: 10, lockingScript: Constants.lockingScript2), + outpoint: TransactionOutPoint(hash: Data(), index: 1) + ), + UnspentTransaction( + output: TransactionOutput(value: 20, lockingScript: Constants.lockingScript2), + outpoint: TransactionOutPoint(hash: Data(), index: 2) + ) + ] + + static let unspentTranscationsData = unspentTranscationsRawJSON.data(using: .utf8)! + + static let unspentTranscationsCorruptedData = Data(unspentTranscationsData.shuffled()) + + static let unspentTranscationsRawJSON: String = """ +[ + { + "txid": "1", + "vout": 1, + "value": 10, + "status": { + "confirmed": true + } + }, + { + "txid": "1", + "vout": 2, + "value": 20, + "status": { + "confirmed": true + } + }, + { + "txid": "1", + "vout": 3, + "value": 30, + "status": { + "confirmed": false + } + } +] +""" +} diff --git a/AdamantTests/Stubs/APICoreProtocolMock.swift b/AdamantTests/Stubs/APICoreProtocolMock.swift new file mode 100644 index 000000000..402519c2b --- /dev/null +++ b/AdamantTests/Stubs/APICoreProtocolMock.swift @@ -0,0 +1,79 @@ +// +// APICoreProtocolMock.swift +// Adamant +// +// Created by Christian Benua on 09.01.2025. +// Copyright © 2025 Adamant. All rights reserved. +// + +import CommonKit +import Foundation +import Alamofire + +final actor APICoreProtocolMock: APICoreProtocol { + + var invokedSendRequestMultipartFormData: Bool = false + var invokedSendRequestMultipartFormDataCount: Int = 0 + var invokedSendRequestMultipartFormDataParameters: (origin: NodeOrigin, path: String, models: [MultipartFormDataModel], timeout: TimeoutSize)? + var invokedSendRequestMultipartFormDataParametersList: [(origin: NodeOrigin, path: String, models: [MultipartFormDataModel], timeout: TimeoutSize)] = [] + var stubbedSendRequestMultipartFormDataResult: APIResponseModel! + + func sendRequestMultipartFormData( + origin: NodeOrigin, + path: String, + models: [MultipartFormDataModel], + timeout: TimeoutSize, + uploadProgress: @escaping ((Progress) -> Void) + ) async -> APIResponseModel { + invokedSendRequestMultipartFormData = true + invokedSendRequestMultipartFormDataCount += 1 + invokedSendRequestMultipartFormDataParameters = (origin, path, models, timeout) + invokedSendRequestMultipartFormDataParametersList.append((origin, path, models, timeout)) + return stubbedSendRequestMultipartFormDataResult + } + + var invokedSendRequestBasicGeneric: Bool = false + var invokedSendRequestBasicGenericCount: Int = 0 + var invokedSendRequestBasicGenericParameters: (origin: NodeOrigin, path: String, method: HTTPMethod, parameters: Encodable, timeout: TimeoutSize)? + var invokedSendRequestBasicGenericParametersList: [(origin: NodeOrigin, path: String, method: HTTPMethod, parameters: Encodable, timeout: TimeoutSize)] = [] + var stubbedSendRequestBasicGenericResult: APIResponseModel! + + func sendRequestBasic( + origin: NodeOrigin, + path: String, + method: HTTPMethod, + parameters: Parameters, + encoding: APIParametersEncoding, + timeout: TimeoutSize, + downloadProgress: @escaping ((Progress) -> Void) + ) async -> APIResponseModel { + invokedSendRequestBasicGeneric = true + invokedSendRequestBasicGenericCount += 1 + invokedSendRequestBasicGenericParameters = (origin, path, method, parameters, timeout) + invokedSendRequestBasicGenericParametersList.append((origin, path, method, parameters, timeout)) + + return stubbedSendRequestBasicGenericResult + } + + var invokedSendRequestBasic: Bool = false + var invokedSendRequestBasicCount: Int = 0 + var invokedSendRequestBasicParameters: (origin: NodeOrigin, path: String, method: HTTPMethod, jsonParameters: Any, timeout: TimeoutSize)? + var invokedSendRequestBasicParametersList: [(origin: NodeOrigin, path: String, method: HTTPMethod, jsonParameters: Any, timeout: TimeoutSize)] = [] + var stubbedSendRequestBasicResult: APIResponseModel! + + /// jsonParameters - arrays and dictionaries are allowed only + func sendRequestBasic( + origin: NodeOrigin, + path: String, + method: HTTPMethod, + jsonParameters: Any, + timeout: TimeoutSize + ) async -> APIResponseModel { + invokedSendRequestBasic = true + invokedSendRequestBasicCount += 1 + invokedSendRequestBasicParameters = (origin, path, method, jsonParameters, timeout) + invokedSendRequestBasicParametersList.append((origin, path, method, jsonParameters, timeout)) + + return stubbedSendRequestBasicResult + } +} diff --git a/AdamantTests/Stubs/AddressConverterMock.swift b/AdamantTests/Stubs/AddressConverterMock.swift new file mode 100644 index 000000000..62e044805 --- /dev/null +++ b/AdamantTests/Stubs/AddressConverterMock.swift @@ -0,0 +1,36 @@ +// +// AddressConverterMock.swift +// Adamant +// +// Created by Christian Benua on 09.01.2025. +// Copyright © 2025 Adamant. All rights reserved. +// + +import BitcoinKit +import Foundation + +final class AddressConverterMock: AddressConverter { + + var invokedConvertAddress: Bool = false + var invokedConvertAddressCount: Int = 0 + var stubbedInvokedConvertAddressResult: Result! + + func convert(address: String) throws -> Address { + invokedConvertAddress = true + invokedConvertAddressCount += 1 + + switch stubbedInvokedConvertAddressResult! { + case let .failure(error): + throw error + case let .success(result): + return result + } + } + + func convert(lockingScriptPayload: Data, type: ScriptType) throws -> Address { + fatalError("\(#file).\(#function) is not implemented") + } + func convert(publicKey: PublicKey, type: ScriptType) throws -> Address { + fatalError("\(#file).\(#function) is not implemented") + } +} diff --git a/AdamantTests/Stubs/BitcoinKitTransactionFactoryProtocolMock.swift b/AdamantTests/Stubs/BitcoinKitTransactionFactoryProtocolMock.swift new file mode 100644 index 000000000..b59dfe7dd --- /dev/null +++ b/AdamantTests/Stubs/BitcoinKitTransactionFactoryProtocolMock.swift @@ -0,0 +1,66 @@ +// +// BitcoinKitTransactionFactoryProtocolMock.swift +// Adamant +// +// Created by Christian Benua on 11.01.2025. +// Copyright © 2025 Adamant. All rights reserved. +// + +@testable import Adamant +import BitcoinKit + +final class BitcoinKitTransactionFactoryProtocolMock: BitcoinKitTransactionFactoryProtocol { + + var invokedCreateTransaction: Bool = false + var invokedCreateTransactionCount: Int = 0 + var invokedCreateTransactionParameters: ( + toAddress: Address, + amount: UInt64, + fee: UInt64, + changeAddress: Address, + utxos: [UnspentTransaction], + lockTime: UInt32, + keys: [PrivateKey] + )? + var stubbedTransactionFactory: (( + _ toAddress: Address, + _ amount: UInt64, + _ fee: UInt64, + _ changeAddress: Address, + _ utxos: [UnspentTransaction], + _ lockTime: UInt32, + _ keys: [PrivateKey] + ) -> Transaction)? + + func createTransaction( + toAddress: Address, + amount: UInt64, + fee: UInt64, + changeAddress: Address, + utxos: [UnspentTransaction], + lockTime: UInt32, + keys: [PrivateKey] + ) -> Transaction { + invokedCreateTransaction = true + invokedCreateTransactionCount += 1 + invokedCreateTransactionParameters = ( + toAddress, + amount, + fee, + changeAddress, + utxos, + lockTime, + keys + ) + + return stubbedTransactionFactory!( + toAddress, + amount, + fee, + changeAddress, + utxos, + lockTime, + keys + ) + } +} diff --git a/AdamantTests/Stubs/BtcApiServiceProtocolMock.swift b/AdamantTests/Stubs/BtcApiServiceProtocolMock.swift new file mode 100644 index 000000000..19e16e878 --- /dev/null +++ b/AdamantTests/Stubs/BtcApiServiceProtocolMock.swift @@ -0,0 +1,41 @@ +// +// BtcApiServiceProtocol.swift +// Adamant +// +// Created by Christian Benua on 09.01.2025. +// Copyright © 2025 Adamant. All rights reserved. +// + +@testable import Adamant +import CommonKit +import Foundation + +final class BtcApiServiceProtocolMock: BtcApiServiceProtocol { + + var api: BtcApiCore! + + func request( + waitsForConnectivity: Bool, + _ request: @Sendable @escaping (APICoreProtocol, NodeOrigin) async -> ApiServiceResult + ) async -> WalletServiceResult { + await api.request(origin: NodeOrigin(url: URL(string: "http://samplenodeorigin.com")!)) { core, origin in + await request(core, origin) + } + } + + func getStatusInfo() async -> WalletServiceResult { + return .failure(.networkError) + } + + var nodesInfo: CommonKit.NodesListInfo { + fatalError("\(#file).\(#function) is not implemented") + } + + var nodesInfoPublisher: CommonKit.AnyObservable { + fatalError("\(#file).\(#function) is not implemented") + } + + func healthCheck() { + fatalError("\(#file).\(#function) is not implemented") + } +}