diff --git a/Tangem/App/Factories/WalletModels/CommonWalletModelsFactory.swift b/Tangem/App/Factories/WalletModels/CommonWalletModelsFactory.swift index 0240b2965b..ffe8ea68d4 100644 --- a/Tangem/App/Factories/WalletModels/CommonWalletModelsFactory.swift +++ b/Tangem/App/Factories/WalletModels/CommonWalletModelsFactory.swift @@ -64,13 +64,17 @@ struct CommonWalletModelsFactory { ) } - func makeStakingManager(tokenItem: TokenItem, address: String) -> StakingManager? { + func makeStakingManager(publicKey: Data, tokenItem: TokenItem, address: String) -> StakingManager? { guard let integrationId = StakingFeatureProvider().yieldId(for: tokenItem), let item = tokenItem.stakingTokenItem else { return nil } - let wallet = StakingWallet(item: item, address: address) + let wallet = StakingWallet( + item: item, + address: address, + publicKey: publicKey + ) return StakingDependenciesFactory().makeStakingManager(integrationId: integrationId, wallet: wallet) } } @@ -98,7 +102,11 @@ extension CommonWalletModelsFactory: WalletModelsFactory { let shouldPerformHealthCheck = shouldPerformHealthCheck(blockchain: currentBlockchain, amountType: .coin) let mainCoinModel = WalletModel( walletManager: walletManager, - stakingManager: makeStakingManager(tokenItem: tokenItem, address: walletManager.wallet.address), + stakingManager: makeStakingManager( + publicKey: walletManager.wallet.publicKey.blockchainKey, + tokenItem: tokenItem, + address: walletManager.wallet.address + ), transactionHistoryService: transactionHistoryService, amountType: .coin, shouldPerformHealthCheck: shouldPerformHealthCheck, @@ -119,7 +127,11 @@ extension CommonWalletModelsFactory: WalletModelsFactory { let shouldPerformHealthCheck = shouldPerformHealthCheck(blockchain: currentBlockchain, amountType: amountType) let tokenModel = WalletModel( walletManager: walletManager, - stakingManager: makeStakingManager(tokenItem: tokenItem, address: walletManager.wallet.address), + stakingManager: makeStakingManager( + publicKey: walletManager.wallet.publicKey.blockchainKey, + tokenItem: tokenItem, + address: walletManager.wallet.address + ), transactionHistoryService: transactionHistoryService, amountType: amountType, shouldPerformHealthCheck: shouldPerformHealthCheck, diff --git a/Tangem/App/Models/TokenItem/TokenItem+StakingTokenItem.swift b/Tangem/App/Models/TokenItem/TokenItem+StakingTokenItem.swift index a76f80d54f..1d26958877 100644 --- a/Tangem/App/Models/TokenItem/TokenItem+StakingTokenItem.swift +++ b/Tangem/App/Models/TokenItem/TokenItem+StakingTokenItem.swift @@ -14,7 +14,10 @@ extension TokenItem { StakeKitNetworkType(rawValue: blockchain.coinId).map { network in StakingTokenItem( network: network, - contractAddress: contractAddress + contractAddress: contractAddress, + name: name, + decimals: decimalCount, + symbol: currencySymbol ) } } diff --git a/Tangem/App/Services/EnvironmentProvider/StakingFeatureProvider.swift b/Tangem/App/Services/EnvironmentProvider/StakingFeatureProvider.swift index f3c1727bb9..dba08c44d6 100644 --- a/Tangem/App/Services/EnvironmentProvider/StakingFeatureProvider.swift +++ b/Tangem/App/Services/EnvironmentProvider/StakingFeatureProvider.swift @@ -65,9 +65,9 @@ extension StakingFeatureProvider { var testableBlockchainIds: Set { [ - StakingTokenItem(network: .solana, contractAddress: nil), - StakingTokenItem(network: .cosmos, contractAddress: nil), - StakingTokenItem(network: .ethereum, contractAddress: "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0"), + StakingTokenItem(network: .solana, contractAddress: nil, name: "Solana", decimals: 9, symbol: "SOL"), + StakingTokenItem(network: .cosmos, contractAddress: nil, name: "Cosmos", decimals: 6, symbol: "ATOM"), + StakingTokenItem(network: .ethereum, contractAddress: "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0", name: "Ethereum", decimals: 18, symbol: "ETH"), ] } diff --git a/Tangem/Preview Content/Mocks/Staking/YieldInfo+Mock.swift b/Tangem/Preview Content/Mocks/Staking/YieldInfo+Mock.swift index 47113cc993..d3495208d9 100644 --- a/Tangem/Preview Content/Mocks/Staking/YieldInfo+Mock.swift +++ b/Tangem/Preview Content/Mocks/Staking/YieldInfo+Mock.swift @@ -33,7 +33,7 @@ extension YieldInfo { ), ], defaultValidator: nil, - item: .init(network: .tron, contractAddress: nil), + item: .init(network: .tron, contractAddress: nil, name: "", decimals: 0, symbol: ""), unbondingPeriod: .days(14), warmupPeriod: .days(0), rewardClaimingType: .manual, diff --git a/TangemApp.xcodeproj/project.pbxproj b/TangemApp.xcodeproj/project.pbxproj index 2c92fd5b7d..33162df7b2 100644 --- a/TangemApp.xcodeproj/project.pbxproj +++ b/TangemApp.xcodeproj/project.pbxproj @@ -11,6 +11,9 @@ 0A0DB7582C48FB0000003450 /* RewardRateValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A0DB7572C48FAFF00003450 /* RewardRateValues.swift */; }; 0A0E01772C64AA79009E01F2 /* StakingSendAmountValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A0E01762C64AA79009E01F2 /* StakingSendAmountValidator.swift */; }; 0A1992A62B5FBDF800344312 /* CurrencyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A1992A52B5FBDF800344312 /* CurrencyTests.swift */; }; + 0A3BF9692C6CDAD900163492 /* AdditionalAddresses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A3BF9682C6CDAD900163492 /* AdditionalAddresses.swift */; }; + 0A3BF96B2C6CE96B00163492 /* StakingAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A3BF96A2C6CE96B00163492 /* StakingAction.swift */; }; + 0A3BF96F2C6DDB9000163492 /* StakingActionRequestParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A3BF96E2C6DDB9000163492 /* StakingActionRequestParams.swift */; }; 0A81B3732BB2D24A0008F21C /* BlockchainURLSchemesParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A81B3722BB2D24A0008F21C /* BlockchainURLSchemesParser.swift */; }; 0A81B38E2BB3018E0008F21C /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 0A81B38D2BB3018E0008F21C /* ZIPFoundation */; }; 0AB5A4732C5267CB00491764 /* SegmentedPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB5A4722C5267CB00491764 /* SegmentedPicker.swift */; }; @@ -1887,6 +1890,9 @@ 0A0E01762C64AA79009E01F2 /* StakingSendAmountValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StakingSendAmountValidator.swift; sourceTree = ""; }; 0A1992A52B5FBDF800344312 /* CurrencyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CurrencyTests.swift; sourceTree = ""; }; 0A35044D2E5DF9354751E2DE /* Pods-TangemStakingTests.release(alpha).xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TangemStakingTests.release(alpha).xcconfig"; path = "Target Support Files/Pods-TangemStakingTests/Pods-TangemStakingTests.release(alpha).xcconfig"; sourceTree = ""; }; + 0A3BF9682C6CDAD900163492 /* AdditionalAddresses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdditionalAddresses.swift; sourceTree = ""; }; + 0A3BF96A2C6CE96B00163492 /* StakingAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StakingAction.swift; sourceTree = ""; }; + 0A3BF96E2C6DDB9000163492 /* StakingActionRequestParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StakingActionRequestParams.swift; sourceTree = ""; }; 0A81B3722BB2D24A0008F21C /* BlockchainURLSchemesParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockchainURLSchemesParser.swift; sourceTree = ""; }; 0AB5A4722C5267CB00491764 /* SegmentedPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentedPicker.swift; sourceTree = ""; }; 0BE949936543D22F51E09DBD /* Pods-Tangem-TangemUITests.release(alpha).xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tangem-TangemUITests.release(alpha).xcconfig"; path = "Target Support Files/Pods-Tangem-TangemUITests/Pods-Tangem-TangemUITests.release(alpha).xcconfig"; sourceTree = ""; }; @@ -9500,6 +9506,9 @@ 0A0DB7572C48FAFF00003450 /* RewardRateValues.swift */, EF0FBB882C53FFC10095A6BD /* ActionTransaction.swift */, EF0FBB8A2C53FFCD0095A6BD /* ActionStatus.swift */, + 0A3BF9682C6CDAD900163492 /* AdditionalAddresses.swift */, + 0A3BF96A2C6CE96B00163492 /* StakingAction.swift */, + 0A3BF96E2C6DDB9000163492 /* StakingActionRequestParams.swift */, ); path = Models; sourceTree = ""; @@ -12376,6 +12385,7 @@ EFB019472C4172D6009EE857 /* StakeKitDTO+NetworkType.swift in Sources */, EF407E5D2C04D64B00366733 /* StakingAPIProvider.swift in Sources */, EFBC5C732C00A74100B662E0 /* YieldInfo.swift in Sources */, + 0A3BF9692C6CDAD900163492 /* AdditionalAddresses.swift in Sources */, EFBC5C752C00A74100B662E0 /* StakingAPIService.swift in Sources */, EFBC5C902C00B8B800B662E0 /* StakeKitDTO+Yield.swift in Sources */, EF407E382C04B09A00366733 /* Logger.swift in Sources */, @@ -12394,7 +12404,9 @@ EF72DC062C467001003DD97F /* StakingTokenItem.swift in Sources */, EF031A282C08E65A001535C9 /* StakingCalculator.swift in Sources */, EF8DBCDD2C060A03009072CF /* StakingManager.swift in Sources */, + 0A3BF96F2C6DDB9000163492 /* StakingActionRequestParams.swift in Sources */, EFBC5C792C00A79E00B662E0 /* StakeKitStakingAPIService.swift in Sources */, + 0A3BF96B2C6CE96B00163492 /* StakingAction.swift in Sources */, EFB019492C4174D1009EE857 /* EnterAction.swift in Sources */, EF407E5F2C04D65B00366733 /* CommonStakingAPIProvider.swift in Sources */, EFB019432C41710F009EE857 /* StakingBalanceInfo.swift in Sources */, diff --git a/TangemStaking/Models/AdditionalAddresses.swift b/TangemStaking/Models/AdditionalAddresses.swift new file mode 100644 index 0000000000..8dc8bfb41b --- /dev/null +++ b/TangemStaking/Models/AdditionalAddresses.swift @@ -0,0 +1,13 @@ +// +// AdditionalAddresses.swift +// TangemStaking +// +// Created by Dmitry Fedorov on 14.08.2024. +// Copyright © 2024 Tangem AG. All rights reserved. +// + +import Foundation + +public struct AdditionalAddresses { + let cosmosPubKey: String? +} diff --git a/TangemStaking/Models/StakingAction.swift b/TangemStaking/Models/StakingAction.swift new file mode 100644 index 0000000000..a7b5474ded --- /dev/null +++ b/TangemStaking/Models/StakingAction.swift @@ -0,0 +1,27 @@ +// +// StakingAction.swift +// TangemStaking +// +// Created by Dmitry Fedorov on 14.08.2024. +// Copyright © 2024 Tangem AG. All rights reserved. +// + +import Foundation + +public struct StakingAction { + let amount: Decimal + let validator: String + let type: ActionType + + public init(amount: Decimal, validator: String, type: StakingAction.ActionType) { + self.amount = amount + self.validator = validator + self.type = type + } + + public enum ActionType { + case stake + case claimRewards + case unstake + } +} diff --git a/TangemStaking/Models/StakingActionRequestParams.swift b/TangemStaking/Models/StakingActionRequestParams.swift new file mode 100644 index 0000000000..a0f1a166c1 --- /dev/null +++ b/TangemStaking/Models/StakingActionRequestParams.swift @@ -0,0 +1,34 @@ +// +// StakingActionRequestParams.swift +// TangemStaking +// +// Created by Dmitry Fedorov on 15.08.2024. +// Copyright © 2024 Tangem AG. All rights reserved. +// + +import Foundation + +public struct StakingActionRequestParams { + let amount: Decimal + let address: String + let additionalAddresses: AdditionalAddresses? + let token: StakingTokenItem? + let validator: String + let integrationId: String + + init( + amount: Decimal, + address: String, + additionalAddresses: AdditionalAddresses? = nil, + token: StakingTokenItem? = nil, + validator: String, + integrationId: String + ) { + self.amount = amount + self.address = address + self.additionalAddresses = additionalAddresses + self.token = token + self.validator = validator + self.integrationId = integrationId + } +} diff --git a/TangemStaking/Models/StakingTokenItem.swift b/TangemStaking/Models/StakingTokenItem.swift index 141f46d564..01c07afce7 100644 --- a/TangemStaking/Models/StakingTokenItem.swift +++ b/TangemStaking/Models/StakingTokenItem.swift @@ -11,9 +11,21 @@ import Foundation public struct StakingTokenItem: Hashable { public let network: StakeKitNetworkType public let contractAddress: String? + public let name: String + public let decimals: Int + public let symbol: String - public init(network: StakeKitNetworkType, contractAddress: String?) { + public init( + network: StakeKitNetworkType, + contractAddress: String? = nil, + name: String, + decimals: Int, + symbol: String + ) { self.network = network self.contractAddress = contractAddress + self.name = name + self.decimals = decimals + self.symbol = symbol } } diff --git a/TangemStaking/Models/StakingWallet.swift b/TangemStaking/Models/StakingWallet.swift index 205163f9d8..f2a03e4040 100644 --- a/TangemStaking/Models/StakingWallet.swift +++ b/TangemStaking/Models/StakingWallet.swift @@ -11,9 +11,11 @@ import Foundation public struct StakingWallet: Hashable { public let item: StakingTokenItem public let address: String + public let publicKey: Data - public init(item: StakingTokenItem, address: String) { + public init(item: StakingTokenItem, address: String, publicKey: Data) { self.item = item self.address = address + self.publicKey = publicKey } } diff --git a/TangemStaking/Services/StakeKitMapper/StakeKitMapper.swift b/TangemStaking/Services/StakeKitMapper/StakeKitMapper.swift index 6ef871e071..7923606d76 100644 --- a/TangemStaking/Services/StakeKitMapper/StakeKitMapper.swift +++ b/TangemStaking/Services/StakeKitMapper/StakeKitMapper.swift @@ -192,7 +192,13 @@ struct StakeKitMapper { throw StakeKitMapperError.noData("StakeKitNetworkType not found") } - return StakingTokenItem(network: network, contractAddress: token.address) + return StakingTokenItem( + network: network, + contractAddress: token.address, + name: token.name, + decimals: token.decimals, + symbol: token.symbol + ) } func mapToRewardType(from rewardType: StakeKitDTO.Yield.Info.Response.RewardType) -> RewardType { @@ -252,6 +258,17 @@ struct StakeKitMapper { rewardsPendingAction(from: balance)?.passthrough } + func mapToTokenDTO(from tokenItem: StakingTokenItem) -> StakeKitDTO.Token { + StakeKitDTO.Token( + network: tokenItem.network.rawValue, + name: tokenItem.name, + decimals: tokenItem.decimals, + address: tokenItem.contractAddress, + symbol: tokenItem.symbol, + logoURI: nil + ) + } + private func rewardsPendingAction(from balance: StakeKitDTO.Balances.Response.Balance) -> StakeKitDTO.Balances.Response.Balance.PendingAction? { guard balance.type == .rewards else { return nil diff --git a/TangemStaking/Services/StakingAPIProvider/CommonStakingAPIProvider.swift b/TangemStaking/Services/StakingAPIProvider/CommonStakingAPIProvider.swift index 3741633994..c37cac3486 100644 --- a/TangemStaking/Services/StakingAPIProvider/CommonStakingAPIProvider.swift +++ b/TangemStaking/Services/StakingAPIProvider/CommonStakingAPIProvider.swift @@ -36,16 +36,22 @@ class CommonStakingAPIProvider: StakingAPIProvider { return balancesInfo } - func estimateStakeFee( - amount: Decimal, - address: String, - validator: String, - integrationId: String - ) async throws -> Decimal { + func estimateStakeFee(params: StakingActionRequestParams) async throws -> Decimal { let request = StakeKitDTO.EstimateGas.Enter.Request( - integrationId: integrationId, - addresses: .init(address: address), - args: .init(amount: amount.description, validatorAddress: validator) + integrationId: params.integrationId, + addresses: .init( + address: params.address, + additionalAddresses: params.additionalAddresses.flatMap { + StakeKitDTO.Address.AdditionalAddresses(cosmosPubKey: $0.cosmosPubKey) + } + ), + args: .init( + amount: params.amount.description, + validatorAddress: params.validator, + inputToken: params.token.flatMap { + mapper.mapToTokenDTO(from: $0) + } + ) ) let response = try await service.estimateGasEnterAction(request: request) @@ -55,16 +61,22 @@ class CommonStakingAPIProvider: StakingAPIProvider { return result } - func estimateUnstakeFee( - amount: Decimal, - address: String, - validator: String, - integrationId: String - ) async throws -> Decimal { + func estimateUnstakeFee(params: StakingActionRequestParams) async throws -> Decimal { let request = StakeKitDTO.EstimateGas.Exit.Request( - integrationId: integrationId, - addresses: .init(address: address), - args: .init(amount: amount.description, validatorAddress: validator) + integrationId: params.integrationId, + addresses: .init( + address: params.address, + additionalAddresses: params.additionalAddresses.flatMap { + StakeKitDTO.Address.AdditionalAddresses(cosmosPubKey: $0.cosmosPubKey) + } + ), + args: .init( + amount: params.amount.description, + validatorAddress: params.validator, + inputToken: params.token.flatMap { + mapper.mapToTokenDTO(from: $0) + } + ) ) let response = try await service.estimateGasExitAction(request: request) @@ -75,18 +87,26 @@ class CommonStakingAPIProvider: StakingAPIProvider { } func estimateClaimRewardsFee( - amount: Decimal, - address: String, - validator: String, - integrationId: String, + params: StakingActionRequestParams, passthrough: String ) async throws -> Decimal { let request = StakeKitDTO.EstimateGas.Pending.Request( type: .claimRewards, - integrationId: integrationId, + integrationId: params.integrationId, passthrough: passthrough, - addresses: .init(address: address), - args: .init(amount: amount.description, validatorAddress: validator) + addresses: .init( + address: params.address, + additionalAddresses: params.additionalAddresses.flatMap { + StakeKitDTO.Address.AdditionalAddresses(cosmosPubKey: $0.cosmosPubKey) + } + ), + args: .init( + amount: params.amount.description, + validatorAddress: params.validator, + inputToken: params.token.flatMap { + mapper.mapToTokenDTO(from: $0) + } + ) ) let response = try await service.estimateGasPendingAction(request: request) @@ -96,11 +116,22 @@ class CommonStakingAPIProvider: StakingAPIProvider { return result } - func enterAction(amount: Decimal, address: String, validator: String, integrationId: String) async throws -> EnterAction { + func enterAction(params: StakingActionRequestParams) async throws -> EnterAction { let request = StakeKitDTO.Actions.Enter.Request( - integrationId: integrationId, - addresses: .init(address: address), - args: .init(amount: amount.description, validatorAddress: validator) + integrationId: params.integrationId, + addresses: .init( + address: params.address, + additionalAddresses: params.additionalAddresses.flatMap { + StakeKitDTO.Address.AdditionalAddresses(cosmosPubKey: $0.cosmosPubKey) + } + ), + args: .init( + amount: params.amount.description, + validatorAddress: params.validator, + inputToken: params.token.flatMap { + mapper.mapToTokenDTO(from: $0) + } + ) ) let response = try await service.enterAction(request: request) @@ -108,11 +139,22 @@ class CommonStakingAPIProvider: StakingAPIProvider { return enterAction } - func exitAction(amount: Decimal, address: String, validator: String, integrationId: String) async throws -> ExitAction { + func exitAction(params: StakingActionRequestParams) async throws -> ExitAction { let request = StakeKitDTO.Actions.Exit.Request( - integrationId: integrationId, - addresses: .init(address: address), - args: .init(amount: amount.description, validatorAddress: validator) + integrationId: params.integrationId, + addresses: .init( + address: params.address, + additionalAddresses: params.additionalAddresses.flatMap { + StakeKitDTO.Address.AdditionalAddresses(cosmosPubKey: $0.cosmosPubKey) + } + ), + args: .init( + amount: params.amount.description, + validatorAddress: params.validator, + inputToken: params.token.flatMap { + mapper.mapToTokenDTO(from: $0) + } + ) ) let response = try await service.exitAction(request: request) diff --git a/TangemStaking/Services/StakingAPIProvider/StakingAPIProvider.swift b/TangemStaking/Services/StakingAPIProvider/StakingAPIProvider.swift index 7b0057950e..779e80f9d6 100644 --- a/TangemStaking/Services/StakingAPIProvider/StakingAPIProvider.swift +++ b/TangemStaking/Services/StakingAPIProvider/StakingAPIProvider.swift @@ -13,18 +13,12 @@ public protocol StakingAPIProvider { func yield(integrationId: String) async throws -> YieldInfo func balances(wallet: StakingWallet) async throws -> [StakingBalanceInfo] - func estimateStakeFee(amount: Decimal, address: String, validator: String, integrationId: String) async throws -> Decimal - func estimateUnstakeFee(amount: Decimal, address: String, validator: String, integrationId: String) async throws -> Decimal - func estimateClaimRewardsFee( - amount: Decimal, - address: String, - validator: String, - integrationId: String, - passthrough: String - ) async throws -> Decimal + func estimateStakeFee(params: StakingActionRequestParams) async throws -> Decimal + func estimateUnstakeFee(params: StakingActionRequestParams) async throws -> Decimal + func estimateClaimRewardsFee(params: StakingActionRequestParams, passthrough: String) async throws -> Decimal - func enterAction(amount: Decimal, address: String, validator: String, integrationId: String) async throws -> EnterAction - func exitAction(amount: Decimal, address: String, validator: String, integrationId: String) async throws -> ExitAction + func enterAction(params: StakingActionRequestParams) async throws -> EnterAction + func exitAction(params: StakingActionRequestParams) async throws -> ExitAction func pendingAction() async throws // TODO: https://tangem.atlassian.net/browse/IOS-7482 func transaction(id: String) async throws -> StakingTransactionInfo diff --git a/TangemStaking/Services/StakingAPIService/DTOs/StakeKitDTO+Actions.swift b/TangemStaking/Services/StakingAPIService/DTOs/StakeKitDTO+Actions.swift index a23a1afdb6..d165d76ec6 100644 --- a/TangemStaking/Services/StakingAPIService/DTOs/StakeKitDTO+Actions.swift +++ b/TangemStaking/Services/StakingAPIService/DTOs/StakeKitDTO+Actions.swift @@ -85,11 +85,6 @@ extension StakeKitDTO { let integrationId: String let addresses: Address let args: Args - - struct Args: Encodable { - let amount: String - let validatorAddress: String - } } struct Response: Decodable { @@ -110,11 +105,6 @@ extension StakeKitDTO { let integrationId: String let addresses: Address let args: Args - - struct Args: Encodable { - let amount: String - let validatorAddress: String? - } } struct Response: Decodable { @@ -139,16 +129,23 @@ extension StakeKitDTO { let args: Args } - struct Args: Encodable { - let amount: String - let validatorAddress: String - } - struct Response: Decodable { let amount: String? let token: Token let gasLimit: String } } + + struct Args: Encodable { + let amount: String + let validatorAddress: String + let inputToken: Token? + + init(amount: String, validatorAddress: String, inputToken: StakeKitDTO.Token? = nil) { + self.amount = amount + self.validatorAddress = validatorAddress + self.inputToken = inputToken + } + } } } diff --git a/TangemStaking/Services/StakingAPIService/DTOs/StakeKitDTO.swift b/TangemStaking/Services/StakingAPIService/DTOs/StakeKitDTO.swift index f041b118d1..982384a559 100644 --- a/TangemStaking/Services/StakingAPIService/DTOs/StakeKitDTO.swift +++ b/TangemStaking/Services/StakingAPIService/DTOs/StakeKitDTO.swift @@ -20,10 +20,10 @@ enum StakeKitDTO { struct Token: Codable { let network: String - let name: String? - let decimals: Int? + let name: String + let decimals: Int let address: String? - let symbol: String? + let symbol: String let logoURI: String? } @@ -49,6 +49,40 @@ enum StakeKitDTO { struct Address: Codable { let address: String + let additionalAddresses: AdditionalAddresses? + + init(address: String, additionalAddresses: StakeKitDTO.Address.AdditionalAddresses? = nil) { + self.address = address + self.additionalAddresses = additionalAddresses + } + + struct AdditionalAddresses: Codable { + let cosmosPubKey: String? + let binanceBeaconAddress: String? + let stakeAccounts: [String]? + let lidoStakeAccounts: [String]? + let tezosPubKey: String? + let cAddressBech: String? + let pAddressBech: String? + + init( + cosmosPubKey: String? = nil, + binanceBeaconAddress: String? = nil, + stakeAccounts: [String]? = nil, + lidoStakeAccounts: [String]? = nil, + tezosPubKey: String? = nil, + cAddressBech: String? = nil, + pAddressBech: String? = nil + ) { + self.cosmosPubKey = cosmosPubKey + self.binanceBeaconAddress = binanceBeaconAddress + self.stakeAccounts = stakeAccounts + self.lidoStakeAccounts = lidoStakeAccounts + self.tezosPubKey = tezosPubKey + self.cAddressBech = cAddressBech + self.pAddressBech = pAddressBech + } + } } struct Required: Decodable { diff --git a/TangemStaking/Services/StakingManager/CommonStakingManager.swift b/TangemStaking/Services/StakingManager/CommonStakingManager.swift index e32a3991b4..8e8c5cfe78 100644 --- a/TangemStaking/Services/StakingManager/CommonStakingManager.swift +++ b/TangemStaking/Services/StakingManager/CommonStakingManager.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import TangemSdk class CommonStakingManager { private let integrationId: String @@ -60,24 +61,36 @@ extension CommonStakingManager: StakingManager { switch (state, action.type) { case (.availableToStake(let yieldInfo), .stake): return try await provider.estimateStakeFee( - amount: action.amount, - address: wallet.address, - validator: action.validator, - integrationId: yieldInfo.id + params: StakingActionRequestParams( + amount: action.amount, + address: wallet.address, + additionalAddresses: additionalAddresses(), + token: yieldInfo.item, + validator: action.validator, + integrationId: yieldInfo.id + ) ) case (.staked(let staked), .stake): return try await provider.estimateStakeFee( - amount: action.amount, - address: wallet.address, - validator: action.validator, - integrationId: staked.yieldInfo.id + params: StakingActionRequestParams( + amount: action.amount, + address: wallet.address, + additionalAddresses: additionalAddresses(), + token: staked.yieldInfo.item, + validator: action.validator, + integrationId: staked.yieldInfo.id + ) ) case (.staked(let staked), .unstake): return try await provider.estimateUnstakeFee( - amount: action.amount, - address: wallet.address, - validator: action.validator, - integrationId: staked.yieldInfo.id + params: StakingActionRequestParams( + amount: action.amount, + address: wallet.address, + additionalAddresses: additionalAddresses(), + token: staked.yieldInfo.item, + validator: action.validator, + integrationId: staked.yieldInfo.id + ) ) case (.staked(let staked), .claimRewards): guard let balance = staked.balance(validator: action.validator) else { @@ -89,10 +102,14 @@ extension CommonStakingManager: StakingManager { } return try await provider.estimateClaimRewardsFee( - amount: action.amount, - address: wallet.address, - validator: action.validator, - integrationId: staked.yieldInfo.id, + params: StakingActionRequestParams( + amount: action.amount, + address: wallet.address, + additionalAddresses: additionalAddresses(), + token: staked.yieldInfo.item, + validator: action.validator, + integrationId: staked.yieldInfo.id + ), passthrough: passthrough ) default: @@ -105,15 +122,25 @@ extension CommonStakingManager: StakingManager { switch (state, action.type) { case (.availableToStake(let yieldInfo), .stake): return try await getTransactionToStake( - amount: action.amount, - validator: action.validator, - integrationId: yieldInfo.id + params: StakingActionRequestParams( + amount: action.amount, + address: wallet.address, + additionalAddresses: additionalAddresses(), + token: yieldInfo.item, + validator: action.validator, + integrationId: yieldInfo.id + ) ) case (.staked(let staked), .stake): return try await getTransactionToStake( - amount: action.amount, - validator: action.validator, - integrationId: staked.yieldInfo.id + params: StakingActionRequestParams( + amount: action.amount, + address: wallet.address, + additionalAddresses: additionalAddresses(), + token: staked.yieldInfo.item, + validator: action.validator, + integrationId: staked.yieldInfo.id + ) ) case (.staked(let staked), .unstake): guard let balance = staked.balance(validator: action.validator) else { @@ -121,9 +148,14 @@ extension CommonStakingManager: StakingManager { } return try await getTransactionToUnstake( - amount: balance.blocked, - validator: action.validator, - integrationId: staked.yieldInfo.id + params: StakingActionRequestParams( + amount: balance.blocked, + address: wallet.address, + additionalAddresses: additionalAddresses(), + token: staked.yieldInfo.item, + validator: action.validator, + integrationId: staked.yieldInfo.id + ) ) default: throw StakingManagerError.stakingManagerStateNotSupportTransactionAction(action: action) @@ -153,13 +185,8 @@ private extension CommonStakingManager { return .staked(.init(balances: balances, yieldInfo: yield, canStakeMore: canStakeMore)) } - func getTransactionToStake(amount: Decimal, validator: String, integrationId: String) async throws -> StakingTransactionInfo { - let action = try await provider.enterAction( - amount: amount, - address: wallet.address, - validator: validator, - integrationId: integrationId - ) + func getTransactionToStake(params: StakingActionRequestParams) async throws -> StakingTransactionInfo { + let action = try await provider.enterAction(params: params) guard let transactionId = action.transactions.first(where: { $0.stepIndex == action.currentStepIndex })?.id else { throw StakingManagerError.transactionNotFound @@ -173,12 +200,8 @@ private extension CommonStakingManager { return transaction } - func getTransactionToUnstake(amount: Decimal, validator: String, integrationId: String) async throws -> StakingTransactionInfo { - let action = try await provider.exitAction( - amount: amount, address: wallet.address, - validator: validator, - integrationId: integrationId - ) + func getTransactionToUnstake(params: StakingActionRequestParams) async throws -> StakingTransactionInfo { + let action = try await provider.exitAction(params: params) guard let transactionId = action.transactions.first(where: { $0.stepIndex == action.currentStepIndex })?.id else { throw StakingManagerError.transactionNotFound @@ -204,6 +227,19 @@ private extension CommonStakingManager { return false } } + + func additionalAddresses() -> AdditionalAddresses? { + switch wallet.item.network { + case .cosmos: + guard let compressedPublicKey = try? Secp256k1Key(with: wallet.publicKey).compress() else { + return nil + } + + return AdditionalAddresses(cosmosPubKey: compressedPublicKey.base64EncodedString()) + default: + return nil + } + } } // MARK: - Log diff --git a/TangemStaking/Services/StakingManager/StakingManager.swift b/TangemStaking/Services/StakingManager/StakingManager.swift index a21429cdfc..27261c3e5d 100644 --- a/TangemStaking/Services/StakingManager/StakingManager.swift +++ b/TangemStaking/Services/StakingManager/StakingManager.swift @@ -18,24 +18,6 @@ public protocol StakingManager { func transaction(action: StakingAction) async throws -> StakingTransactionInfo } -public struct StakingAction { - let amount: Decimal - let validator: String - let type: ActionType - - public init(amount: Decimal, validator: String, type: StakingAction.ActionType) { - self.amount = amount - self.validator = validator - self.type = type - } - - public enum ActionType { - case stake - case claimRewards - case unstake - } -} - public enum StakingManagerState: Hashable, CustomStringConvertible { case loading case notEnabled