Skip to content

Gem ios earn facade#1718

Open
gemdev111 wants to merge 35 commits intomainfrom
gem-ios-earn-facade
Open

Gem ios earn facade#1718
gemdev111 wants to merge 35 commits intomainfrom
gem-ios-earn-facade

Conversation

@gemdev111
Copy link
Contributor

No description provided.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @gemdev111, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a new 'Earn' feature to the application, enabling users to participate in yield-generating activities. This involved a significant refactoring of the existing 'Staking' module into a more generalized 'Earn' module, along with extensive updates across the UI, view models, data models, persistence layer, and transaction signing logic to support the new functionality. The changes aim to provide users with a comprehensive interface for managing their earn positions and interacting with various yield providers.

Highlights

  • New 'Earn' Feature Introduction: A comprehensive 'Earn' feature has been integrated, allowing users to engage in yield-generating activities. This includes new UI scenes, view models, and data structures to support depositing and withdrawing assets for earning.
  • Refactoring of 'Staking' Module: The existing 'Staking' module has been largely refactored and renamed to 'Earn', indicating a broader scope for yield-related functionalities. Many files previously associated with staking are now part of the new 'Earn' module.
  • UI and ViewModel Enhancements: The AssetScene now displays earn balances and an 'Earn' button. Corresponding view models like AssetSceneViewModel and StakeDetailSceneViewModel have been updated to incorporate earn-specific properties and actions, such as APR display and navigation to earn details.
  • Core Data Model Expansion: Fundamental data models including AssetMetaData, Balance, DelegationValidator, AmountType, SelectedAssetType, TransactionLoadMetadata, and TransferDataType have been extended to include new properties and types specifically for the 'Earn' feature.
  • Persistence Layer Updates: New database migrations have been added to the Store module to support 'isEarnable', 'earnApr' in AssetRecord, 'earn' and 'earnAmount' in BalanceRecord, and 'providerType' in StakeValidatorRecord, ensuring proper data persistence for the new feature.
  • Transaction Signing Logic for Earn: The EthereumSigner has been updated with a new 'signEarn' function to handle earn-related transactions, including logic for ERC20 approvals and generic contract calls, ensuring secure interaction with earn protocols.
  • Dedicated Earn Service: A new EarnService has been introduced to manage the fetching and updating of earn-related data, such as yield providers and user positions, centralizing the business logic for this feature.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • Features/Assets/Sources/Scenes/AssetScene.swift
    • Updated to display earn balance and an earn button when applicable
    • Refactored stake view into a NavigationCustomLink
  • Features/Assets/Sources/ViewModels/AssetSceneViewModel.swift
    • Extended to include properties and actions related to the new earn feature, such as showEarnBalance, earnTitle, earnAprText, showEarnButton, and onSelectEarn()
  • Features/Earn/Sources/Scenes/EarnDetailScene.swift
    • Added a new SwiftUI view for displaying details of an earn delegation
  • Features/Earn/Sources/Scenes/EarnScene.swift
    • Added a new SwiftUI view for the main earn scene, displaying providers and positions
  • Features/Earn/Sources/ViewModels/DelegationHeaderViewModel.swift
    • Added a new view model for delegation header information
  • Features/Earn/Sources/ViewModels/DelegationStateViewModel.swift
    • Added a new view model for displaying delegation state
  • Features/Earn/Sources/ViewModels/DelegationViewModel.swift
    • Added a new view model for displaying delegation details, supporting both stake and earn provider types
  • Features/Earn/Sources/ViewModels/EarnDetailSceneViewModel.swift
    • Added a new view model for the earn detail scene, handling deposit and withdraw actions
  • Features/Earn/Sources/ViewModels/EarnSceneViewModel.swift
    • Added a new view model for the earn scene, managing earn providers and positions
  • Features/Earn/Sources/ViewModels/YieldProviderViewModel.swift
    • Added a new view model for yield provider display
  • Features/Earn/Sources/Views/DelegationView.swift
    • Added a new SwiftUI view for displaying delegation information
  • Features/Earn/Sources/Views/ValidatorImageView.swift
    • Added a new SwiftUI view for displaying validator images, supporting both stake and earn provider types
  • Features/Staking/Package.resolved
    • Removed the package resolution file for the old Staking module
  • Features/Staking/Package.swift
    • Renamed the package from 'Staking' to 'Earn'
    • Updated dependencies to include FeatureServices and Style
  • Features/Staking/Sources/Extentions/DelegationState+Staking.swift
    • Renamed the file and its containing module
  • Features/Staking/Sources/Scenes/StakeDetailScene.swift
    • Renamed the file and its containing module
  • Features/Staking/Sources/Scenes/StakeScene.swift
    • Renamed the file and its containing module
  • Features/Staking/Sources/Scenes/StakeValidatorsScene.swift
    • Renamed the file and its containing module
  • Features/Staking/Sources/Types/AmountInputAction.swift
    • Removed the file
  • Features/Staking/Sources/Types/StakeValidatorsType.swift
    • Renamed the file and its containing module
  • Features/Staking/Sources/ViewModels/StakeDelegationViewModel.swift
    • Renamed the file and its containing module
  • Features/Staking/Sources/ViewModels/StakeDetailSceneViewModel.swift
    • Renamed the file and its containing module
    • Updated stake action types
  • Features/Staking/Sources/ViewModels/StakeRecommendedValidators.swift
    • Renamed the file and its containing module
  • Features/Staking/Sources/ViewModels/StakeSceneViewModel.swift
    • Renamed the file and its containing module
    • Updated request types and stake destination
  • Features/Staking/Sources/ViewModels/StakeValidatorViewModel.swift
    • Renamed the file and its containing module
    • Updated validator name logic to support earn providers
  • Features/Staking/Sources/ViewModels/StakeValidatorsViewModel.swift
    • Renamed the file and its containing module
  • Features/Staking/Sources/ViewModels/ValidatorHeaderViewModel.swift
    • Renamed the file and its containing module
  • Features/Staking/Sources/Views/StakeDelegationView.swift
    • Renamed the file and its containing module
  • Features/Staking/Sources/Views/ValidatorImageView.swift
    • Removed the file
  • Features/Staking/Sources/Views/ValidatorSelectionView.swift
    • Renamed the file and its containing module
  • Features/Staking/Sources/Views/ValidatorView.swift
    • Renamed the file and its containing module
  • Features/Staking/Tests/ViewModels/StakeDelegationViewModelTests.swift
    • Renamed the file and its containing module
    • Updated imports
  • Features/Staking/Tests/ViewModels/StakeDetailSceneViewModelTests.swift
    • Renamed the file and its containing module
    • Updated imports
  • Features/Staking/Tests/ViewModels/StakeRecommendedValidatorsTests.swift
    • Renamed the file and its containing module
    • Updated imports
  • Features/Staking/Tests/ViewModels/StakeSceneViewModelTests.swift
    • Renamed the file and its containing module
    • Updated imports
  • Features/Staking/Tests/ViewModels/StakeValidatorViewModelTests.swift
    • Renamed the file and its containing module
    • Updated imports
  • Features/Transactions/Sources/Types/TransactionFilterType.swift
    • Updated to include earnDeposit and earnWithdraw transaction types under the .stake filter
  • Features/Transactions/Sources/ViewModels/TransactionParticipantViewModel.swift
    • Updated to exclude earnDeposit and earnWithdraw from participant item models and resource titles
  • Features/Transfer/Package.swift
    • Updated package dependencies to use the new 'Earn' module instead of 'Staking'
  • Features/Transfer/Sources/Navigation/AmountNavigationView.swift
    • Updated imports to use the new 'Earn' module
  • Features/Transfer/Sources/Scenes/AmountScene.swift
    • Updated imports to use the new 'Earn' module
    • Added a new section for earn provider display
  • Features/Transfer/Sources/Services/TransferExecutor.swift
    • Updated to include .earn in BroadcastOptions for Solana chain
  • Features/Transfer/Sources/Types/AmountDataProvider.swift
    • Extended to include a new .earn case
    • Updated mapping logic for stake actions
  • Features/Transfer/Sources/Types/AmountInputConfig.swift
    • Updated keyboard type logic and action style to support the new earn feature
  • Features/Transfer/Sources/ViewModels/AmountEarnViewModel.swift
    • Added a new view model for handling earn amount input
  • Features/Transfer/Sources/ViewModels/AmountFreezeViewModel.swift
    • Updated imports to use the new 'Earn' module
  • Features/Transfer/Sources/ViewModels/AmountStakeViewModel.swift
    • Refactored StakeAction into StakeAmountType
    • Updated initialization logic
  • Features/Transfer/Sources/ViewModels/ConfirmAppViewModel.swift
    • Updated to include .earn in cases where no app URL, short name, or image is displayed
  • Features/Transfer/Sources/ViewModels/ConfirmDetailsViewModel.swift
    • Updated to include .earn in cases where no details are displayed
  • Features/Transfer/Sources/ViewModels/ConfirmMemoViewModel.swift
    • Updated to include .earn in cases where memo is not shown
  • Features/Transfer/Sources/ViewModels/ConfirmNetworkViewModel.swift
    • Updated to include .earn in cases where only network name is displayed
  • Features/Transfer/Sources/ViewModels/ConfirmRecipientViewModel.swift
    • Updated to include .earn in cases where recipient title is generic and recipient details are not shown
  • Features/Transfer/Sources/ViewModels/TransferDataViewModel.swift
    • Updated to include title, app URL, and available balance logic for .earn type
  • Features/Transfer/Tests/ViewModels/AmountSceneViewModelTests.swift
    • Updated tests to reflect changes in AmountType.stake and AmountType.stakeWithdraw
  • Gem.xcodeproj/project.pbxproj
    • Modified project file to reflect file renames, additions, and framework dependencies for the new 'Earn' module and service
  • Gem/Navigation/Assets/SelectedAssetNavigationStack.swift
    • Updated to import Earn
    • Added navigation destination for .earn type
  • Gem/Navigation/Earn/EarnNavigationView.swift
    • Added a new navigation view for the earn feature
  • Gem/Navigation/Staking/StakeNavigationView.swift
    • Renamed the file and its containing module
    • Updated imports
  • Gem/Services/ServicesFactory.swift
    • Updated to import EarnService
    • Initialized EarnService with StakeStore and GatewayService
  • Gem/Services/ViewModelFactory.swift
    • Updated imports
    • Added earnService dependency
    • Included new factory methods for earnScene and earnDetailScene
  • Gem/Views/MainTabView.swift
    • Updated onComplete logic to include .earn type
  • Packages/Blockchain/Sources/Gateway/GatewayService.swift
    • Extended with new methods earnProviders and earnPositions to fetch earn-related data
  • Packages/Blockchain/Sources/Protocols/ChainServiceable.swift
    • Updated defaultPriority to include .earn type
  • Packages/ChainServices/StakeService/StakeService.swift
    • Modified to pass providerType to StakeStore methods for filtering validators and delegations
  • Packages/Components/Sources/Types/EmptyContentType.swift
    • Added .earn case for empty content display
  • Packages/FeatureServices/BalanceService/BalanceService.swift
    • Extended mapToUpdateBalanceType to handle .earn balance updates
  • Packages/FeatureServices/EarnService/EarnService.swift
    • Added a new service for managing earn data, including fetching providers and positions
  • Packages/FeatureServices/EarnService/TestKit/EarnService+TestKit.swift
    • Added a mock implementation for EarnServiceable for testing
  • Packages/FeatureServices/Package.swift
    • Updated package manifest to include EarnService and EarnServiceTestKit
  • Packages/GemstonePrimitives/Sources/Extensions/GemDelegationValidator+GemstonePrimitives.swift
    • Added mapping extensions for GemEarnProviderType and EarnProviderType
    • Updated GemDelegationValidator mapping to include providerType
  • Packages/GemstonePrimitives/Sources/Extensions/GemEarnData+GemstonePrimitives.swift
    • Added mapping extensions for GemEarnData and Primitives.EarnData
  • Packages/GemstonePrimitives/Sources/Extensions/GemEarnType+GemstonePrimitives.swift
    • Added mapping extensions for GemEarnType and Primitives.EarnType
  • Packages/GemstonePrimitives/Sources/Extensions/GemTransactionInputType+GemstonePrimitives.swift
    • Updated to include .earn case for asset extraction and mapping to TransferDataType
  • Packages/GemstonePrimitives/Sources/Extensions/GemTransactionLoadMetadata+GemstonePrimitives.swift
    • Updated evm case to include earnData and corresponding mapping logic
  • Packages/GemstonePrimitives/Sources/Extensions/TransferDataType+GemstonePrimitives.swift
    • Updated to include .earn case for asset extraction
  • Packages/Primitives/Sources/AmountInput.swift
    • Added AmountInputAction typealias
  • Packages/Primitives/Sources/AmountType.swift
    • Introduced new enums StakeAmountType and EarnAmountType
    • Updated AmountType to use these new enums
  • Packages/Primitives/Sources/AssetBalanceType.swift
    • Added .earn case to AssetBalanceType
    • Updated available and metadata properties
    • Added earnChange property to AssetBalance
  • Packages/Primitives/Sources/AssetMetadata.swift
    • Added isEarnEnabled and earnApr properties
  • Packages/Primitives/Sources/Balance.swift
    • Added earn property to Balance struct
  • Packages/Primitives/Sources/BalanceType.swift
    • Added .earn case to BalanceType
  • Packages/Primitives/Sources/Delegation.swift
    • Added providerType to DelegationValidator
  • Packages/Primitives/Sources/EarnData.swift
    • Added a new struct EarnData
  • Packages/Primitives/Sources/EarnProviderType.swift
    • Added a new enum EarnProviderType
  • Packages/Primitives/Sources/EarnTransaction.swift
    • Added a new struct EarnTransaction
  • Packages/Primitives/Sources/EarnType.swift
    • Added a new enum EarnType
  • Packages/Primitives/Sources/Extensions/AssetData+Primitives.swift
    • Updated balances dictionary to include BalanceType.earn
  • Packages/Primitives/Sources/Extensions/Delegation+Primitives.swift
    • Updated DelegationValidator.mock() to include providerType
  • Packages/Primitives/Sources/Extensions/Transaction+Primitives.swift
    • Updated assetIds to include earnDeposit and earnWithdraw
  • Packages/Primitives/Sources/SelectedAssetType.swift
    • Added .earn case to SelectedAssetType
    • Updated id and recentActivityData properties
  • Packages/Primitives/Sources/TransactionLoadMetadata.swift
    • Updated evm case to include earnData
    • Added getEarnData() method
  • Packages/Primitives/Sources/TransactionType.swift
    • Added earnDeposit and earnWithdraw cases
  • Packages/Primitives/Sources/TransferDataType.swift
    • Added .earn case
    • Updated transactionType, chain, recipient, assetIds, and shouldIgnoreValueCheck properties
  • Packages/Primitives/Sources/YieldProvider.swift
    • Added a new enum YieldProvider
  • Packages/Primitives/TestKit/AssetMetaData+PrimitivesTestKit.swift
    • Updated mock data to include isEarnEnabled and earnApr
  • Packages/Primitives/TestKit/DelegationValidator+PrimitivesTestKit.swift
    • Updated mock data to include providerType
  • Packages/PrimitivesComponents/Sources/Types/TransactionHeaderBuilder.swift
    • Updated to include earnDeposit and earnWithdraw for header type building
  • Packages/PrimitivesComponents/Sources/ViewModels/AssetDataViewModel.swift
    • Added earnApr property
  • Packages/PrimitivesComponents/Sources/ViewModels/EmptyContentTypeViewModel.swift
    • Added .earn case for empty content title, description, and image
  • Packages/PrimitivesComponents/Sources/ViewModels/TransactionViewModel.swift
    • Updated to include earnDeposit and earnWithdraw for various display properties
  • Packages/Signer/Sources/Chains/EthereumSigner.swift
    • Added signEarn function to handle earn transactions, including ERC20 approval logic
  • Packages/Signer/Sources/Signable.swift
    • Added signEarn to the Signable protocol
  • Packages/Signer/Sources/Signer.swift
    • Updated sign method to dispatch to signEarn for .earn type
  • Packages/Store/Sources/Migrations.swift
    • Added new migrations for 'Add earn support' and 'Add providerType to stake_validators'
  • Packages/Store/Sources/Models/AssetRecord.swift
    • Added isEarnable and earnApr properties
    • Updated mapping
  • Packages/Store/Sources/Models/BalanceRecord.swift
    • Added earn and earnAmount properties
    • Updated totalAmountSQlCreation
  • Packages/Store/Sources/Models/StakeValidatorRecord.swift
    • Added providerType property
    • Updated mapping
  • Packages/Store/Sources/Requests/AssetRequest.swift
    • Updated AssetData.empty and AssetData.loading to include new earn properties
  • Packages/Store/Sources/Requests/StakeDelegationsRequest.swift
    • Renamed the file to DelegationsRequest
    • Updated to filter by providerType
  • Packages/Store/Sources/Requests/StakeValidatorsRequest.swift
    • Removed the file
  • Packages/Store/Sources/Requests/ValidatorsRequest.swift
    • Added a new request for fetching validators based on chain and provider type
  • Packages/Store/Sources/Stores/BalanceStore.swift
    • Updated updateBalance to handle .earn balance changes
  • Packages/Store/Sources/Stores/StakeStore.swift
    • Modified getValidatorsActive, getValidators, and getDelegations to accept providerType
    • Used new DelegationsRequest and ValidatorsRequest
    • Added deleteDelegations
  • Packages/Store/Sources/Types/PriceAlertAssetRecordInfo.swift
    • Updated mock data to include new earn properties
  • Packages/Store/Sources/Types/UpdateBalanceType.swift
    • Added UpdateEarnBalance
    • Updated metadata property
  • Packages/Style/Sources/Images.swift
    • Added EarnProviders enum with yo image
  • Packages/Style/Sources/Resources/Assets.xcassets/yield_providers/Contents.json
    • Added asset catalog for yield providers
  • Packages/Style/Sources/Resources/Assets.xcassets/yield_providers/yo.imageset/Contents.json
    • Added image set for 'yo' yield provider
  • Packages/Style/Sources/Resources/Assets.xcassets/yield_providers/yo.imageset/yo.svg
    • Added SVG image for 'yo' yield provider
