@@ -18,8 +18,10 @@ public struct Utilities {
1818 static func publicToAddressData( _ publicKey: Data ) -> Data ? {
1919 var publicKey = publicKey
2020 if publicKey. count == 33 {
21- guard ( publicKey [ 0 ] == 2 || publicKey [ 0 ] == 3 ) ,
22- let decompressedKey = SECP256K1 . combineSerializedPublicKeys ( keys: [ publicKey] , outputCompressed: false ) else {
21+ guard
22+ ( publicKey [ 0 ] == 2 || publicKey [ 0 ] == 3 ) ,
23+ let decompressedKey = SECP256K1 . combineSerializedPublicKeys ( keys: [ publicKey] , outputCompressed: false )
24+ else {
2325 return nil
2426 }
2527 publicKey = decompressedKey
@@ -98,7 +100,7 @@ public struct Utilities {
98100 return parseToBigUInt ( amount, decimals: unitDecimals)
99101 }
100102
101- /// Parse a user-supplied string using the number of decimals.
103+ /// Parse a string using the number of decimals.
102104 /// If input is non-numeric or precision is not sufficient - returns nil.
103105 /// Allowed decimal separators are ".", ",".
104106 public static func parseToBigUInt( _ amount: String , decimals: Int = 18 ) -> BigUInt ? {
@@ -107,22 +109,28 @@ public struct Utilities {
107109 guard components. count == 1 || components. count == 2 else { return nil }
108110 let unitDecimals = decimals
109111 guard let beforeDecPoint = BigUInt ( components [ 0 ] , radix: 10 ) else { return nil }
110- var mainPart = beforeDecPoint* BigUInt ( 10 ) . power ( unitDecimals)
112+ var mainPart = beforeDecPoint * BigUInt( 10 ) . power ( unitDecimals)
111113 if components. count == 2 {
112114 let numDigits = components [ 1 ] . count
113115 guard numDigits <= unitDecimals else { return nil }
114116 guard let afterDecPoint = BigUInt ( components [ 1 ] , radix: 10 ) else { return nil }
115- let extraPart = afterDecPoint* BigUInt ( 10 ) . power ( unitDecimals- numDigits)
116- mainPart = mainPart + extraPart
117+ let extraPart = afterDecPoint * BigUInt( 10 ) . power ( unitDecimals- numDigits)
118+ mainPart += extraPart
117119 }
118120 return mainPart
119121 }
120122
121- /// Formats a BigInt object to String. The supplied number is first divided into integer and decimal part based on " units" ,
122- /// then limit the decimal part to "decimals" symbols and uses a " decimalSeparator" as a separator.
123+ /// Formats a ` BigInt` object to ` String` . The supplied number is first divided into integer and decimal part based on ` units` value ,
124+ /// then limits the decimal part to `formattingDecimals` symbols and uses a ` decimalSeparator` as a separator.
123125 /// Fallbacks to scientific format if higher precision is required.
124126 ///
125- /// Returns nil of formatting is not possible to satisfy.
127+ /// - Parameters:
128+ /// - bigNumber: number to format;
129+ /// - units: unit to format number to;
130+ /// - formattingDecimals: the number of decimals that should be in the final formatted number;
131+ /// - decimalSeparator: decimals separator;
132+ /// - fallbackToScientific: if should fallback to scienctific representation like `1.23e-10`.
133+ /// - Returns: formatted number or `nil` if formatting was not possible.
126134 public static func formatToPrecision( _ bigNumber: BigInt , units: Utilities . Units = . ether, formattingDecimals: Int = 4 , decimalSeparator: String = " . " , fallbackToScientific: Bool = false ) -> String {
127135 let magnitude = bigNumber. magnitude
128136 let formatted = formatToPrecision ( magnitude, units: units, formattingDecimals: formattingDecimals, decimalSeparator: decimalSeparator, fallbackToScientific: fallbackToScientific)
@@ -134,13 +142,19 @@ public struct Utilities {
134142 }
135143 }
136144
137- /// Formats a BigUInt object to String. The supplied number is first divided into integer and decimal part based on " units" ,
138- /// then limits the decimal part to " formattingDecimals" symbols and uses a " decimalSeparator" as a separator.
145+ /// Formats a ` BigUInt` object to ` String` . The supplied number is first divided into integer and decimal part based on ` units` value ,
146+ /// then limits the decimal part to ` formattingDecimals` symbols and uses a ` decimalSeparator` as a separator.
139147 /// Fallbacks to scientific format if higher precision is required.
140148 ///
141- /// Returns nil of formatting is not possible to satisfy.
149+ /// - Parameters:
150+ /// - bigNumber: number to format;
151+ /// - units: unit to format number to;
152+ /// - formattingDecimals: the number of decimals that should be in the final formatted number;
153+ /// - decimalSeparator: decimals separator;
154+ /// - fallbackToScientific: if should fallback to scienctific representation like `1.23e-10`.
155+ /// - Returns: formatted number or `nil` if formatting was not possible.
142156 public static func formatToPrecision( _ bigNumber: BigUInt , units: Utilities . Units = . ether, formattingDecimals: Int = 4 , decimalSeparator: String = " . " , fallbackToScientific: Bool = false ) -> String {
143- if bigNumber == 0 {
157+ guard bigNumber != 0 else {
144158 return " 0 "
145159 }
146160 let unitDecimals = units. decimals
@@ -150,47 +164,60 @@ public struct Utilities {
150164 }
151165 let divisor = BigUInt ( 10 ) . power ( unitDecimals)
152166 let ( quotient, remainder) = bigNumber. quotientAndRemainder ( dividingBy: divisor)
153- var fullRemainder = " \( remainder) "
154- let fullPaddedRemainder = fullRemainder. leftPadding ( toLength: unitDecimals, withPad: " 0 " )
167+
168+ guard toDecimals != 0 else {
169+ return " \( quotient) "
170+ }
171+
172+ let remainderStr = " \( remainder) "
173+ let fullPaddedRemainder = remainderStr. leftPadding ( toLength: unitDecimals, withPad: " 0 " )
155174 let remainderPadded = fullPaddedRemainder [ 0 ..< toDecimals]
156- if remainderPadded == String ( repeating: " 0 " , count: toDecimals) {
157- if quotient != 0 {
158- return " \( quotient) "
159- } else if fallbackToScientific {
160- var firstDigit = 0
161- for char in fullPaddedRemainder {
162- if char == " 0 " {
163- firstDigit = firstDigit + 1
164- } else {
165- let firstDecimalUnit = String ( fullPaddedRemainder [ firstDigit ..< firstDigit+ 1 ] )
166- var remainingDigits = " "
167- let numOfRemainingDecimals = fullPaddedRemainder. count - firstDigit - 1
168- if numOfRemainingDecimals <= 0 {
169- remainingDigits = " "
170- } else if numOfRemainingDecimals > formattingDecimals {
171- let end = firstDigit+ 1 + formattingDecimals > fullPaddedRemainder. count ? fullPaddedRemainder. count: firstDigit+ 1 + formattingDecimals
172- remainingDigits = String ( fullPaddedRemainder [ firstDigit+ 1 ..< end] )
173- } else {
174- remainingDigits = String ( fullPaddedRemainder [ firstDigit+ 1 ..< fullPaddedRemainder. count] )
175- }
176- if remainingDigits != " " {
177- fullRemainder = firstDecimalUnit + decimalSeparator + remainingDigits
178- } else {
179- fullRemainder = firstDecimalUnit
180- }
181- firstDigit = firstDigit + 1
182- break
183- }
184- }
185- return fullRemainder + " e- " + String( firstDigit)
186- }
175+
176+ guard remainderPadded == String ( repeating: " 0 " , count: toDecimals) else {
177+ return " \( quotient) " + decimalSeparator + remainderPadded
178+ }
179+
180+ if fallbackToScientific {
181+ return formatToScientificRepresentation ( remainderStr, remainder: fullPaddedRemainder, decimals: formattingDecimals, decimalSeparator: decimalSeparator)
187182 }
188- if toDecimals == 0 {
183+
184+ guard quotient == 0 else {
189185 return " \( quotient) "
190186 }
187+
191188 return " \( quotient) " + decimalSeparator + remainderPadded
192189 }
193190
191+ private static func formatToScientificRepresentation( _ remainder: String , remainder fullPaddedRemainder: String , decimals: Int , decimalSeparator: String ) -> String {
192+ var remainder = remainder
193+ var firstDigit = 0
194+ for char in fullPaddedRemainder {
195+ if char == " 0 " {
196+ firstDigit += 1
197+ } else {
198+ let firstDecimalUnit = String ( fullPaddedRemainder [ firstDigit ..< firstDigit + 1 ] )
199+ var remainingDigits = " "
200+ let numOfRemainingDecimals = fullPaddedRemainder. count - firstDigit - 1
201+ if numOfRemainingDecimals <= 0 {
202+ remainingDigits = " "
203+ } else if numOfRemainingDecimals > decimals {
204+ let end = firstDigit + 1 + decimals > fullPaddedRemainder. count ? fullPaddedRemainder. count : firstDigit + 1 + decimals
205+ remainingDigits = String ( fullPaddedRemainder [ firstDigit + 1 ..< end] )
206+ } else {
207+ remainingDigits = String ( fullPaddedRemainder [ firstDigit + 1 ..< fullPaddedRemainder. count] )
208+ }
209+ if !remainingDigits. isEmpty {
210+ remainder = firstDecimalUnit + decimalSeparator + remainingDigits
211+ } else {
212+ remainder = firstDecimalUnit
213+ }
214+ firstDigit += 1
215+ break
216+ }
217+ }
218+ return remainder + " e- " + String( firstDigit)
219+ }
220+
194221 /// Recover the Ethereum address from recoverable secp256k1 signature. Message is first hashed using the "personal hash" protocol.
195222 /// BE WARNED - changing a message will result in different Ethereum address, but not in error.
196223 ///
0 commit comments