| 1 | /// Parses the string [value] as a fixed-point decimal literal and returns its |
| 2 | /// [BigInt] value. |
| 3 | /// |
| 4 | /// The number of fractional digits is determined by [decimals]. |
| 5 | /// |
| 6 | /// Returns `null` if the input [value] is not a valid fixed-point literal |
| 7 | /// (e.g., non-numeric characters, too many fractional digits). |
| 8 | /// |
| 9 | /// Like [parseFixed], except that this function returns `null` for invalid inputs |
| 10 | /// instead of throwing. |
| 11 | BigInt? tryParseFixed(String value, int decimals) { |
| 12 | try { |
| 13 | return parseFixed(value, decimals); |
| 14 | } on FormatException catch (_) { |
| 15 | return null; |
| 16 | } |
| 17 | } |
| 18 | |
| 19 | /// Parses the string [value] as a fixed-point decimal literal and returns its |
| 20 | /// [BigInt] value. |
| 21 | /// |
| 22 | /// The number of fractional digits is determined by [decimals]. |
| 23 | /// |
| 24 | /// Throws a [FormatException] if the input [value] is not a valid fixed-point literal |
| 25 | /// (e.g., non-numeric characters, too many fractional digits). |
| 26 | /// |
| 27 | /// Rather than throwing and immediately catching the [FormatException], |
| 28 | /// instead use [tryParseFixed] to handle a potential parsing error. |
| 29 | BigInt parseFixed(String value, int decimals) { |
| 30 | final multiplier = getMultiplier(decimals); |
| 31 | |
| 32 | /// handle weird cases where users enter spaces and currency after the amount |
| 33 | /// This should be handled from UI field to prevent non numerical values |
| 34 | /// but will be in the refactoring |
| 35 | if (value.contains(" ")) { |
| 36 | value = value.split(" ").first; |
| 37 | } |
| 38 | |
| 39 | final negative = value.startsWith("-"); |
| 40 | if (negative) { |
| 41 | value = value.substring(1); |
| 42 | } |
| 43 | |
| 44 | if (value == ".") { |
| 45 | throw FormatException("missing value, value, $value"); |
| 46 | } |
| 47 | |
| 48 | if (value.startsWith(".")) { |
| 49 | value = "0$value"; |
| 50 | } |
| 51 | |
| 52 | final comps = value.split("."); |
| 53 | if (comps.length > 2) { |
| 54 | throw FormatException("too many decimal points, value, $value"); |
| 55 | } |
| 56 | |
| 57 | final whole = comps.isNotEmpty ? comps[0] : "0"; |
| 58 | final fraction = (comps.length == 2 ? comps[1] : "").padRight(decimals, "0"); |
| 59 | |
| 60 | if (fraction.length > multiplier.length - 1) { |
| 61 | throw FormatException( |
| 62 | "fractional component(${fraction.length}) exceeds decimals(${decimals}), underflow, parseFixed", |
| 63 | ); |
| 64 | } |
| 65 | |
| 66 | final wholeValue = BigInt.parse(whole); |
| 67 | final fractionValue = fraction.isEmpty ? BigInt.zero : BigInt.parse(fraction); |
| 68 | final multiplierValue = BigInt.parse(multiplier); |
| 69 | |
| 70 | var wei = (wholeValue * multiplierValue) + fractionValue; |
| 71 | |
| 72 | if (negative) { |
| 73 | wei *= BigInt.from(-1); |
| 74 | } |
| 75 | |
| 76 | return wei; |
| 77 | } |
| 78 | |
| 79 | // Returns a string "1" followed by decimal "0"s |
| 80 | String getMultiplier(int decimals) => "1".padRight(decimals + 1, "0"); |