spb/os-vault Public
Self-custody, multi-chain crypto wallet for macOS. One recovery phrase, six chain families, zero API keys — nothing leaves your Mac.
Swift 96%
Shell 3.4%
Makefile 0.6%
1//2// TokenAmount.swift3// OS Vault4//5// Author: Simon-Pierre Boucher6// Mail: contact@spboucher.ai7//89import Foundation10import BigInt1112/// Integer-only conversion between base units and display strings.13/// `Double` is banned for money: 6- and 18-decimal tokens both exceed the14/// 53-bit mantissa long before real-world balances do.15public enum TokenAmount {1617 /// "12.5" with decimals 6 → 12_500_000. Returns nil on malformed input or18 /// more fraction digits than the token supports. Accepts "," as separator.19 public static func parse(_ input: String, decimals: Int) -> BigUInt? {20 let normalized = input21 .trimmingCharacters(in: .whitespacesAndNewlines)22 .replacingOccurrences(of: ",", with: ".")23 guard !normalized.isEmpty, normalized != "." else { return nil }2425 let parts = normalized.split(separator: ".", omittingEmptySubsequences: false)26 guard parts.count <= 2 else { return nil }2728 let wholePart = String(parts[0])29 let fracPart = parts.count == 2 ? String(parts[1]) : ""30 guard wholePart.allSatisfy(\.isNumber), fracPart.allSatisfy(\.isNumber) else { return nil }31 guard !(wholePart.isEmpty && fracPart.isEmpty) else { return nil }32 guard fracPart.count <= decimals else { return nil }3334 let whole = BigUInt(wholePart.isEmpty ? "0" : wholePart, radix: 10) ?? 035 let paddedFrac = fracPart.padding(toLength: decimals, withPad: "0", startingAt: 0)36 let frac = paddedFrac.isEmpty ? BigUInt(0) : (BigUInt(paddedFrac, radix: 10) ?? 0)37 return whole * BigUInt(10).power(decimals) + frac38 }3940 /// 12_500_000 with decimals 6 → "12.5". Trailing zeros trimmed,41 /// optionally capped to `maxFractionDigits` (truncated, never rounded up —42 /// a wallet must not display more than the user owns).43 public static func format(_ units: BigUInt, decimals: Int, maxFractionDigits: Int? = nil) -> String {44 let divisor = BigUInt(10).power(decimals)45 let whole = units / divisor46 var frac = String(units % divisor)47 frac = String(repeating: "0", count: max(0, decimals - frac.count)) + frac48 if let cap = maxFractionDigits, frac.count > cap {49 frac = String(frac.prefix(cap))50 }51 while frac.hasSuffix("0") { frac.removeLast() }52 return frac.isEmpty ? String(whole) : "\(whole).\(frac)"53 }5455 /// Wei → ETH display string (18 decimals), for gas costs.56 public static func formatWei(_ wei: BigUInt, maxFractionDigits: Int = 8) -> String {57 format(wei, decimals: 18, maxFractionDigits: maxFractionDigits)58 }59}60