Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: swiftlint issues in Utilities.swift #779

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 74 additions & 47 deletions Sources/Web3Core/Utility/Utilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ public struct Utilities {
static func publicToAddressData(_ publicKey: Data) -> Data? {
var publicKey = publicKey
if publicKey.count == 33 {
guard (publicKey[0] == 2 || publicKey[0] == 3),
let decompressedKey = SECP256K1.combineSerializedPublicKeys(keys: [publicKey], outputCompressed: false) else {
guard
(publicKey[0] == 2 || publicKey[0] == 3),
let decompressedKey = SECP256K1.combineSerializedPublicKeys(keys: [publicKey], outputCompressed: false)
else {
return nil
}
publicKey = decompressedKey
Expand Down Expand Up @@ -98,7 +100,7 @@ public struct Utilities {
return parseToBigUInt(amount, decimals: unitDecimals)
}

/// Parse a user-supplied string using the number of decimals.
/// Parse a string using the number of decimals.
/// If input is non-numeric or precision is not sufficient - returns nil.
/// Allowed decimal separators are ".", ",".
public static func parseToBigUInt(_ amount: String, decimals: Int = 18) -> BigUInt? {
Expand All @@ -107,22 +109,28 @@ public struct Utilities {
guard components.count == 1 || components.count == 2 else { return nil }
let unitDecimals = decimals
guard let beforeDecPoint = BigUInt(components[0], radix: 10) else { return nil }
var mainPart = beforeDecPoint*BigUInt(10).power(unitDecimals)
var mainPart = beforeDecPoint * BigUInt(10).power(unitDecimals)
if components.count == 2 {
let numDigits = components[1].count
guard numDigits <= unitDecimals else { return nil }
guard let afterDecPoint = BigUInt(components[1], radix: 10) else { return nil }
let extraPart = afterDecPoint*BigUInt(10).power(unitDecimals-numDigits)
mainPart = mainPart + extraPart
let extraPart = afterDecPoint * BigUInt(10).power(unitDecimals-numDigits)
mainPart += extraPart
}
return mainPart
}

/// Formats a BigInt object to String. The supplied number is first divided into integer and decimal part based on "units",
/// then limit the decimal part to "decimals" symbols and uses a "decimalSeparator" as a separator.
/// Formats a `BigInt` object to `String`. The supplied number is first divided into integer and decimal part based on `units` value,
/// then limits the decimal part to `formattingDecimals` symbols and uses a `decimalSeparator` as a separator.
/// Fallbacks to scientific format if higher precision is required.
///
/// Returns nil of formatting is not possible to satisfy.
/// - Parameters:
/// - bigNumber: number to format;
/// - units: unit to format number to;
/// - formattingDecimals: the number of decimals that should be in the final formatted number;
/// - decimalSeparator: decimals separator;
/// - fallbackToScientific: if should fallback to scienctific representation like `1.23e-10`.
/// - Returns: formatted number or `nil` if formatting was not possible.
public static func formatToPrecision(_ bigNumber: BigInt, units: Utilities.Units = .ether, formattingDecimals: Int = 4, decimalSeparator: String = ".", fallbackToScientific: Bool = false) -> String {
let magnitude = bigNumber.magnitude
let formatted = formatToPrecision(magnitude, units: units, formattingDecimals: formattingDecimals, decimalSeparator: decimalSeparator, fallbackToScientific: fallbackToScientific)
Expand All @@ -134,13 +142,19 @@ public struct Utilities {
}
}

/// Formats a BigUInt object to String. The supplied number is first divided into integer and decimal part based on "units",
/// then limits the decimal part to "formattingDecimals" symbols and uses a "decimalSeparator" as a separator.
/// Formats a `BigUInt` object to `String`. The supplied number is first divided into integer and decimal part based on `units` value,
/// then limits the decimal part to `formattingDecimals` symbols and uses a `decimalSeparator` as a separator.
/// Fallbacks to scientific format if higher precision is required.
///
/// Returns nil of formatting is not possible to satisfy.
/// - Parameters:
/// - bigNumber: number to format;
/// - units: unit to format number to;
/// - formattingDecimals: the number of decimals that should be in the final formatted number;
/// - decimalSeparator: decimals separator;
/// - fallbackToScientific: if should fallback to scienctific representation like `1.23e-10`.
/// - Returns: formatted number or `nil` if formatting was not possible.
public static func formatToPrecision(_ bigNumber: BigUInt, units: Utilities.Units = .ether, formattingDecimals: Int = 4, decimalSeparator: String = ".", fallbackToScientific: Bool = false) -> String {
if bigNumber == 0 {
guard bigNumber != 0 else {
return "0"
}
let unitDecimals = units.decimals
Expand All @@ -150,47 +164,60 @@ public struct Utilities {
}
let divisor = BigUInt(10).power(unitDecimals)
let (quotient, remainder) = bigNumber.quotientAndRemainder(dividingBy: divisor)
var fullRemainder = "\(remainder)"
let fullPaddedRemainder = fullRemainder.leftPadding(toLength: unitDecimals, withPad: "0")

guard toDecimals != 0 else {
return "\(quotient)"
}

let remainderStr = "\(remainder)"
let fullPaddedRemainder = remainderStr.leftPadding(toLength: unitDecimals, withPad: "0")
let remainderPadded = fullPaddedRemainder[0..<toDecimals]
if remainderPadded == String(repeating: "0", count: toDecimals) {
if quotient != 0 {
return "\(quotient)"
} else if fallbackToScientific {
var firstDigit = 0
for char in fullPaddedRemainder {
if char == "0" {
firstDigit = firstDigit + 1
} else {
let firstDecimalUnit = String(fullPaddedRemainder[firstDigit ..< firstDigit+1])
var remainingDigits = ""
let numOfRemainingDecimals = fullPaddedRemainder.count - firstDigit - 1
if numOfRemainingDecimals <= 0 {
remainingDigits = ""
} else if numOfRemainingDecimals > formattingDecimals {
let end = firstDigit+1+formattingDecimals > fullPaddedRemainder.count ? fullPaddedRemainder.count: firstDigit+1+formattingDecimals
remainingDigits = String(fullPaddedRemainder[firstDigit+1 ..< end])
} else {
remainingDigits = String(fullPaddedRemainder[firstDigit+1 ..< fullPaddedRemainder.count])
}
if remainingDigits != "" {
fullRemainder = firstDecimalUnit + decimalSeparator + remainingDigits
} else {
fullRemainder = firstDecimalUnit
}
firstDigit = firstDigit + 1
break
}
}
return fullRemainder + "e-" + String(firstDigit)
}

guard remainderPadded == String(repeating: "0", count: toDecimals) else {
return "\(quotient)" + decimalSeparator + remainderPadded
}

if fallbackToScientific {
return formatToScientificRepresentation(remainderStr, remainder: fullPaddedRemainder, decimals: formattingDecimals, decimalSeparator: decimalSeparator)
}
if toDecimals == 0 {

guard quotient == 0 else {
return "\(quotient)"
}

return "\(quotient)" + decimalSeparator + remainderPadded
}

private static func formatToScientificRepresentation(_ remainder: String, remainder fullPaddedRemainder: String, decimals: Int, decimalSeparator: String) -> String {
var remainder = remainder
var firstDigit = 0
for char in fullPaddedRemainder {
if char == "0" {
firstDigit += 1
} else {
let firstDecimalUnit = String(fullPaddedRemainder[firstDigit ..< firstDigit + 1])
var remainingDigits = ""
let numOfRemainingDecimals = fullPaddedRemainder.count - firstDigit - 1
if numOfRemainingDecimals <= 0 {
remainingDigits = ""
} else if numOfRemainingDecimals > decimals {
let end = firstDigit + 1 + decimals > fullPaddedRemainder.count ? fullPaddedRemainder.count : firstDigit + 1 + decimals
remainingDigits = String(fullPaddedRemainder[firstDigit + 1 ..< end])
} else {
remainingDigits = String(fullPaddedRemainder[firstDigit + 1 ..< fullPaddedRemainder.count])
}
if !remainingDigits.isEmpty {
remainder = firstDecimalUnit + decimalSeparator + remainingDigits
} else {
remainder = firstDecimalUnit
}
firstDigit += 1
break
}
}
return remainder + "e-" + String(firstDigit)
}

/// Recover the Ethereum address from recoverable secp256k1 signature. Message is first hashed using the "personal hash" protocol.
/// BE WARNED - changing a message will result in different Ethereum address, but not in error.
///
Expand Down
18 changes: 9 additions & 9 deletions Tests/web3swiftTests/localTests/NumberFormattingUtilTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,55 +15,55 @@ class NumberFormattingUtilTests: LocalTestCase {
func testNumberFormattingUtil() throws {
let balance = BigInt("-1000000000000000000")
let formatted = Utilities.formatToPrecision(balance, units: .ether, formattingDecimals: 4, decimalSeparator: ",")
XCTAssert(formatted == "-1")
XCTAssertEqual(formatted, "-1")
}

func testNumberFormattingUtil2() throws {
let balance = BigInt("-1000000000000000")
let formatted = Utilities.formatToPrecision(balance, units: .ether, formattingDecimals: 4, decimalSeparator: ",")
XCTAssert(formatted == "-0,0010")
XCTAssertEqual(formatted, "-0,0010")
}

func testNumberFormattingUtil3() throws {
let balance = BigInt("-1000000000000")
let formatted = Utilities.formatToPrecision(balance, units: .ether, formattingDecimals: 4, decimalSeparator: ",")
XCTAssert(formatted == "-0,0000")
XCTAssertEqual(formatted, "-0,0000")
}

func testNumberFormattingUtil4() throws {
let balance = BigInt("-1000000000000")
let formatted = Utilities.formatToPrecision(balance, units: .ether, formattingDecimals: 9, decimalSeparator: ",")
XCTAssert(formatted == "-0,000001000")
XCTAssertEqual(formatted, "-0,000001000")
}

func testNumberFormattingUtil5() throws {
let balance = BigInt("-1")
let formatted = Utilities.formatToPrecision(balance, units: .ether, formattingDecimals: 9, decimalSeparator: ",", fallbackToScientific: true)
XCTAssert(formatted == "-1e-18")
XCTAssertEqual(formatted, "-1e-18")
}

func testNumberFormattingUtil6() throws {
let balance = BigInt("0")
let formatted = Utilities.formatToPrecision(balance, units: .ether, formattingDecimals: 9, decimalSeparator: ",")
XCTAssert(formatted == "0")
XCTAssertEqual(formatted, "0")
}

func testNumberFormattingUtil7() throws {
let balance = BigInt("-1100000000000000000")
let formatted = Utilities.formatToPrecision(balance, units: .ether, formattingDecimals: 4, decimalSeparator: ",")
XCTAssert(formatted == "-1,1000")
XCTAssertEqual(formatted, "-1,1000")
}

func testNumberFormattingUtil8() throws {
let balance = BigInt("100")
let formatted = Utilities.formatToPrecision(balance, units: .ether, formattingDecimals: 4, decimalSeparator: ",", fallbackToScientific: true)
XCTAssert(formatted == "1,00e-16")
XCTAssertEqual(formatted, "1,00e-16")
}

func testNumberFormattingUtil9() throws {
let balance = BigInt("1000000")
let formatted = Utilities.formatToPrecision(balance, units: .ether, formattingDecimals: 4, decimalSeparator: ",", fallbackToScientific: true)
XCTAssert(formatted == "1,0000e-12")
XCTAssertEqual(formatted, "1,0000e-12")
}

func testFormatPreccissionFallbacksToUnitsDecimals() throws {
Expand Down