diff --git a/Features/Stake/Sources/ViewModels/EarnSceneViewModel.swift b/Features/Stake/Sources/ViewModels/EarnSceneViewModel.swift index a51d8fed4..068c07dba 100644 --- a/Features/Stake/Sources/ViewModels/EarnSceneViewModel.swift +++ b/Features/Stake/Sources/ViewModels/EarnSceneViewModel.swift @@ -6,7 +6,6 @@ import Foundation import Localization import Primitives import Store -import Style import EarnService import PrimitivesComponents @@ -52,7 +51,6 @@ public final class EarnSceneViewModel { var title: String { Localized.Common.earn } var assetTitle: String { AssetViewModel(asset: asset).title } - private var apr: Double? { providers.first.map(\.apr).flatMap { $0 > 0 ? $0 : nil } ?? assetData.metadata.earnApr diff --git a/Features/Transactions/Sources/ViewModels/TransactionParticipantViewModel.swift b/Features/Transactions/Sources/ViewModels/TransactionParticipantViewModel.swift index ba0055ea9..368dca8c2 100644 --- a/Features/Transactions/Sources/ViewModels/TransactionParticipantViewModel.swift +++ b/Features/Transactions/Sources/ViewModels/TransactionParticipantViewModel.swift @@ -20,8 +20,8 @@ extension TransactionParticipantViewModel: ItemModelProvidable { var itemModel: TransactionItemModel { switch transactionViewModel.transaction.transaction.type { case .stakeFreeze, .stakeUnfreeze: resourceItemModel - case .transfer, .transferNFT, .tokenApproval, .smartContractCall, .stakeDelegate: participantItemModel - case .swap, .stakeUndelegate, .stakeRedelegate, .stakeRewards, .stakeWithdraw, .assetActivation, .perpetualOpenPosition, .perpetualClosePosition, .perpetualModifyPosition, .earnDeposit, .earnWithdraw: .empty + case .earnDeposit, .earnWithdraw, .transfer, .transferNFT, .tokenApproval, .smartContractCall, .stakeDelegate: participantItemModel + case .swap, .stakeUndelegate, .stakeRedelegate, .stakeRewards, .stakeWithdraw, .assetActivation, .perpetualOpenPosition, .perpetualClosePosition, .perpetualModifyPosition: .empty } } } @@ -82,8 +82,10 @@ extension TransactionParticipantViewModel { Localized.Stake.validator case .stakeFreeze, .stakeUnfreeze: Localized.Stake.resource + case .earnDeposit, .earnWithdraw: + Localized.Common.provider case .swap, .stakeUndelegate, .stakeRedelegate, .stakeRewards, .stakeWithdraw, - .assetActivation, .perpetualOpenPosition, .perpetualClosePosition, .perpetualModifyPosition, .earnDeposit, .earnWithdraw: nil + .assetActivation, .perpetualOpenPosition, .perpetualClosePosition, .perpetualModifyPosition: nil } } } diff --git a/Features/Transfer/Sources/ViewModels/AmountEarnViewModel.swift b/Features/Transfer/Sources/ViewModels/AmountEarnViewModel.swift index 9e7734e08..70806c94a 100644 --- a/Features/Transfer/Sources/ViewModels/AmountEarnViewModel.swift +++ b/Features/Transfer/Sources/ViewModels/AmountEarnViewModel.swift @@ -64,11 +64,7 @@ final class AmountEarnViewModel: AmountDataProvidable { } func recipientData() -> RecipientData { - let provider = switch action { - case .deposit(let provider): provider - case .withdraw(let delegation): delegation.validator - } - return RecipientData( + RecipientData( recipient: Recipient(name: provider.name, address: provider.id, memo: nil), amount: nil ) @@ -84,7 +80,10 @@ final class AmountEarnViewModel: AmountDataProvidable { ) return TransferData( type: .earn(asset, action, earnData), - recipientData: recipientData(), + recipientData: RecipientData( + recipient: Recipient(name: provider.name, address: earnData.contractAddress, memo: nil), + amount: nil + ), value: value, canChangeValue: canChangeValue ) diff --git a/Features/Transfer/Sources/ViewModels/ConfirmRecipientViewModel.swift b/Features/Transfer/Sources/ViewModels/ConfirmRecipientViewModel.swift index bd6936085..089cc753c 100644 --- a/Features/Transfer/Sources/ViewModels/ConfirmRecipientViewModel.swift +++ b/Features/Transfer/Sources/ViewModels/ConfirmRecipientViewModel.swift @@ -63,7 +63,8 @@ extension ConfirmRecipientViewModel { case .sign: Localized.Asset.contract case .send: Localized.Transfer.Recipient.title } - case .transfer, .deposit, .withdrawal, .transferNft, .tokenApprove, .account, .perpetual, .earn: Localized.Transfer.Recipient.title + case .earn: Localized.Common.provider + case .transfer, .deposit, .withdrawal, .transferNft, .tokenApprove, .account, .perpetual: Localized.Transfer.Recipient.title } } @@ -79,8 +80,8 @@ extension ConfirmRecipientViewModel { } case .account, .swap, - .perpetual, - .earn: false + .perpetual: false + case .earn: true case .generic: switch model.type.outputAction { case .sign: false diff --git a/Gem/Navigation/Stake/EarnNavigationView.swift b/Gem/Navigation/Stake/EarnNavigationView.swift index 311de39ad..0a06a7215 100644 --- a/Gem/Navigation/Stake/EarnNavigationView.swift +++ b/Gem/Navigation/Stake/EarnNavigationView.swift @@ -1,6 +1,5 @@ // Copyright (c). Gem Wallet. All rights reserved. -import Foundation import SwiftUI import Primitives import Stake diff --git a/Gem/Services/ServicesFactory.swift b/Gem/Services/ServicesFactory.swift index 820278228..4d41da456 100644 --- a/Gem/Services/ServicesFactory.swift +++ b/Gem/Services/ServicesFactory.swift @@ -106,15 +106,15 @@ struct ServicesFactory { walletStore: storeManager.walletStore, avatarService: avatarService ) - let earnService = EarnService( - store: storeManager.stakeStore, - gatewayService: gatewayService - ) let balanceService = Self.makeBalanceService( balanceStore: storeManager.balanceStore, assetsService: assetsService, chainFactory: chainServiceFactory ) + let earnService = EarnService( + store: storeManager.stakeStore, + gatewayService: gatewayService + ) let stakeService = Self.makeStakeService( stakeStore: storeManager.stakeStore, addressStore: storeManager.addressStore, diff --git a/Packages/Blockchain/Sources/Gateway/GatewayChainService.swift b/Packages/Blockchain/Sources/Gateway/GatewayChainService.swift index 09c66a06e..eaf9e387c 100644 --- a/Packages/Blockchain/Sources/Gateway/GatewayChainService.swift +++ b/Packages/Blockchain/Sources/Gateway/GatewayChainService.swift @@ -35,8 +35,8 @@ extension GatewayChainService: ChainBalanceable { try await gateway.getStakeBalance(chain: chain, address: address) } - public func getEarnBalance(for address: String) async throws -> [AssetBalance] { - try await gateway.getEarnBalance(chain: chain, address: address) + public func getEarnBalance(for address: String, tokenIds: [AssetId]) async throws -> [AssetBalance] { + try await gateway.getEarnBalance(chain: chain, address: address, tokenIds: tokenIds) } } diff --git a/Packages/Blockchain/Sources/Gateway/GatewayService.swift b/Packages/Blockchain/Sources/Gateway/GatewayService.swift index b2fc422b9..3ed66cc7d 100644 --- a/Packages/Blockchain/Sources/Gateway/GatewayService.swift +++ b/Packages/Blockchain/Sources/Gateway/GatewayService.swift @@ -50,8 +50,8 @@ extension GatewayService { try await gateway.getBalanceStaking(chain: chain.rawValue, address: address)?.map() } - public func getEarnBalance(chain: Primitives.Chain, address: String) async throws -> [AssetBalance] { - try await gateway.getBalanceEarn(chain: chain.rawValue, address: address) + public func getEarnBalance(chain: Primitives.Chain, address: String, tokenIds: [Primitives.AssetId]) async throws -> [AssetBalance] { + try await gateway.getBalanceEarn(chain: chain.rawValue, address: address, tokenIds: tokenIds.compactMap(\.tokenId)) .map { try $0.map() } } } @@ -162,13 +162,12 @@ extension GatewayService { // MARK: - Earn extension GatewayService { - public func earnProviders(assetId: Primitives.AssetId) -> [DelegationValidator] { - gateway.getEarnProviders(assetId: assetId.identifier).compactMap { try? $0.map() } + public func earnProviders(assetId: Primitives.AssetId) throws -> [DelegationValidator] { + try gateway.getEarnProviders(assetId: assetId.identifier).map { try $0.map() } } - public func earnPositions(chain: Primitives.Chain, address: String, assetIds: [Primitives.AssetId]) async throws -> [DelegationBase] { - try await gateway.getEarnPositions(chain: chain.rawValue, address: address, assetIds: assetIds.ids) - .map { try $0.map() } + public func earnPositions(address: String, assetId: Primitives.AssetId) async throws -> [DelegationBase] { + try await gateway.getEarnPositions(address: address, assetId: assetId.identifier).map { try $0.map() } } public func getEarnData( diff --git a/Packages/Blockchain/Sources/Protocols/ChainServiceable.swift b/Packages/Blockchain/Sources/Protocols/ChainServiceable.swift index 698e57a83..5f9fb7009 100644 --- a/Packages/Blockchain/Sources/Protocols/ChainServiceable.swift +++ b/Packages/Blockchain/Sources/Protocols/ChainServiceable.swift @@ -22,7 +22,7 @@ public protocol ChainBalanceable: Sendable { func coinBalance(for address: String) async throws -> AssetBalance func tokenBalance(for address: String, tokenIds: [AssetId]) async throws -> [AssetBalance] func getStakeBalance(for address: String) async throws -> AssetBalance? - func getEarnBalance(for address: String) async throws -> [AssetBalance] + func getEarnBalance(for address: String, tokenIds: [AssetId]) async throws -> [AssetBalance] } public protocol ChainFeeRateFetchable: Sendable { @@ -75,7 +75,7 @@ public protocol ChainNodeStatusFetchable: Sendable { protocol ChainFeePriorityPreference: Sendable {} public extension ChainBalanceable { - func getEarnBalance(for address: String) async throws -> [AssetBalance] { + func getEarnBalance(for address: String, tokenIds: [AssetId]) async throws -> [AssetBalance] { return [] } } diff --git a/Packages/Blockchain/TestKit/ChainServiceMock.swift b/Packages/Blockchain/TestKit/ChainServiceMock.swift index 13228bc17..e46926600 100644 --- a/Packages/Blockchain/TestKit/ChainServiceMock.swift +++ b/Packages/Blockchain/TestKit/ChainServiceMock.swift @@ -46,7 +46,7 @@ extension ChainServiceMock { stakeBalance } - public func getEarnBalance(for address: String) async throws -> [AssetBalance] { + public func getEarnBalance(for address: String, tokenIds: [AssetId]) async throws -> [AssetBalance] { [] } diff --git a/Packages/FeatureServices/BalanceService/BalanceFetcher.swift b/Packages/FeatureServices/BalanceService/BalanceFetcher.swift index a9fd9259e..a0c4b57f8 100644 --- a/Packages/FeatureServices/BalanceService/BalanceFetcher.swift +++ b/Packages/FeatureServices/BalanceService/BalanceFetcher.swift @@ -29,8 +29,10 @@ struct BalanceFetcher: Sendable { .getStakeBalance(for: address) } - func getEarnBalance(chain: Chain, address: String) async throws -> [AssetBalance] { - try await chainServiceFactory.service(for: chain).getEarnBalance(for: address) + func getEarnBalance(chain: Chain, address: String, tokenIds: [AssetId]) async throws -> [AssetBalance] { + try await chainServiceFactory + .service(for: chain) + .getEarnBalance(for: address, tokenIds: tokenIds) } func getTokenBalance( diff --git a/Packages/FeatureServices/BalanceService/BalanceService.swift b/Packages/FeatureServices/BalanceService/BalanceService.swift index dc316a180..3ef6f8c5d 100644 --- a/Packages/FeatureServices/BalanceService/BalanceService.swift +++ b/Packages/FeatureServices/BalanceService/BalanceService.swift @@ -60,9 +60,6 @@ extension BalanceService: BalanceUpdater { group.addTask { await updateCoinStakeBalance(walletId: walletId, asset: chain.assetId, address: address) } - group.addTask { - await updateEarnBalance(walletId: walletId, chain: chain, address: address) - } } // token balance @@ -70,6 +67,9 @@ extension BalanceService: BalanceUpdater { group.addTask { await updateTokenBalances(walletId: walletId, chain: chain, tokenIds: tokenIds, address: address) } + group.addTask { + await updateEarnBalance(walletId: walletId, chain: chain, address: address, tokenIds: tokenIds) + } } } @@ -132,11 +132,11 @@ extension BalanceService { } @discardableResult - private func updateEarnBalance(walletId: WalletId, chain: Chain, address: String) async -> [AssetBalanceChange] { + private func updateEarnBalance(walletId: WalletId, chain: Chain, address: String, tokenIds: [AssetId]) async -> [AssetBalanceChange] { await updateBalanceAsync( walletId: walletId, chain: chain, - fetchBalance: { try await fetcher.getEarnBalance(chain: chain, address: address) }, + fetchBalance: { try await fetcher.getEarnBalance(chain: chain, address: address, tokenIds: tokenIds) }, mapBalance: { $0.earnChange } ) } diff --git a/Packages/FeatureServices/EarnService/EarnService.swift b/Packages/FeatureServices/EarnService/EarnService.swift index 95202a19a..2366d1e86 100644 --- a/Packages/FeatureServices/EarnService/EarnService.swift +++ b/Packages/FeatureServices/EarnService/EarnService.swift @@ -1,7 +1,6 @@ // Copyright (c). Gem Wallet. All rights reserved. import Blockchain -import Foundation import Primitives import Store @@ -19,11 +18,12 @@ public struct EarnService: Sendable { } public func update(walletId: WalletId, assetId: AssetId, address: String) async throws { - let providers = await gatewayService.earnProviders(assetId: assetId) + let apr = try store.getEarnApr(assetId: assetId) ?? 0 + let providers = try await gatewayService.earnProviders(assetId: assetId) + .map { DelegationValidator(chain: $0.chain, id: $0.id, name: $0.name, isActive: $0.isActive, commission: $0.commission, apr: apr, providerType: $0.providerType) } try store.updateValidators(providers) - let positions = try await gatewayService.earnPositions(chain: assetId.chain, address: address, assetIds: [assetId]) - + let positions = try await gatewayService.earnPositions(address: address, assetId: assetId) try updatePositions(walletId: walletId, assetId: assetId, positions: positions) } diff --git a/Packages/FeatureServices/EarnService/TestKit/EarnService+TestKit.swift b/Packages/FeatureServices/EarnService/TestKit/EarnService+TestKit.swift index c652f5ee0..7ea7f75ec 100644 --- a/Packages/FeatureServices/EarnService/TestKit/EarnService+TestKit.swift +++ b/Packages/FeatureServices/EarnService/TestKit/EarnService+TestKit.swift @@ -1,6 +1,5 @@ // Copyright (c). Gem Wallet. All rights reserved. -import Foundation import Primitives import PrimitivesTestKit import EarnService diff --git a/Packages/FeatureServices/Package.swift b/Packages/FeatureServices/Package.swift index 5d4155060..30a861dc8 100644 --- a/Packages/FeatureServices/Package.swift +++ b/Packages/FeatureServices/Package.swift @@ -618,6 +618,7 @@ let package = Package( "Primitives", "Store", "Blockchain", + "BalanceService", ], path: "EarnService", exclude: ["TestKit"] diff --git a/Packages/PrimitivesComponents/Sources/ViewModels/TransactionViewModel.swift b/Packages/PrimitivesComponents/Sources/ViewModels/TransactionViewModel.swift index 488b874f1..f60d185c6 100644 --- a/Packages/PrimitivesComponents/Sources/ViewModels/TransactionViewModel.swift +++ b/Packages/PrimitivesComponents/Sources/ViewModels/TransactionViewModel.swift @@ -137,7 +137,7 @@ public struct TransactionViewModel: Sendable { case .perpetualModifyPosition: return .empty case .earnDeposit: - return Localized.Transfer.Stake.title + return Localized.Common.earn case .earnWithdraw: return Localized.Transfer.Withdraw.title } @@ -212,15 +212,17 @@ public struct TransactionViewModel: Sendable { case .stakeUnfreeze: guard let title = getResourceTitle() else { return .none } return String(format: "%@ %@", Localized.Transfer.from, title) + case .earnDeposit: + return String(format: "%@ %@", Localized.Transfer.to, getDisplayName(address: transaction.transaction.to, chain: chain)) + case .earnWithdraw: + return String(format: "%@ %@", Localized.Transfer.from, getDisplayName(address: transaction.transaction.to, chain: chain)) case .swap, .stakeRewards, .stakeWithdraw, .assetActivation, .perpetualModifyPosition, .perpetualOpenPosition, - .perpetualClosePosition, - .earnDeposit, - .earnWithdraw: + .perpetualClosePosition: guard let metadata = transaction.transaction.metadata?.decode(TransactionPerpetualMetadata.self) else { return .none } diff --git a/Packages/Store/Sources/Stores/AssetStore.swift b/Packages/Store/Sources/Stores/AssetStore.swift index 329e3be3e..3a4670851 100644 --- a/Packages/Store/Sources/Stores/AssetStore.swift +++ b/Packages/Store/Sources/Stores/AssetStore.swift @@ -38,7 +38,9 @@ public struct AssetStore: Sendable { AssetRecord.Columns.isSellable.set(to: asset.properties.isSellable), AssetRecord.Columns.isSwappable.set(to: asset.properties.isSwapable), AssetRecord.Columns.isStakeable.set(to: asset.properties.isStakeable), - AssetRecord.Columns.stakingApr.set(to: asset.properties.stakingApr) + AssetRecord.Columns.isEarnable.set(to: asset.properties.isEarnable), + AssetRecord.Columns.stakingApr.set(to: asset.properties.stakingApr), + AssetRecord.Columns.earnApr.set(to: asset.properties.earnApr) ) } } diff --git a/Packages/Store/Sources/Stores/StakeStore.swift b/Packages/Store/Sources/Stores/StakeStore.swift index e2a0df37c..3062d16cc 100644 --- a/Packages/Store/Sources/Stores/StakeStore.swift +++ b/Packages/Store/Sources/Stores/StakeStore.swift @@ -20,6 +20,15 @@ public struct StakeStore: Sendable { .map { $0.stakingApr } ?? .none } } + + public func getEarnApr(assetId: AssetId) throws -> Double? { + try db.read { db in + try AssetRecord + .filter(key: assetId.identifier) + .fetchOne(db) + .map { $0.earnApr } ?? .none + } + } public func updateDelegations(walletId: WalletId, delegations: [DelegationBase]) throws { try db.write { db in diff --git a/core b/core index 032481596..edcca4ee6 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 032481596f3c1c6c8b60d67fcbe4f84cabb8f36a +Subproject commit edcca4ee6b1302f6d26914ced0314576d99a933c