diff --git a/novawallet.xcodeproj/project.pbxproj b/novawallet.xcodeproj/project.pbxproj index 3728fee36..03fdc0625 100644 --- a/novawallet.xcodeproj/project.pbxproj +++ b/novawallet.xcodeproj/project.pbxproj @@ -166,6 +166,8 @@ 0C252BBF2B3D3CEB0047308F /* TypeRegistry+Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C252BBE2B3D3CEB0047308F /* TypeRegistry+Node.swift */; }; 0C259EA42B46721B00CB86E4 /* ProxyMessageSheetViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C259EA32B46721B00CB86E4 /* ProxyMessageSheetViewFactory.swift */; }; 0C259EA82B46C55C00CB86E4 /* ExtrinsicSigningErrorHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C259EA72B46C55C00CB86E4 /* ExtrinsicSigningErrorHandling.swift */; }; + 0C28DF0D2D1AC69F0016DB8E /* SNAddressType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DAC197268D3DD9002D0DF4 /* SNAddressType.swift */; }; + 0C28DF1C2D2008670016DB8E /* AssetFungibilityPreservationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C28DF1B2D2008670016DB8E /* AssetFungibilityPreservationProvider.swift */; }; 0C29B5382A4C68A500E35C6D /* AnimationUpdatibleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C29B5372A4C68A500E35C6D /* AnimationUpdatibleView.swift */; }; 0C2A3C932CDC813B00A0E2B3 /* AssetsExchangeOperationFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C2A3C922CDC813B00A0E2B3 /* AssetsExchangeOperationFactory.swift */; }; 0C2A3C952CDC8ADB00A0E2B3 /* SwapAssetSelectionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C2A3C942CDC8ADB00A0E2B3 /* SwapAssetSelectionModel.swift */; }; @@ -881,6 +883,7 @@ 0CFA161E2B0CED07007AF885 /* GovTreasurySpentLocalHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CFA161D2B0CED07007AF885 /* GovTreasurySpentLocalHandler.swift */; }; 0CFA16202B0CEF31007AF885 /* GovTreasuryApproveHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CFA161F2B0CEF31007AF885 /* GovTreasuryApproveHandler.swift */; }; 0CFFB9D32D11A67C00172E8C /* XcmTokensArrivalDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CFFB9D22D11A67C00172E8C /* XcmTokensArrivalDetector.swift */; }; + 0CFFB9D92D17592500172E8C /* OperatingSystemApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CFFB9D82D17592500172E8C /* OperatingSystemApi.swift */; }; 0D5245ED354CC52A842C85A0 /* TransferConfirmViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AD8B98AB03AAF06AA891695 /* TransferConfirmViewLayout.swift */; }; 0D8213272889988B78188D9A /* DAppWalletAuthInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 337EC62037D657258BCBC02F /* DAppWalletAuthInteractor.swift */; }; 0DACB56C0BDD4C984FE3C15C /* AssetReceiveWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C1179A25C22AF0875A1ADCD /* AssetReceiveWireframe.swift */; }; @@ -5528,6 +5531,7 @@ 0C252BBE2B3D3CEB0047308F /* TypeRegistry+Node.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TypeRegistry+Node.swift"; sourceTree = ""; }; 0C259EA32B46721B00CB86E4 /* ProxyMessageSheetViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyMessageSheetViewFactory.swift; sourceTree = ""; }; 0C259EA72B46C55C00CB86E4 /* ExtrinsicSigningErrorHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtrinsicSigningErrorHandling.swift; sourceTree = ""; }; + 0C28DF1B2D2008670016DB8E /* AssetFungibilityPreservationProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetFungibilityPreservationProvider.swift; sourceTree = ""; }; 0C29B5372A4C68A500E35C6D /* AnimationUpdatibleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimationUpdatibleView.swift; sourceTree = ""; }; 0C2A3C922CDC813B00A0E2B3 /* AssetsExchangeOperationFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetsExchangeOperationFactory.swift; sourceTree = ""; }; 0C2A3C942CDC8ADB00A0E2B3 /* SwapAssetSelectionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwapAssetSelectionModel.swift; sourceTree = ""; }; @@ -6283,6 +6287,7 @@ 0CFA161D2B0CED07007AF885 /* GovTreasurySpentLocalHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GovTreasurySpentLocalHandler.swift; sourceTree = ""; }; 0CFA161F2B0CEF31007AF885 /* GovTreasuryApproveHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GovTreasuryApproveHandler.swift; sourceTree = ""; }; 0CFFB9D22D11A67C00172E8C /* XcmTokensArrivalDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcmTokensArrivalDetector.swift; sourceTree = ""; }; + 0CFFB9D82D17592500172E8C /* OperatingSystemApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperatingSystemApi.swift; sourceTree = ""; }; 0D32A720511D1D3B0F479AFE /* DAppBrowserTabListProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DAppBrowserTabListProtocols.swift; sourceTree = ""; }; 0D37CF4AFB06AF3AC2F78057 /* ImportCloudPasswordPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ImportCloudPasswordPresenter.swift; sourceTree = ""; }; 0D3FE2CE7F9F2836755DBA63 /* GovernanceUnlockConfirmProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GovernanceUnlockConfirmProtocols.swift; sourceTree = ""; }; @@ -17051,13 +17056,14 @@ path = PerformOperation; sourceTree = ""; }; - 84468A05286652EF00BCBE00 /* Operation */ = { + 84468A05286652EF00BCBE00 /* AssetOperations */ = { isa = PBXGroup; children = ( 84468A062866530100BCBE00 /* AssetStorageInfoOperationFactory.swift */, 2D32BE1F2C6CF4A20047F520 /* AssetTransferAggregationFactory.swift */, + 0C28DF1B2D2008670016DB8E /* AssetFungibilityPreservationProvider.swift */, ); - path = Operation; + path = AssetOperations; sourceTree = ""; }; 844ADE7C28CB34F600EE29F7 /* AutomationTime */ = { @@ -19113,6 +19119,7 @@ 0CDEF1652C27EC83003878F2 /* RuntimeMetadataRepositoryFactory.swift */, 0C7104782C2AC0F300487E64 /* InMemoryCaching.swift */, 0C33E8B92D011D2E0090096A /* Debouncer.swift */, + 0CFFB9D82D17592500172E8C /* OperatingSystemApi.swift */, ); path = Helpers; sourceTree = ""; @@ -19468,6 +19475,7 @@ 8490154E24ACD521008F705E /* Substrate */ = { isa = PBXGroup; children = ( + 84468A05286652EF00BCBE00 /* AssetOperations */, 0CEB6B3F2CA50DB400609DC2 /* AssetConverters */, 0CCE3AAD2BF5BBBF00D55F03 /* Operations */, 84A3B89C2836CF8F00DE2669 /* Coders */, @@ -21739,7 +21747,6 @@ isa = PBXGroup; children = ( 84ED6BDC28688C9000B3C558 /* View */, - 84468A05286652EF00BCBE00 /* Operation */, 84E25BEA27E87D3D00290BF1 /* Validation */, 8466780D27EB28FF007935D3 /* BaseTransfer */, DA7D18D3AF772CC2385C228C /* TransferSetup */, @@ -25569,6 +25576,7 @@ 7778FE3E2B90CCDD0023E801 /* SubstrateStorageVersion.swift in Sources */, 7778FE3D2B90CCA20023E801 /* String+Split.swift in Sources */, 7778FE502B9108AA0023E801 /* CompoundOperationWrapper+Result.swift in Sources */, + 0C28DF0D2D1AC69F0016DB8E /* SNAddressType.swift in Sources */, 0C8FDBA02CAD022700775D7F /* SubstrateDataModel.xcdatamodeld in Sources */, 77EDF9242B96DCEB003266B1 /* ProxyAccountModel.swift in Sources */, 7778FE402B90F42D0023E801 /* PriceDataMapper.swift in Sources */, @@ -27923,6 +27931,7 @@ 84B8AA7929F905C800347A37 /* WalletConnectStateInitiating.swift in Sources */, 8468B87224F63D3A00B76BC6 /* AddAccount+AccountCreateWireframe.swift in Sources */, 9DFB37659A6B911A4D54623E /* AccountConfirmInteractor.swift in Sources */, + 0C28DF1C2D2008670016DB8E /* AssetFungibilityPreservationProvider.swift in Sources */, 84BAFCD626AF64CB00871E86 /* SelectValidatorsViewLayout.swift in Sources */, 84C5ADD52812745F006D7388 /* WalletAccountViewModel.swift in Sources */, 8863C7AC29D499D30068AD54 /* Web3NameService.swift in Sources */, @@ -28617,6 +28626,7 @@ 848CCB442832EE9B00A1FD00 /* GeneralStorageSubscriptionFactory.swift in Sources */, 8483B15828F98C9F0048B295 /* ReferendumVotersViewModel.swift in Sources */, 2D7BEBD62C23818B00BBCB57 /* NetworkManageNodeWireframe.swift in Sources */, + 0CFFB9D92D17592500172E8C /* OperatingSystemApi.swift in Sources */, 84BB3CEE267CD6B500676FFE /* CrowdloanContributionDict.swift in Sources */, 8473F4B4282BD5A1007CC55A /* StakingRelaychainInteractor.swift in Sources */, 845B821B26EF80BC00D25C72 /* MetaAccountModel.swift in Sources */, diff --git a/novawallet/Common/Helpers/AddressConversion.swift b/novawallet/Common/Helpers/AddressConversion.swift index 31d0ef4c6..3265c7491 100644 --- a/novawallet/Common/Helpers/AddressConversion.swift +++ b/novawallet/Common/Helpers/AddressConversion.swift @@ -58,6 +58,24 @@ extension AccountAddress { } } + func toChainAccountIdOrSubstrateGeneric( + using conversion: ChainFormat + ) throws -> AccountId { + switch conversion { + case .ethereum: + return try extractEthereumAccountId() + case let .substrate(prefix): + let addressFactory = SS58AddressFactory() + let type = try addressFactory.type(fromAddress: self).uint16Value + + guard type == prefix || type == SNAddressType.genericSubstrate.rawValue else { + throw AccountAddressConversionError.invalidChainAddress + } + + return try addressFactory.accountId(fromAddress: self, type: type) + } + } + func toSubstrateAccountId(using prefix: UInt16? = nil) throws -> AccountId { let factory = SS58AddressFactory() diff --git a/novawallet/Common/Helpers/OperatingSystemApi.swift b/novawallet/Common/Helpers/OperatingSystemApi.swift new file mode 100644 index 000000000..cd9edc5a5 --- /dev/null +++ b/novawallet/Common/Helpers/OperatingSystemApi.swift @@ -0,0 +1,24 @@ +import UIKit + +protocol OperatingSystemMediating: AnyObject { + func disableScreenSleep() + func enableScreenSleep() +} + +final class OperatingSystemMediator { + let application: UIApplication + + init(application: UIApplication = .shared) { + self.application = application + } +} + +extension OperatingSystemMediator: OperatingSystemMediating { + func disableScreenSleep() { + application.isIdleTimerDisabled = true + } + + func enableScreenSleep() { + application.isIdleTimerDisabled = false + } +} diff --git a/novawallet/Common/Ledger/SupportedLedgerApps.swift b/novawallet/Common/Ledger/SupportedLedgerApps.swift index 52c19c694..3b7b76c1d 100644 --- a/novawallet/Common/Ledger/SupportedLedgerApps.swift +++ b/novawallet/Common/Ledger/SupportedLedgerApps.swift @@ -17,8 +17,8 @@ extension SupportedLedgerApp { [ SupportedLedgerApp(chainId: KnowChainId.polkadot, coin: 354, cla: 0x90, type: .substrate), SupportedLedgerApp(chainId: KnowChainId.kusama, coin: 434, cla: 0x99, type: .substrate), - SupportedLedgerApp(chainId: KnowChainId.statemint, coin: 354, cla: 0x96, type: .substrate), - SupportedLedgerApp(chainId: KnowChainId.statemine, coin: 434, cla: 0x97, type: .substrate), + SupportedLedgerApp(chainId: KnowChainId.polkadotAssetHub, coin: 354, cla: 0x96, type: .substrate), + SupportedLedgerApp(chainId: KnowChainId.kusamaAssetHub, coin: 434, cla: 0x97, type: .substrate), SupportedLedgerApp(chainId: KnowChainId.karura, coin: 686, cla: 0x9A, type: .substrate), SupportedLedgerApp(chainId: KnowChainId.acala, coin: 787, cla: 0x9B, type: .substrate), SupportedLedgerApp(chainId: KnowChainId.nodle, coin: 1003, cla: 0x98, type: .substrate), diff --git a/novawallet/Common/Model/KnownChainIds.swift b/novawallet/Common/Model/KnownChainIds.swift index 70bfd7ff7..20c80d737 100644 --- a/novawallet/Common/Model/KnownChainIds.swift +++ b/novawallet/Common/Model/KnownChainIds.swift @@ -2,10 +2,10 @@ import Foundation enum KnowChainId { static let kusama = "b0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe" - static let statemine = "48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a" + static let kusamaAssetHub = "48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a" static let polkadot = "91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3" + static let polkadotAssetHub = "68d56f15f85d3136970ec16946040bc1752654e906147f7e43e9d539d7c3de2f" static let acala = "fc41b9bd8ef8fe53d58c7ea67c794c7ec9a73daf05e6d54b14ff6342c99ba64c" - static let statemint = "68d56f15f85d3136970ec16946040bc1752654e906147f7e43e9d539d7c3de2f" static let edgeware = "742a2ca70c2fda6cee4f8df98d64c4c670a052d9568058982dad9d5a7a135c5b" static let karura = "baf5aabe40646d11f0ee8abbdc64f4a4b7674925cba08e4a05ff9ebed6e2126b" static let nodle = "97da7ede98d7bad4e36b4d734b6055425a3be036da2a332ea5a7037656427a21" diff --git a/novawallet/Common/Services/AssetExchange/AssetExchangePathFilter.swift b/novawallet/Common/Services/AssetExchange/AssetExchangePathFilter.swift index 673182395..aac5d7f6f 100644 --- a/novawallet/Common/Services/AssetExchange/AssetExchangePathFilter.swift +++ b/novawallet/Common/Services/AssetExchange/AssetExchangePathFilter.swift @@ -49,6 +49,10 @@ extension AssetExchangePathFilter: GraphEdgeFiltering { return true } + if edge.requiresOriginKeepAliveOnIntermediatePosition() { + return false + } + let canPayFees = (chainAssetIn.isUtilityAsset || feeSupport.canPayFee(inNonNative: chainAssetIn)) && edge.canPayNonNativeFeesInIntermediatePosition() diff --git a/novawallet/Common/Services/AssetExchange/AssetHubExchange/AssetHubExchangeEdge.swift b/novawallet/Common/Services/AssetExchange/AssetHubExchange/AssetHubExchangeEdge.swift index c8cb786bf..af5debf9d 100644 --- a/novawallet/Common/Services/AssetExchange/AssetHubExchange/AssetHubExchangeEdge.swift +++ b/novawallet/Common/Services/AssetExchange/AssetHubExchange/AssetHubExchangeEdge.swift @@ -78,6 +78,10 @@ extension AssetHubExchangeEdge: AssetExchangableGraphEdge { true } + func requiresOriginKeepAliveOnIntermediatePosition() -> Bool { + false + } + func beginMetaOperation( for amountIn: Balance, amountOut: Balance diff --git a/novawallet/Common/Services/AssetExchange/AssetHubExchange/AssetHubExchangeMetaOperation.swift b/novawallet/Common/Services/AssetExchange/AssetHubExchange/AssetHubExchangeMetaOperation.swift index 080045f30..21cce1d78 100644 --- a/novawallet/Common/Services/AssetExchange/AssetHubExchange/AssetHubExchangeMetaOperation.swift +++ b/novawallet/Common/Services/AssetExchange/AssetHubExchange/AssetHubExchangeMetaOperation.swift @@ -4,4 +4,5 @@ final class AssetHubExchangeMetaOperation: AssetExchangeBaseMetaOperation {} extension AssetHubExchangeMetaOperation: AssetExchangeMetaOperationProtocol { var label: AssetExchangeMetaOperationLabel { .swap } + var requiresOriginAccountKeepAlive: Bool { false } } diff --git a/novawallet/Common/Services/AssetExchange/AssetsExchangePathCostEstimator.swift b/novawallet/Common/Services/AssetExchange/AssetsExchangePathCostEstimator.swift index 322071a51..e03d8d077 100644 --- a/novawallet/Common/Services/AssetExchange/AssetsExchangePathCostEstimator.swift +++ b/novawallet/Common/Services/AssetExchange/AssetsExchangePathCostEstimator.swift @@ -32,7 +32,7 @@ extension AssetsExchangePathCostEstimator: AssetsExchangePathCostEstimating { ) -> CompoundOperationWrapper { let operation = ClosureOperation { guard let usdtTiedAsset = self.chainRegistry.getChain( - for: KnowChainId.statemint + for: KnowChainId.polkadotAssetHub )?.chainAssetForSymbol("USDT") else { return .zero } diff --git a/novawallet/Common/Services/AssetExchange/Common/AnyAssetExchangeEdge.swift b/novawallet/Common/Services/AssetExchange/Common/AnyAssetExchangeEdge.swift index b96a0ac4d..69cffb555 100644 --- a/novawallet/Common/Services/AssetExchange/Common/AnyAssetExchangeEdge.swift +++ b/novawallet/Common/Services/AssetExchange/Common/AnyAssetExchangeEdge.swift @@ -16,6 +16,7 @@ class AnyAssetExchangeEdge { private let shouldIgnoreFeeRequirementClosure: (any AssetExchangableGraphEdge) -> Bool private let canPayFeesInIntermediatePositionClosure: () -> Bool + private let requiresKeepAliveOnIntermediatePositionClosure: () -> Bool private let typeClosure: () -> AssetExchangeEdgeType private let beginMetaOperationClosure: (Balance, Balance) throws -> AssetExchangeMetaOperationProtocol @@ -37,6 +38,7 @@ class AnyAssetExchangeEdge { appendToOperationClosure = edge.appendToOperation shouldIgnoreFeeRequirementClosure = edge.shouldIgnoreFeeRequirement canPayFeesInIntermediatePositionClosure = edge.canPayNonNativeFeesInIntermediatePosition + requiresKeepAliveOnIntermediatePositionClosure = edge.requiresOriginKeepAliveOnIntermediatePosition typeClosure = { edge.type } beginMetaOperationClosure = edge.beginMetaOperation appendToMetaOperationClosure = edge.appendToMetaOperation @@ -74,6 +76,10 @@ extension AnyAssetExchangeEdge: AssetExchangableGraphEdge { canPayFeesInIntermediatePositionClosure() } + func requiresOriginKeepAliveOnIntermediatePosition() -> Bool { + requiresKeepAliveOnIntermediatePositionClosure() + } + func beginMetaOperation(for amountIn: Balance, amountOut: Balance) throws -> AssetExchangeMetaOperationProtocol { try beginMetaOperationClosure(amountIn, amountOut) } diff --git a/novawallet/Common/Services/AssetExchange/Common/AssetExchangeGraphEdge.swift b/novawallet/Common/Services/AssetExchange/Common/AssetExchangeGraphEdge.swift index fdc6cfe76..d7cd4680a 100644 --- a/novawallet/Common/Services/AssetExchange/Common/AssetExchangeGraphEdge.swift +++ b/novawallet/Common/Services/AssetExchange/Common/AssetExchangeGraphEdge.swift @@ -13,6 +13,8 @@ protocol AssetExchangableGraphEdge: GraphQuotableEdge { func canPayNonNativeFeesInIntermediatePosition() -> Bool + func requiresOriginKeepAliveOnIntermediatePosition() -> Bool + var type: AssetExchangeEdgeType { get } func beginMetaOperation(for amountIn: Balance, amountOut: Balance) throws -> AssetExchangeMetaOperationProtocol diff --git a/novawallet/Common/Services/AssetExchange/Common/AssetExchangeMetaOperationProtocol.swift b/novawallet/Common/Services/AssetExchange/Common/AssetExchangeMetaOperationProtocol.swift index cf39e758b..4acde416d 100644 --- a/novawallet/Common/Services/AssetExchange/Common/AssetExchangeMetaOperationProtocol.swift +++ b/novawallet/Common/Services/AssetExchange/Common/AssetExchangeMetaOperationProtocol.swift @@ -3,6 +3,15 @@ import Foundation enum AssetExchangeMetaOperationLabel: Equatable { case swap case transfer + + var isTransfer: Bool { + switch self { + case .transfer: + true + case .swap: + false + } + } } protocol AssetExchangeMetaOperationProtocol { @@ -11,6 +20,7 @@ protocol AssetExchangeMetaOperationProtocol { var amountIn: Balance { get } var amountOut: Balance { get } var label: AssetExchangeMetaOperationLabel { get } + var requiresOriginAccountKeepAlive: Bool { get } } class AssetExchangeBaseMetaOperation { diff --git a/novawallet/Common/Services/AssetExchange/CrosschainExchange/CrosschainAssetsExchangeProvider.swift b/novawallet/Common/Services/AssetExchange/CrosschainExchange/CrosschainAssetsExchangeProvider.swift index 6b3811d1b..225a71bea 100644 --- a/novawallet/Common/Services/AssetExchange/CrosschainExchange/CrosschainAssetsExchangeProvider.swift +++ b/novawallet/Common/Services/AssetExchange/CrosschainExchange/CrosschainAssetsExchangeProvider.swift @@ -10,12 +10,14 @@ final class CrosschainAssetsExchangeProvider: AssetsExchangeBaseProvider { let userStorageFacade: StorageFacadeProtocol let substrateStorageFacade: StorageFacadeProtocol let signingWrapperFactory: SigningWrapperFactoryProtocol + let fungibilityPreservationProvider: AssetFungibilityPreservationProviding init( wallet: MetaAccountModel, syncService: XcmTransfersSyncServiceProtocol, chainRegistry: ChainRegistryProtocol, pathCostEstimator: AssetsExchangePathCostEstimating, + fungibilityPreservationProvider: AssetFungibilityPreservationProviding, signingWrapperFactory: SigningWrapperFactoryProtocol, userStorageFacade: StorageFacadeProtocol, substrateStorageFacade: StorageFacadeProtocol, @@ -27,6 +29,7 @@ final class CrosschainAssetsExchangeProvider: AssetsExchangeBaseProvider { self.signingWrapperFactory = signingWrapperFactory self.userStorageFacade = userStorageFacade self.substrateStorageFacade = substrateStorageFacade + self.fungibilityPreservationProvider = fungibilityPreservationProvider super.init( chainRegistry: chainRegistry, @@ -90,6 +93,7 @@ final class CrosschainAssetsExchangeProvider: AssetsExchangeBaseProvider { ), xcmTransfers: xcmTransfers, executionTimeEstimator: AssetExchangeTimeEstimator(chainRegistry: chainRegistry), + fungibilityPreservationProvider: fungibilityPreservationProvider, operationQueue: operationQueue, logger: logger ) diff --git a/novawallet/Common/Services/AssetExchange/CrosschainExchange/CrosschainExchangeEdge.swift b/novawallet/Common/Services/AssetExchange/CrosschainExchange/CrosschainExchangeEdge.swift index b820635a0..2b999ead9 100644 --- a/novawallet/Common/Services/AssetExchange/CrosschainExchange/CrosschainExchangeEdge.swift +++ b/novawallet/Common/Services/AssetExchange/CrosschainExchange/CrosschainExchangeEdge.swift @@ -72,6 +72,18 @@ extension CrosschainExchangeEdge: AssetExchangableGraphEdge { deliveryFeeNotPaidOrFromHolding() } + func requiresOriginKeepAliveOnIntermediatePosition() -> Bool { + guard + let chainIn = host.allChains[origin.chainId], + let chainAssetIn = chainIn.chainAsset(for: origin.assetId) else { + return false + } + + return host.fungibilityPreservationProvider.requiresPreservationForCrosschain( + assetIn: chainAssetIn + ) + } + func beginMetaOperation( for amountIn: Balance, amountOut: Balance @@ -92,11 +104,16 @@ extension CrosschainExchangeEdge: AssetExchangableGraphEdge { throw ChainModelFetchError.noAsset(assetId: destination.assetId) } + let keepAlive = host.fungibilityPreservationProvider.requiresPreservationForCrosschain( + assetIn: assetIn + ) + return CrosschainExchangeMetaOperation( assetIn: assetIn, assetOut: assetOut, amountIn: amountIn, - amountOut: amountOut + amountOut: amountOut, + requiresOriginAccountKeepAlive: keepAlive ) } diff --git a/novawallet/Common/Services/AssetExchange/CrosschainExchange/CrosschainExchangeHost.swift b/novawallet/Common/Services/AssetExchange/CrosschainExchange/CrosschainExchangeHost.swift index c331341ba..730b79999 100644 --- a/novawallet/Common/Services/AssetExchange/CrosschainExchange/CrosschainExchangeHost.swift +++ b/novawallet/Common/Services/AssetExchange/CrosschainExchange/CrosschainExchangeHost.swift @@ -10,6 +10,7 @@ protocol CrosschainExchangeHostProtocol { var xcmTransfers: XcmTransfers { get } var operationQueue: OperationQueue { get } var executionTimeEstimator: AssetExchangeTimeEstimating { get } + var fungibilityPreservationProvider: AssetFungibilityPreservationProviding { get } var logger: LoggerProtocol { get } } @@ -22,6 +23,7 @@ final class CrosschainExchangeHost: CrosschainExchangeHostProtocol { let resolutionFactory: XcmTransferResolutionFactoryProtocol let xcmTransfers: XcmTransfers let executionTimeEstimator: AssetExchangeTimeEstimating + let fungibilityPreservationProvider: AssetFungibilityPreservationProviding let operationQueue: OperationQueue let logger: LoggerProtocol @@ -34,6 +36,7 @@ final class CrosschainExchangeHost: CrosschainExchangeHostProtocol { resolutionFactory: XcmTransferResolutionFactoryProtocol, xcmTransfers: XcmTransfers, executionTimeEstimator: AssetExchangeTimeEstimating, + fungibilityPreservationProvider: AssetFungibilityPreservationProviding, operationQueue: OperationQueue, logger: LoggerProtocol ) { @@ -45,6 +48,7 @@ final class CrosschainExchangeHost: CrosschainExchangeHostProtocol { self.resolutionFactory = resolutionFactory self.xcmTransfers = xcmTransfers self.executionTimeEstimator = executionTimeEstimator + self.fungibilityPreservationProvider = fungibilityPreservationProvider self.operationQueue = operationQueue self.logger = logger } diff --git a/novawallet/Common/Services/AssetExchange/CrosschainExchange/CrosschainExchangeMetaOperation.swift b/novawallet/Common/Services/AssetExchange/CrosschainExchange/CrosschainExchangeMetaOperation.swift index 9b0cdf7ec..aaa0c4d1a 100644 --- a/novawallet/Common/Services/AssetExchange/CrosschainExchange/CrosschainExchangeMetaOperation.swift +++ b/novawallet/Common/Services/AssetExchange/CrosschainExchange/CrosschainExchangeMetaOperation.swift @@ -1,6 +1,25 @@ import Foundation -final class CrosschainExchangeMetaOperation: AssetExchangeBaseMetaOperation {} +final class CrosschainExchangeMetaOperation: AssetExchangeBaseMetaOperation { + let requiresOriginAccountKeepAlive: Bool + + init( + assetIn: ChainAsset, + assetOut: ChainAsset, + amountIn: Balance, + amountOut: Balance, + requiresOriginAccountKeepAlive: Bool + ) { + self.requiresOriginAccountKeepAlive = requiresOriginAccountKeepAlive + + super.init( + assetIn: assetIn, + assetOut: assetOut, + amountIn: amountIn, + amountOut: amountOut + ) + } +} extension CrosschainExchangeMetaOperation: AssetExchangeMetaOperationProtocol { var label: AssetExchangeMetaOperationLabel { .transfer } diff --git a/novawallet/Common/Services/AssetExchange/CrosschainExchange/CrosschainExchangeOperationPrototype.swift b/novawallet/Common/Services/AssetExchange/CrosschainExchange/CrosschainExchangeOperationPrototype.swift index 1ffdc3e92..c9c3b8d6a 100644 --- a/novawallet/Common/Services/AssetExchange/CrosschainExchange/CrosschainExchangeOperationPrototype.swift +++ b/novawallet/Common/Services/AssetExchange/CrosschainExchange/CrosschainExchangeOperationPrototype.swift @@ -26,7 +26,7 @@ final class CrosschainExchangeOperationPrototype: AssetExchangeBaseOperationProt private extension CrosschainExchangeOperationPrototype { private func isChainWithExpensiveCrossChain(chainId: ChainModel.Id) -> Bool { - chainId == KnowChainId.polkadot || chainId == KnowChainId.statemint + chainId == KnowChainId.polkadot || chainId == KnowChainId.polkadotAssetHub } } diff --git a/novawallet/Common/Services/AssetExchange/Facade/AssetExchangeFacade.swift b/novawallet/Common/Services/AssetExchange/Facade/AssetExchangeFacade.swift index 5ced93030..5b55794f1 100644 --- a/novawallet/Common/Services/AssetExchange/Facade/AssetExchangeFacade.swift +++ b/novawallet/Common/Services/AssetExchange/Facade/AssetExchangeFacade.swift @@ -22,6 +22,7 @@ final class AssetExchangeFacade { ), chainRegistry: params.chainRegistry, pathCostEstimator: pathCostEstimator, + fungibilityPreservationProvider: AssetFungibilityPreservationProvider.createFromKnownChains(), signingWrapperFactory: params.signingWrapperFactory, userStorageFacade: params.userDataStorageFacade, substrateStorageFacade: params.substrateStorageFacade, diff --git a/novawallet/Common/Services/AssetExchange/HydraExchange/AssetsHydraExchangeEdge.swift b/novawallet/Common/Services/AssetExchange/HydraExchange/AssetsHydraExchangeEdge.swift index 9ee1abf16..b00ae0b6f 100644 --- a/novawallet/Common/Services/AssetExchange/HydraExchange/AssetsHydraExchangeEdge.swift +++ b/novawallet/Common/Services/AssetExchange/HydraExchange/AssetsHydraExchangeEdge.swift @@ -52,6 +52,10 @@ class AssetsHydraExchangeEdge { true } + func requiresOriginKeepAliveOnIntermediatePosition() -> Bool { + false + } + func beginMetaOperation( for amountIn: Balance, amountOut: Balance diff --git a/novawallet/Common/Services/AssetExchange/HydraExchange/AssetsHydraExchangeProvider.swift b/novawallet/Common/Services/AssetExchange/HydraExchange/AssetsHydraExchangeProvider.swift index 757a96bc1..f18746a67 100644 --- a/novawallet/Common/Services/AssetExchange/HydraExchange/AssetsHydraExchangeProvider.swift +++ b/novawallet/Common/Services/AssetExchange/HydraExchange/AssetsHydraExchangeProvider.swift @@ -227,7 +227,10 @@ final class AssetsHydraExchangeProvider: AssetsExchangeBaseProvider { let omnipoolExchange = createOmnipoolExchange(from: swapHost, registeringStateIn: exchangeStateRegistrar) - let stableswapExchange = createStableswapExchange(from: swapHost, registeringStateIn: exchangeStateRegistrar) + let stableswapExchange = createStableswapExchange( + from: swapHost, + registeringStateIn: exchangeStateRegistrar + ) let xykExchange = createXYKExchange(from: swapHost, registeringStateIn: exchangeStateRegistrar) diff --git a/novawallet/Common/Services/AssetExchange/HydraExchange/HydraExchangeMetaOperation.swift b/novawallet/Common/Services/AssetExchange/HydraExchange/HydraExchangeMetaOperation.swift index 10ce0f3a8..ba3988c48 100644 --- a/novawallet/Common/Services/AssetExchange/HydraExchange/HydraExchangeMetaOperation.swift +++ b/novawallet/Common/Services/AssetExchange/HydraExchange/HydraExchangeMetaOperation.swift @@ -4,4 +4,5 @@ class HydraExchangeMetaOperation: AssetExchangeBaseMetaOperation {} extension HydraExchangeMetaOperation: AssetExchangeMetaOperationProtocol { var label: AssetExchangeMetaOperationLabel { .swap } + var requiresOriginAccountKeepAlive: Bool { false } } diff --git a/novawallet/Common/Substrate/AssetConverters/ParachainResolver.swift b/novawallet/Common/Substrate/AssetConverters/ParachainResolver.swift index a530d247a..d6866ad23 100644 --- a/novawallet/Common/Substrate/AssetConverters/ParachainResolver.swift +++ b/novawallet/Common/Substrate/AssetConverters/ParachainResolver.swift @@ -21,9 +21,9 @@ final class ParachainResolver: ParachainResolving { switch relaychainId { case KnowChainId.polkadot: - return .createWithResult(KnowChainId.statemint) + return .createWithResult(KnowChainId.polkadotAssetHub) case KnowChainId.kusama: - return .createWithResult(KnowChainId.statemine) + return .createWithResult(KnowChainId.kusamaAssetHub) default: return .createWithResult(nil) } diff --git a/novawallet/Common/Substrate/AssetOperations/AssetFungibilityPreservationProvider.swift b/novawallet/Common/Substrate/AssetOperations/AssetFungibilityPreservationProvider.swift new file mode 100644 index 000000000..476917e5f --- /dev/null +++ b/novawallet/Common/Substrate/AssetOperations/AssetFungibilityPreservationProvider.swift @@ -0,0 +1,38 @@ +import Foundation + +protocol AssetFungibilityPreservationProviding { + func requiresPreservationForCrosschain(assetIn: ChainAsset) -> Bool +} + +final class AssetFungibilityPreservationProvider { + let allAssets: Set + let concreteAssets: Set + + init(allAssets: Set, concreteAssets: Set) { + self.allAssets = allAssets + self.concreteAssets = concreteAssets + } +} + +extension AssetFungibilityPreservationProvider: AssetFungibilityPreservationProviding { + func requiresPreservationForCrosschain(assetIn: ChainAsset) -> Bool { + allAssets.contains(assetIn.chain.chainId) || concreteAssets.contains(assetIn.chainAssetId) + } +} + +extension AssetFungibilityPreservationProvider { + static func createFromKnownChains() -> AssetFungibilityPreservationProvider { + AssetFungibilityPreservationProvider( + allAssets: [ + KnowChainId.polkadotAssetHub, + KnowChainId.kusamaAssetHub + ], + concreteAssets: [ + ChainAssetId( + chainId: KnowChainId.astar, + assetId: AssetModel.utilityAssetId + ) + ] + ) + } +} diff --git a/novawallet/Modules/Transfer/Operation/AssetStorageInfoOperationFactory.swift b/novawallet/Common/Substrate/AssetOperations/AssetStorageInfoOperationFactory.swift similarity index 100% rename from novawallet/Modules/Transfer/Operation/AssetStorageInfoOperationFactory.swift rename to novawallet/Common/Substrate/AssetOperations/AssetStorageInfoOperationFactory.swift diff --git a/novawallet/Modules/Transfer/Operation/AssetTransferAggregationFactory.swift b/novawallet/Common/Substrate/AssetOperations/AssetTransferAggregationFactory.swift similarity index 100% rename from novawallet/Modules/Transfer/Operation/AssetTransferAggregationFactory.swift rename to novawallet/Common/Substrate/AssetOperations/AssetTransferAggregationFactory.swift diff --git a/novawallet/Modules/DApp/DAppOperationConfirm/DAppOperationConfirmInteractor+Proccessing.swift b/novawallet/Modules/DApp/DAppOperationConfirm/DAppOperationConfirmInteractor+Proccessing.swift index f45ab0d7d..212fa16fb 100644 --- a/novawallet/Modules/DApp/DAppOperationConfirm/DAppOperationConfirmInteractor+Proccessing.swift +++ b/novawallet/Modules/DApp/DAppOperationConfirm/DAppOperationConfirmInteractor+Proccessing.swift @@ -61,7 +61,10 @@ extension DAppOperationConfirmInteractor { let extrinsic = try extrinsicOperation.extractNoCancellableResultData() - guard let extrinsicAccountId = try? extrinsic.address.toAccountId(using: chain.chainFormat) else { + guard + let extrinsicAccountId = try? extrinsic.address.toChainAccountIdOrSubstrateGeneric( + using: chain.chainFormat + ) else { throw DAppOperationConfirmInteractorError.extrinsicBadField(name: "address: \(extrinsic.address)") } @@ -72,13 +75,6 @@ extension DAppOperationConfirmInteractor { throw ChainAccountFetchingError.accountNotExists } - guard accountResponse.toAddress() == extrinsic.address else { - throw DAppOperationConfirmInteractorError.addressMismatch( - actual: extrinsic.address, - expected: accountResponse.toAddress() ?? "" - ) - } - guard let specVersion = BigUInt.fromHexString(extrinsic.specVersion) else { throw DAppOperationConfirmInteractorError.extrinsicBadField(name: "specVersion") diff --git a/novawallet/Modules/Nft/Services/ChainModel+Nft.swift b/novawallet/Modules/Nft/Services/ChainModel+Nft.swift index 4dfbb2151..230c81abb 100644 --- a/novawallet/Modules/Nft/Services/ChainModel+Nft.swift +++ b/novawallet/Modules/Nft/Services/ChainModel+Nft.swift @@ -5,13 +5,13 @@ extension ChainModel { switch chainId { case KnowChainId.kusama: return [NftSource(chainId: chainId, type: .rmrkV2)] - case KnowChainId.statemine: + case KnowChainId.kusamaAssetHub: return [ NftSource(chainId: chainId, type: .kodadot) ] case KnowChainId.polkadot: return [NftSource(chainId: chainId, type: .pdc20)] - case KnowChainId.statemint: + case KnowChainId.polkadotAssetHub: return [NftSource(chainId: chainId, type: .kodadot)] default: return [] diff --git a/novawallet/Modules/Nft/Services/KodaDot/KodaDotNftOperationFactory.swift b/novawallet/Modules/Nft/Services/KodaDot/KodaDotNftOperationFactory.swift index 3fd8330b2..d86717267 100644 --- a/novawallet/Modules/Nft/Services/KodaDot/KodaDotNftOperationFactory.swift +++ b/novawallet/Modules/Nft/Services/KodaDot/KodaDotNftOperationFactory.swift @@ -13,9 +13,9 @@ enum KodaDotAssetHubApi { static func apiForChain(_ chainId: ChainModel.Id) -> URL? { switch chainId { - case KnowChainId.statemine: + case KnowChainId.kusamaAssetHub: return KodaDotAssetHubApi.kusamaAssetHub - case KnowChainId.statemint: + case KnowChainId.polkadotAssetHub: return KodaDotAssetHubApi.polkadotAssetHub default: return nil diff --git a/novawallet/Modules/Swaps/Base/Model/SwapMaxModel.swift b/novawallet/Modules/Swaps/Base/Model/SwapMaxModel.swift index b3615485c..38519cd1d 100644 --- a/novawallet/Modules/Swaps/Base/Model/SwapMaxModel.swift +++ b/novawallet/Modules/Swaps/Base/Model/SwapMaxModel.swift @@ -6,12 +6,16 @@ struct SwapMaxModel { let receiveChainAsset: ChainAsset? let balance: AssetBalance? let feeModel: AssetExchangeFee? + let quote: AssetExchangeQuote? let payAssetExistense: AssetBalanceExistence? let receiveAssetExistense: AssetBalanceExistence? let accountInfo: AccountInfo? - func minBalanceCoveredByFrozen(in balance: AssetBalance) -> Bool { - let minBalance = payAssetExistense?.minBalance ?? 0 + func minBalanceCoveredByFrozen( + in balance: AssetBalance, + assetExistence: AssetBalanceExistence? + ) -> Bool { + let minBalance = assetExistence?.minBalance ?? 0 return balance.transferable + minBalance <= balance.balanceCountingEd } @@ -35,6 +39,10 @@ struct SwapMaxModel { accountInfo?.hasConsumers ?? false } + var needMinBalanceDueToOriginKeepAlive: Bool { + quote?.metaOperations.first?.requiresOriginAccountKeepAlive ?? false + } + var needMinBalanceDueToPostsubmissionFee: Bool { guard let payChainAsset, payChainAsset.isUtilityAsset, @@ -45,16 +53,21 @@ struct SwapMaxModel { return feeModel.hasOriginPostSubmissionByAccount } - var shouldKeepMinBalance: Bool { - needMinBalanceDueConsumers || + var shouldKeepNativeMinBalance: Bool { + needMinBalanceDueToOriginKeepAlive || + needMinBalanceDueConsumers || needMinBalanceDueToPostsubmissionFee || needMinBalanceDueToReceiveInsufficiency } + var shouldKeepCustomMinBalance: Bool { + needMinBalanceDueToOriginKeepAlive + } + private func calculateForNativeAsset(_ payChainAsset: ChainAsset, balance: AssetBalance) -> Decimal { var maxAmount = balance.transferable - if shouldKeepMinBalance, !minBalanceCoveredByFrozen(in: balance) { + if shouldKeepNativeMinBalance, !minBalanceCoveredByFrozen(in: balance, assetExistence: payAssetExistense) { let minBalance = payAssetExistense?.minBalance ?? 0 maxAmount = maxAmount.subtractOrZero(minBalance) } @@ -68,8 +81,17 @@ struct SwapMaxModel { } private func calculateForCustomAsset(_ payChainAsset: ChainAsset, balance: AssetBalance) -> Decimal { + var maxAmount = balance.transferable + + if + shouldKeepCustomMinBalance, + !minBalanceCoveredByFrozen(in: balance, assetExistence: payAssetExistense) { + let minBalance = payAssetExistense?.minBalance ?? 0 + maxAmount = maxAmount.subtractOrZero(minBalance) + } + guard let feeModel = feeModel else { - return balance.transferable.decimal(precision: payChainAsset.asset.precision) + return maxAmount.decimal(precision: payChainAsset.asset.precision) } let fee: Balance = if payChainAsset.chainAssetId == feeChainAsset?.chainAssetId { @@ -78,9 +100,7 @@ struct SwapMaxModel { feeModel.postSubmissionFeeInAssetIn(payChainAsset) } - let maxAmount = balance.transferable.subtractOrZero(fee) - - return maxAmount.decimal(precision: payChainAsset.asset.precision) + return maxAmount.subtractOrZero(fee).decimal(precision: payChainAsset.asset.precision) } func calculate() -> Decimal { diff --git a/novawallet/Modules/Swaps/Base/SwapBasePresenter.swift b/novawallet/Modules/Swaps/Base/SwapBasePresenter.swift index cca2f5785..9153503cf 100644 --- a/novawallet/Modules/Swaps/Base/SwapBasePresenter.swift +++ b/novawallet/Modules/Swaps/Base/SwapBasePresenter.swift @@ -123,6 +123,7 @@ class SwapBasePresenter { receiveChainAsset: getReceiveChainAsset(), balance: payAssetBalance, feeModel: fee, + quote: quote, payAssetExistense: payAssetBalanceExistense, receiveAssetExistense: receiveAssetBalanceExistense, accountInfo: accountInfo diff --git a/novawallet/Modules/Swaps/Execution/SwapExecutionInteractor.swift b/novawallet/Modules/Swaps/Execution/SwapExecutionInteractor.swift index 957299b62..ef2ff18bb 100644 --- a/novawallet/Modules/Swaps/Execution/SwapExecutionInteractor.swift +++ b/novawallet/Modules/Swaps/Execution/SwapExecutionInteractor.swift @@ -4,19 +4,24 @@ final class SwapExecutionInteractor { weak var presenter: SwapExecutionInteractorOutputProtocol? let assetsExchangeService: AssetsExchangeServiceProtocol + let osMediator: OperatingSystemMediating let operationQueue: OperationQueue init( assetsExchangeService: AssetsExchangeServiceProtocol, + osMediator: OperatingSystemMediating, operationQueue: OperationQueue ) { self.assetsExchangeService = assetsExchangeService + self.osMediator = osMediator self.operationQueue = operationQueue } } extension SwapExecutionInteractor: SwapExecutionInteractorInputProtocol { func submit(using estimation: AssetExchangeFee) { + osMediator.disableScreenSleep() + let wrapper = assetsExchangeService.submit( using: estimation, notifyingIn: .main @@ -29,6 +34,8 @@ extension SwapExecutionInteractor: SwapExecutionInteractorInputProtocol { inOperationQueue: operationQueue, runningCallbackIn: .main ) { [weak self] result in + self?.osMediator.enableScreenSleep() + switch result { case let .success(amount): self?.presenter?.didCompleteFullExecution(received: amount) diff --git a/novawallet/Modules/Swaps/Execution/SwapExecutionViewFactory.swift b/novawallet/Modules/Swaps/Execution/SwapExecutionViewFactory.swift index 1e15f7834..cd32822e6 100644 --- a/novawallet/Modules/Swaps/Execution/SwapExecutionViewFactory.swift +++ b/novawallet/Modules/Swaps/Execution/SwapExecutionViewFactory.swift @@ -11,6 +11,7 @@ struct SwapExecutionViewFactory { let interactor = SwapExecutionInteractor( assetsExchangeService: flowState.setupAssetExchangeService(), + osMediator: OperatingSystemMediator(), operationQueue: OperationManagerFacade.sharedDefaultQueue ) diff --git a/novawallet/Modules/Swaps/Validation/SwapDataValidatorFactory.swift b/novawallet/Modules/Swaps/Validation/SwapDataValidatorFactory.swift index b7eda845a..46bf6b03f 100644 --- a/novawallet/Modules/Swaps/Validation/SwapDataValidatorFactory.swift +++ b/novawallet/Modules/Swaps/Validation/SwapDataValidatorFactory.swift @@ -161,11 +161,22 @@ final class SwapDataValidatorFactory: SwapDataValidatorFactoryProtocol { ).value(for: locale) } - self?.presentable.presentMinBalanceViolatedDueDeliveryFee( + self?.presentable.presentMinBalanceViolatedAfterOperation( from: view, minBalance: minBalance ?? "", locale: locale ) + case let .originKeepAlive(model): + let minBalance = viewModelFactory.amountFromValue( + targetAssetInfo: params.payChainAsset.assetDisplayInfo, + value: model.minBalance + ).value(for: locale) + + self?.presentable.presentMinBalanceViolatedAfterOperation( + from: view, + minBalance: minBalance, + locale: locale + ) } }, preservesCondition: { insufficientReason == nil diff --git a/novawallet/Modules/Swaps/Validation/SwapErrorPresentable.swift b/novawallet/Modules/Swaps/Validation/SwapErrorPresentable.swift index a7afa3b34..f929355f3 100644 --- a/novawallet/Modules/Swaps/Validation/SwapErrorPresentable.swift +++ b/novawallet/Modules/Swaps/Validation/SwapErrorPresentable.swift @@ -39,7 +39,7 @@ protocol SwapErrorPresentable: BaseErrorPresentable { locale: Locale ) - func presentMinBalanceViolatedDueDeliveryFee( + func presentMinBalanceViolatedAfterOperation( from view: ControllerBackedProtocol, minBalance: String, locale: Locale @@ -128,7 +128,7 @@ extension SwapErrorPresentable where Self: AlertPresentable & ErrorPresentable { present(message: message, title: title, closeAction: closeAction, from: view) } - func presentMinBalanceViolatedDueDeliveryFee( + func presentMinBalanceViolatedAfterOperation( from view: ControllerBackedProtocol, minBalance: String, locale: Locale diff --git a/novawallet/Modules/Swaps/Validation/SwapModel.swift b/novawallet/Modules/Swaps/Validation/SwapModel.swift index 2cc9424ed..b6015e33a 100644 --- a/novawallet/Modules/Swaps/Validation/SwapModel.swift +++ b/novawallet/Modules/Swaps/Validation/SwapModel.swift @@ -25,11 +25,16 @@ struct SwapModel { let minBalance: Decimal } + struct InsufficientDueOriginKeepAlive { + let minBalance: Decimal + } + enum InsufficientBalanceReason { case amountToHigh(InsufficientDueBalance) case feeInNativeAsset(InsufficientDueNativeFee) case feeInPayAsset(InsufficientDuePayAssetFee) case deliveryFee(InsufficientDueDeliveryFee) + case originKeepAlive(InsufficientDueOriginKeepAlive) case violatingConsumers(InsufficientDueConsumers) } @@ -207,6 +212,23 @@ struct SwapModel { return .deliveryFee(model) } + func checkEnoughBalanceForOriginKeepAlive() -> InsufficientBalanceReason? { + guard + let firstOperation = quote?.metaOperations.first, + firstOperation.requiresOriginAccountKeepAlive, + payTokenProviderWillBeKilled else { + return nil + } + + let minBalance = payAssetExistense?.minBalance.decimal( + assetInfo: payChainAsset.assetDisplayInfo + ) ?? 0 + + let model = InsufficientDueOriginKeepAlive(minBalance: minBalance) + + return .originKeepAlive(model) + } + func checkBalanceSufficiency() -> InsufficientBalanceReason? { if let insufficient = checkEnoughBalanceToSpend() { return insufficient @@ -224,6 +246,10 @@ struct SwapModel { return insufficient } + if let insufficient = checkEnoughBalanceForOriginKeepAlive() { + return insufficient + } + if let insufficient = checkNotViolatingConsumers() { return insufficient } @@ -253,6 +279,12 @@ struct SwapModel { return totalInNativeAsset.subtractOrZero(feeInNativeAsset) < minBalance } + var payTokenProviderWillBeKilled: Bool { + let minBalance = payAssetExistense?.minBalance ?? 0 + + return payAssetTotalBalanceAfterSwap < minBalance + } + func checkReceiveBalanceAboveMin() -> CannotReceiveReason? { let amountAfterSwap = (receiveAssetBalance?.balanceCountingEd ?? 0) + (quote?.route.amountOut ?? 0) let minBalance = receiveAssetExistense?.minBalance ?? 0 diff --git a/novawallet/Modules/Transfer/BaseTransfer/CrossChain/CrossChainTransferInteractor.swift b/novawallet/Modules/Transfer/BaseTransfer/CrossChain/CrossChainTransferInteractor.swift index c0144c2a9..368a019b5 100644 --- a/novawallet/Modules/Transfer/BaseTransfer/CrossChain/CrossChainTransferInteractor.swift +++ b/novawallet/Modules/Transfer/BaseTransfer/CrossChain/CrossChainTransferInteractor.swift @@ -25,6 +25,7 @@ class CrossChainTransferInteractor: RuntimeConstantFetching { let walletLocalSubscriptionFactory: WalletLocalSubscriptionFactoryProtocol let priceLocalSubscriptionFactory: PriceProviderFactoryProtocol let substrateStorageFacade: StorageFacadeProtocol + let fungibilityPreservationProvider: AssetFungibilityPreservationProviding let operationQueue: OperationQueue private lazy var callFactory = SubstrateCallFactory() @@ -71,6 +72,7 @@ class CrossChainTransferInteractor: RuntimeConstantFetching { feeProxy: XcmExtrinsicFeeProxyProtocol, extrinsicService: XcmTransferServiceProtocol, resolutionFactory: XcmTransferResolutionFactoryProtocol, + fungibilityPreservationProvider: AssetFungibilityPreservationProviding, walletRemoteWrapper: WalletRemoteSubscriptionWrapperProtocol, walletLocalSubscriptionFactory: WalletLocalSubscriptionFactoryProtocol, priceLocalSubscriptionFactory: PriceProviderFactoryProtocol, @@ -86,6 +88,7 @@ class CrossChainTransferInteractor: RuntimeConstantFetching { self.feeProxy = feeProxy self.extrinsicService = extrinsicService self.resolutionFactory = resolutionFactory + self.fungibilityPreservationProvider = fungibilityPreservationProvider self.walletRemoteWrapper = walletRemoteWrapper self.walletLocalSubscriptionFactory = walletLocalSubscriptionFactory self.priceLocalSubscriptionFactory = priceLocalSubscriptionFactory @@ -231,6 +234,7 @@ class CrossChainTransferInteractor: RuntimeConstantFetching { setupUtilityAssetPriceProviderIfNeeded() provideMinBalance() + provideOriginRequiresKeepAlive() presenter?.didCompleteSetup(result: .success(())) } @@ -356,6 +360,14 @@ class CrossChainTransferInteractor: RuntimeConstantFetching { } } + private func provideOriginRequiresKeepAlive() { + let keepAlive = fungibilityPreservationProvider.requiresPreservationForCrosschain( + assetIn: originChainAsset + ) + + presenter?.didReceiveRequiresOriginKeepAlive(keepAlive) + } + private func cancelSetupCall() { let cancellingCall = setupCall setupCall = nil diff --git a/novawallet/Modules/Transfer/BaseTransfer/CrossChain/CrossChainTransferPresenter.swift b/novawallet/Modules/Transfer/BaseTransfer/CrossChain/CrossChainTransferPresenter.swift index 24e378ca2..22ee9d01e 100644 --- a/novawallet/Modules/Transfer/BaseTransfer/CrossChain/CrossChainTransferPresenter.swift +++ b/novawallet/Modules/Transfer/BaseTransfer/CrossChain/CrossChainTransferPresenter.swift @@ -33,6 +33,7 @@ class CrossChainTransferPresenter { private(set) var networkFee: ExtrinsicFeeProtocol? private(set) var crossChainFee: XcmFeeModelProtocol? + private(set) var requiresOriginKeepAlive: Bool? let networkViewModelFactory: NetworkViewModelFactoryProtocol let sendingBalanceViewModelFactory: BalanceViewModelFactoryProtocol @@ -104,6 +105,31 @@ class CrossChainTransferPresenter { fatalError("Child classes must implement this method") } + func getSendingAmount() -> Decimal? { + fatalError("Child classes must implement this method") + } + + func getTotalSpendingWithoutNetworkFee() -> Decimal? { + guard let utilityAsset = originChainAsset.chain.utilityAsset() else { + return nil + } + + let utilityAssetInfo = ChainAsset(chain: originChainAsset.chain, asset: utilityAsset).assetDisplayInfo + + let sendingAmount = getSendingAmount() + + let originDeliveryFeeSpending = isOriginUtilityTransfer ? + crossChainFee?.senderPart.decimal(assetInfo: utilityAssetInfo) : + nil + + let crosschainFeeSpending = crossChainFee?.holdingPart.decimal( + assetInfo: originChainAsset.assetDisplayInfo + ) + + return (sendingAmount ?? 0) + (originDeliveryFeeSpending ?? 0) + + (crosschainFeeSpending ?? 0) + } + func updateCrossChainFee(_ newValue: XcmFeeModelProtocol?) { crossChainFee = newValue } @@ -195,6 +221,17 @@ class CrossChainTransferPresenter { locale: selectedLocale ), + dataValidatingFactory.notViolatingKeepAlive( + for: .init( + totalSpendingAmount: getTotalSpendingWithoutNetworkFee(), + networkFee: isOriginUtilityTransfer ? networkFee : nil, + balance: senderSendingAssetBalance?.balanceCountingEd, + minBalance: originSendingMinBalance, + requiresKeepAlive: requiresOriginKeepAlive + ), + locale: selectedLocale + ), + dataValidatingFactory.receiverWillHaveAssetAccount( sendingAmount: sendingAmount, totalAmount: recepientSendingAssetBalance?.balanceCountingEd, @@ -284,6 +321,12 @@ class CrossChainTransferPresenter { destUtilityMinBalance = value } + func didReceiveRequiresOriginKeepAlive(_ value: Bool) { + logger?.debug("Requires origin keep alive: \(value)") + + requiresOriginKeepAlive = value + } + func didCompleteSetup(result _: Result) {} func didReceiveError(_: Error) {} diff --git a/novawallet/Modules/Transfer/TransferConfirm/CrossChain/TransferCrossChainConfirmInteractor.swift b/novawallet/Modules/Transfer/TransferConfirm/CrossChain/TransferCrossChainConfirmInteractor.swift index 6fa77f521..69aee61c7 100644 --- a/novawallet/Modules/Transfer/TransferConfirm/CrossChain/TransferCrossChainConfirmInteractor.swift +++ b/novawallet/Modules/Transfer/TransferConfirm/CrossChain/TransferCrossChainConfirmInteractor.swift @@ -43,6 +43,7 @@ final class TransferCrossChainConfirmInteractor: CrossChainTransferInteractor { feeProxy: feeProxy, extrinsicService: extrinsicService, resolutionFactory: resolutionFactory, + fungibilityPreservationProvider: AssetFungibilityPreservationProvider.createFromKnownChains(), walletRemoteWrapper: walletRemoteWrapper, walletLocalSubscriptionFactory: walletLocalSubscriptionFactory, priceLocalSubscriptionFactory: priceLocalSubscriptionFactory, diff --git a/novawallet/Modules/Transfer/TransferConfirm/CrossChain/TransferCrossChainConfirmPresenter.swift b/novawallet/Modules/Transfer/TransferConfirm/CrossChain/TransferCrossChainConfirmPresenter.swift index 7ec0a377d..4e07d5e96 100644 --- a/novawallet/Modules/Transfer/TransferConfirm/CrossChain/TransferCrossChainConfirmPresenter.swift +++ b/novawallet/Modules/Transfer/TransferConfirm/CrossChain/TransferCrossChainConfirmPresenter.swift @@ -158,6 +158,10 @@ final class TransferCrossChainConfirmPresenter: CrossChainTransferPresenter { // MARK: Subsclass + override func getSendingAmount() -> Decimal? { + amount + } + override func refreshOriginFee() { let assetInfo = originChainAsset.assetDisplayInfo @@ -290,7 +294,7 @@ extension TransferCrossChainConfirmPresenter: TransferConfirmPresenterProtocol { let utilityAssetInfo = ChainAsset(chain: originChainAsset.chain, asset: utilityAsset).assetDisplayInfo let validators: [DataValidating] = baseValidators( - for: amount, + for: getSendingAmount(), recepientAddress: recepientAccountAddress, utilityAssetInfo: utilityAssetInfo, selectedLocale: selectedLocale diff --git a/novawallet/Modules/Transfer/TransferSetup/CrossChain/CrossChainTransferSetupPresenter.swift b/novawallet/Modules/Transfer/TransferSetup/CrossChain/CrossChainTransferSetupPresenter.swift index a6e86a3da..5808787a0 100644 --- a/novawallet/Modules/Transfer/TransferSetup/CrossChain/CrossChainTransferSetupPresenter.swift +++ b/novawallet/Modules/Transfer/TransferSetup/CrossChain/CrossChainTransferSetupPresenter.swift @@ -200,7 +200,8 @@ final class CrossChainTransferSetupPresenter: CrossChainTransferPresenter, * before paying delivery fee. So make sure we will have at least ed and don't burn any tokens on account kill */ let hasOriginDeliveryFee = (crossChainFee?.senderPart ?? 0) > 0 - let minimumBalanceValue = hasOriginDeliveryFee && isOriginUtilityTransfer ? originSendingMinBalance ?? 0 : 0 + let keepAlive = (hasOriginDeliveryFee && isOriginUtilityTransfer) || (requiresOriginKeepAlive ?? false) + let minimumBalanceValue = keepAlive ? originSendingMinBalance ?? 0 : 0 let precision = originChainAsset.assetDisplayInfo.assetPrecision @@ -366,6 +367,10 @@ final class CrossChainTransferSetupPresenter: CrossChainTransferPresenter, _ = wireframe.present(error: error, from: view, locale: selectedLocale) } + + override func getSendingAmount() -> Decimal? { + inputResult?.absoluteValue(from: maxTransferrable()) + } } extension CrossChainTransferSetupPresenter: TransferSetupChildPresenterProtocol { @@ -420,18 +425,7 @@ extension CrossChainTransferSetupPresenter: TransferSetupChildPresenterProtocol let utilityAssetInfo = ChainAsset(chain: originChainAsset.chain, asset: utilityAsset).assetDisplayInfo - let sendingAmount = inputResult?.absoluteValue(from: maxTransferrable()) - - let originDeliveryFeeSpending = isOriginUtilityTransfer ? - crossChainFee?.senderPart.decimal(assetInfo: utilityAssetInfo) : - nil - - let crosschainFeeSpending = crossChainFee?.holdingPart.decimal( - assetInfo: originChainAsset.assetDisplayInfo - ) - - let totalSpending = (sendingAmount ?? 0) + (originDeliveryFeeSpending ?? 0) + - (crosschainFeeSpending ?? 0) + let sendingAmount = getSendingAmount() var validators: [DataValidating] = baseValidators( for: sendingAmount, @@ -447,7 +441,7 @@ extension CrossChainTransferSetupPresenter: TransferSetupChildPresenterProtocol ), dataValidatingFactory.willBeReaped( - amount: totalSpending, + amount: getTotalSpendingWithoutNetworkFee(), fee: isOriginUtilityTransfer ? networkFee : nil, totalAmount: senderSendingAssetBalance?.balanceCountingEd, minBalance: originSendingMinBalance, diff --git a/novawallet/Modules/Transfer/TransferSetup/CrossChain/CrossChainTransferSetupProtocols.swift b/novawallet/Modules/Transfer/TransferSetup/CrossChain/CrossChainTransferSetupProtocols.swift index c1840cb92..2dd7882cb 100644 --- a/novawallet/Modules/Transfer/TransferSetup/CrossChain/CrossChainTransferSetupProtocols.swift +++ b/novawallet/Modules/Transfer/TransferSetup/CrossChain/CrossChainTransferSetupProtocols.swift @@ -21,6 +21,7 @@ protocol CrossChainTransferSetupInteractorOutputProtocol: AnyObject { func didReceiveOriginSendingMinBalance(_ value: BigUInt) func didReceiveDestSendingExistence(_ value: AssetBalanceExistence) func didReceiveDestUtilityMinBalance(_ value: BigUInt) + func didReceiveRequiresOriginKeepAlive(_ value: Bool) func didCompleteSetup(result: Result) func didReceiveError(_ error: Error) } diff --git a/novawallet/Modules/Transfer/TransferSetup/CrossChain/TransferSetupPresenterFactory+CrossChain.swift b/novawallet/Modules/Transfer/TransferSetup/CrossChain/TransferSetupPresenterFactory+CrossChain.swift index 051d4a55d..2923a5273 100644 --- a/novawallet/Modules/Transfer/TransferSetup/CrossChain/TransferSetupPresenterFactory+CrossChain.swift +++ b/novawallet/Modules/Transfer/TransferSetup/CrossChain/TransferSetupPresenterFactory+CrossChain.swift @@ -142,6 +142,7 @@ extension TransferSetupPresenterFactory { feeProxy: XcmExtrinsicFeeProxy(), extrinsicService: extrinsicService, resolutionFactory: resolutionFactory, + fungibilityPreservationProvider: AssetFungibilityPreservationProvider.createFromKnownChains(), walletRemoteWrapper: walletRemoteSubscriptionWrapper, walletLocalSubscriptionFactory: WalletLocalSubscriptionFactory.shared, priceLocalSubscriptionFactory: PriceProviderFactory.shared, diff --git a/novawallet/Modules/Transfer/TransferSetup/TransferSetupProtocols.swift b/novawallet/Modules/Transfer/TransferSetup/TransferSetupProtocols.swift index 945b9d42d..90045900b 100644 --- a/novawallet/Modules/Transfer/TransferSetup/TransferSetupProtocols.swift +++ b/novawallet/Modules/Transfer/TransferSetup/TransferSetupProtocols.swift @@ -1,5 +1,4 @@ import BigInt - import SoraFoundation protocol TransferSetupChildViewProtocol: ControllerBackedProtocol, Localizable { diff --git a/novawallet/Modules/Transfer/Validation/TransferDataValidatorFactory.swift b/novawallet/Modules/Transfer/Validation/TransferDataValidatorFactory.swift index aafa75803..94c4aa1df 100644 --- a/novawallet/Modules/Transfer/Validation/TransferDataValidatorFactory.swift +++ b/novawallet/Modules/Transfer/Validation/TransferDataValidatorFactory.swift @@ -13,6 +13,14 @@ struct CrossChainValidationAtLeastEdForDeliveryFee { let minBalance: BigUInt? } +struct CrossChainValidationOriginKeepAlive { + let totalSpendingAmount: Decimal? + let networkFee: ExtrinsicFeeProtocol? + let balance: Balance? + let minBalance: Balance? + let requiresKeepAlive: Bool? +} + protocol TransferDataValidatorFactoryProtocol: BaseDataValidatingFactoryProtocol { func willBeReaped( amount: Decimal?, @@ -72,6 +80,11 @@ protocol TransferDataValidatorFactoryProtocol: BaseDataValidatingFactoryProtocol locale: Locale ) -> DataValidating + func notViolatingKeepAlive( + for params: CrossChainValidationOriginKeepAlive, + locale: Locale + ) -> DataValidating + func has(crosschainFee: XcmFeeModelProtocol?, locale: Locale, onError: (() -> Void)?) -> DataValidating } @@ -333,6 +346,48 @@ final class TransferDataValidatorFactory: TransferDataValidatorFactoryProtocol { }) } + func notViolatingKeepAlive( + for params: CrossChainValidationOriginKeepAlive, + locale: Locale + ) -> DataValidating { + let totalSpendingInPlank = params.totalSpendingAmount?.toSubstrateAmount( + precision: assetDisplayInfo.assetPrecision + ) ?? 0 + + return ErrorConditionViolation(onError: { [weak self] in + guard let view = self?.view, let assetInfo = self?.assetDisplayInfo else { + return + } + + let tokenFormatter = AssetBalanceFormatterFactory().createTokenFormatter(for: assetInfo) + + let minBalanceDecimal = params.minBalance?.decimal(assetInfo: assetInfo) ?? 0 + + let minBalanceString = tokenFormatter.value(for: locale).stringFromDecimal(minBalanceDecimal) ?? "" + + self?.presentable.presentKeepAliveViolatedForCrosschain( + from: view, + minBalance: minBalanceString, + locale: locale + ) + + }, preservesCondition: { + guard + let balance = params.balance, + let minBalance = params.minBalance, + let requiresKeepAlive = params.requiresKeepAlive else { + return true + } + + if requiresKeepAlive { + let feeAmount = params.networkFee?.amountForCurrentAccount ?? 0 + return totalSpendingInPlank + feeAmount + minBalance <= balance + } else { + return true + } + }) + } + func canPayCrossChainFee( for amount: Decimal?, fee: CrossChainValidationFee?, diff --git a/novawallet/Modules/Transfer/Validation/TransferErrorPresentable.swift b/novawallet/Modules/Transfer/Validation/TransferErrorPresentable.swift index 9e6c308c9..a6a0fd78e 100644 --- a/novawallet/Modules/Transfer/Validation/TransferErrorPresentable.swift +++ b/novawallet/Modules/Transfer/Validation/TransferErrorPresentable.swift @@ -25,6 +25,12 @@ protocol TransferErrorPresentable: BaseErrorPresentable { availableBalance: String, locale: Locale? ) + + func presentKeepAliveViolatedForCrosschain( + from view: ControllerBackedProtocol, + minBalance: String, + locale: Locale? + ) } extension TransferErrorPresentable where Self: AlertPresentable & ErrorPresentable { @@ -141,4 +147,20 @@ extension TransferErrorPresentable where Self: AlertPresentable & ErrorPresentab present(message: message, title: title, closeAction: closeAction, from: view) } + + func presentKeepAliveViolatedForCrosschain( + from view: ControllerBackedProtocol, + minBalance: String, + locale: Locale? + ) { + let title = R.string.localizable.commonInsufficientBalance(preferredLanguages: locale?.rLanguages) + let message = R.string.localizable.swapDeliveryFeeErrorMessage( + minBalance, + preferredLanguages: locale?.rLanguages + ) + + let closeAction = R.string.localizable.commonClose(preferredLanguages: locale?.rLanguages) + + present(message: message, title: title, closeAction: closeAction, from: view) + } } diff --git a/novawalletIntegrationTests/AssetsExchange/AssetsExchangeTests.swift b/novawalletIntegrationTests/AssetsExchange/AssetsExchangeTests.swift index d213e4ff7..7860fc3cc 100644 --- a/novawalletIntegrationTests/AssetsExchange/AssetsExchangeTests.swift +++ b/novawalletIntegrationTests/AssetsExchange/AssetsExchangeTests.swift @@ -8,7 +8,9 @@ final class AssetsExchangeTests: XCTestCase { guard let dotPolkadot = params.chainRegistry.getChain(for: KnowChainId.polkadot)?.utilityChainAsset(), - let usdtAssetHubId = params.chainRegistry.getChain(for: KnowChainId.statemint)?.chainAssetForSymbol("USDT")?.chainAssetId else { + let usdtAssetHubId = params.chainRegistry.getChain( + for: KnowChainId.polkadotAssetHub + )?.chainAssetForSymbol("USDT")?.chainAssetId else { XCTFail("No chain or asset") return } @@ -52,7 +54,9 @@ final class AssetsExchangeTests: XCTestCase { guard let polkadotUtilityAsset = params.chainRegistry.getChain(for: KnowChainId.polkadot)?.utilityChainAssetId(), let hydraUtilityAsset = params.chainRegistry.getChain(for: KnowChainId.hydra)?.utilityChainAssetId(), - let assetHubUtilityAsset = params.chainRegistry.getChain(for: KnowChainId.statemint)?.utilityChainAssetId() else { + let assetHubUtilityAsset = params.chainRegistry.getChain( + for: KnowChainId.polkadotAssetHub + )?.utilityChainAssetId() else { XCTFail("No chain or asset") return } @@ -125,7 +129,9 @@ final class AssetsExchangeTests: XCTestCase { guard let dotPolkadot = params.chainRegistry.getChain(for: KnowChainId.polkadot)?.utilityChainAsset(), - let usdtAssetHubId = params.chainRegistry.getChain(for: KnowChainId.statemint)?.chainAssetForSymbol("USDT")?.chainAssetId else { + let usdtAssetHubId = params.chainRegistry.getChain( + for: KnowChainId.polkadotAssetHub + )?.chainAssetForSymbol("USDT")?.chainAssetId else { XCTFail("No chain or asset") return } @@ -233,6 +239,7 @@ final class AssetsExchangeTests: XCTestCase { ), chainRegistry: params.chainRegistry, pathCostEstimator: pathCostEstimator, + fungibilityPreservationProvider: AssetFungibilityPreservationProvider.createFromKnownChains(), signingWrapperFactory: SigningWrapperFactory(), userStorageFacade: params.userDataStorageFacade, substrateStorageFacade: params.substrateStorageFacade, diff --git a/novawalletIntegrationTests/WalletRemoteQueryFactoryTests.swift b/novawalletIntegrationTests/WalletRemoteQueryFactoryTests.swift index cdc6da549..0a5a2d464 100644 --- a/novawalletIntegrationTests/WalletRemoteQueryFactoryTests.swift +++ b/novawalletIntegrationTests/WalletRemoteQueryFactoryTests.swift @@ -107,7 +107,7 @@ final class WalletRemoteQueryFactoryTests: XCTestCase { let accountId = try "F53d3jeyFvb2eYsgAERhjC8mogao4Kg4GsdezrqiT8aj55v".toAccountId() let balance = try performQuery( for: accountId, - chainId: KnowChainId.statemine, + chainId: KnowChainId.kusamaAssetHub, assetId: 7 ) Logger.shared.info("Did receive: \(balance)") @@ -121,7 +121,7 @@ final class WalletRemoteQueryFactoryTests: XCTestCase { let accountId = try "Cn1mVjBBvLJUWE8GQoeR7JduGt2GxhUXrx191ob3Si6HA9E".toAccountId() let balance = try performQuery( for: accountId, - chainId: KnowChainId.statemine, + chainId: KnowChainId.kusamaAssetHub, assetId: 7 ) Logger.shared.info("Did receive: \(balance)") diff --git a/novawalletIntegrationTests/XcmTransfersFeeTests.swift b/novawalletIntegrationTests/XcmTransfersFeeTests.swift index 9f816ebe6..856f3dc47 100644 --- a/novawalletIntegrationTests/XcmTransfersFeeTests.swift +++ b/novawalletIntegrationTests/XcmTransfersFeeTests.swift @@ -120,7 +120,7 @@ class XcmTransfersFeeTests: XCTestCase { func testKusamaStatemineCrosschainFee() throws { let originChainId = KnowChainId.kusama - let destinationChainId = KnowChainId.statemine + let destinationChainId = KnowChainId.kusamaAssetHub let assetId: AssetModel.Id = 0 let beneficiary = AccountId.zeroAccountId(of: 32) let amount: BigUInt = 1_000_000_000_00 @@ -138,7 +138,7 @@ class XcmTransfersFeeTests: XCTestCase { } func testStatemineKusamaCrosschainFee() throws { - let originChainId = KnowChainId.statemine + let originChainId = KnowChainId.kusamaAssetHub let destinationChainId = KnowChainId.kusama let assetId: AssetModel.Id = 0 let beneficiary = AccountId.zeroAccountId(of: 32)