Skip to content

Commit a3fecba

Browse files
authored
Merge pull request #668 from albertopeam/bugfix/infinite-recursion-warning
Infinite recursion warning #667
2 parents d8dd9a2 + 06df849 commit a3fecba

File tree

10 files changed

+131
-74
lines changed

10 files changed

+131
-74
lines changed

Sources/Core/Utility/Utilities.swift

Lines changed: 55 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public struct Utilities {
8787
/// Parse a user-supplied string using the number of decimals for particular Ethereum unit.
8888
/// If input is non-numeric or precision is not sufficient - returns nil.
8989
/// Allowed decimal separators are ".", ",".
90-
public static func parseToBigUInt(_ amount: String, units: Utilities.Units = .eth) -> BigUInt? {
90+
public static func parseToBigUInt(_ amount: String, units: Utilities.Units = .ether) -> BigUInt? {
9191
let unitDecimals = units.decimals
9292
return parseToBigUInt(amount, decimals: unitDecimals)
9393
}
@@ -112,29 +112,14 @@ public struct Utilities {
112112
return mainPart
113113
}
114114

115-
/// Formats a BigInt object to String. The supplied number is first divided into integer and decimal part based on "toUnits",
116-
/// then limit the decimal part to "decimals" symbols and uses a "decimalSeparator" as a separator.
117-
///
118-
/// Returns nil of formatting is not possible to satisfy.
119-
static func formatToEthereumUnits(_ bigNumber: BigInt, toUnits: Utilities.Units = .eth, decimals: Int = 4, decimalSeparator: String = ".") -> String? {
120-
let magnitude = BigInt(bigNumber.magnitude)
121-
guard let formatted = formatToEthereumUnits(magnitude, toUnits: toUnits, decimals: decimals, decimalSeparator: decimalSeparator) else {return nil}
122-
switch bigNumber.sign {
123-
case .plus:
124-
return formatted
125-
case .minus:
126-
return "-" + formatted
127-
}
128-
}
129-
130-
/// Formats a BigInt object to String. The supplied number is first divided into integer and decimal part based on "toUnits",
115+
/// Formats a BigInt object to String. The supplied number is first divided into integer and decimal part based on "units",
131116
/// then limit the decimal part to "decimals" symbols and uses a "decimalSeparator" as a separator.
132117
/// Fallbacks to scientific format if higher precision is required.
133118
///
134119
/// Returns nil of formatting is not possible to satisfy.
135-
public static func formatToPrecision(_ bigNumber: BigInt, numberDecimals: Int = 18, formattingDecimals: Int = 4, decimalSeparator: String = ".", fallbackToScientific: Bool = false) -> String? {
120+
public static func formatToPrecision(_ bigNumber: BigInt, units: Utilities.Units = .ether, formattingDecimals: Int = 4, decimalSeparator: String = ".", fallbackToScientific: Bool = false) -> String {
136121
let magnitude = bigNumber.magnitude
137-
guard let formatted = formatToPrecision(magnitude, numberDecimals: numberDecimals, formattingDecimals: formattingDecimals, decimalSeparator: decimalSeparator, fallbackToScientific: fallbackToScientific) else {return nil}
122+
let formatted = formatToPrecision(magnitude, units: units, formattingDecimals: formattingDecimals, decimalSeparator: decimalSeparator, fallbackToScientific: fallbackToScientific)
138123
switch bigNumber.sign {
139124
case .plus:
140125
return formatted
@@ -143,24 +128,16 @@ public struct Utilities {
143128
}
144129
}
145130

146-
// /// Formats a BigUInt object to String. The supplied number is first divided into integer and decimal part based on "toUnits",
147-
// /// then limit the decimal part to "decimals" symbols and uses a "decimalSeparator" as a separator.
148-
// ///
149-
// /// Returns nil of formatting is not possible to satisfy.
150-
// static func formatToEthereumUnits(_ bigNumber: BigUInt, toUnits: Utilities.Units = .eth, decimals: Int = 4, decimalSeparator: String = ".", fallbackToScientific: Bool = false) -> String? {
151-
// return formatToPrecision(bigNumber, numberDecimals: toUnits.decimals, formattingDecimals: decimals, decimalSeparator: decimalSeparator, fallbackToScientific: fallbackToScientific)
152-
// }
153-
154-
/// Formats a BigUInt object to String. The supplied number is first divided into integer and decimal part based on "numberDecimals",
131+
/// Formats a BigUInt object to String. The supplied number is first divided into integer and decimal part based on "units",
155132
/// then limits the decimal part to "formattingDecimals" symbols and uses a "decimalSeparator" as a separator.
156133
/// Fallbacks to scientific format if higher precision is required.
157134
///
158135
/// Returns nil of formatting is not possible to satisfy.
159-
public static func formatToPrecision(_ bigNumber: BigUInt, numberDecimals: Int = 18, formattingDecimals: Int = 4, decimalSeparator: String = ".", fallbackToScientific: Bool = false) -> String? {
136+
public static func formatToPrecision(_ bigNumber: BigUInt, units: Utilities.Units = .ether, formattingDecimals: Int = 4, decimalSeparator: String = ".", fallbackToScientific: Bool = false) -> String {
160137
if bigNumber == 0 {
161138
return "0"
162139
}
163-
let unitDecimals = numberDecimals
140+
let unitDecimals = units.decimals
164141
var toDecimals = formattingDecimals
165142
if unitDecimals < toDecimals {
166143
toDecimals = unitDecimals
@@ -310,32 +287,57 @@ public struct Utilities {
310287
extension Utilities {
311288
/// Various units used in Ethereum ecosystem
312289
public enum Units {
313-
case eth
314290
case wei
315-
case Kwei
316-
case Mwei
317-
case Gwei
318-
case Microether
319-
case Finney
291+
case kwei
292+
case babbage
293+
case femtoether
294+
case mwei
295+
case lovelace
296+
case picoether
297+
case gwei
298+
case shannon
299+
case nanoether
300+
case nano
301+
case microether
302+
case szabo
303+
case micro
304+
case finney
305+
case milliether
306+
case milli
307+
case ether
308+
case kether
309+
case grand
310+
case mether
311+
case gether
312+
case tether
313+
case custom(Int)
320314

321315
public var decimals: Int {
322-
get {
323-
switch self {
324-
case .eth:
325-
return 18
326-
case .wei:
327-
return 0
328-
case .Kwei:
329-
return 3
330-
case .Mwei:
331-
return 6
332-
case .Gwei:
333-
return 9
334-
case .Microether:
335-
return 12
336-
case .Finney:
337-
return 15
338-
}
316+
switch self {
317+
case .wei:
318+
return 0
319+
case .kwei, .babbage, .femtoether:
320+
return 3
321+
case .mwei, .lovelace, .picoether:
322+
return 6
323+
case .gwei, .shannon, .nanoether, .nano:
324+
return 9
325+
case .microether, .szabo, .micro:
326+
return 12
327+
case .finney, .milliether, .milli:
328+
return 15
329+
case .ether:
330+
return 18
331+
case .kether, .grand:
332+
return 21
333+
case .mether:
334+
return 24
335+
case .gether:
336+
return 27
337+
case .tether:
338+
return 30
339+
case .custom(let decimals):
340+
return max(0, decimals)
339341
}
340342
}
341343
}

Sources/web3swift/Utils/ENS/ETHRegistrarController.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public extension ENS {
6666
}
6767

6868
public func registerName(from: EthereumAddress, name: String, owner: EthereumAddress, duration: UInt, secret: String, price: String) throws -> WriteOperation {
69-
guard let amount = Utilities.parseToBigUInt(price, units: .eth) else {throw Web3Error.inputError(desc: "Wrong price: no way for parsing to ether units")}
69+
guard let amount = Utilities.parseToBigUInt(price, units: .ether) else {throw Web3Error.inputError(desc: "Wrong price: no way for parsing to ether units")}
7070
defaultOptions.value = amount
7171
defaultOptions.from = from
7272
defaultOptions.to = self.address
@@ -75,7 +75,7 @@ public extension ENS {
7575
}
7676

7777
public func extendNameRegistration(from: EthereumAddress, name: String, duration: UInt32, price: String) throws -> WriteOperation {
78-
guard let amount = Utilities.parseToBigUInt(price, units: .eth) else {throw Web3Error.inputError(desc: "Wrong price: no way for parsing to ether units")}
78+
guard let amount = Utilities.parseToBigUInt(price, units: .ether) else {throw Web3Error.inputError(desc: "Wrong price: no way for parsing to ether units")}
7979
defaultOptions.value = amount
8080
defaultOptions.from = from
8181
defaultOptions.to = self.address

Tests/web3swiftTests/localTests/BasicLocalNodeTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class BasicLocalNodeTests: LocalTestCase {
5151
let parameters = [] as [AnyObject]
5252
let sendTx = contract.createWriteOperation("fallback", parameters: parameters)!
5353

54-
let valueToSend = Utilities.parseToBigUInt("1.0", units: .eth)!
54+
let valueToSend = try XCTUnwrap(Utilities.parseToBigUInt("1.0", units: .ether))
5555
sendTx.transaction.value = valueToSend
5656
sendTx.transaction.from = allAddresses[0]
5757

Tests/web3swiftTests/localTests/LocalTestCase.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class LocalTestCase: XCTestCase {
2323
let allAddresses = try! await web3.eth.ownedAccounts()
2424
let sendToAddress = allAddresses[0]
2525
let contract = web3.contract(Web3.Utils.coldWalletABI, at: sendToAddress, abiVersion: 2)
26-
let value = Utilities.parseToBigUInt("1.0", units: .eth)!
26+
let value = try XCTUnwrap(Utilities.parseToBigUInt("1.0", units: .ether))
2727
let from = allAddresses[0]
2828
let writeTX = contract!.createWriteOperation("fallback")!
2929
writeTX.transaction.from = from

Tests/web3swiftTests/localTests/NumberFormattingUtilTests.swift

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,56 +15,61 @@ class NumberFormattingUtilTests: LocalTestCase {
1515
func testNumberFormattingUtil() throws {
1616
let balance = BigInt("-1000000000000000000")
1717
print("this is print")
18-
let formatted = Utilities.formatToPrecision(balance, numberDecimals: 18, formattingDecimals: 4, decimalSeparator: ",")
18+
let formatted = Utilities.formatToPrecision(balance, units: .ether, formattingDecimals: 4, decimalSeparator: ",")
1919
XCTAssert(formatted == "-1")
2020
}
2121

2222
func testNumberFormattingUtil2() throws {
2323
let balance = BigInt("-1000000000000000")
24-
let formatted = Utilities.formatToPrecision(balance, numberDecimals: 18, formattingDecimals: 4, decimalSeparator: ",")
24+
let formatted = Utilities.formatToPrecision(balance, units: .ether, formattingDecimals: 4, decimalSeparator: ",")
2525
XCTAssert(formatted == "-0,0010")
2626
}
2727

2828
func testNumberFormattingUtil3() throws {
2929
let balance = BigInt("-1000000000000")
30-
let formatted = Utilities.formatToPrecision(balance, numberDecimals: 18, formattingDecimals: 4, decimalSeparator: ",")
30+
let formatted = Utilities.formatToPrecision(balance, units: .ether, formattingDecimals: 4, decimalSeparator: ",")
3131
XCTAssert(formatted == "-0,0000")
3232
}
3333

3434
func testNumberFormattingUtil4() throws {
3535
let balance = BigInt("-1000000000000")
36-
let formatted = Utilities.formatToPrecision(balance, numberDecimals: 18, formattingDecimals: 9, decimalSeparator: ",")
36+
let formatted = Utilities.formatToPrecision(balance, units: .ether, formattingDecimals: 9, decimalSeparator: ",")
3737
XCTAssert(formatted == "-0,000001000")
3838
}
3939

4040
func testNumberFormattingUtil5() throws {
4141
let balance = BigInt("-1")
42-
let formatted = Utilities.formatToPrecision(balance, numberDecimals: 18, formattingDecimals: 9, decimalSeparator: ",", fallbackToScientific: true)
42+
let formatted = Utilities.formatToPrecision(balance, units: .ether, formattingDecimals: 9, decimalSeparator: ",", fallbackToScientific: true)
4343
XCTAssert(formatted == "-1e-18")
4444
}
4545

4646
func testNumberFormattingUtil6() throws {
4747
let balance = BigInt("0")
48-
let formatted = Utilities.formatToPrecision(balance, numberDecimals: 18, formattingDecimals: 9, decimalSeparator: ",")
48+
let formatted = Utilities.formatToPrecision(balance, units: .ether, formattingDecimals: 9, decimalSeparator: ",")
4949
XCTAssert(formatted == "0")
5050
}
5151

5252
func testNumberFormattingUtil7() throws {
5353
let balance = BigInt("-1100000000000000000")
54-
let formatted = Utilities.formatToPrecision(balance, numberDecimals: 18, formattingDecimals: 4, decimalSeparator: ",")
54+
let formatted = Utilities.formatToPrecision(balance, units: .ether, formattingDecimals: 4, decimalSeparator: ",")
5555
XCTAssert(formatted == "-1,1000")
5656
}
5757

5858
func testNumberFormattingUtil8() throws {
5959
let balance = BigInt("100")
60-
let formatted = Utilities.formatToPrecision(balance, numberDecimals: 18, formattingDecimals: 4, decimalSeparator: ",", fallbackToScientific: true)
60+
let formatted = Utilities.formatToPrecision(balance, units: .ether, formattingDecimals: 4, decimalSeparator: ",", fallbackToScientific: true)
6161
XCTAssert(formatted == "1,00e-16")
6262
}
6363

6464
func testNumberFormattingUtil9() throws {
6565
let balance = BigInt("1000000")
66-
let formatted = Utilities.formatToPrecision(balance, numberDecimals: 18, formattingDecimals: 4, decimalSeparator: ",", fallbackToScientific: true)
66+
let formatted = Utilities.formatToPrecision(balance, units: .ether, formattingDecimals: 4, decimalSeparator: ",", fallbackToScientific: true)
6767
XCTAssert(formatted == "1,0000e-12")
6868
}
69-
69+
70+
func testFormatPreccissionFallbacksToUnitsDecimals() throws {
71+
let bInt = BigInt(1_700_000_000_000_000_000)
72+
let result = Utilities.formatToPrecision(bInt, units: .ether, formattingDecimals: Utilities.Units.ether.decimals + 1, decimalSeparator: ",")
73+
XCTAssertEqual(result, "1,700000000000000000")
74+
}
7075
}

Tests/web3swiftTests/localTests/TransactionsTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,7 @@ class TransactionsTests: XCTestCase {
632632
let sendToAddress = EthereumAddress("0xe22b8979739D724343bd002F9f432F5990879901")!
633633
let allAddresses = try await web3.eth.ownedAccounts()
634634
let contract = web3.contract(Web3.Utils.coldWalletABI, at: sendToAddress, abiVersion: 2)
635-
let value = Utilities.parseToBigUInt("1.0", units: .eth)
635+
let value = Utilities.parseToBigUInt("1.0", units: .ether)
636636
let from = allAddresses[0]
637637
let writeTX = contract!.createWriteOperation("fallback")!
638638
writeTX.transaction.from = from
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//
2+
// UtilitiesTests.swift
3+
//
4+
// Created by albertopeam on 15/11/22.
5+
//
6+
7+
import XCTest
8+
import BigInt
9+
import Core
10+
11+
@testable import web3swift
12+
13+
class UtilitiesTests: XCTestCase {
14+
// MARK: - units
15+
16+
struct Test {
17+
let input: Utilities.Units
18+
let output: Int
19+
}
20+
21+
func testUnitsDecimals() throws {
22+
let units: [Test] = [.init(input: .wei, output: 0),
23+
.init(input: .kwei, output: 3),
24+
.init(input: .babbage, output: 3),
25+
.init(input: .femtoether, output: 3),
26+
.init(input: .mwei, output: 6),
27+
.init(input: .lovelace, output: 6),
28+
.init(input: .picoether, output: 6),
29+
.init(input: .gwei, output: 9),
30+
.init(input: .shannon, output: 9),
31+
.init(input: .nanoether, output: 9),
32+
.init(input: .nano, output: 9),
33+
.init(input: .szabo, output: 12),
34+
.init(input: .microether, output: 12),
35+
.init(input: .micro, output: 12),
36+
.init(input: .finney, output: 15),
37+
.init(input: .milliether, output: 15),
38+
.init(input: .milli, output: 15),
39+
.init(input: .ether, output: 18),
40+
.init(input: .kether, output: 21),
41+
.init(input: .grand, output: 21),
42+
.init(input: .mether, output: 24),
43+
.init(input: .gether, output: 27),
44+
.init(input: .tether, output: 30),
45+
]
46+
units.forEach { test in
47+
XCTAssertEqual(test.input.decimals, test.output)
48+
}
49+
}
50+
}

Tests/web3swiftTests/remoteTests/EIP1559Tests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ final class EIP1559Tests: XCTestCase {
1818
type: .eip1559,
1919
to: EthereumAddress("0xb47292B7bBedA4447564B8336E4eD1f93735e7C7")!,
2020
chainID: web3.provider.network!.chainID,
21-
value: Utilities.parseToBigUInt("0.1", units: .eth)!,
21+
value: try XCTUnwrap(Utilities.parseToBigUInt("0.1", units: .ether)),
2222
gasLimit: 21_000
2323
)
2424
// Vitalik's address
@@ -34,7 +34,7 @@ final class EIP1559Tests: XCTestCase {
3434
type: .eip1559,
3535
to: EthereumAddress("0xeBec795c9c8bBD61FFc14A6662944748F299cAcf")!,
3636
chainID: web3.provider.network!.chainID,
37-
value: Utilities.parseToBigUInt("0.1", units: .eth)!,
37+
value: try XCTUnwrap(Utilities.parseToBigUInt("0.1", units: .ether)),
3838
gasLimit: 21_000
3939
)
4040
// Vitalik's address

Tests/web3swiftTests/remoteTests/InfuraTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class InfuraTests: XCTestCase {
1515
let web3 = await Web3.InfuraMainnetWeb3(accessToken: Constants.infuraToken)
1616
let address = EthereumAddress("0xd61b5ca425F8C8775882d4defefC68A6979DBbce")!
1717
let balance = try await web3.eth.getBalance(for: address)
18-
let balString = Utilities.formatToPrecision(balance, numberDecimals: Utilities.Units.eth.decimals, formattingDecimals: 3)
18+
let balString = Utilities.formatToPrecision(balance, units: .ether, formattingDecimals: 3)
1919
XCTAssertNotNil(balString)
2020
}
2121

Tests/web3swiftTests/remoteTests/PolicyResolverTests.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ final class PolicyResolverTests: XCTestCase {
2020
type: .eip1559,
2121
to: EthereumAddress("0xb47292B7bBedA4447564B8336E4eD1f93735e7C7")!,
2222
chainID: web3.provider.network!.chainID,
23-
value: Utilities.parseToBigUInt("0.1", units: .eth)!,
23+
value: try XCTUnwrap(Utilities.parseToBigUInt("0.1", units: .ether)),
2424
gasLimit: 21_000
2525
)
2626
// Vitalik's address
@@ -42,7 +42,7 @@ final class PolicyResolverTests: XCTestCase {
4242
type: .legacy,
4343
to: EthereumAddress("0xb47292B7bBedA4447564B8336E4eD1f93735e7C7")!,
4444
chainID: web3.provider.network!.chainID,
45-
value: Utilities.parseToBigUInt("0.1", units: .eth)!,
45+
value: try XCTUnwrap(Utilities.parseToBigUInt("0.1", units: .ether)),
4646
gasLimit: 21_000
4747
)
4848
// Vitalik's address
@@ -67,7 +67,7 @@ final class PolicyResolverTests: XCTestCase {
6767
type: .eip1559,
6868
to: EthereumAddress("0xb47292B7bBedA4447564B8336E4eD1f93735e7C7")!,
6969
chainID: web3.provider.network!.chainID,
70-
value: Utilities.parseToBigUInt("0.1", units: .eth)!,
70+
value: try XCTUnwrap(Utilities.parseToBigUInt("0.1", units: .ether)),
7171
gasLimit: 21_000
7272
)
7373
// Vitalik's address

0 commit comments

Comments
 (0)