Skip to content

Commit e4b8791

Browse files
Merge branch 'develop' into feature/EIP-1559
2 parents 18a7c3b + c45a71c commit e4b8791

File tree

6 files changed

+237
-12
lines changed

6 files changed

+237
-12
lines changed

.swiftlint.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
excluded:
2+
- Carthage
3+
- Pods
4+
- .build
5+
- Build
6+
- DerivedData
7+
8+
disabled_rules:
9+
- type_name
10+
- identifier_name
11+
- line_length
12+
- multiple_closures_with_trailing_closure
13+
- todo
14+
15+
opt_in_rules:
16+
- weak_delegate
17+
- unused_import
18+
- unneeded_parentheses_in_closure_argument
19+
- trailing_closure
20+
- static_operator
21+
- redundant_nil_coalescing
22+
- override_in_extension
23+
- legacy_objc_type
24+
- implicitly_unwrapped_optional
25+
- force_unwrapping
26+
- empty_string
27+
- closure_body_length
28+
- fallthrough
29+
30+
# force warnings
31+
force_cast: error
32+
force_try: error
33+
34+
custom_rules:
35+
commented_out_code:
36+
included: ".*\\.swift" # regex that defines paths to include during linting. optional.
37+
excluded: ".*Test(s)?\\.swift" # regex that defines paths to exclude during linting. optional
38+
name: "Commented out code" # rule name. optional.
39+
regex: "^\\/\\/\\s*(@|\\.?([a-z]|(\\})))" # matching pattern
40+
capture_group: 0 # number of regex capture group to highlight the rule violation at. optional.
41+
match_kinds: # SyntaxKinds to match. optional.
42+
- comment
43+
message: "No commented code in devel branch allowed." # violation message. optional.
44+
severity: warning # violation severity. optional.

