Skip to content

Commit 9165f45

Browse files
author
Alex Vlasov
committed
implement graceful fallback for ERC20 tokens that defined name or symbol as "bytes32" instead of "string"
1 parent 02e9d97 commit 9165f45

File tree

2 files changed

+67
-16
lines changed

2 files changed

+67
-16
lines changed

web3swift/ABIv2/Classes/ABIv2Decoding.swift

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -197,16 +197,20 @@ extension ABIv2Decoder {
197197
guard data.count >= pointer + type.memoryUsage else {return (nil, nil)}
198198
let dataSlice = data[pointer ..< pointer + type.memoryUsage]
199199
let bn = BigUInt(dataSlice)
200-
if bn > UINT64_MAX {
200+
if bn > UINT64_MAX || bn >= data.count {
201+
// there are ERC20 contracts that use bytes32 intead of string. Let's be optimistic and return some data
202+
if case .string = type {
203+
let nextElement = pointer + type.memoryUsage
204+
let preambula = BigUInt(32).abiEncode(bits: 256)!
205+
return (preambula + Data(dataSlice), nextElement)
206+
} else if case .dynamicBytes = type {
207+
let nextElement = pointer + type.memoryUsage
208+
let preambula = BigUInt(32).abiEncode(bits: 256)!
209+
return (preambula + Data(dataSlice), nextElement)
210+
}
201211
return (nil, nil)
202-
// let nextElement = pointer + type.memoryUsage
203-
// return (Data(), nextElement)
204-
// return (Data(repeating: 0x00, count: 32), nextElement)
205212
}
206213
let elementPointer = UInt64(bn)
207-
guard elementPointer < data.count else {
208-
return (nil, nil)
209-
}
210214
let elementItself = data[elementPointer ..< UInt64(data.count)]
211215
let nextElement = pointer + type.memoryUsage
212216
// print("Got element itself: \n" + elementItself.toHexString())

web3swiftTests/web3swiftTests.swift

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1896,7 +1896,57 @@ class web3swiftTests: XCTestCase {
18961896
var options = Web3Options.defaultOptions()
18971897
options.from = userAddress
18981898
let parameters = [userAddress] as [AnyObject]
1899-
// let transactionIntermediate = contract?.method("balanceOf", parameters:parameters, options: options)
1899+
let transactionIntermediate = contract?.method("balanceOf", parameters:parameters, options: options)
1900+
let callback = { (res: Result<AnyObject, Web3Error>) -> () in
1901+
switch res {
1902+
case .success(let balanceResult):
1903+
guard let result = balanceResult as? [String: Any] else {
1904+
XCTFail()
1905+
break
1906+
}
1907+
guard let bal = result["balance"] as? BigUInt else {
1908+
XCTFail()
1909+
break
1910+
}
1911+
print("Balance of " + tokenSymbol + " is " + String(bal))
1912+
case .failure(let error):
1913+
print(error)
1914+
XCTFail()
1915+
fatalError()
1916+
}
1917+
OperationQueue.current?.underlyingQueue?.async {
1918+
expected = expected - 1
1919+
// print(String(expected) + " tokens left to update")
1920+
if expected == 0 {
1921+
semaphore.signal()
1922+
}
1923+
}
1924+
1925+
}
1926+
transactionIntermediate?.call(options: options, onBlock: "latest", callback: callback, queue: web3.queue)
1927+
}
1928+
let _ = semaphore.wait(timeout: .distantFuture)
1929+
}
1930+
1931+
func testGetAllTokenNames()
1932+
{
1933+
// let semaphore = DispatchSemaphore(value: 0)
1934+
let url = URL.init(string: "https://raw.githubusercontent.com/kvhnuke/etherwallet/mercury/app/scripts/tokens/ethTokens.json")
1935+
let tokensData = try! Data.init(contentsOf: url!)
1936+
let tokensJSON = try! JSONSerialization.jsonObject(with: tokensData, options: []) as! [[String: Any]]
1937+
let jsonString = "[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_from\",\"type\":\"address\"},{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"balance\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"},{\"name\":\"_extraData\",\"type\":\"bytes\"}],\"name\":\"approveAndCall\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"},{\"name\":\"_spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"remaining\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"_initialAmount\",\"type\":\"uint256\"},{\"name\":\"_tokenName\",\"type\":\"string\"},{\"name\":\"_decimalUnits\",\"type\":\"uint8\"},{\"name\":\"_tokenSymbol\",\"type\":\"string\"}],\"type\":\"constructor\"},{\"payable\":false,\"type\":\"fallback\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_owner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_spender\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},]"
1938+
let web3 = Web3.InfuraMainnetWeb3()
1939+
let userAddress = EthereumAddress("0xc011bf81e3f88931cf331856e45fab6b6450e54c")
1940+
var expected = tokensJSON.count
1941+
print(String(expected) + " tokens to update")
1942+
let semaphore = DispatchSemaphore(value: 0)
1943+
for token in tokensJSON {
1944+
let tokenSymbol = token["symbol"] as! String
1945+
let tokenAddress = EthereumAddress(token["address"] as! String)
1946+
let contract = web3.contract(jsonString, at: tokenAddress, abiVersion: 2)
1947+
XCTAssert(contract != nil, "Failed to create ERC20 contract from ABI")
1948+
var options = Web3Options.defaultOptions()
1949+
options.from = userAddress
19001950
let transactionIntermediate = contract?.method("name", options: options)
19011951
let callback = { (res: Result<AnyObject, Web3Error>) -> () in
19021952
switch res {
@@ -1905,22 +1955,19 @@ class web3swiftTests: XCTestCase {
19051955
XCTFail()
19061956
break
19071957
}
1908-
// guard let bal = result["balance"] as? BigUInt else {
1909-
// XCTFail()
1910-
// break
1911-
// }
1912-
if (result["name"] != nil) {
1913-
print(result["name"])
1958+
guard let bal = result["0"] as? String else {
1959+
XCTFail()
1960+
break
19141961
}
1915-
// print("Balance of " + tokenSymbol + " is " + String(bal))
1962+
print("Name of " + tokenSymbol + " is " + String(bal))
19161963
case .failure(let error):
19171964
print(error)
1965+
print("Name of " + tokenSymbol + " is undefined")
19181966
// XCTFail()
19191967
// fatalError()
19201968
}
19211969
OperationQueue.current?.underlyingQueue?.async {
19221970
expected = expected - 1
1923-
// print(String(expected) + " tokens left to update")
19241971
if expected == 0 {
19251972
semaphore.signal()
19261973
}

0 commit comments

Comments
 (0)