Skip to content

Commit 796f866

Browse files
Merge pull request #623 from JeneaVranceanu/fix/erc-20
fix: ERC20 read properties function fixed - multiple inlined async/await was causing issues
2 parents 76b3eba + 0952215 commit 796f866

File tree

5 files changed

+61
-92
lines changed

5 files changed

+61
-92
lines changed

Sources/web3swift/Tokens/ERC20/Web3+ERC20.swift

Lines changed: 25 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -181,60 +181,39 @@ protocol ERC20BaseProperties: AnyObject {
181181
var _decimals: UInt8? { get set }
182182
var _hasReadProperties: Bool { get set }
183183
func readProperties() async throws
184-
func name() async throws -> String
185-
func symbol() async throws -> String
186-
func decimals() async throws -> UInt8
184+
func name() -> String?
185+
func symbol() -> String?
186+
func decimals() -> UInt8?
187187
}
188+
188189
extension ERC20BaseProperties {
189-
public func name() async throws -> String {
190-
try await self.readProperties()
191-
return self._name ?? ""
190+
public func name() -> String? {
191+
_name
192192
}
193193

194-
public func symbol() async throws -> String {
195-
try await self.readProperties()
196-
return self._symbol ?? ""
194+
public func symbol() -> String? {
195+
_symbol
197196
}
198197

199-
public func decimals() async throws -> UInt8 {
200-
try await self.readProperties()
201-
return self._decimals ?? 255
198+
public func decimals() -> UInt8? {
199+
_decimals
202200
}
203201

204-
func readProperties() async throws {
205-
if self._hasReadProperties {
206-
return
207-
}
208-
let contract = self.contract
202+
public func readProperties() async throws {
203+
guard !_hasReadProperties else { return }
209204
guard contract.contract.address != nil else {return}
210-
async let namePromise = contract
211-
.createReadOperation("name", parameters: [AnyObject](), extraData: Data() )?
212-
.callContractMethod()
213-
214-
async let symbolPromise = try await contract
215-
.createReadOperation("symbol", parameters: [AnyObject](), extraData: Data() )?
216-
.callContractMethod()
217-
218-
async let decimalPromise = try await contract
219-
.createReadOperation("decimals", parameters: [AnyObject](), extraData: Data() )?
220-
.callContractMethod()
221-
222-
let resolvedPromises = try await ["name":namePromise, "symbol":symbolPromise, "decimals":decimalPromise]
223-
224-
if let nameResult = resolvedPromises["name"], let name = nameResult?["0"] as? String {
225-
print(name)
226-
_name = name
227-
}
228-
229-
if let symbolResult = resolvedPromises["symbol"], let symbol = symbolResult?["0"] as? String {
230-
print(symbol)
231-
_symbol = symbol
232-
}
233-
234-
if let decimalsResult = resolvedPromises["decimals"], let decimals = decimalsResult?["0"] as? BigUInt {
235-
_decimals = UInt8(decimals)
236-
}
237-
238-
self._hasReadProperties = true
205+
_name = try await contract
206+
.createReadOperation("name")?
207+
.callContractMethod()["0"] as? String
208+
209+
_symbol = try await contract
210+
.createReadOperation("symbol")?
211+
.callContractMethod()["0"] as? String
212+
213+
let decimals = try await contract
214+
.createReadOperation("decimals")?
215+
.callContractMethod()["0"] as? BigUInt
216+
_decimals = decimals != nil ? UInt8(decimals!) : nil
217+
_hasReadProperties = true
239218
}
240219
}

Sources/web3swift/Tokens/ST20/Web3+ST20.swift

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,6 @@ public class ST20: IST20, ERC20BaseProperties {
5656
self.abi = abi
5757
}
5858

59-
// Must be 18!
60-
public func decimals() async throws -> UInt8 {
61-
try await self.readProperties()
62-
if self._decimals != nil {
63-
return self._decimals!
64-
}
65-
return 18
66-
}
67-
6859
func tokenDetails() async throws -> [UInt32] {
6960
let contract = self.contract
7061
self.transaction.callOnBlock = .latest

Tests/web3swiftTests/localTests/ERC20ClassTests.swift

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,32 @@ import Core
1111

1212
class ERC20ClassTests: LocalTestCase {
1313

14-
// func testERC20TokenCreation() async throws {
15-
// let (web3, _, receipt, _) = try await TestHelpers.localDeployERC20()
16-
// let erc20token = ERC20.init(web3: web3, provider: web3.provider, address: receipt.contractAddress!)
17-
// // MARK: No data used in call
18-
// try await erc20token.readProperties()
19-
//
20-
// // MARK: - Duplicated call readProperties
21-
// // MARK: No data used in call
22-
// let symbol = try await erc20token.symbol()
23-
// // MARK: - Duplicated call readProperties
24-
// // MARK: No data used in call
25-
// let name = try await erc20token.name()
26-
// // MARK: - Duplicated call readProperties
27-
// // MARK: No data used in call
28-
// let decimals = try await erc20token.decimals()
29-
// XCTAssertEqual(symbol, "w3s")
30-
// XCTAssertEqual(name, "web3swift")
31-
// XCTAssertEqual(decimals, 18)
32-
// }
14+
func testERC20TokenCreation() async throws {
15+
let (web3, _, receipt, _) = try await TestHelpers.localDeployERC20()
16+
func testRun() async throws {
17+
let erc20token = ERC20(web3: web3, provider: web3.provider, address: receipt.contractAddress!)
18+
// All async reads happen in readProperties
19+
try await erc20token.readProperties()
20+
XCTAssertEqual(erc20token.symbol(), "w3s")
21+
XCTAssertEqual(erc20token.name(), "web3swift")
22+
XCTAssertEqual(erc20token.decimals(), 18)
23+
}
24+
/// We had an issue with multiple async reads performed at the same point in time
25+
/// sometimes returning wrong values (actually values of each other).
26+
/// The issue is most likely related to async/await feautre of Swift.
27+
/// Due to that was decided to add a loop to execute the same async calls that checks the same ERC20 properties
28+
/// multiple times. All calls must succeed.
29+
/// Each run executes 3 async read operations.
30+
/// DO NOT REMOVE THE LOOP!
31+
for _ in 0...100 {
32+
do {
33+
try await testRun()
34+
} catch {
35+
XCTFail("Failed to validate ERC20 fields due to an error: \(error.localizedDescription)")
36+
break
37+
}
38+
}
39+
}
3340

3441
func testERC20tokenBalanceAndAllowance() async throws {
3542
let (web3, _, receipt, _) = try await TestHelpers.localDeployERC20()

Tests/web3swiftTests/localTests/ERC20Tests.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,8 @@ class ERC20Tests: LocalTestCase {
1313

1414
func testERC20name() async throws {
1515
let (web3, _, receipt, _) = try await TestHelpers.localDeployERC20()
16-
17-
let parameters = [] as [AnyObject]
1816
let contract = web3.contract(Web3.Utils.erc20ABI, at: receipt.contractAddress!)!
19-
let readTX = contract.createReadOperation("name", parameters:parameters)!
17+
let readTX = contract.createReadOperation("name")!
2018
readTX.transaction.from = EthereumAddress("0xe22b8979739D724343bd002F9f432F5990879901")
2119
let response = try await readTX.callContractMethod()
2220
let name = response["0"] as? String

Tests/web3swiftTests/remoteTests/ST20AndSecurityTokenTests.swift

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,15 @@ import Core
1515
// MARK: Works only with network connection
1616
class ST20AndSecurityTokenTests: XCTestCase {
1717

18-
// FIXME: This test fails, it should be fixed in 3.0.1.
19-
// func testERC20TokenCreation() async throws {
20-
// let web3 = await Web3.InfuraKovanWeb3(accessToken: Constants.infuraToken)
21-
// let w3sTokenAddress = EthereumAddress("0x2dD33957C90880bE4Ee9fd5F703110BDA2E579EC")!
22-
// let st20token = ST20.init(web3: web3, provider: web3.provider, address: w3sTokenAddress)
23-
//// try await st20token.readProperties()
24-
// let symbol = try await st20token.symbol()
25-
// let name = try await st20token.name()
26-
// let decimals = try await st20token.decimals()
27-
// // FIXME Reading sometimes messes values.
28-
// // XCTAssertEqual failed: ("Mimi") is not equal to ("MIMI")
29-
// XCTAssertEqual(symbol, "MIMI")
30-
// XCTAssertEqual(name, "Mimi")
31-
// XCTAssertEqual(decimals, 18)
32-
// }
18+
func testERC20TokenCreation() async throws {
19+
let web3 = await Web3.InfuraKovanWeb3(accessToken: Constants.infuraToken)
20+
let w3sTokenAddress = EthereumAddress("0x2dD33957C90880bE4Ee9fd5F703110BDA2E579EC")!
21+
let st20token = ST20.init(web3: web3, provider: web3.provider, address: w3sTokenAddress)
22+
try await st20token.readProperties()
23+
XCTAssertEqual(st20token.symbol(), "MIMI")
24+
XCTAssertEqual(st20token.name(), "Mimi")
25+
XCTAssertEqual(st20token.decimals(), 18)
26+
}
3327

3428
func testST20tokenBalanceAndAllowance() async throws {
3529
let web3 = await Web3.InfuraKovanWeb3(accessToken: Constants.infuraToken)

0 commit comments

Comments
 (0)