Sources/web3swift/EthereumABI/ABIEncoding.swift

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,3 +383,75 @@ extension ABIEncoder {
383383
return nil
384384
}
385385
}
386+
387+
// MARK: - SoliditySHA3 implementation based on web3js
388+
389+
public extension ABIEncoder {
390+
/**
391+
A convenience implementation of web3js [soliditySha3](https://web3js.readthedocs.io/en/v1.2.11/web3-utils.html?highlight=soliditySha3#soliditysha3)
392+
that is based on web3swift [`ABIEncoder`](https://github.com/skywinder/web3swift/blob/develop/Sources/web3swift/EthereumABI/ABIEncoding.swift ).
393+
*/
394+
static func soliditySha3(_ values: [Any]) throws -> Data {
395+
try abiEncode(values).sha3(.keccak256)
396+
}
397+
398+
static func soliditySha3(_ value: Any) throws -> Data {
399+
if let values = value as? [Any] {
400+
return try abiEncode(values).sha3(.keccak256)
401+
} else {
402+
return try abiEncode(value).sha3(.keccak256)
403+
}
404+
}
405+
406+
/// Using AnyObject any number can be represented as Bool and Bool can be represented as number.
407+
/// That will lead to invalid hash output. DO NOT USE THIS FUNCTION.
408+
/// This function will exist to intentionally throw an error that will raise awareness that the hash output can be potentially,
409+
/// and most likely will be, wrong.
410+
/// - Parameter values: to hash
411+
/// - Returns: solidity sha3 hash
412+
static func soliditySha3(_ values: [AnyObject]) throws -> Data {
413+
throw Web3Error.inputError(desc: "AnyObject creates ambiguity and does not guarantee that the output will be correct. Please, use `soliditySha3(Any) or soliditySha3([Any]) instead.`")
414+
}
415+
416+
/// See docs for ``soliditySha3(_ values: [AnyObject])``
417+
static func soliditySha3(_ value: AnyObject) throws -> Data {
418+
throw Web3Error.inputError(desc: "AnyObject creates ambiguity and does not guarantee that the output will be correct. Please, use `soliditySha3(Any) or soliditySha3([Any]) instead.`")
419+
}
420+
421+
static func abiEncode(_ values: [Any]) throws -> Data {
422+
return try values.map {
423+
try abiEncode($0)
424+
}.reduce(into: Data()) { partialResult, nextElement in
425+
partialResult.append(nextElement)
426+
}
427+
}
428+
429+
static func abiEncode(_ value: Any) throws -> Data {
430+
if let v = value as? Bool {
431+
return Data(v ? [0b1] : [0b0])
432+
} else if let v = value as? Int {
433+
return ABIEncoder.convertToData(BigInt(exactly: v)?.abiEncode(bits: 256)! as AnyObject)!
434+
} else if let v = value as? Int8 {
435+
return ABIEncoder.convertToData(BigInt(exactly: v)?.abiEncode(bits: 8) as AnyObject)!
436+
} else if let v = value as? Int16 {
437+
return ABIEncoder.convertToData(BigInt(exactly: v)?.abiEncode(bits: 16)! as AnyObject)!
438+
} else if let v = value as? Int32 {
439+
return ABIEncoder.convertToData(BigInt(exactly: v)?.abiEncode(bits: 32)! as AnyObject)!
440+
} else if let v = value as? Int64 {
441+
return ABIEncoder.convertToData(BigInt(exactly: v)?.abiEncode(bits: 64)! as AnyObject)!
442+
} else if let v = value as? UInt {
443+
return ABIEncoder.convertToData(BigUInt(exactly: v)?.abiEncode(bits: 256)! as AnyObject)!
444+
} else if let v = value as? UInt8 {
445+
return ABIEncoder.convertToData(BigUInt(exactly: v)?.abiEncode(bits: 8)! as AnyObject)!
446+
} else if let v = value as? UInt16 {
447+
return ABIEncoder.convertToData(BigUInt(exactly: v)?.abiEncode(bits: 16)! as AnyObject)!
448+
} else if let v = value as? UInt32 {
449+
return ABIEncoder.convertToData(BigUInt(exactly: v)?.abiEncode(bits: 32)! as AnyObject)!
450+
} else if let v = value as? UInt64 {
451+
return ABIEncoder.convertToData(BigUInt(exactly: v)?.abiEncode(bits: 64)! as AnyObject)!
452+
} else if let data = ABIEncoder.convertToData(value as AnyObject) {
453+
return data
454+
}
455+
throw Web3Error.inputError(desc: "SoliditySha3: `abiEncode` accepts an Int/UInt (any of 8, 16, 32, 64 bits long), HEX string, Bool, Data, BigInt or BigUInt instance. Given value is of type \(type(of: value)).")
456+
}
457+
}

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

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ public class ERC1155: IERC1155 {
8585
self._hasReadProperties = true
8686
}.wait()
8787
}
88-
89-
func safeTransferFrom(from: EthereumAddress, to: EthereumAddress, originalOwner: EthereumAddress, id: BigUInt, value: BigUInt, data: [UInt8]) throws -> WriteTransaction {
88+
89+
public func safeTransferFrom(from: EthereumAddress, to: EthereumAddress, originalOwner: EthereumAddress, id: BigUInt, value: BigUInt, data: [UInt8]) throws -> WriteTransaction {
9090
let contract = self.contract
9191
var basicOptions = TransactionOptions()
9292
basicOptions.from = from
@@ -95,8 +95,8 @@ public class ERC1155: IERC1155 {
9595
let tx = contract.write("safeTransferFrom", parameters: [originalOwner, to, id, value, data] as [AnyObject], transactionOptions: basicOptions)!
9696
return tx
9797
}
98-
99-
func safeBatchTransferFrom(from: EthereumAddress, to: EthereumAddress, originalOwner: EthereumAddress, ids: [BigUInt], values: [BigUInt], data: [UInt8]) throws -> WriteTransaction {
98+
99+
public func safeBatchTransferFrom(from: EthereumAddress, to: EthereumAddress, originalOwner: EthereumAddress, ids: [BigUInt], values: [BigUInt], data: [UInt8]) throws -> WriteTransaction {
100100
let contract = self.contract
101101
var basicOptions = TransactionOptions()
102102
basicOptions.from = from
@@ -105,17 +105,17 @@ public class ERC1155: IERC1155 {
105105
let tx = contract.write("safeBatchTransferFrom", parameters: [originalOwner, to, ids, values, data] as [AnyObject], transactionOptions: basicOptions)!
106106
return tx
107107
}
108-
109-
func balanceOf(account: EthereumAddress, id: BigUInt) throws -> BigUInt {
108+
109+
public func balanceOf(account: EthereumAddress, id: BigUInt) throws -> BigUInt {
110110
let contract = self.contract
111111
var transactionOptions = TransactionOptions()
112112
transactionOptions.callOnBlock = .latest
113113
let result = try contract.read("balanceOf", parameters: [account, id] as [AnyObject], extraData: Data(), transactionOptions: self.transactionOptions)!.call(transactionOptions: transactionOptions)
114114
guard let res = result["0"] as? BigUInt else {throw Web3Error.processingError(desc: "Failed to get result of expected type from the Ethereum node")}
115115
return res
116116
}
117-
118-
func setApprovalForAll(from: EthereumAddress, operator user: EthereumAddress, approved: Bool, scope: Data) throws -> WriteTransaction {
117+
118+
public func setApprovalForAll(from: EthereumAddress, operator user: EthereumAddress, approved: Bool, scope: Data) throws -> WriteTransaction {
119119
let contract = self.contract
120120
var basicOptions = TransactionOptions()
121121
basicOptions.from = from
@@ -124,17 +124,17 @@ public class ERC1155: IERC1155 {
124124
let tx = contract.write("setApprovalForAll", parameters: [user, approved, scope] as [AnyObject], transactionOptions: basicOptions)!
125125
return tx
126126
}
127-
128-
func isApprovedForAll(owner: EthereumAddress, operator user: EthereumAddress, scope: Data) throws -> Bool {
127+
128+
public func isApprovedForAll(owner: EthereumAddress, operator user: EthereumAddress, scope: Data) throws -> Bool {
129129
let contract = self.contract
130130
var basicOptions = TransactionOptions()
131131
basicOptions.callOnBlock = .latest
132132
let result = try contract.read("isApprovedForAll", parameters: [owner, user, scope] as [AnyObject], extraData: Data(), transactionOptions: self.transactionOptions)!.call(transactionOptions: transactionOptions)
133133
guard let res = result["0"] as? Bool else {throw Web3Error.processingError(desc: "Failed to get result of expected type from the Ethereum node")}
134134
return res
135135
}
136-
137-
func supportsInterface(interfaceID: String) throws -> Bool {
136+
137+
public func supportsInterface(interfaceID: String) throws -> Bool {
138138
let contract = self.contract
139139
var transactionOptions = TransactionOptions()
140140
transactionOptions.callOnBlock = .latest
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
//
2+
// ABIEncoderSoliditySha3Test.swift
3+
// Tests
4+
//
5+
// Created by JeneaVranceanu on 28/03/2022.
6+
// Copyright © 2022 web3swift. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import XCTest
11+
@testable import web3swift
12+
13+
class ABIEncoderSoliditySha3Test: XCTestCase {
14+
15+
func test_soliditySha3() throws {
16+
var hex = try ABIEncoder.soliditySha3(true).toHexString().addHexPrefix()
17+
assert(hex == "0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2")
18+
hex = try ABIEncoder.soliditySha3(-10).toHexString().addHexPrefix()
19+
assert(hex == "0xd6fb717f7e270a360f5093ce6a7a3752183e89c9a9afe5c0cb54b458a304d3d5")
20+
hex = try ABIEncoder.soliditySha3(Data.fromHex("0xfff23243")!).toHexString().addHexPrefix()
21+
assert(hex == "0x0ee4597224d3499c72aa0c309b0d0cb80ff3c2439a548c53edb479abfd6927ba")
22+
hex = try ABIEncoder.soliditySha3(UInt(234564535)).toHexString().addHexPrefix()
23+
assert(hex == "0xb2daf574dc6ceac97e984c8a3ffce3c1ec19e81cc6b18aeea67b3ac2666f4e97")
24+
25+
hex = try ABIEncoder.soliditySha3([UInt(234564535), Data.fromHex("0xfff23243")!, true, -10]).toHexString().addHexPrefix()
26+
assert(hex == "0x3e27a893dc40ef8a7f0841d96639de2f58a132be5ae466d40087a2cfa83b7179")
27+
28+
hex = try ABIEncoder.soliditySha3("Hello!%").toHexString().addHexPrefix()
29+
assert(hex == "0x661136a4267dba9ccdf6bfddb7c00e714de936674c4bdb065a531cf1cb15c7fc")
30+
31+
// This is not JS. '234' (with single or double qoutes) will be a String, not any kind of number.
32+
// From Web3JS docs:> web3.utils.soliditySha3('234'); // auto detects: uint256
33+
34+
hex = try ABIEncoder.soliditySha3(0xea).toHexString().addHexPrefix()
35+
assert(hex == "0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2")
36+
37+
hex = try ABIEncoder.soliditySha3(234).toHexString().addHexPrefix()
38+
assert(hex == "0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2")
39+
40+
hex = try ABIEncoder.soliditySha3(UInt64(234)).toHexString().addHexPrefix()
41+
assert(hex == "0x6e48b7f8b342032bfa46a07cf85358feee0efe560d6caa87d342f24cdcd07b0c")
42+
43+
hex = try ABIEncoder.soliditySha3(UInt(234)).toHexString().addHexPrefix()
44+
assert(hex == "0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2")
45+
46+
hex = try ABIEncoder.soliditySha3("0x407D73d8a49eeb85D32Cf465507dd71d507100c1").toHexString().addHexPrefix()
47+
assert(hex == "0x4e8ebbefa452077428f93c9520d3edd60594ff452a29ac7d2ccc11d47f3ab95b")
48+
49+
hex = try ABIEncoder.soliditySha3(Data.fromHex("0x407D73d8a49eeb85D32Cf465507dd71d507100c1")!).toHexString().addHexPrefix()
50+
assert(hex == "0x4e8ebbefa452077428f93c9520d3edd60594ff452a29ac7d2ccc11d47f3ab95b")
51+
52+
hex = try ABIEncoder.soliditySha3(EthereumAddress("0x407D73d8a49eeb85D32Cf465507dd71d507100c1")!).toHexString().addHexPrefix()
53+
assert(hex == "0x4e8ebbefa452077428f93c9520d3edd60594ff452a29ac7d2ccc11d47f3ab95b")
54+
55+
56+
hex = try ABIEncoder.soliditySha3("Hello!%").toHexString().addHexPrefix()
57+
assert(hex == "0x661136a4267dba9ccdf6bfddb7c00e714de936674c4bdb065a531cf1cb15c7fc")
58+
59+
hex = try ABIEncoder.soliditySha3(Int8(-23)).toHexString().addHexPrefix()
60+
assert(hex == "0xdc046d75852af4aea44a770057190294068a953828daaaab83800e2d0a8f1f35")
61+
62+
hex = try ABIEncoder.soliditySha3(EthereumAddress("0x85F43D8a49eeB85d32Cf465507DD71d507100C1d")!).toHexString().addHexPrefix()
63+
assert(hex == "0xe88edd4848fdce08c45ecfafd2fbfdefc020a7eafb8178e94c5feaeec7ac0bb4")
64+
65+
hex = try ABIEncoder.soliditySha3(["Hello!%", Int8(-23), EthereumAddress("0x85F43D8a49eeB85d32Cf465507DD71d507100C1d")!]).toHexString().addHexPrefix()
66+
assert(hex == "0xa13b31627c1ed7aaded5aecec71baf02fe123797fffd45e662eac8e06fbe4955")
67+
}
68+
69+
func test_soliditySha3Fail_FloatDouble() throws {
70+
assert((try? ABIEncoder.soliditySha3(Float(1))) == nil)
71+
assert((try? ABIEncoder.soliditySha3(Double(1))) == nil)
72+
assert((try? ABIEncoder.soliditySha3(CGFloat(1))) == nil)
73+
assert((try? ABIEncoder.soliditySha3([Float(1)])) == nil)
74+
assert((try? ABIEncoder.soliditySha3([Double(1)])) == nil)
75+
assert((try? ABIEncoder.soliditySha3([CGFloat(1)])) == nil)
76+
}
77+
78+
/// `[AnyObject]` is not allowed to be used directly as input for `solidtySha3`.
79+
/// `AnyObject` erases type data making it impossible to encode some types correctly,
80+
/// e.g.: Bool can be treated as Int (8/16/32/64) and 0/1 numbers can be treated as Bool.
81+
func test_soliditySha3Fail_1() throws {
82+
var didFail = false
83+
do {
84+
let _ = try ABIEncoder.soliditySha3([""] as [AnyObject])
85+
} catch {
86+
didFail = true
87+
}
88+
XCTAssertTrue(didFail)
89+
}
90+
91+
/// `AnyObject` is not allowed to be used directly as input for `solidtySha3`.
92+
/// `AnyObject` erases type data making it impossible to encode some types correctly,
93+
/// e.g.: Bool can be treated as Int (8/16/32/64) and 0/1 numbers can be treated as Bool.
94+
func test_soliditySha3Fail_2() throws {
95+
var didFail = false
96+
do {
97+
let _ = try ABIEncoder.soliditySha3("" as AnyObject)
98+
} catch {
99+
didFail = true
100+
}
101+
XCTAssertTrue(didFail)
102+
}
103+
}

Tests/web3swiftTests/remoteTests/RemoteTests.xctestplan

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
{
1616
"skippedTests" : [
1717
"EIP1559Tests",
18+
"ABIEncoderSoliditySha3Test",
1819
"DataConversionTests",
1920
"EIP712Tests",
21+
"SoliditySha3Test",
2022
"web3swiftAdvancedABIv2Tests",
2123
"web3swiftBasicLocalNodeTests",
2224
"web3swiftEIP67Tests",

web3swift.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@
194194
5CF7E8BC276B79380009900F /* web3swiftWebsocketTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF7E8B7276B79380009900F /* web3swiftWebsocketTests.swift */; };
195195
604FA4FF27ECBDC80021108F /* DataConversionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 604FA4FE27ECBDC80021108F /* DataConversionTests.swift */; };
196196
CB50A52827060BD600D7E39B /* EIP712Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB50A52727060BD600D7E39B /* EIP712Tests.swift */; };
197+
D6A3D9B827F1E785009E3BCF /* ABIEncoderSoliditySha3Test.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3D9B727F1E785009E3BCF /* ABIEncoderSoliditySha3Test.swift */; };
197198
E22A911F241ED71A00EC1021 /* browser.min.js in Resources */ = {isa = PBXBuildFile; fileRef = E22A911E241ED71A00EC1021 /* browser.min.js */; };
198199
E2B76710241ED479007EBFE3 /* browser.js in Resources */ = {isa = PBXBuildFile; fileRef = E2B7670F241ED479007EBFE3 /* browser.js */; };
199200
E2EDC5EA241EDE3600410EA6 /* BrowserViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2EDC5E9241EDE3600410EA6 /* BrowserViewController.swift */; platformFilter = ios; };
@@ -415,6 +416,7 @@
415416
604FA4FE27ECBDC80021108F /* DataConversionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataConversionTests.swift; sourceTree = "<group>"; };
416417
CB50A52727060BD600D7E39B /* EIP712Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EIP712Tests.swift; sourceTree = "<group>"; };
417418
CB50A52927060C5300D7E39B /* EIP712.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EIP712.swift; sourceTree = "<group>"; };
419+
D6A3D9B727F1E785009E3BCF /* ABIEncoderSoliditySha3Test.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ABIEncoderSoliditySha3Test.swift; sourceTree = "<group>"; };
418420
E22A911E241ED71A00EC1021 /* browser.min.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = browser.min.js; sourceTree = "<group>"; };
419421
E2B7670F241ED479007EBFE3 /* browser.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = browser.js; sourceTree = "<group>"; };
420422
E2EDC5E9241EDE3600410EA6 /* BrowserViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserViewController.swift; sourceTree = "<group>"; };
@@ -1010,6 +1012,7 @@
10101012
5CF7E893276B79270009900F /* web3swiftTransactionsTests.swift */,
10111013
5CF7E89E276B79280009900F /* web3swiftUserCases.swift */,
10121014
CB50A52727060BD600D7E39B /* EIP712Tests.swift */,
1015+
D6A3D9B727F1E785009E3BCF /* ABIEncoderSoliditySha3Test.swift */,
10131016
5CDEF973275A74670004A2F2 /* LocalTests.xctestplan */,
10141017
);
10151018
path = localTests;
@@ -1408,6 +1411,7 @@
14081411
5CF7E8AC276B792A0009900F /* web3swiftObjCTests.swift in Sources */,
14091412
5CF7E8A3276B792A0009900F /* web3swiftPersonalSignatureTests.swift in Sources */,
14101413
5CF7E8BB276B79380009900F /* web3swiftENSTests.swift in Sources */,
1414+
D6A3D9B827F1E785009E3BCF /* ABIEncoderSoliditySha3Test.swift in Sources */,
14111415
);
14121416
runOnlyForDeploymentPostprocessing = 0;
14131417
};

0 commit comments

Comments
 (0)