Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8c75107
Integrate earn balance updates into services
gemdev111 Mar 10, 2026
ed410d4
Update core
gemdev111 Mar 12, 2026
732b153
Handle earn provider labels and views
gemdev111 Mar 12, 2026
26af974
Merge branch 'main' into earn-yielder-integration
gemdev111 Mar 12, 2026
a3569b4
Fix tests
gemdev111 Mar 12, 2026
8a722e2
Update core
gemdev111 Mar 12, 2026
1264cfb
Merge branch 'main' into earn-yielder-integration
gemdev111 Mar 12, 2026
d6de511
Delete TransactionStateService+TestKit.swift
gemdev111 Mar 12, 2026
0aef566
Merge branch 'main' into earn-yielder-integration
gemdev111 Mar 16, 2026
d5879a0
Update core
gemdev111 Mar 16, 2026
2ae445c
undo TransactionStateService
gemdev111 Mar 16, 2026
954d776
updates core
gemdev111 Mar 17, 2026
303e6a7
Removed uneeded checks
gemdev111 Mar 17, 2026
0ffc887
Pass token IDs to earn balance fetch for targeted RPC calls
gemdev111 Mar 17, 2026
788cbb2
Update core
gemdev111 Mar 17, 2026
a5e8579
Update core , added update for earn apr
gemdev111 Mar 18, 2026
e4c0392
Merge branch 'main' into earn-yielder-integration
gemdev111 Mar 18, 2026
b5017fb
Persist earnApr from API to asset store
gemdev111 Mar 18, 2026
4a1a9b1
Show earn provider in confirm and transaction history
gemdev111 Mar 18, 2026
a3c8b6d
Update TransactionViewModel.swift
gemdev111 Mar 18, 2026
4603ecd
Update core
gemdev111 Mar 18, 2026
b1e656f
Merge branch 'main' into earn-yielder-integration
gemdev111 Mar 18, 2026
9bd0b87
Added missed isEarnable
gemdev111 Mar 18, 2026
b1fbb0a
Merge branch 'main' into earn-yielder-integration
gemdev111 Mar 19, 2026
f5badb0
Update core
gemdev111 Mar 19, 2026
daf9d3d
Update core
gemdev111 Mar 19, 2026
c8b1991
Cleanup
gemdev111 Mar 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions Features/Stake/Sources/ViewModels/EarnSceneViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import Foundation
import Localization
import Primitives
import Store
import Style
import EarnService
import PrimitivesComponents

Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
Expand Down Expand Up @@ -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
}
}
}
11 changes: 5 additions & 6 deletions Features/Transfer/Sources/ViewModels/AmountEarnViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand All @@ -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
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand All @@ -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
Expand Down
1 change: 0 additions & 1 deletion Gem/Navigation/Stake/EarnNavigationView.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c). Gem Wallet. All rights reserved.

import Foundation
import SwiftUI
import Primitives
import Stake
Expand Down
8 changes: 4 additions & 4 deletions Gem/Services/ServicesFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions Packages/Blockchain/Sources/Gateway/GatewayChainService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down
13 changes: 6 additions & 7 deletions Packages/Blockchain/Sources/Gateway/GatewayService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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() }
}
}
Expand Down Expand Up @@ -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(
Expand Down
4 changes: 2 additions & 2 deletions Packages/Blockchain/Sources/Protocols/ChainServiceable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 []
}
}
Expand Down
2 changes: 1 addition & 1 deletion Packages/Blockchain/TestKit/ChainServiceMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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] {
[]
}

Expand Down
6 changes: 4 additions & 2 deletions Packages/FeatureServices/BalanceService/BalanceFetcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
10 changes: 5 additions & 5 deletions Packages/FeatureServices/BalanceService/BalanceService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,16 @@ 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
if !tokenIds.isEmpty {
group.addTask {
await updateTokenBalances(walletId: walletId, chain: chain, tokenIds: tokenIds, address: address)
}
group.addTask {
await updateEarnBalance(walletId: walletId, chain: chain, address: address, tokenIds: tokenIds)
}
}
}

