@@ -18,8 +18,10 @@ public struct Utilities {
18
18
static func publicToAddressData( _ publicKey: Data ) -> Data ? {
19
19
var publicKey = publicKey
20
20
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 {
23
25
return nil
24
26
}
25
27
publicKey = decompressedKey
@@ -98,7 +100,7 @@ public struct Utilities {
98
100
return parseToBigUInt ( amount, decimals: unitDecimals)
99
101
}
100
102
101
- /// Parse a user-supplied string using the number of decimals.
103
+ /// Parse a string using the number of decimals.
102
104
/// If input is non-numeric or precision is not sufficient - returns nil.
103
105
/// Allowed decimal separators are ".", ",".
104
106
public static func parseToBigUInt( _ amount: String , decimals: Int = 18 ) -> BigUInt ? {
@@ -107,22 +109,28 @@ public struct Utilities {
107
109
guard components. count == 1 || components. count == 2 else { return nil }
108
110
let unitDecimals = decimals
109
111
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)
111
113
if components. count == 2 {
112
114
let numDigits = components [ 1 ] . count
113
115
guard numDigits <= unitDecimals else { return nil }
114
116
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
117
119
}
118
120
return mainPart
119
121
}
120
122
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.
123
125
/// Fallbacks to scientific format if higher precision is required.
124
126
///
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.
126
134
public static func formatToPrecision( _ bigNumber: BigInt , units: Utilities . Units = . ether, formattingDecimals: Int = 4 , decimalSeparator: String = " . " , fallbackToScientific: Bool = false ) -> String {
127
135
let magnitude = bigNumber. magnitude
128
136
let formatted = formatToPrecision ( magnitude, units: units, formattingDecimals: formattingDecimals, decimalSeparator: decimalSeparator, fallbackToScientific: fallbackToScientific)
@@ -134,13 +142,19 @@ public struct Utilities {
134
142
}
135
143
}
136
144
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.
139
147
/// Fallbacks to scientific format if higher precision is required.
140
148
///
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.
142
156
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 {
144
158
return " 0 "
145
159
}
146
160
let unitDecimals = units. decimals
@@ -150,47 +164,60 @@ public struct Utilities {
150
164
}
151
165
let divisor = BigUInt ( 10 ) . power ( unitDecimals)
152
166
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 " )
155
174
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)
187
182
}
188
- if toDecimals == 0 {
183
+
184
+ guard quotient == 0 else {
189
185
return " \( quotient) "
190
186
}
187
+
191
188
return " \( quotient) " + decimalSeparator + remainderPadded
192
189
}
193
190
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
+
194
221
/// Recover the Ethereum address from recoverable secp256k1 signature. Message is first hashed using the "personal hash" protocol.
195
222
/// BE WARNED - changing a message will result in different Ethereum address, but not in error.
196
223
///
0 commit comments