Skip to content
This repository was archived by the owner on Mar 26, 2026. It is now read-only.

Commit 2847b9a

Browse files
authored
Refactor amount handling to use AssetData (#1105)
* Refactor amount handling to use AssetBalancePrice Replaces AmountService with direct usage of AssetBalancePrice and AssetBalancePriceRequest for price and balance retrieval in transfer flows. Updates navigation, view models, and factories to remove AmountService dependency, streamlining asset price and balance observation. Adds new primitives and store request types for asset balance and price. Cleans up related tests and navigation stack initializations. * Update AssetBalancePriceRequest.swift * Updates AssetSceneViewModelTests Removed unused assetid from AssetBalancePrice, since we have id in price * Removed stakeservice * Update JobRunnerTests.swift * Update AmountSceneViewModel.swift * Updates tests * Minor updates Improve getRecipientAddress in AmountSceneViewModel Improve AssetBalancePriceRequest for app consistent style * Refactor asset balance and price handling in transfer feature Replaces AssetBalancePrice and related request/types with AssetData and AssetRequest in AmountSceneViewModel and related views/tests. Removes obsolete AssetBalancePrice, AssetBalancePriceRequest, and BalancePriceInfo files. Updates usages to reflect new data model for asset balance and price. Minor test timing adjustments in JobRunnerTests. * Update core * Update StakeService+TestKit.swift * Update AmountSceneViewModel.swift
1 parent 7a43a49 commit 2847b9a

14 files changed

Lines changed: 82 additions & 140 deletions

File tree

Features/Transfer/Package.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ let package = Package(
6666
.product(name: "ChainService", package: "ChainServices"),
6767
.product(name: "WalletService", package: "SystemServices"),
6868
.product(name: "WalletsService", package: "FeatureServices"),
69-
.product(name: "StakeService", package: "ChainServices"),
7069
.product(name: "NodeService", package: "ChainServices"),
7170
.product(name: "TransactionService", package: "FeatureServices"),
7271
.product(name: "ScanService", package: "ChainServices"),
@@ -83,7 +82,6 @@ let package = Package(
8382
dependencies: [
8483
"Transfer",
8584
.product(name: "PrimitivesTestKit", package: "Primitives"),
86-
.product(name: "StakeServiceTestKit", package: "ChainServices"),
8785
.product(name: "WalletsServiceTestKit", package: "FeatureServices"),
8886
.product(name: "BlockchainTestKit", package: "Blockchain"),
8987
.product(name: "ScanServiceTestKit", package: "ChainServices"),

Features/Transfer/Sources/Navigation/AmountNavigationView.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import InfoSheet
88
import Components
99
import FiatConnect
1010
import PrimitivesComponents
11+
import Store
1112

1213
public struct AmountNavigationView: View {
1314
@State private var model: AmountSceneViewModel
@@ -20,6 +21,11 @@ public struct AmountNavigationView: View {
2021
AmountScene(
2122
model: model
2223
)
24+
.onChangeObserveQuery(
25+
request: $model.assetRequest,
26+
value: $model.assetData,
27+
action: model.onChangeAssetBalance
28+
)
2329
.sheet(item: $model.isPresentingSheet) {
2430
switch $0 {
2531
case let .infoAction(type):

Features/Transfer/Sources/Navigation/RecipientNavigationView.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,14 @@ import QRScanner
88

99
public struct RecipientNavigationView: View {
1010
@State private var model: RecipientSceneViewModel
11-
12-
private let amountService: AmountService
1311
private let confirmService: ConfirmService
1412
private let onComplete: VoidAction
1513

1614
public init(
17-
amountService: AmountService,
1815
confirmService: ConfirmService,
1916
model: RecipientSceneViewModel,
2017
onComplete: VoidAction
2118
) {
22-
self.amountService = amountService
2319
self.confirmService = confirmService
2420
_model = State(initialValue: model)
2521
self.onComplete = onComplete
@@ -39,7 +35,6 @@ public struct RecipientNavigationView: View {
3935
model: AmountSceneViewModel(
4036
input: AmountInput(type: .transfer(recipient: data), asset: model.asset),
4137
wallet: model.wallet,
42-
amountService: amountService,
4338
onTransferAction: model.onTransferAction
4439
)
4540
)

Features/Transfer/Sources/Services/AmountService.swift

Lines changed: 0 additions & 35 deletions
This file was deleted.

Features/Transfer/Sources/ViewModels/AmountSceneViewModel.swift

Lines changed: 36 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,19 @@ import Localization
1010
import Preferences
1111
import Primitives
1212
import PrimitivesComponents
13-
import StakeService
1413
import Staking
14+
import Store
1515
import Style
1616
import Validators
1717
import WalletsService
18+
import Blockchain
1819

1920
@MainActor
2021
@Observable
2122
public final class AmountSceneViewModel {
2223
private let input: AmountInput
2324
private let wallet: Wallet
2425

25-
private let amountService: AmountService
2626
private let onTransferAction: TransferDataAction
2727

2828
private let formatter = ValueFormatter(style: .full)
@@ -32,11 +32,13 @@ public final class AmountSceneViewModel {
3232
private let perpetualPriceFormatter = PerpetualPriceFormatter()
3333

3434
private var currentValidator: DelegationValidator?
35-
private var currentDelegation: Delegation?
3635
private var amountInputType: AmountInputType = .asset {
3736
didSet { amountInputModel.update(validators: inputValidators) }
3837
}
3938

39+
var assetRequest: AssetRequest
40+
var assetData: AssetData = .empty
41+
4042
var amountInputModel: InputValidationViewModel = InputValidationViewModel()
4143
var delegation: DelegationValidator?
4244
var isPresentingSheet: AmountSheetType?
@@ -45,15 +47,17 @@ public final class AmountSceneViewModel {
4547
public init(
4648
input: AmountInput,
4749
wallet: Wallet,
48-
amountService: AmountService,
4950
onTransferAction: TransferDataAction
5051
) {
5152
self.input = input
5253
self.wallet = wallet
53-
self.amountService = amountService
5454
self.onTransferAction = onTransferAction
55-
self.currentValidator = defaultValidator
55+
self.assetRequest = AssetRequest(
56+
walletId: wallet.walletId.id,
57+
assetId: input.asset.id
58+
)
5659

60+
self.currentValidator = defaultValidator
5761
self.amountInputModel = InputValidationViewModel(mode: .onDemand, validators: inputValidators)
5862

5963
// set amount if avaialbe in recipientData
@@ -69,7 +73,6 @@ public final class AmountSceneViewModel {
6973

7074
var isInputDisabled: Bool { !canChangeValue }
7175
var isBalanceViewEnabled: Bool { !isInputDisabled }
72-
var isNextEnabled: Bool { actionButtonState == .normal }
7376

7477
var actionButtonState: ButtonState {
7578
amountInputModel.text.isNotEmpty && amountInputModel.isValid ? .normal : .disabled
@@ -79,6 +82,7 @@ public final class AmountSceneViewModel {
7982
var maxTitle: String { Localized.Transfer.max }
8083
var nextTitle: String { Localized.Common.next }
8184
var continueTitle: String { Localized.Common.continue }
85+
var isNextEnabled: Bool { actionButtonState == .normal }
8286

8387
var inputConfig: any CurrencyInputConfigurable {
8488
AmountInputConfig(
@@ -174,6 +178,10 @@ extension AmountSceneViewModel {
174178
}
175179
}
176180

181+
func onChangeAssetBalance(_: AssetData, _: AssetData) {
182+
amountInputModel.update(validators: inputValidators)
183+
}
184+
177185
func onSelectNextButton() {
178186
do {
179187
try onNext()
@@ -250,30 +258,27 @@ extension AmountSceneViewModel {
250258
)
251259
}
252260

261+
private func recipientAddress(chain: StakeChain?, validatorId: String) -> String {
262+
switch chain {
263+
case .cosmos, .osmosis, .injective, .sei, .celestia, .solana, .sui, .tron: validatorId
264+
case .smartChain: StakeHub.address
265+
case .none, .some(.hyperCore): ""
266+
}
267+
}
268+
253269
private var recipientData: RecipientData {
254270
switch type {
255-
case .transfer(recipient: let recipient):
256-
return recipient
257-
case .deposit(recipient: let recipient):
258-
return recipient
259-
case .withdraw(recipient: let recipient):
260-
return recipient
261-
case .perpetual(let recipient, _):
262-
return recipient
271+
case .transfer(recipient: let recipient): recipient
272+
case .deposit(recipient: let recipient): recipient
273+
case .withdraw(recipient: let recipient): recipient
274+
case .perpetual(let recipient, _): recipient
263275
case .stake,
264276
.stakeUnstake,
265277
.stakeRedelegate,
266-
.stakeWithdraw:
267-
let recipientAddress = amountService.getRecipientAddress(
268-
chain: asset.chain.stakeChain,
269-
type: type,
270-
validatorId: currentValidator?.id
271-
)
272-
273-
return RecipientData(
278+
.stakeWithdraw: RecipientData(
274279
recipient: Recipient(
275280
name: currentValidator?.name,
276-
address: recipientAddress ?? "",
281+
address: (currentValidator?.id).flatMap { recipientAddress(chain: asset.chain.stakeChain, validatorId: $0) } ?? "",
277282
memo: Localized.Stake.viagem
278283
),
279284
amount: .none
@@ -284,7 +289,7 @@ extension AmountSceneViewModel {
284289
private var inputValidators: [any TextValidator] {
285290
let source: AmountValidator.Source = switch amountInputType {
286291
case .asset: .asset
287-
case .fiat: .fiat(price: getAssetPrice(), converter: valueConverter)
292+
case .fiat: .fiat(price: assetData.price?.mapToAssetPrice(assetId: asset.id), converter: valueConverter)
288293
}
289294
switch input.type {
290295
case .transfer,
@@ -318,19 +323,19 @@ extension AmountSceneViewModel {
318323
}
319324

320325
private var amountValue: String {
321-
guard let price = getAssetPrice() else { return .zero }
326+
guard let price = assetData.price else { return .zero }
322327
return (try? valueConverter.convertToAmount(
323328
fiatValue: amountInputModel.text,
324-
price: price,
329+
price: price.mapToAssetPrice(assetId: asset.id),
325330
decimals: asset.decimals.asInt
326331
)).or(.zero)
327332
}
328333

329334
private var fiatValue: Decimal {
330-
guard let price = getAssetPrice() else { return .zero }
335+
guard let price = assetData.price else { return .zero }
331336
return (try? valueConverter.convertToFiat(
332337
amount: amountInputModel.text,
333-
price: price
338+
price: price.mapToAssetPrice(assetId: asset.id)
334339
)).or(.zero)
335340
}
336341

@@ -437,10 +442,6 @@ extension AmountSceneViewModel {
437442
try formatter.inputNumber(from: amount, decimals: asset.decimals.asInt)
438443
}
439444

440-
private func getAssetPrice() -> AssetPrice? {
441-
try? amountService.getPrice(for: asset.id)
442-
}
443-
444445
private var minimumValue: BigInt {
445446
let stakeChain = asset.chain.stakeChain
446447
switch type {
@@ -483,15 +484,9 @@ extension AmountSceneViewModel {
483484
private var availableValue: BigInt {
484485
switch input.type {
485486
case .transfer, .deposit, .perpetual, .stake:
486-
guard let balance = try? amountService.getBalance(walletId: wallet.walletId, assetId: asset.id.identifier) else {
487-
return .zero
488-
}
489-
return balance.available
487+
return assetData.balance.available
490488
case .withdraw:
491-
guard let balance = try? amountService.getBalance(walletId: wallet.walletId, assetId: asset.id.identifier) else {
492-
return .zero
493-
}
494-
return balance.withdrawable
489+
return assetData.balance.withdrawable
495490
case .stakeUnstake(let delegation):
496491
return delegation.base.balanceValue
497492
case .stakeRedelegate(let delegation, _, _):

Features/Transfer/Sources/ViewModels/RecipientSceneViewModel.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import NameService
1212
import Keystore
1313
import WalletsService
1414
import NodeService
15-
import StakeService
1615
import SwiftUI
1716
import ScanService
1817
import Formatters

Features/Transfer/Tests/ViewModels/AmountSceneViewModelTests.swift

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ struct AmountSceneViewModelTests {
1616
@Test
1717
func testMaxButton() {
1818
let model = AmountSceneViewModel.mock()
19-
2019
#expect(model.amountInputModel.isValid)
2120

2221
model.onSelectMaxButton()
@@ -54,23 +53,14 @@ struct AmountSceneViewModelTests {
5453
extension AmountSceneViewModel {
5554
static func mock(
5655
type: AmountType = .transfer(recipient: .mock()),
57-
asset: Asset = .mockEthereum()
56+
assetData: AssetData = .mock(balance: .mock())
5857
) -> AmountSceneViewModel {
59-
let balanceStore = BalanceStore.mock(db: .mockAssets())
60-
return AmountSceneViewModel(
61-
input: AmountInput(type: type, asset: asset),
58+
let model = AmountSceneViewModel(
59+
input: AmountInput(type: type, asset: assetData.asset),
6260
wallet: .mock(),
63-
amountService: AmountService(
64-
priceService: .mock(),
65-
balanceService:.mock(
66-
balanceStore: balanceStore,
67-
assetsService: .mock(balanceStore: balanceStore),
68-
chainServiceFactory: .mock()
69-
),
70-
stakeService: .mock()
71-
),
72-
onTransferAction: { _ in
73-
}
61+
onTransferAction: { _ in }
7462
)
63+
model.assetData = assetData
64+
return model
7565
}
7666
}

Gem/Navigation/Assets/SelectAssetSceneNavigationStack.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ struct SelectAssetSceneNavigationStack: View {
2121
@Environment(\.walletService) private var walletService
2222
@Environment(\.walletsService) private var walletsService
2323
@Environment(\.keystore) private var keystore
24-
@Environment(\.stakeService) private var stakeService
2524
@Environment(\.scanService) private var scanService
2625
@Environment(\.balanceService) private var balanceService
2726
@Environment(\.priceService) private var priceService
@@ -76,11 +75,6 @@ struct SelectAssetSceneNavigationStack: View {
7675
switch input.type {
7776
case .send:
7877
RecipientNavigationView(
79-
amountService: AmountService(
80-
priceService: priceService,
81-
balanceService: balanceService,
82-
stakeService: stakeService
83-
),
8478
confirmService: ConfirmServiceFactory.create(
8579
keystore: keystore,
8680
nodeService: nodeService,

Gem/Navigation/Assets/SelectedAssetNavigationStack.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,6 @@ struct SelectedAssetNavigationStack: View {
5252
switch input.type {
5353
case .send(let type):
5454
RecipientNavigationView(
55-
amountService: AmountService(
56-
priceService: priceService,
57-
balanceService: balanceService,
58-
stakeService: stakeService
59-
),
6055
confirmService: ConfirmServiceFactory.create(
6156
keystore: keystore,
6257
nodeService: nodeService,

Gem/Services/ViewModelFactory.swift

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,16 +122,9 @@ public struct ViewModelFactory: Sendable {
122122
wallet: Wallet,
123123
onTransferAction: TransferDataAction
124124
) -> AmountSceneViewModel {
125-
let amountService = AmountService(
126-
priceService: priceService,
127-
balanceService: balanceService,
128-
stakeService: stakeService
129-
)
130-
131125
return AmountSceneViewModel(
132126
input: input,
133127
wallet: wallet,
134-
amountService: amountService,
135128
onTransferAction: onTransferAction
136129
)
137130
}

0 commit comments

Comments
 (0)