Skip to content

Commit d1c6f12

Browse files
chore: soliditySha3 is now part of ABIEncoder
1 parent e8eb2ff commit d1c6f12

File tree

6 files changed

+107
-112
lines changed

6 files changed

+107
-112
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ on:
66
- master
77
- develop
88
- hotfix
9-
- feat/solidity-sha3
109
paths:
1110
- Packag*.swift
1211
- web3swift.podspec

Sources/web3swift/EthereumABI/ABIEncoding.swift

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ import Foundation
77
import BigInt
88

99
public struct ABIEncoder {
10-
11-
}
12-
13-
extension ABIEncoder {
1410
public static func convertToBigUInt(_ value: AnyObject) -> BigUInt? {
1511
switch value {
1612
case let v as BigUInt:
@@ -386,3 +382,75 @@ extension ABIEncoder {
386382
return nil
387383
}
388384
}
385+
386+
// MARK: - SoliditySHA3 implementation based on web3js
387+
388+
public extension ABIEncoder {
389+
/**
390+
A convenience implementation of web3js [soliditySha3](https://web3js.readthedocs.io/en/v1.2.11/web3-utils.html?highlight=soliditySha3#soliditysha3)
391+
that is based on web3swift [`ABIEncoder`](https://github.com/skywinder/web3swift/blob/develop/Sources/web3swift/EthereumABI/ABIEncoding.swift ).
392+
*/
393+
static func soliditySha3(_ values: [Any]) throws -> Data {
394+
try abiEncode(values).sha3(.keccak256)
395+
}
396+
397+
static func soliditySha3(_ value: Any) throws -> Data {
398+
if let values = value as? [Any] {
399+
return try abiEncode(values).sha3(.keccak256)
400+
} else {
401+
return try abiEncode(value).sha3(.keccak256)
402+
}
403+
}
404+
405+
/// Using AnyObject any number can be represented as Bool and Bool can be represented as number.
406+
/// That will lead to invalid hash output. DO NOT USE THIS FUNCTION.
407+
/// This function will exist to intentionally throw an error that will raise awareness that the hash output can be potentially,
408+
/// and most likely will be, wrong.
409+
/// - Parameter values: to hash
410+
/// - Returns: solidity sha3 hash
411+
static func soliditySha3(_ values: [AnyObject]) throws -> Data {
412+
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.`")
413+
}
414+
415+
/// See docs for ``soliditySha3(_ values: [AnyObject])``
416+
static func soliditySha3(_ value: AnyObject) throws -> Data {
417+
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.`")
418+
}
419+
420+
static func abiEncode(_ values: [Any]) throws -> Data {
421+
return try values.map {
422+
try abiEncode($0)
423+
}.reduce(into: Data()) { partialResult, nextElement in
424+
partialResult.append(nextElement)
425+
}
426+
}
427+
428+
static func abiEncode(_ value: Any) throws -> Data {
429+
if let v = value as? Bool {
430+
return Data(v ? [0b1] : [0b0])
431+
} else if let v = value as? Int {
432+
return ABIEncoder.convertToData(BigInt(exactly: v)?.abiEncode(bits: 256)! as AnyObject)!
433+
} else if let v = value as? Int8 {
434+
return ABIEncoder.convertToData(BigInt(exactly: v)?.abiEncode(bits: 8) as AnyObject)!
435+
} else if let v = value as? Int16 {
436+
return ABIEncoder.convertToData(BigInt(exactly: v)?.abiEncode(bits: 16)! as AnyObject)!
437+
} else if let v = value as? Int32 {
438+
return ABIEncoder.convertToData(BigInt(exactly: v)?.abiEncode(bits: 32)! as AnyObject)!
439+
} else if let v = value as? Int64 {
440+
return ABIEncoder.convertToData(BigInt(exactly: v)?.abiEncode(bits: 64)! as AnyObject)!
441+
} else if let v = value as? UInt {
442+
return ABIEncoder.convertToData(BigUInt(exactly: v)?.abiEncode(bits: 256)! as AnyObject)!
443+
} else if let v = value as? UInt8 {
444+
return ABIEncoder.convertToData(BigUInt(exactly: v)?.abiEncode(bits: 8)! as AnyObject)!
445+
} else if let v = value as? UInt16 {
446+
return ABIEncoder.convertToData(BigUInt(exactly: v)?.abiEncode(bits: 16)! as AnyObject)!
447+
} else if let v = value as? UInt32 {
448+
return ABIEncoder.convertToData(BigUInt(exactly: v)?.abiEncode(bits: 32)! as AnyObject)!
449+
} else if let v = value as? UInt64 {
450+
return ABIEncoder.convertToData(BigUInt(exactly: v)?.abiEncode(bits: 64)! as AnyObject)!
451+
} else if let data = ABIEncoder.convertToData(value as AnyObject) {
452+
return data
453+
}
454+
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)).")
455+
}
456+
}

