← Code knowledge base
Reviewed Flutter implementation · 2026-10-27

exchange_fees_and_limits.dart

mobile_flutter/lib/features/wallets/domain/exchange_fees_and_limits.dart

Immutable source snapshot based on revision de78e63ed65c. Original line numbers are retained. This is implementation evidence; check the public API catalogue before adapting it.

The mobile /api/v1/mobile routes belong to the example backend. Company credentials stay on that backend. Some sections outside the documented product scope may be omitted.

Lines 1–111

/// Account-scoped provider values. Unknown values stay unknown, never zero.class BoomFiFeesAndLimits {  const BoomFiFeesAndLimits(      {this.minDeposit,      this.maxDeposit,      this.minWithdraw,      this.maxWithdraw,      this.withdrawFee,      this.withdrawFeeRate});   factory BoomFiFeesAndLimits.fromJson(Map<String, dynamic> json) {    String? read(String snake, String camel, String pascal) {      final raw = json[snake] ?? json[camel] ?? json[pascal];      final text = raw?.toString().trim();      return text != null && _Amount.parse(text) != null ? text : null;    }     return BoomFiFeesAndLimits(      minDeposit: read('min_deposit', 'minDeposit', 'MinDeposit'),      maxDeposit: read('max_deposit', 'maxDeposit', 'MaxDeposit'),      minWithdraw: read('min_withdraw', 'minWithdraw', 'MinWithdraw'),      maxWithdraw: read('max_withdraw', 'maxWithdraw', 'MaxWithdraw'),      withdrawFee: read('withdraw_fee', 'withdrawFee', 'WithdrawFee'),      withdrawFeeRate:          read('withdraw_fee_rate', 'withdrawFeeRate', 'WithdrawFeeRate'),    );  }   final String? minDeposit, maxDeposit, minWithdraw, maxWithdraw;  final String? withdrawFee, withdrawFeeRate;  // Confirmed by BoomFi: 0.01 represents 1%.  String? get withdrawFeePercentage => withdrawFeeRate == null      ? null      : _Amount.parse(withdrawFeeRate!)?.asPercentage();   bool get hasWithdrawalLimits => minWithdraw != null || maxWithdraw != null;   String? validate(String amount, String currency, {required bool withdrawal}) {    final value = _Amount.parse(amount.replaceAll(',', '.'));    if (value == null) return 'Enter a valid amount.';    final min = withdrawal ? minWithdraw : minDeposit;    final max = withdrawal ? maxWithdraw : maxDeposit;    final action = withdrawal ? 'withdrawal' : 'deposit';    if (min != null && value.compareTo(_Amount.parse(min)!) < 0) {      return 'Minimum $action is $min $currency.';    }    if (max != null && value.compareTo(_Amount.parse(max)!) > 0) {      return 'Maximum $action is $max $currency.';    }    return null;  }   List<String> descriptions(String currency, {required bool withdrawal}) {    final min = withdrawal ? minWithdraw : minDeposit;    final max = withdrawal ? maxWithdraw : maxDeposit;    final action = withdrawal ? 'withdrawal' : 'deposit';    return [      if (min != null) 'Minimum $action: $min $currency',      if (max != null) 'Maximum $action: $max $currency',      if (withdrawal) ...[        if (withdrawFee != null) 'Fixed withdrawal fee: $withdrawFee $currency',        if (withdrawFee == null)          'Withdrawal fee unavailable. The provider may charge a fee.',        if (withdrawFeePercentage != null)          'Variable withdrawal fee: $withdrawFeePercentage%',      ],    ];  }} // Compare decimal strings exactly, including tiny crypto amounts at a boundary.// A double comparison can round an amount just outside a limit onto the limit.class _Amount {  const _Amount(this.units, this.scale);  final BigInt units;  final int scale;   static _Amount? parse(String text) {    if (text.length > 200) return null;    if (text.startsWith('.')) text = '0$text';    final match =        RegExp(r'^\+?(\d+)(?:\.(\d*))?(?:[eE]([+-]?\d+))?$').firstMatch(text);    if (match == null) return null;    final fraction = match[2] ?? '';    final exponent = int.tryParse(match[3] ?? '0');    if (exponent == null || exponent.abs() > 100) return null;    final units = BigInt.parse('${match[1]}$fraction');    final scale = fraction.length - exponent;    return scale < 0        ? _Amount(units * BigInt.from(10).pow(-scale), 0)        : _Amount(units, scale);  }   String asPercentage() {    final digits =        (units * BigInt.from(100)).toString().padLeft(scale + 1, '0');    if (scale == 0) return digits;    final split = digits.length - scale;    final formatted = '${digits.substring(0, split)}.${digits.substring(split)}'        .replaceFirst(RegExp(r'0+$'), '');    return formatted.endsWith('.')        ? formatted.substring(0, formatted.length - 1)        : formatted;  }   int compareTo(_Amount other) {    final common = scale > other.scale ? scale : other.scale;    return (units * BigInt.from(10).pow(common - scale))        .compareTo(other.units * BigInt.from(10).pow(common - other.scale));  }}
HOPPA DEVELOPER ASSISTANT

What are you building?

From a question to an implementation.

Get a step-by-step flow, example code and the docs behind it. Choose a starting point or describe your feature.

Answers use the public API guide and example app code. Check the linked reference before shipping. Explore code sources ↗

Your question and recent conversation are sent to our AI provider. Don’t include API keys, personal data or card details.