Expand Down Expand Up @@ -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 }
)
}
Expand Down
8 changes: 4 additions & 4 deletions Packages/FeatureServices/EarnService/EarnService.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c). Gem Wallet. All rights reserved.

import Blockchain
import Foundation
import Primitives
import Store

Expand All @@ -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)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c). Gem Wallet. All rights reserved.

import Foundation
import Primitives
import PrimitivesTestKit
import EarnService
Expand Down
1 change: 1 addition & 0 deletions Packages/FeatureServices/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,7 @@ let package = Package(
"Primitives",
"Store",
"Blockchain",
"BalanceService",
],
path: "EarnService",
exclude: ["TestKit"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down
4 changes: 3 additions & 1 deletion Packages/Store/Sources/Stores/AssetStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
)
}
}
Expand Down
9 changes: 9 additions & 0 deletions Packages/Store/Sources/Stores/StakeStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion core
Submodule core updated 53 files
+18 −0 Cargo.lock
+1 −0 Cargo.toml
+9 −0 crates/gem_evm/src/contracts/erc4626.rs
+3 −1 crates/gem_evm/src/contracts/mod.rs
+1 −0 crates/gem_evm/src/lib.rs
+21 −0 crates/gem_evm/src/rpc/client.rs
+2 −2 crates/gem_evm/src/rpc/mapper.rs
+1 −3 crates/gem_evm/src/slippage.rs
+22 −6 crates/gem_jsonrpc/src/alien.rs
+2 −0 crates/gem_jsonrpc/src/lib.rs
+13 −0 crates/primitives/src/earn_type.rs
+8 −0 crates/primitives/src/yield_provider.rs
+5 −8 crates/swapper/src/across/provider.rs
+2 −1 crates/swapper/src/alien/mock.rs
+2 −9 crates/swapper/src/alien/mod.rs
+2 −1 crates/swapper/src/alien/reqwest_provider.rs
+6 −2 crates/swapper/src/chainflip/provider.rs
+4 −8 crates/swapper/src/client_factory.rs
+7 −104 crates/swapper/src/config.rs
+13 −0 crates/swapper/src/fees/mod.rs
+88 −0 crates/swapper/src/fees/referral.rs
+6 −1 crates/swapper/src/fees/reserve.rs
+49 −0 crates/swapper/src/fees/slippage.rs
+1 −3 crates/swapper/src/lib.rs
+1 −4 crates/swapper/src/models.rs
+1 −1 crates/swapper/src/near_intents/assets.rs
+1 −1 crates/swapper/src/near_intents/provider.rs
+2 −1 crates/swapper/src/proxy/provider.rs
+0 −1 crates/swapper/src/referrer.rs
+1 −1 crates/swapper/src/relay/provider.rs
+2 −3 crates/swapper/src/swapper.rs
+2 −2 crates/swapper/src/uniswap/v3/commands.rs
+1 −1 crates/swapper/src/uniswap/v3/provider.rs
+1 −1 crates/swapper/src/uniswap/v4/commands.rs
+1 −1 crates/swapper/src/uniswap/v4/provider.rs
+30 −0 crates/yielder/Cargo.toml
+17 −0 crates/yielder/src/client_factory.rs
+42 −0 crates/yielder/src/error.rs
+9 −0 crates/yielder/src/lib.rs
+13 −0 crates/yielder/src/provider.rs
+61 −0 crates/yielder/src/yielder.rs
+31 −0 crates/yielder/src/yo/assets.rs
+128 −0 crates/yielder/src/yo/client.rs
+9 −0 crates/yielder/src/yo/contract.rs
+96 −0 crates/yielder/src/yo/mapper.rs
+13 −0 crates/yielder/src/yo/mod.rs
+120 −0 crates/yielder/src/yo/provider.rs
+1 −0 gemstone/Cargo.toml
+2 −3 gemstone/src/alien/client.rs
+8 −2 gemstone/src/alien/provider.rs
+3 −4 gemstone/src/config/swap_config.rs
+13 −11 gemstone/src/gateway/mod.rs
+1 −2 gemstone/src/gem_swapper/mod.rs
Loading