Sources/web3swift/EthereumABI/SoliditySha3.swift

Lines changed: 0 additions & 78 deletions
This file was deleted.

Tests/web3swiftTests/localTests/SoliditySha3.swift renamed to Tests/web3swiftTests/localTests/ABIEncoderSoliditySha3Test.swift

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// soliditySha3.swift
2+
// ABIEncoderSoliditySha3Test.swift
33
// Tests
44
//
55
// Created by JeneaVranceanu on 28/03/2022.
@@ -10,69 +10,78 @@ import Foundation
1010
import XCTest
1111
@testable import web3swift
1212

13-
class SoliditySha3Test: XCTestCase {
13+
class ABIEncoderSoliditySha3Test: XCTestCase {
1414

1515
func test_soliditySha3() throws {
16-
var hex = soliditySha3(true).toHexString().addHexPrefix()
16+
var hex = try ABIEncoder.soliditySha3(true).toHexString().addHexPrefix()
1717
assert(hex == "0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2")
18-
hex = soliditySha3(-10).toHexString().addHexPrefix()
18+
hex = try ABIEncoder.soliditySha3(-10).toHexString().addHexPrefix()
1919
assert(hex == "0xd6fb717f7e270a360f5093ce6a7a3752183e89c9a9afe5c0cb54b458a304d3d5")
20-
hex = soliditySha3(Data.fromHex("0xfff23243")!).toHexString().addHexPrefix()
20+
hex = try ABIEncoder.soliditySha3(Data.fromHex("0xfff23243")!).toHexString().addHexPrefix()
2121
assert(hex == "0x0ee4597224d3499c72aa0c309b0d0cb80ff3c2439a548c53edb479abfd6927ba")
22-
hex = soliditySha3(UInt(234564535)).toHexString().addHexPrefix()
22+
hex = try ABIEncoder.soliditySha3(UInt(234564535)).toHexString().addHexPrefix()
2323
assert(hex == "0xb2daf574dc6ceac97e984c8a3ffce3c1ec19e81cc6b18aeea67b3ac2666f4e97")
2424

25-
hex = soliditySha3([UInt(234564535), Data.fromHex("0xfff23243")!, true, -10]).toHexString().addHexPrefix()
25+
hex = try ABIEncoder.soliditySha3([UInt(234564535), Data.fromHex("0xfff23243")!, true, -10]).toHexString().addHexPrefix()
2626
assert(hex == "0x3e27a893dc40ef8a7f0841d96639de2f58a132be5ae466d40087a2cfa83b7179")
2727

28-
hex = soliditySha3("Hello!%").toHexString().addHexPrefix()
28+
hex = try ABIEncoder.soliditySha3("Hello!%").toHexString().addHexPrefix()
2929
assert(hex == "0x661136a4267dba9ccdf6bfddb7c00e714de936674c4bdb065a531cf1cb15c7fc")
3030

3131
// This is not JS. '234' (with single or double qoutes) will be a String, not any kind of number.
3232
// From Web3JS docs:> web3.utils.soliditySha3('234'); // auto detects: uint256
3333

34-
hex = soliditySha3(0xea).toHexString().addHexPrefix()
34+
hex = try ABIEncoder.soliditySha3(0xea).toHexString().addHexPrefix()
3535
assert(hex == "0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2")
3636

37-
hex = soliditySha3(234).toHexString().addHexPrefix()
37+
hex = try ABIEncoder.soliditySha3(234).toHexString().addHexPrefix()
3838
assert(hex == "0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2")
3939

40-
hex = soliditySha3(UInt64(234)).toHexString().addHexPrefix()
40+
hex = try ABIEncoder.soliditySha3(UInt64(234)).toHexString().addHexPrefix()
4141
assert(hex == "0x6e48b7f8b342032bfa46a07cf85358feee0efe560d6caa87d342f24cdcd07b0c")
4242

43-
hex = soliditySha3(UInt(234)).toHexString().addHexPrefix()
43+
hex = try ABIEncoder.soliditySha3(UInt(234)).toHexString().addHexPrefix()
4444
assert(hex == "0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2")
4545

46-
hex = soliditySha3("0x407D73d8a49eeb85D32Cf465507dd71d507100c1").toHexString().addHexPrefix()
46+
hex = try ABIEncoder.soliditySha3("0x407D73d8a49eeb85D32Cf465507dd71d507100c1").toHexString().addHexPrefix()
4747
assert(hex == "0x4e8ebbefa452077428f93c9520d3edd60594ff452a29ac7d2ccc11d47f3ab95b")
4848

49-
hex = soliditySha3(Data.fromHex("0x407D73d8a49eeb85D32Cf465507dd71d507100c1")!).toHexString().addHexPrefix()
49+
hex = try ABIEncoder.soliditySha3(Data.fromHex("0x407D73d8a49eeb85D32Cf465507dd71d507100c1")!).toHexString().addHexPrefix()
5050
assert(hex == "0x4e8ebbefa452077428f93c9520d3edd60594ff452a29ac7d2ccc11d47f3ab95b")
5151

52-
hex = soliditySha3(EthereumAddress("0x407D73d8a49eeb85D32Cf465507dd71d507100c1")!).toHexString().addHexPrefix()
52+
hex = try ABIEncoder.soliditySha3(EthereumAddress("0x407D73d8a49eeb85D32Cf465507dd71d507100c1")!).toHexString().addHexPrefix()
5353
assert(hex == "0x4e8ebbefa452077428f93c9520d3edd60594ff452a29ac7d2ccc11d47f3ab95b")
5454

5555

56-
hex = soliditySha3("Hello!%").toHexString().addHexPrefix()
56+
hex = try ABIEncoder.soliditySha3("Hello!%").toHexString().addHexPrefix()
5757
assert(hex == "0x661136a4267dba9ccdf6bfddb7c00e714de936674c4bdb065a531cf1cb15c7fc")
5858

59-
hex = soliditySha3(Int8(-23)).toHexString().addHexPrefix()
59+
hex = try ABIEncoder.soliditySha3(Int8(-23)).toHexString().addHexPrefix()
6060
assert(hex == "0xdc046d75852af4aea44a770057190294068a953828daaaab83800e2d0a8f1f35")
6161

62-
hex = soliditySha3(EthereumAddress("0x85F43D8a49eeB85d32Cf465507DD71d507100C1d")!).toHexString().addHexPrefix()
62+
hex = try ABIEncoder.soliditySha3(EthereumAddress("0x85F43D8a49eeB85d32Cf465507DD71d507100C1d")!).toHexString().addHexPrefix()
6363
assert(hex == "0xe88edd4848fdce08c45ecfafd2fbfdefc020a7eafb8178e94c5feaeec7ac0bb4")
6464

65-
hex = soliditySha3(["Hello!%", Int8(-23), EthereumAddress("0x85F43D8a49eeB85d32Cf465507DD71d507100C1d")!]).toHexString().addHexPrefix()
65+
hex = try ABIEncoder.soliditySha3(["Hello!%", Int8(-23), EthereumAddress("0x85F43D8a49eeB85d32Cf465507DD71d507100C1d")!]).toHexString().addHexPrefix()
6666
assert(hex == "0xa13b31627c1ed7aaded5aecec71baf02fe123797fffd45e662eac8e06fbe4955")
6767
}
6868

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+
6978
/// `[AnyObject]` is not allowed to be used directly as input for `solidtySha3`.
7079
/// `AnyObject` erases type data making it impossible to encode some types correctly,
7180
/// e.g.: Bool can be treated as Int (8/16/32/64) and 0/1 numbers can be treated as Bool.
7281
func test_soliditySha3Fail_1() throws {
7382
var didFail = false
7483
do {
75-
let _ = try soliditySha3([""] as [AnyObject])
84+
let _ = try ABIEncoder.soliditySha3([""] as [AnyObject])
7685
} catch {
7786
didFail = true
7887
}
@@ -85,7 +94,7 @@ class SoliditySha3Test: XCTestCase {
8594
func test_soliditySha3Fail_2() throws {
8695
var didFail = false
8796
do {
88-
let _ = try soliditySha3("" as AnyObject)
97+
let _ = try ABIEncoder.soliditySha3("" as AnyObject)
8998
} catch {
9099
didFail = true
91100
}

Tests/web3swiftTests/remoteTests/RemoteTests.xctestplan

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"testTargets" : [
1515
{
1616
"skippedTests" : [
17+
"ABIEncoderSoliditySha3Test",
1718
"EIP712Tests",
1819
"SoliditySha3Test",
1920
"web3swiftAdvancedABIv2Tests",

0 commit comments

Comments
 (0)