Skip to content

Commit 29d3701

Browse files
committed
Add dust threshold error handling and localization
Introduces a new InfoSheetType for dust threshold errors and updates error handling in transfer and info sheet flows to support it. Refactors ChainCoreError to simplify dust threshold representation and error mapping. Updates localization strings for dust threshold errors and adds a new 'no data available' message in all supported languages.
1 parent d1aa8fd commit 29d3701

File tree

69 files changed

+135
-74
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+135
-74
lines changed

Features/InfoSheet/Sources/Factory/InfoSheetModelFactory.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,13 @@ public struct InfoSheetModelFactory {
179179
description: Localized.Errors.ScanTransaction.memoRequired(symbol),
180180
image: .image(Images.Logo.logo)
181181
)
182+
case let .dustThreshold(chain, image):
183+
return InfoSheetModel(
184+
title: Localized.Errors.transferError,
185+
description: Localized.Errors.dustThreshold(chain.asset.name),
186+
image: .assetImage(image),
187+
button: .url(Docs.url(.dust))
188+
)
182189
}
183190
}
184191
}

Features/InfoSheet/Sources/Types/InfoSheetType.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public enum InfoSheetType: Identifiable, Sendable, Equatable {
1818
case watchWallet
1919
case stakeLockTime(Image?)
2020
case stakeApr(Image?)
21+
case dustThreshold(Chain, image: AssetImage)
2122
// swaps
2223
case priceImpact
2324
case slippage
@@ -38,7 +39,6 @@ public enum InfoSheetType: Identifiable, Sendable, Equatable {
3839
case maliciousTransaction
3940
case memoRequired(symbol: String)
4041

41-
4242
public var id: String {
4343
switch self {
4444
case .networkFee: "networkFees"
@@ -62,6 +62,7 @@ public enum InfoSheetType: Identifiable, Sendable, Equatable {
6262
case .autoclose: "autoClose"
6363
case .maliciousTransaction: "maliciousTransaction"
6464
case let .memoRequired(symbol): "memoRequired_\(symbol)"
65+
case let .dustThreshold(chain, _): "dustThreshold_\(chain.rawValue)"
6566
}
6667
}
6768

Features/Transfer/Sources/Types/ChainCoreError+Localizations.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ extension ChainCoreError: @retroactive LocalizedError {
99
switch self {
1010
case .cantEstimateFee, .feeRateMissed: Localized.Errors.unableEstimateNetworkFee
1111
case .incorrectAmount: Localized.Errors.invalidAmount
12-
case .dustThreshold(let chain): Localized.Errors.dustThreshold(chain.asset.name)
12+
case .dustThreshold: "The network considers this amount dust — spending it costs more than it's worth."
1313
}
1414
}
1515
}

Features/Transfer/Sources/ViewModels/ConfirmErrorViewModel.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import Components
44
import Primitives
55
import Localization
6+
import Blockchain
67

78
struct ConfirmErrorViewModel {
89
private let state: StateViewType<TransactionInputViewModel>
@@ -24,7 +25,7 @@ extension ConfirmErrorViewModel: ItemModelProvidable {
2425
guard let error = listError else { return .empty }
2526
return .error(
2627
title: Localized.Errors.errorOccured,
27-
error: error,
28+
error: ChainCoreError.fromError(error) ?? error,
2829
onInfoAction: { onSelectListError(error) }
2930
)
3031
}

Features/Transfer/Sources/ViewModels/ConfirmTransferSceneViewModel.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,15 @@ extension ConfirmTransferSceneViewModel {
171171
isPresentingSheet = .info(.memoRequired(symbol: symbol))
172172
}
173173
default:
174-
break
174+
if let chainError = ChainCoreError.fromError(error) {
175+
switch chainError {
176+
case .dustThreshold:
177+
let asset = dataModel.asset
178+
isPresentingSheet = .info(.dustThreshold(asset.chain, image: AssetViewModel(asset: asset).assetImage))
179+
case .feeRateMissed, .cantEstimateFee, .incorrectAmount:
180+
break
181+
}
182+
}
175183
}
176184
}
177185

Packages/Blockchain/Sources/ChainCoreError.swift

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,35 @@
33
import Foundation
44
import WalletCore
55
import Primitives
6+
import Gemstone
67

7-
public enum ChainCoreError: Error, Equatable {
8+
public enum ChainCoreError: String, Error, Equatable {
89
case feeRateMissed
910
case cantEstimateFee
10-
case incorrectAmount
11-
case dustThreshold(chain: Chain)
12-
13-
public static func fromWalletCore(for chain: Chain, _ error: CommonSigningError) throws {
11+
case incorrectAmount
12+
case dustThreshold
13+
14+
public static func fromWalletCore(_ error: CommonSigningError) throws {
1415
let chainError: ChainCoreError? = switch error {
1516
case .errorDustAmountRequested,
16-
.errorNotEnoughUtxos: ChainCoreError.dustThreshold(chain: chain)
17+
.errorNotEnoughUtxos: ChainCoreError.dustThreshold
1718
case .ok: .none
1819
default: ChainCoreError.cantEstimateFee
1920
}
20-
21+
2122
if let error = chainError {
2223
throw error
2324
}
2425
}
26+
27+
28+
public static func fromError(_ error: Error) -> ChainCoreError? {
29+
for errorCase in [ChainCoreError.dustThreshold, .feeRateMissed, .cantEstimateFee, .incorrectAmount] {
30+
if error.localizedDescription.contains(errorCase.rawValue) {
31+
return errorCase
32+
}
33+
}
34+
35+
return nil
36+
}
2537
}

Packages/Blockchain/Sources/EstimateFeeService/BitcoinService.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public final class BitcoinService: Sendable {
5656
}
5757
let plan: BitcoinTransactionPlan = AnySigner.plan(input: signingInput, coin: coinType)
5858

59-
try ChainCoreError.fromWalletCore(for: primitiveChain, plan.error)
59+
try ChainCoreError.fromWalletCore(plan.error)
6060

6161
return Fee(
6262
fee: BigInt(plan.fee),
@@ -81,7 +81,7 @@ extension BitcoinService: GemGatewayEstimateFee {
8181
public func getFee(chain: Gemstone.Chain, input: Gemstone.GemTransactionLoadInput) async throws -> Gemstone.GemTransactionLoadFee? {
8282
return try calculateFee(input: try input.map()).map()
8383
}
84-
84+
8585
public func getFeeData(chain: Gemstone.Chain, input: GemTransactionLoadInput) async throws -> String? {
8686
.none
8787
}

Packages/Localization/Sources/Localized.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,9 +361,9 @@ public enum Localized {
361361
public static let decoding = Localized.tr("Localizable", "errors.decoding", fallback: "Decoding Error")
362362
/// Failed to decode the QR code. Please try again with a different QR code.
363363
public static let decodingQr = Localized.tr("Localizable", "errors.decoding_qr", fallback: "Failed to decode the QR code. Please try again with a different QR code.")
364-
/// The transaction failed because the amount is too small to meet the %@ network’s minimum requirement (dust threshold). This limit ensures the transaction value covers the fees and processing costs. Increase the amount or reduce the fees to proceed.
364+
/// The transaction failed because the amount is too small to meet the %@ network’s minimum requirement (dust threshold). This limit ensures the transaction value covers the fees and processing costs.
365365
public static func dustThreshold(_ p1: Any) -> String {
366-
return Localized.tr("Localizable", "errors.dust_threshold", String(describing: p1), fallback: "The transaction failed because the amount is too small to meet the %@ network’s minimum requirement (dust threshold). This limit ensures the transaction value covers the fees and processing costs. Increase the amount or reduce the fees to proceed.")
366+
return Localized.tr("Localizable", "errors.dust_threshold", String(describing: p1), fallback: "The transaction failed because the amount is too small to meet the %@ network’s minimum requirement (dust threshold). This limit ensures the transaction value covers the fees and processing costs.")
367367
}
368368
/// Error
369369
public static let error = Localized.tr("Localizable", "errors.error", fallback: "Error")
@@ -383,6 +383,8 @@ public enum Localized {
383383
public static let invalidNetworkId = Localized.tr("Localizable", "errors.invalid_network_id", fallback: "Invalid Network ID")
384384
/// Invalid URL
385385
public static let invalidUrl = Localized.tr("Localizable", "errors.invalid_url", fallback: "Invalid URL")
386+
/// No data available
387+
public static let noDataAvailable = Localized.tr("Localizable", "errors.no_data_available", fallback: "No data available")
386388
/// Not Supported
387389
public static let notSupported = Localized.tr("Localizable", "errors.not_supported", fallback: "Not Supported")
388390
/// This device does not support QR code scanning. You can only select QR code image from library.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
"NSCameraUsageDescription" = "تحتاج محفظة Gem Wallet إلى الوصول إلى الكاميرا الخاصة بك لمسح رموز QR.";
22
"NSPhotoLibraryAddUsageDescription" = "يتطلب هذا التطبيق الوصول إلى مكتبة الصور الخاصة بك لحفظ NFTs.";
33
"NSFaceIDUsageDescription" = "يساعد تمكين Face ID محفظة Gem Wallet على الحفاظ على أصولك آمنة.";
4-
"NSPhotoLibraryUsageDescription" = "نحن بحاجة إلى الوصول إلى مكتبة الصور الخاصة بك حتى نتمكن من تحديد الصور.";
4+
"NSPhotoLibraryUsageDescription" = "نحن بحاجة إلى الوصول إلى مكتبة الصور الخاصة بك حتى نتمكن من تحديد الصور.";

Packages/Localization/Sources/Resources/ar.lproj/Localizable.strings

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,3 +513,4 @@
513513
"banner.perpetuals.title" = "Trade Perpetuals on Hyperliquid";
514514
"banner.perpetuals.description" = "Deposit, trade, and earn with Hyperliquid perpetuals";
515515
"recent_activity.title" = "Recent";
516+
"errors.no_data_available" = "لا توجد بيانات متاحة";

0 commit comments

Comments
 (0)