Activity
  • The pull request was initiated by gemdev111 with the title 'Gem ios earn facade' and no additional description, indicating a focus on implementing a new 'Earn' feature.
  • The changes involve extensive refactoring of the existing 'Staking' module into a new 'Earn' module, along with significant additions and modifications across various layers of the application, suggesting a substantial development effort.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the 'Earn' feature, providing a unified interface for yield-generating activities. The implementation includes new scenes, view models, and service layers, along with necessary database migrations and signing logic for EVM chains. While the architecture is consistent with the rest of the project, there are critical safety issues in the database migrations and potential runtime crashes in the view models that should be addressed.

}

migrator.registerMigration("Add earn support") { db in
try? db.alter(table: AssetRecord.databaseTableName) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using try? in database migrations is highly discouraged. If a migration fails (e.g., due to a schema conflict or database lock), the failure is silently ignored, leaving the database in an inconsistent state. Subsequent code that relies on these schema changes will likely crash or behave unpredictably. Use try to ensure that migration errors are caught and handled by the DatabaseMigrator. This pattern should be corrected on lines 397, 404, and 411 as well.

Suggested change
try? db.alter(table: AssetRecord.databaseTableName) {
try db.alter(table: AssetRecord.databaseTableName) {


var depositDestination: AmountInput {
AmountInput(
type: .earn(.deposit(provider: providers.first!)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Force unwrapping providers.first! is unsafe. Although the UI visibility is currently controlled by showDeposit, this property is public and could be accessed by other components or tests when providers is empty, causing a crash. Consider making depositDestination optional or providing a safe fallback.

func fetch() async {
viewState = .loading
do {
let address = (try? wallet.account(for: asset.id.chain))?.address ?? ""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If the wallet does not contain an account for the asset's chain, address will be an empty string. Calling earnService.update with an empty address is likely to result in an invalid API request or incorrect data fetching. It is safer to guard against a missing account and return early.

Suggested change
let address = (try? wallet.account(for: asset.id.chain))?.address ?? ""
guard let address = (try? wallet.account(for: asset.id.chain))?.address else { return }

@gemdev111 gemdev111 force-pushed the gem-ios-earn-facade branch 2 times, most recently from 6760b81 to 9f40288 Compare February 25, 2026 15:10
// Copyright (c). Gem Wallet. All rights reserved.

public enum DelegationDetailActionType: Hashable, Identifiable {
public var id: Self { self }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

improve naming

.asset(assetImage: AssetViewModel(asset: asset).assetImage)
}

public var availableActions: [DelegationDetailActionType] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add unit test

self.delegation = delegation
self.currencyCode = currencyCode
self.asset = asset ?? delegation.base.assetId.chain.asset
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.asset = asset ?? delegation.base.assetId.chain.asset
self.asset = asset


public init(
delegation: Delegation,
asset: Asset? = nil,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
asset: Asset? = nil,
asset: Asset,

}

var title: String { Localized.Common.earn }
var assetTitle: String { "\(asset.symbol) (\(asset.chain.asset.name))" }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be in AssetViewModel


var depositDestination: AmountInput {
AmountInput(
type: .earn(.deposit(providers.first!)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
type: .earn(.deposit(providers.first!)),
type: .earn(.deposit(providers.first)),

}

func makeTransferData(value: BigInt) async throws -> TransferData {
let address = (try? wallet.account(for: asset.chain).address) ?? ""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let address = (try? wallet.account(for: asset.chain).address) ?? ""
let address = try wallet.account(for: asset.chain).address)


var earnBalanceText: String {
let balance = assetData.balance.earn
guard balance > 0 else { return "0" }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
guard balance > 0 else { return "0" }
guard balance > 0 else { return .zero }

@@ -0,0 +1,9 @@
// Copyright (c). Gem Wallet. All rights reserved.

public enum DelegationActionType: Hashable, Identifiable {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DelegationActionType.swift

}

public var showManage: Bool {
!availableActions.isEmpty
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
!availableActions.isEmpty
availableActions.isNotEmpty


var aprTitle: String { Localized.Stake.apr("") }
var aprValue: String {
let apr = providers.first.map(\.apr).flatMap { $0 > 0 ? $0 : nil }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

split to var private apr: Double?

.getStakeBalance(for: address)
}

func getEarnBalance(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do 1 liner

case panora
case thorchain
case jupiter
case okx
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add it back

))
}

func signEarn(input: SignerInput, privateKey: Data) throws -> [String] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reuse code with signSwap


import Foundation

public struct EarnTransaction: Codable, Sendable {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove


import Foundation

public struct EarnData: Codable, Equatable, Hashable, Sendable {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public struct EarnData: Codable, Equatable, Hashable, Sendable {
public struct StakeData: Codable, Equatable, Hashable, Sendable {

or similar, that's reused for Stake and Earn

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use StakeData, data and to should not be optional.

Move AmountInputAction to Primitives

Add providerType to store and earn DB queries
Update imports for Staking to Earn rename

Add earn FFI mappings

Refactor AmountType with StakeAmountType

Add earn transaction types and exhaustiveness fixes

Add earn scenes and navigation

Add earn balance and metadata support

Add earn button to asset detail screen

Add earn asset handling and validator UI

Introduce assetRequest and assetData to EarnSceneViewModel and observe asset updates in EarnNavigationView to surface asset metadata (e.g. earn APR). Use assetData.metadata.earnApr as a fallback when computing APR. Rename showEarn -> showEarnBalance and update related computed properties (showBalances, showEarnButton) and inline the stake header link in AssetScene while adding accessibility identifiers for stake and earn. Update Validator views: map earn providers to YieldProvider display name in StakeValidatorViewModel and make ValidatorImageView public to show provider images (earn) or validator images (stake). Also update AmountScene to display the earn provider in the amount input section.

Regenerate typeshare types and fix callers

Add getEarnData gateway method

Move RecipientNavigationView to Gem target

Add EarnData to earn transaction flow

Add loading indicator to amount toolbar
Refactor StakeDelegation to DelegationViewModel

Replace the old StakeDelegationViewModel/view/header with a unified DelegationViewModel and DelegationView. Key changes:

- Deleted StakeDelegationViewModel, StakeDelegationView, and ValidatorHeaderViewModel.
- Introduced DelegationViewModel changes (adds ExplorerService, validatorUrl) and new unit tests (DelegationViewModelTests).
- Updated StakeScene to use DelegationView and model.navigationDestination(for:) instead of the old types.
- StakeSceneViewModel now accepts a currencyCode, maps delegations to DelegationViewModel, and exposes navigationDestination(for:) helper.
- StakeDetailSceneViewModel updated to use DelegationViewModel and DelegationHeaderViewModel.
- ViewModelFactory now supplies Preferences.standard.currency when building stake view models.
- Submodule 'core' reference bumped.

These changes consolidate delegation presentation and navigation logic, centralize currency handling, and wire explorer URLs for validators.

Introduce ValidatorViewModel and refactor validator logic

Centralize validator representation by adding ValidatorViewModel and moving name/image/url/apr logic into it. Remove legacy view models and helpers (StakeValidatorViewModel, YieldProviderViewModel, DelegationHeaderViewModel, DelegationState+Staking) and update DelegationViewModel to use ValidatorViewModel (also conforming DelegationViewModel to HeaderViewModel). Update views and view models (ValidatorImageView, ValidatorSelectionView, ValidatorView, StakeValidatorsViewModel, EarnDetail/StakeDetail scenes) to construct and consume ValidatorViewModel. Rename and update tests accordingly (StakeValidatorViewModelTests -> ValidatorViewModelTests). Misc: small formatting/import adjustments and use of DelegationStateViewModel for state title.
Rename StakeDetailScene to DelegationDetailScene and introduce DelegationDetailSceneViewModel to unify stake and earn provider handling. Add DelegationDetailActionType and consolidate availableActions/action handling into the new view model (ForEach-driven action list). Remove legacy EarnDetailScene, StakeDetailSceneViewModel and EarnDetailSceneViewModel. Update DelegationStateViewModel to expose color and textStyle and refactor DelegationViewModel to use it. Wire navigation and factory: update EarnNavigationView/StakeNavigationView and ViewModelFactory to use delegationDetailScene and pass validators. Adjust tests and mocks to the new DelegationDetailSceneViewModel.
Replace request/value pairs with ObservableQuery in EarnSceneViewModel (assetQuery, positionsQuery, providersQuery) and expose computed properties (assetData, positions, providers). Update initial query values. Update UI bindings: EarnNavigationView now uses .bindQuery(...) and removes an unused Store import. Rename StakeDetailSceneViewModelTests to DelegationDetailSceneViewModelTests. Update DelegationDetailScene to call model.onSelectAction(...) instead of performAction. Add earn-related defaults to AssetData (isEarnEnabled, earnApr).
Add EarnService support across the transaction system: handle .earn transaction types in TransactionFactory, propagate earnService through ServicesFactory into TransactionStateService, and wire it into TransactionPostProcessingService to call earnService.update for earnDeposit/earnWithdraw. Update package manifest to include EarnService and test dependencies, and extend the TransactionStateService test kit with an EarnService.mock provider. These changes enable processing and post-processing of earn-related transactions.
Expose and propagate 'earn' balances throughout the stack: added ChainBalanceable.getEarnBalance and a default empty implementation; implemented getEarnBalance in GatewayService and GatewayChainService to forward and map gateway responses; updated ChainServiceMock to stub earn balances. BalanceFetcher can now request earn balances, and BalanceService schedules and handles earn balance updates (added updateEarnBalance and a task to fetch/update earn balances, mapping via .earnChange). These changes enable retrieval and processing of earn/yield balances alongside existing coin/stake/token balances.
Simplify and tighten Earn-related APIs: remove the chain parameter from EarnService.getEarnData and its protocol, add an assetIds parameter to GatewayService.earnPositions, and make GatewayService.earnProviders synchronous and non-throwing (using compactMap). Update EarnService to fetch providers (non-throwing) then request positions for the specific asset, and adjust callers/test mocks (AmountEarnViewModel and MockEarnService) to match the new signatures. Also updates the core submodule commit reference.
- BalanceService: guard on earnAmount > 0 in updateEarnBalance to skip chain calls when not needed
  - EarnService: write BalanceRecord.earn from positions to bootstrap earn balance
  - EarnService: extract getEarnUpdateBalance for clean separation
  - Update ServicesFactory and TestKit for EarnService balanceStore dependency
Replace scattered stateText/stateStyle accessors with a single stateModel property that returns DelegationStateViewModel. Updated DelegationViewModel and DelegationDetailSceneViewModel to expose stateModel, and adjusted DelegationView and DelegationDetailScene to use stateModel.title and stateModel.textStyle. This centralizes state presentation logic and removes duplicated mapping of state to title/style.
Replace silent optional fallback that returned an empty address with a throwing account lookup in EarnSceneViewModel.fetch and AmountEarnViewModel.makeTransferData. This removes the previous (try? ...)?.address ?? "" pattern so errors from wallet.account(for:) surface instead of allowing an empty address to be used, enabling proper error handling and preventing invalid requests.
Refactor DelegationDetail types and files to DelegationScene: rename scene, view model, and action enum (DelegationDetailScene -> DelegationScene, DelegationDetailSceneViewModel -> DelegationSceneViewModel, DelegationDetailActionType -> DelegationActionType). Update usages in navigation and ViewModelFactory, adjust internal properties (chain -> stakeChain, recommendedCurrentValidator -> recommendedValidator) and related logic. Add a new StakeTestKit target to Package.swift and include a DelegationSceneViewModel test helper and updated tests; remove the old DelegationDetail tests. Also update the core submodule reference.
Rename StakeValidatorsScene, StakeValidatorsViewModel, and StakeValidatorsType to ValidatorSelectScene, ValidatorSelectSceneViewModel, and ValidatorSelectType respectively. Update related call sites and API: replace model/state types, initializer parameters, and the stakeValidatorsType property with validatorSelectType (used in AmountNavigationView and AmountStakeViewModel) to match the new names.
Avoid force-unwrapping providers.first in EarnSceneViewModel by making depositDestination return an optional AmountInput and returning nil when there are no providers. Update TransferDataViewModel to use Localized.Wallet.deposit for the earn-deposit case instead of Localized.Transfer.Stake.title to display the correct label. Changes improve safety and correct the UI string.
Make Asset a required parameter of DelegationViewModel (remove optional fallback to delegation.base.assetId.chain.asset) and update all call sites accordingly. Also switch EarnSceneViewModel to use AssetViewModel.title for asset display. Tests and TestKit updated to pass the asset where needed. This enforces explicit asset injection and centralizes asset title formatting.
  - Create reusable AprViewModel in PrimitivesComponents for consistent APR formatting (green styled value via TextValue)
  - Replace duplicated APR formatting logic across StakeSceneViewModel, EarnSceneViewModel, DelegationSceneViewModel, and ValidatorViewModel with AprViewModel
  - Consolidate stakeApr/earnApr in AssetDataViewModel into apr(for: StakeProviderType)
  - Remove redundant aprText wrapper properties — callers use aprModel.text directly
Rename AssetImageTitleView to AssetPreviewView and switch consumers to use AssetViewModel instead of the removed AssetImageTitleViewModel. Add subtitleSymbol to AssetViewModel (hides symbol when equal to name), update Receive/Recipient scenes to use AssetPreviewView(model: assetModel), and adjust Recipient/Receive view models accordingly. Update tests to cover subtitleSymbol and networkFullName expectations.
Introduce provider-based handling for stake and earn balances across the feature: replace separate showStakedBalance/showEarnBalance with showProviderBalance(for:), add balanceTitle(for:) and aprModel(for:) helpers, and update AssetScene to use these methods for titles/subtitles. Move showStakedBalanceTypes to a private static and make onSelectBuy/onSelectSwap private. Update BalanceViewModel to provide balanceTextWithSymbol(for:) (including handling .earn) and forward that from AssetDataViewModel. Update test fixtures: add earn to Balance testkit, add AssetDataViewModel testkit mock, and extend unit tests to cover provider visibility, titles and balance formatting.
Rename Features/Stake/Sources/Types/DelegationAction.swift to DelegationActionType.swift and update usages. Replace `!...isEmpty` checks with the `isNotEmpty` property in DelegationSceneViewModel and StakeSceneViewModel for clarity. Also reformat a BalanceFetcher method signature into a single line (no behavior changes).
Remove Earn-specific balance persistence and the BalanceStore dependency from EarnService. Deleted BalanceStore.hasEarnBalance and the EarnService.balanceStore property and related updateEarnBalance logic; updated EarnService initializer and tests accordingly. Adjusted ServicesFactory to stop passing balanceStore into EarnService. Removed Formatters import and Package dependency from FeatureServices where unused. Updated BalanceService to always attempt fetching earn balances (removed the hasEarnBalance guard). These changes centralize/decouple earn balance handling and simplify the EarnService surface.
Replace EarnData with ContractCallData across the codebase and update all usages and mappings. Changes include:
- Primitives: remove EarnData, add ContractCallData type and test mock.
- TransferDataType and TransactionLoadMetadata: use contractCall (ContractCallData) instead of stakeData/EarnData.
- GatewayService, EarnService and mocks: return and propagate ContractCallData.
- GemstonePrimitives: rename/migrate mapping extensions from GemEarnData to GemContractCallData and update map() functions.
- EthereumSigner: adapt signing logic to use contractCall.callData and contractCall.contractAddress.
- Remove unused GemStakeData mapping file and update submodule pointer.
These edits align types and mappings to better represent EVM contract call payloads and propagate the rename throughout services, primitives, tests and signers.
Replace legacy buildBaseInput helpers with a unified buildSigningInput and move signing helpers into a private extension. Introduce buildApprovalInput and signContractCall to centralize contract call and approval logic (handling nonces, gas limits and optional approvals). Replace fatalError usages with throwing AnyError for invalid input types and rename local variables (e.g. base -> signingInput). Remove old buildBaseInputCustom/buildBaseInput implementations and consolidate AnySigner signing logic into a single private sign(...) helper.
@gemdev111 gemdev111 marked this pull request as ready for review February 26, 2026 18:12
Wrap Earn-related UI and logic in #if DEBUG checks so the Earn feature is only available in debug builds. AssetSceneViewModel: showEarnButton and the .earn case in showProviderBalance now return false in non-debug builds. SelectedAssetNavigationStack: EarnNavigationView is shown only under #if DEBUG; otherwise EmptyView is used. This hides experimental/unfinished Earn functionality from non-debug (release) builds.
@gemdev111 gemdev111 requested a review from gemcoder21 February 26, 2026 18:38
Apply the APR subtitle style to asset list items in AssetScene so APR subtitles use the correct styling. Normalize asset title formatting to avoid redundant "Name (Name)" when an asset's name equals its symbol. Correct EmptyContentTypeViewModel to use Earn localized strings (title and description) for the .earn case. Mirror the asset title formatting change in TransferAmountCalculatorError as well. Files updated: AssetScene.swift, AssetViewModel.swift, EmptyContentTypeViewModel.swift, TransferAmountCalculatorError.swift.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants