Skip to content
This repository was archived by the owner on Mar 26, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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: 2 additions & 0 deletions Features/FiatConnect/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ let package = Package(
"Store",
"PrimitivesComponents",
.product(name: "FiatService", package: "FeatureServices"),
.product(name: "BalanceService", package: "FeatureServices"),
],
path: "Sources"
),
Expand All @@ -47,6 +48,7 @@ let package = Package(
"FiatConnect",
.product(name: "PrimitivesTestKit", package: "Primitives"),
.product(name: "FiatServiceTestKit", package: "FeatureServices"),
.product(name: "BalanceServiceTestKit", package: "FeatureServices"),
],
path: "Tests"
),
Expand Down
17 changes: 15 additions & 2 deletions Features/FiatConnect/Sources/ViewModels/FiatSceneViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import Validators
import BigInt
import FiatService
import BalanceService

@MainActor
@Observable
Expand Down Expand Up @@ -43,15 +44,18 @@
fiatService: FiatService,
currencyFormatter: CurrencyFormatter = CurrencyFormatter(currencyCode: Currency.usd.rawValue),
assetAddress: AssetAddress,
walletId: WalletId,
wallet: Wallet,
assetsEnabler: any AssetsEnabler,
type: FiatQuoteType = .buy,
amount: Int? = nil
) {
self.fiatService = fiatService
self.currencyFormatter = currencyFormatter
self.assetAddress = assetAddress
self.walletId = walletId
self.wallet = wallet

Check failure on line 55 in Features/FiatConnect/Sources/ViewModels/FiatSceneViewModel.swift

View workflow job for this annotation

GitHub Actions / Build and Test iPhone simulator

value of type 'FiatSceneViewModel' has no member 'wallet'
self.assetsEnabler = assetsEnabler

Check failure on line 56 in Features/FiatConnect/Sources/ViewModels/FiatSceneViewModel.swift

View workflow job for this annotation

GitHub Actions / Build and Test iPhone simulator

value of type 'FiatSceneViewModel' has no member 'assetsEnabler'
self.type = type
let walletId = wallet.walletId
self.assetQuery = ObservableQuery(AssetRequest(walletId: walletId, assetId: assetAddress.asset.id), initialValue: .with(asset: assetAddress.asset))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

critical

The class property walletId is not being initialized in the init method, which will cause a compilation error. You should initialize it from the wallet parameter. The local walletId variable then becomes redundant.

Please also ensure that wallet and assetsEnabler are declared as properties of FiatSceneViewModel.

Suggested change
self.wallet = wallet
self.assetsEnabler = assetsEnabler
self.type = type
let walletId = wallet.walletId
self.assetQuery = ObservableQuery(AssetRequest(walletId: walletId, assetId: assetAddress.asset.id), initialValue: .with(asset: assetAddress.asset))
self.wallet = wallet
self.assetsEnabler = assetsEnabler
self.type = type
self.walletId = wallet.walletId
self.assetQuery = ObservableQuery(AssetRequest(walletId: self.walletId, assetId: assetAddress.asset.id), initialValue: .with(asset: assetAddress.asset))


let buyOperation = BuyOperation(
Expand Down Expand Up @@ -191,6 +195,7 @@
}

urlState = .data(())
Task { await enableAsset() }
await UIApplication.shared.open(url, options: [:])
} catch {
urlState = .error(error)
Expand Down Expand Up @@ -239,6 +244,14 @@
// MARK: - Private

extension FiatSceneViewModel {
private func enableAsset() async {
do {
try await assetsEnabler.enableAssets(wallet: wallet, assetIds: [asset.id], enabled: true)

Check failure on line 249 in Features/FiatConnect/Sources/ViewModels/FiatSceneViewModel.swift

View workflow job for this annotation

GitHub Actions / Build and Test iPhone simulator

cannot find 'wallet' in scope

Check failure on line 249 in Features/FiatConnect/Sources/ViewModels/FiatSceneViewModel.swift

View workflow job for this annotation

GitHub Actions / Build and Test iPhone simulator

cannot find 'assetsEnabler' in scope
} catch {
debugLog("FiatSceneViewModel enableAsset error: \(error)")
}
Comment on lines +250 to +252

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The error from enableAssets is only logged to the debug console. This means that in release builds, any failure to enable the asset will be silent. Consider using a more robust logging mechanism to track these errors in production.

}

private var balanceModel: BalanceViewModel {
BalanceViewModel(asset: asset, balance: assetData.balance, formatter: valueFormatter)
}
Expand Down
6 changes: 4 additions & 2 deletions Features/FiatConnect/Tests/FiatSceneViewModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Formatters
import FiatService
import FiatServiceTestKit
import BigInt
import BalanceServiceTestKit

@testable import FiatConnect

Expand All @@ -17,13 +18,14 @@ final class FiatSceneViewModelTests {
fiatService: FiatService = .mock(),
currencyFormatter: CurrencyFormatter = .init(locale: Locale.US, currencyCode: Currency.usd.rawValue),
assetAddress: AssetAddress = .mock(),
walletId: WalletId = .mock()
wallet: Wallet = .mock()
) -> FiatSceneViewModel {
FiatSceneViewModel(
fiatService: fiatService,
currencyFormatter: currencyFormatter,
assetAddress: assetAddress,
walletId: walletId
wallet: wallet,
assetsEnabler: .mock()
)
}

Expand Down
2 changes: 0 additions & 2 deletions Features/Transfer/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ let package = Package(
.package(name: "Stake", path: "../Stake"),
.package(name: "WalletConnector", path: "../WalletConnector"),
.package(name: "InfoSheet", path: "../InfoSheet"),
.package(name: "FiatConnect", path: "../FiatConnect"),
.package(name: "Swap", path: "../Swap"),
.package(name: "Perpetuals", path: "../Perpetuals"),
.package(name: "EventPresenterService", path: "../EventPresenterService"),
Expand Down Expand Up @@ -63,7 +62,6 @@ let package = Package(
"Stake",
"WalletConnector",
"InfoSheet",
"FiatConnect",
"Swap",
"Perpetuals",
"EventPresenterService",
Expand Down
6 changes: 3 additions & 3 deletions Features/Transfer/Sources/Scenes/AmountScene.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ import SwiftUI
import struct Stake.ValidatorViewModel
import struct Stake.ValidatorView

struct AmountScene: View {
public struct AmountScene: View {
@FocusState private var focusedField: Bool

private var model: AmountSceneViewModel

init(model: AmountSceneViewModel) {
public init(model: AmountSceneViewModel) {
self.model = model
}

var body: some View {
public var body: some View {
@Bindable var model = model
List {
CurrencyInputValidationView(
Expand Down
55 changes: 2 additions & 53 deletions Features/Transfer/Sources/Scenes/ConfirmTransferScene.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,15 @@ import SwiftUI
import Components
import Style
import Localization
import InfoSheet
import PrimitivesComponents
import FiatConnect
import Swap
import Preferences
import Primitives

public struct ConfirmTransferScene: View {
@State private var model: ConfirmTransferSceneViewModel
@Bindable var model: ConfirmTransferSceneViewModel

public init(model: ConfirmTransferSceneViewModel) {
_model = State(initialValue: model)
self.model = model
}

public var body: some View {
Expand All @@ -36,56 +33,8 @@ public struct ConfirmTransferScene: View {
)
.taskOnce { model.fetch() }
.navigationTitle(model.title)
// TODO: - move to navigation view
.navigationBarTitleDisplayMode(.inline)
.activityIndicator(isLoading: model.confirmingState.isLoading, message: model.progressMessage)
.sheet(item: $model.isPresentingSheet) {
switch $0 {
case .info(let type):
InfoSheetScene(type: type)
case .url(let url):
SFSafariView(url: url)
case .networkFeeSelector:
NetworkFeeSheet(model: model.feeModel)
case .payloadDetails:
NavigationStack {
SimulationPayloadDetailsScene(
primaryFields: model.primaryPayloadFields,
secondaryFields: model.secondaryPayloadFields,
fieldViewModel: model.payloadFieldViewModel(for:),
contextMenuItems: model.contextMenuItems(for:)
)
.presentationDetents([.large])
.presentationBackground(Colors.grayBackground)
}
case .fiatConnect(let assetAddress, let walletId):
NavigationStack {
FiatConnectNavigationView(
model: FiatSceneViewModel(
fiatService: model.fiatService,
assetAddress: assetAddress,
walletId: walletId
)
)
.navigationBarTitleDisplayMode(.inline)
.toolbarDismissItem(type: .close, placement: .topBarLeading)
}
case .swapDetails:
if case let .swapDetails(model) = model.detailsViewModel.itemModel {
NavigationStack {
SwapDetailsView(model: Bindable(model))
.presentationDetentsForCurrentDeviceSize(expandable: true)
.presentationBackground(Colors.grayBackground)
}
}
case .perpetualDetails(let model):
NavigationStack {
PerpetualDetailsView(model: model)
.presentationDetentsForCurrentDeviceSize(expandable: true)
.presentationBackground(Colors.grayBackground)
}
}
}
.alertSheet($model.isPresentingAlertMessage)
}
}
Expand Down
2 changes: 1 addition & 1 deletion Features/Transfer/Sources/Types/AmountDataProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import BigInt
import Primitives

enum AmountDataProvider: AmountDataProvidable, @unchecked Sendable {
public enum AmountDataProvider: AmountDataProvidable, @unchecked Sendable {
case transfer(AmountTransferViewModel)
case stake(AmountStakeViewModel)
case freeze(AmountFreezeViewModel)
Expand Down
6 changes: 3 additions & 3 deletions Features/Transfer/Sources/Types/AmountSheetType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import Components
import Perpetuals
import PrimitivesComponents

enum AmountSheetType: Identifiable {
public enum AmountSheetType: Identifiable {
case infoAction(InfoSheetType)
case fiatConnect(assetAddress: AssetAddress, walletId: WalletId)
case fiatConnect(assetAddress: AssetAddress, wallet: Wallet)
case leverageSelector(selection: SelectionState<LeverageOption>)
case autoclose(AutocloseOpenData)

var id: String {
public var id: String {
switch self {
case let .infoAction(type): "info-action-\(type.id)"
case .fiatConnect: "fiat-connect"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import Primitives
import Components
import PrimitivesComponents

enum ConfirmTransferSheetType: Identifiable, Sendable {
public enum ConfirmTransferSheetType: Identifiable, Sendable {
case info(InfoSheetType)
case networkFeeSelector
case payloadDetails
case url(URL)
case fiatConnect(assetAddress: AssetAddress, walletId: WalletId)
case fiatConnect(assetAddress: AssetAddress, wallet: Wallet)
case swapDetails
case perpetualDetails(PerpetualDetailsViewModel)

var id: String {
public var id: String {
switch self {
case let .info(type): "info-\(type.id)"
case let .url(url): "url-\(url)"
Expand Down
12 changes: 6 additions & 6 deletions Features/Transfer/Sources/Types/SelectionState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
import Foundation

@Observable
final class SelectionState<T> {
let options: [T]
var selected: T
let isEnabled: Bool
let title: String
public final class SelectionState<T> {
public let options: [T]
public var selected: T
public let isEnabled: Bool
public let title: String

init(
public init(
options: [T],
selected: T,
isEnabled: Bool,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Foundation
import Localization
import Primitives

final class AmountEarnViewModel: AmountDataProvidable {
public final class AmountEarnViewModel: AmountDataProvidable {
let asset: Asset
let action: EarnType
private let earnService: any EarnDataProvidable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Primitives
import PrimitivesComponents
import Stake

final class AmountFreezeViewModel: AmountDataProvidable {
public final class AmountFreezeViewModel: AmountDataProvidable {
enum Action {
case freeze
case unfreeze
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ struct AmountPerpetualLimits {
}

@Observable
final class AmountPerpetualViewModel: AmountDataProvidable {
public final class AmountPerpetualViewModel: AmountDataProvidable {
let asset: Asset
let data: PerpetualRecipientData
let leverageSelection: SelectionState<LeverageOption>?
Expand Down
24 changes: 12 additions & 12 deletions Features/Transfer/Sources/ViewModels/AmountSceneViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ public final class AmountSceneViewModel {
private let valueConverter = ValueConverter()
let currencyFormatter: CurrencyFormatter

let provider: AmountDataProvider
public let provider: AmountDataProvider

public let assetQuery: ObservableQuery<AssetRequest>
var assetData: AssetData { assetQuery.value }
var transferState: StateViewType<TransferData> = .noData
public var transferState: StateViewType<TransferData> = .noData
var amountInputModel: InputValidationViewModel
var isPresentingSheet: AmountSheetType?
public var isPresentingSheet: AmountSheetType?

private var amountInputType: AmountInputType = .asset {
didSet { amountInputModel.update(validators: inputValidators) }
Expand Down Expand Up @@ -60,7 +60,7 @@ public final class AmountSceneViewModel {
}
}

var asset: Asset { provider.asset }
public var asset: Asset { provider.asset }
var title: String { provider.title }
var canChangeValue: Bool { provider.canChangeValue }
var isInputDisabled: Bool { !canChangeValue }
Expand Down Expand Up @@ -88,8 +88,8 @@ public final class AmountSceneViewModel {
}

var maxTitle: String { Localized.Transfer.max }
var continueTitle: String { Localized.Common.continue }
var isNextEnabled: Bool { actionButtonState == .normal }
public var continueTitle: String { Localized.Common.continue }
public var isNextEnabled: Bool { actionButtonState == .normal }

var inputConfig: any CurrencyInputConfigurable {
AmountInputConfig(
Expand All @@ -113,11 +113,11 @@ extension AmountSceneViewModel {
}
}

func onChangeAssetBalance(_: AssetData, _: AssetData) {
public func onChangeAssetBalance(_: AssetData, _: AssetData) {
amountInputModel.update(validators: inputValidators)
}

func onSelectNextButton() {
public func onSelectNextButton() {
Task {
await fetch()
}
Expand Down Expand Up @@ -148,7 +148,7 @@ extension AmountSceneViewModel {
isPresentingSheet = .autoclose(perpetual.makeAutocloseData(size: amount))
}

func onAutocloseComplete(takeProfit: InputValidationViewModel, stopLoss: InputValidationViewModel) {
public func onAutocloseComplete(takeProfit: InputValidationViewModel, stopLoss: InputValidationViewModel) {
if case let .perpetual(perpetual) = provider {
perpetual.takeProfit = takeProfit.text.isEmpty ? nil : takeProfit.text
perpetual.stopLoss = stopLoss.text.isEmpty ? nil : stopLoss.text
Expand All @@ -160,11 +160,11 @@ extension AmountSceneViewModel {
cleanInput()
}

func onChangeLeverage(_: LeverageOption, _: LeverageOption) {
public func onChangeLeverage(_: LeverageOption, _: LeverageOption) {
amountInputModel.update(validators: inputValidators)
}

func onValidatorSelected(_ validator: DelegationValidator) {
public func onValidatorSelected(_ validator: DelegationValidator) {
if case let .stake(stake) = provider {
stake.validatorSelection.selected = validator
}
Expand Down Expand Up @@ -214,7 +214,7 @@ private extension AmountSceneViewModel {
func onSelectBuy() {
let senderAddress = (try? wallet.account(for: asset.chain).address) ?? ""
let assetAddress = AssetAddress(asset: asset, address: senderAddress)
isPresentingSheet = .fiatConnect(assetAddress: assetAddress, walletId: wallet.walletId)
isPresentingSheet = .fiatConnect(assetAddress: assetAddress, wallet: wallet)
}

var inputValidators: [any TextValidator] {
Expand Down
Loading
Loading